1、创建win32 dll工程
用VC++6开发win32 dll是非常方便的,它提供了简单通用的向导可以为我们生成一个程序框架,而我们只需在在框架里边加入自己的函数就可以了,现在就看一下如何生成win32 dll程序。
首先,通过VC++6的AppWizard,创建一个“Win32 Dynamic-Link Library”类型的工程MyDll。
然后,在创建类型选择框中,选择“An empty Dll project”创建一个空的dll工程。
2、添加代码
此时,我们就拥有了一个Win32dll的工程框架,但这个工程中什么都没有,下一步就是在工程中添加代码了。同样使用AppWizard创建一个新的cpp文件dllFile.cpp。当然添加文件有多种途径,但尽量使用程序向导,因为它是学习新方法的最好方法。在文件中加入如下代码:
#include
BOOL __stdcall DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}
Windows.h是编写windows程序最基本的头文件,它里面包含了常用的宏及函数的定义,所以至少得包含它。而DllMain函数是win32dll的缺省入口函数,每一个dll中都要有入口函数。通过入口函数其它程序在调用(LoadLibrary)和释放(FreeLibrary)dll时对dll初始化和释放空间。如果在调用此dll时需要分配额外空间,则在此函数中必须同时包含分配和释放空间的代码,函数中ul_reason_for_cal参数表示调用此函数的目的。可以在DllMain中加入switch表示:
switch(ul_reason_for_call)
{
case DLL_PROCESS_ATTACH: //被LoadLibrary调用
break;
case DLL_THREAD_ATTACH: //创建新线程
break;
case DLL_THREAD_DETACH: //线程退出
break;
case DLL_PROCESS_DETACH: //被FreeLibrary调用
break;
}
DllMain的一个参数hModule表示此dll的句柄,在程序内部也可以通过GetModuleFileName函数获得它的值。lpReserved参数是个保留参数。DllMain的返回值表示调用是否成功。至于关键字__stdcall,它是dll函数的一种定义约定,我们将在后面详细讲它。到此为止一个最简单的win32dll程序就写好了,如果你觉得至少应包含一个自定义的函数,那我们再加入如下函数,作用是显示提示对话框的函数:
void __stdcall ShowMessage(const LPCTSTR &lpszMsg)
{
MessageBox(NULL,lpszMsg,"MyDll",MB_OK);
}
我们定义ShowMessage函数为标准调用方式(__stdcall),在以后的dll函数的编写中,我们将都用标准调用方式。
接下来就需要导出ShowMessage函数了,只有导出的函数才能被其它程序调用,为了避免编译器修饰函数,我们使用.DEF文件导出函数,工程中添加一个DllFile.def的文本文件,加入如下代码:
LIBRARY "MyDll"
EXPORTS
ShowMessage
3.编译链接dll
在VC++中可以通过设置编译选项来生成两种程序版本,一个是debug版本,另一个是Release版本。其中debug版本中包含了程序的调试信息,生成的dll文件一般比较大,但它可以进行跟踪调试,查看代码的执行过程。虽然dll不能单独执行,但debug版本的dll在其它程序调用时同样可以跟踪其中的代码。而release版本中没有了附加的调试信息,文件变小,程序执行起来也会快些。至于快多少,这就看你的整个程序的代码的多少和复杂度了。如果是最终发布给用户的程序,那就最好使用了release版本。对于刚刚编写好的dll代码,最好编译成debug版本,这样有助于调试错误。vc++编译器默认设置的是debug版本。
你可以通过菜单project->setting设置每个版本的编译选项,通过这些选项,你能够控制编译过程,优化程序等等,通常最常用的设置就是让编译器直接把生成的程序放到一个特定的目录里,这样一方面省去在众多的生成文件中寻找的你的程序文件的麻烦,另一方面,如果你编写的几个程序之间有调用关系的话,那么建议在编译时就让它们生成到同一个目录中或已确定的目录中。下面我们把dll的输出目录设置为bin。
不过要注意的是,如果你debug和release版本的输出目录设置为同一个目录,那你就得注意别把这两种版本的生成文件混淆了。
这一切设置好了,现在你可以编译程序了,如果顺利,那么一个完整的dll程序“MyDll.dll”就生成了。
使用Visual C++ .NET创建dll
创建win32 dll工程
使用Visual C++ .NET创建dll的过程和Visual C++ 6.0差不多,只是一些命令选项的名称和位置发生了变化,比如要创建一个空win32 dll工程,就需选中win32项目类型中的Win32 项目:
在接下来的应用程序向导窗口中,选择“应用程序设置”页中的“DLL”,并选中附加选项中的“空项目”就可以了。
添加代码:
再以后的添加文件和代码的过程和前面的Visual C++6.0中的完全一样,完全可以按照前面的方法,编写代码。
至于输出目录的设置,你需要执行菜单项目->属性命令,在项目属性中设置输出目录:
编译链接
就像预料中的那样一切ok,接下来我们要通过一个例子讲解如何在应用程序中调用dll并调试执行dll的导出函数。
1、 在应用程序中调用dll
前面我们已经编写好了一个dll,接下来我们要看看如何在程序中调用此dll,其实调用方法前面都讲过了,现在如何通过导入函数调用dll函数。
首先,我们创建一个mfc的对话框应用程序,并通过资源编辑器在窗口上添加一个按钮,然后在程序文件中添加如下声明:
__declspec(dllimport) void __stdcall ShowMessage(const LPCTSTR &lpszMsg);
并在按钮的消息响应函数中使用ShowMessage函数:
ShowMessage(_T("success of calling dll!"));
注意不要忘记了,除了要把MyDll.dll文件复制到生成的应用程序目录下外,还要把Mydll的导入库文件复制到工程目录下,并把它加入到visaul c++的链接库中:
最后编译执行,点击窗口上刚刚添加的按钮,其效果可能就像下图:
当然,也可以不使用导入函数的方法,而直接使用显示链接的方法调用dll。那么你可以通过如下方法调用MyDll.dll,执行效果和通过导入函数的方法无异,并且不需要导入库的支持:
typedef void (__stdcall *dllfunc)(const LPCTSTR &lpszMsg) ;
HINSTANCE hDll = LoadLibrary(_T("mydll.dll"));
if (NULL != hDll)
{
dllfunc ShowMessage = (dllfunc)GetProcAddress(hDll,_T("ShowMessage"));
if (NULL != ShowMessage)
{
ShowMessage(_T("success of calling dll!"));
}
}
FreeLibrary(hDll);
1、 调试动态链接库
其实上面所讲的,都是假设编写的dll没有任何错误,但实际中,dll中的代码出现意外错误也是常有的事,毕竟它和应用程序一样都是由人编写的,尤其当编写的dll较大时,更需要有办法跟踪调试程序了。但是由于dll不能被单独执行,当我们直接按F5执行时会出现如下界面:
不能执行!在可执行应用程序中又无法对dll中的代码设断点,那我们如何调试呢?其实利用前面我们说的设置输出文件目录的方法,再结合设置可执行文件就可以实现在dll工程中调试dll的目的,而且是一劳永逸的。
1、 首先,设置dll工程和应用程序的输出目录设置到同一目录中。
2、 然后设置dll工程中的可执行文件路径和应用程序中路径一样,比如我们把dll和应用程序的可以执行文件都输出到工程外的“bin”目录中,就如下所示:
1、 最后,你在dll工程中按F5命令执行这个应用程序,这时你就可以像调试应用程序那样给你的dll代码设置断点并调试代码了。
我建议你测试和调试dll代码时,同时开两个工程,一个是dll的,用于设置断点,察看运行过程等等。另一个是调用此dll的应用程序,在这里随时编写测试代码,就这么简单。