想一想仍是把这个记录下吧,虽然不难,但因为平时写得很少,总是搞忘了。windows
一、咱们来编写一个简单的DLL程序。函数
首先,咱们来看下入口函数DllMain()。DllMain()有3个参数:工具
(1)hModule:DLL模块的句柄。spa
(2)ul_reason_for_call:DllMain函数被调用的缘由。其取值有4种,分别是DLL_PROCESS_ATTACH(当DLL被某进程加载时DllMain被调用)、DLL_PROCESS_DETACH(当DLL被某进程卸载时DllMain被调用)、DLL_THREAD_ATTACH(进程中有线程被建立时DllMain被调用)、DLL_THREAD_DETACH(进程中有线程结束时DllMain被调用)。线程
(3)lpReserved:保留项。code
函数前面的APIENTRY是一个宏,定义以下:blog
#define APIENTRY WINAPI
WINAPI也是一个宏,表示一种函数调用约定。进程
咱们须要对DllMain()进行一下填充,加个switch。后面详见例子。咱们还须要为之添加一个简单的导出函数。该函数定义以下:文档
extern "C" __declspec(dllexport) VOID MsgBox(char *szMsg);
extern "C"表示该函数以C方式导出。字符串
其实现以下:
VOID MsgBox(char *szMsg){ char szModuleName[MAX_PATH]={0}; GetModuleFileName(NULL,szModuleName,MAX_PATH); MessageBox(NULL,szMsg,szModuleName,MB_OK); }
运行函数后弹出一个对话框,显示一个字符串,并显示其所在的进程的进程名。咱们分别在DLL_PROCESS_ATTACH和DLL_PROCESS_DETACH下加一个对该函数的调用。
以下:
#include <windows.h> extern "C" __declspec(dllexport) VOID MsgBox(char *szMsg); BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved){ switch(ul_reason_for_call){ case DLL_PROCESS_ATTACH: MsgBox("DLL_PROCESS_ATTACH"); break; case DLL_PROCESS_DETACH: MsgBox("DLL_PROCESS_DETACH"); break; case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: break; } return TRUE; } VOID MsgBox(char *szMsg){ char szModuleName[MAX_PATH]={0}; GetModuleFileName(NULL,szModuleName,MAX_PATH); MessageBox(NULL,szMsg,szModuleName,MB_OK); }
编译该代码,会生成两个好玩的文件:6_10_4.dll和6_10_4.lib。前面是DLL文件,后面是库文件。
二、静态调用:
建立一个控制台程序,创建一个cpp文件,添加代码以下:
#include <windows.h> extern "C" VOID MsgBox(char *szMsg); #pragma comment(lib,"6_10_4") int main(int argc,char* argv[]){ MsgBox("hello first dll!"); return 0; }
对该代码直接编译连接,会报错:没法找到6_10_4.lib文件。把这个文件复制到这个cpp目录下,运行会报错,提示没有找到6_10_4.dll文件。一样,也须要把该文件置于cpp目录下。至此,没有问题了,运行依次弹出三个对话框:
三、动态调用:
静态调用就是在编译程序时便把dll信息写入程序里,而动态调用则是在运行时导入dll信息。
控制台程序cpp代码以下:
#include <windows.h> typedef VOID (*PFUNMSG)(char *); int main(int argc,char* argv[]){ HMODULE hModule=LoadLibrary("6_10_4.dll"); if(hModule==NULL){ MessageBox(NULL,"6_10_4.dll文件不存在","DLL加载失败",MB_OK); return -1; } PFUNMSG pFunMsg=(PFUNMSG)GetProcAddress(hModule,"MsgBox"); pFunMsg("hello first dll!"); return 0; }
这里不要求dll文件必定在cpp目录下,在LoadLibrary函数里写入dll的路径便可。运行结果与前面的同样。
四、其余
对于未文档化的API,或是没有提供头文件的API,咱们能够利用LoadLibrary()和GetProcAddress()这两个API函数来实现对前面那些API的调用。
LoadLibrary():
HMODULE LoadLibrary( LPCTSTR lpFileName //file name of module );
该函数只有一个参数,即要加载的DLL文件的路径。
GetProcAddress():
GetProcAddress( HMODULE hModule, //handle to DLL module LPCSTR lpProcName //function name );
该函数有两个参数,hModule是模块的句柄,lpProcName指定要获取函数地址的函数名称。
另外说下,vc6自带的工具"Depends"挺好用的,能够查看dll程序的导出函数等。它在:菜单“开始”->“程序”->“Microsoft Visual Studio 6.0”->“Microsoft Visual Studio 6.0 Tools”->“Depends”。