我酷下载
您的位置:首页-> 技术文章-> -> Visual C++-> VC++动态链接库编程之非MFC DLL(2)

阅读排行




VC++动态链接库编程之非MFC DLL(2)

作者未知 来源未知 加入时间:2006-3-24 人气:134
    4.4 DllMain函数

Windows在加载DLL的时候,需要一个入口函数,就如同控制台或DOS程序需要main函数、WIN32程序需要WinMain函数一样。在前面的例子中,DLL并没有提供DllMain函数,应用工程也能成功引用DLL,这是因为Windows在找不到DllMain的时候,系统会从其它运行库中引入一个不做任何操作的缺省DllMain函数版本,并不意味着DLL可以放弃DllMain函数。

根据编写规范,Windows必须查找并执行DLL里的DllMain函数作为加载DLL的依据,它使得DLL得以保留在内存里。这个函数并不属于导出函数,而是DLL的内部函数。这意味着不能直接在应用工程中引用DllMain函数,DllMain是自动被调用的。

我们来看一个DllMain函数的例子(单击此处下载本工程)。

BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)

{

switch (ul_reason_for_call)

{

case DLL_PROCESS_ATTACH:

printf("\nprocess attach of dll");

break;

case DLL_THREAD_ATTACH:

printf("\nthread attach of dll");

break;

case DLL_THREAD_DETACH:

printf("\nthread detach of dll");

break;

case DLL_PROCESS_DETACH:

printf("\nprocess detach of dll");

break;

}

return TRUE;

}

DllMain函数在DLL被加载和卸载时被调用,在单个线程启动和终止时,DLLMain函数也被调用,ul_reason_for_call指明了被调用的原因。原因共有4种,即PROCESS_ATTACH、PROCESS_DETACH、THREAD_ATTACH和THREAD_DETACH,以switch语句列出。

来仔细解读一下DllMain的函数头BOOL APIENTRY DllMain( HANDLE hModule, WORD ul_reason_for_call, LPVOID lpReserved )。

APIENTRY被定义为__stdcall,它意味着这个函数以标准Pascal的方式进行调用,也就是WINAPI方式;

进程中的每个DLL模块被全局唯一的32字节的HINSTANCE句柄标识,只有在特定的进程内部有效,句柄代表了DLL模块在进程虚拟空间中的起始地址。在Win32中,HINSTANCE和HMODULE的值是相同的,这两种类型可以替换使用,这就是函数参数hModule的来历。

执行下列代码:

hDll = LoadLibrary("..\\Debug\\dllTest.dll");

if (hDll != NULL)

{

addFun = (lpAddFun)GetProcAddress(hDll, MAKEINTRESOURCE(1));

//MAKEINTRESOURCE直接使用导出文件中的序号

if (addFun != NULL)

{

int result = addFun(2, 3);

printf("\ncall add in dll:%d", result);

}

FreeLibrary(hDll);

}

我们看到输出顺序为:

process attach of dll

call add in dll:5

process detach of dll

这一输出顺序验证了DllMain被调用的时机。

代码中的GetProcAddress ( hDll, MAKEINTRESOURCE ( 1 ) )值得留意,它直接通过.def文件中为add函数指定的顺序号访问add函数,具体体现在MAKEINTRESOURCE ( 1 ),MAKEINTRESOURCE是一个通过序号获取函数名的宏,定义为(节选自winuser.h):

#define MAKEINTRESOURCEA(i) (LPSTR)((DWORD)((WORD)(i)))

#define MAKEINTRESOURCEW(i) (LPWSTR)((DWORD)((WORD)(i)))

#ifdef UNICODE

#define MAKEINTRESOURCE MAKEINTRESOURCEW

#else

#define MAKEINTRESOURCE MAKEINTRESOURCEA

4.5 __stdcall约定

如果通过VC++编写的DLL欲被其他语言编写的程序调用,应将函数的调用方式声明为__stdcall方式,WINAPI都采用这种方式,而C/C++缺省的调用方式却为__cdecl。__stdcall方式与__cdecl对函数名最终生成符号的方式不同。若采用C编译方式(在C++中需将函数声明为extern "C"),__stdcall调用约定在输出函数名前面加下划线,后面加“@”符号和参数的字节数,形如_functionname@number;而__cdecl调用约定仅在输出函数名前面加下划线,形如_functionname。

Windows编程中常见的几种函数类型声明宏都是与__stdcall和__cdecl有关的(节选自windef.h):

#define CALLBACK __stdcall //这就是传说中的回调函数

#define WINAPI __stdcall //这就是传说中的WINAPI

#define WINAPIV __cdecl

#define APIENTRY WINAPI //DllMain的入口就在这里

#define APIPRIVATE __stdcall

#define PASCAL __stdcall

在lib.h中,应这样声明add函数:

int __stdcall add(int x, int y);

在应用工程中函数指针类型应定义为:

typedef int(__stdcall *lpAddFun)(int, int);

若在lib.h中将函数声明为__stdcall调用,而应用工程中仍使用typedef int (* lpAddFun)(int,int),运行时将发生错误(因为类型不匹配,在应用工程中仍然是缺省的__cdecl调用),弹出如图7所示的对话框。

图7 调用约定不匹配时的运行错误

图8中的那段话实际上已经给出了错误的原因,即“This is usually a result of…”。




相关文章
  • VC++动态链接库编程之非MFC DLL(3)
  • VC++动态链接库编程之非MFC DLL(1)
  • 相关软件

    联系我们 广告服务 友情链接 版权说明 软件发布 下载帮助

    CopyRight
    2005-2016 www.5qcn.net All Rights Reserved 版权所有 【我酷】下载