之前在文章中提到 过DLL的隐式链接,现在介绍以下DLL的显式载入。DLL显式载入与隐式链接不同的是,我们不需要包含包含DLL导出函数和变量的头文件,不需要显式指定lib文件,比如以下两行代码就可以去掉了。
#include "userdefine.h"
#pragma comment(lib,"userdefine.lib")
上代码:
//userdefine.h
#ifdef USERDEFINE_EXPORTS
#define USERDEFINE_API extern "C" __declspec(dllexport)
#else
#define USERDEFINE_API extern "C" __declspec(dllimport)
#endif
USERDEFINE_API int fnuserdefine(void);
USERDEFINE_API int g_nResult;//尽量避免导出变量
//userdefine.cpp
#define USERDEFINE_API extern "C" __declspec(dllexport)
#include "userdefine.h"
#include <tchar.h>
int g_nResult;
int fnuserdefine(void)
{
g_nResult = 256;
_tprintf(_T("DLL test example\n"));
return g_nResult;
}
//FirstThread
CRITICAL_SECTION g_cs;
UINT WINAPI FirstThread(PVOID pvParam)
{
EnterCriticalSection(&g_cs);
g_num = 0;
for (int i = 0; i <= COUNT; i++)
{
g_num += i;
}
_tprintf(_T("FirstThread g_num=%d TheadID= %d\n"), g_num, GetCurrentThreadId());
LeaveCriticalSection(&g_cs);
return g_num;
}
//dllmain.cpp
#define WIN32_LEAN_AND_MEAN // 从 Windows 头中排除极少使用的资料
// Windows 头文件:
#include <windows.h>
#include <tchar.h>
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
_tprintf(_T("DLL_PROCESS_ATTACH\n"));
break;
case DLL_THREAD_ATTACH:
_tprintf(_T("DLL_THREAD_ATTACH\n"));
break;
case DLL_THREAD_DETACH:
_tprintf(_T("DLL_THREAD_DETACH\n"));
break;
case DLL_PROCESS_DETACH:
_tprintf(_T("DLL_PROCESS_DETACH\n"));
break;
default:
break;
}
return TRUE;
}
//main.cpp
typedef int (CALLBACK *PFN_USERDEFINE)(void);
int _tmain( int argc, TCHAR* argv[] )
{
HMODULE hmodule = GetModuleHandle(_T("userdefine"));
if (NULL == hmodule)
{
hmodule = LoadLibrary(_T("userdefine"));
}
if ( NULL == hmodule)
{
_tprintf(_T("Can not find dynamic library\n"));
return -1;
}
PFN_USERDEFINE pfnUserDefine = (PFN_USERDEFINE)GetProcAddress(hmodule, "fnuserdefine");
if ( NULL != pfnUserDefine )
{
int nRet = pfnUserDefine();
_tprintf(_T("nRet=%d\n"), nRet);
}
InitializeCriticalSection(&g_cs);//初始化关键段
HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, FirstThread, NULL, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
DeleteCriticalSection(&g_cs);
FreeLibrary(hmodule);
dllmain函数是dll入口点处理函数,可以处理进程(或线程)加载dll的时候或者进程(或线程)退出时,dll需要做的工作。
GetModuleHandle函数是判断userdefine.dll有没有被加载,如果没有被加载,那么调用LoadLibrary函数加载dll。我们定义一个PFN_USERDEFINE函数指针,该函数指针的原型就是我们需要调用dll函数的原型,如果成功找到了这个函数,那么我们调用该函数获取其返回值。创建一个线程运行FirstThread函数,线程创建退出时,DLL会得到通知并打印相应信息,最后通过FreeLibrary函数从该进程的地址空间间取消对该Dll的映射。
运行结果如下: