DLL用法
2016-01-21
DLL用法的介绍
1、 DLL的概念 DLL(Dynamic Linkable Library),动态链接库,可以向程序提供一些函数、变量或类。这些可以直接拿来使用。
静态链接库与动态链接库的区别: (1)静态链接库与动态链接库都是共享代码的方式。静态链接库把最后的指令都包含在最终生成的EXE文件中了;动态链接库不必被包含在最终EXE文件中,EXE文件执行时可以“动态”地引用和卸载这个与EXE独立的DLL文件。
(2) 静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。 动态链接库的分类:Visual C++支持三种DLL,它们分别是Non-MFC DLL(非MFC动态库)、MFC Regular DLL(MFC规则DLL)、MFC Extension DLL(MFC扩展DLL)。非MFC动态库不采用MFC类库结构,其导出函数为标准的C接口,能被非MFC或MFC编写的应用程序所调用;MFC规则DLL 包含一个继承自CWinApp的类,但其无消息循环;MFC扩展DLL采用MFC的动态链接版本创建,它只能被用MFC类库所编写的应用程序所调用。

2016-01-21
2、 创建一个DLL
2.1 非MFC的DLL 2.1.1声明导出函数:
extern “C” __declspec(dllexport) int add(int a, int b); 其中 extern “C”为声明为C编译。由于C++编译器在编译的时候会造成其函数名的该变,在其他应用程序中导致函数不可调用,而C编译器则不会在编译后改变其函数名。这样如果用C编译的程序来调用该dll中的函数时,可能会造成找不到该函数。
__declspec(dllexport)表示该函数为DLL输出函数,即其他应用程序可以调用该函数 从dll中声明输出函数有两种方式:
(1) 另外一种方式是采用模块定义(.def) 文件声明,.def文件为链接器提供了有关被链接程序的导出、属性及其他方面的信息。 (2) 用__declspec(dllexport)来声明函数
如果使用Visual C++来创建dll,对于同样用VC创建的exe来说,调用dll没有什么问题。而如果用其他工具来创建的exe来调用dll,就会出现问题。因为即使你不用C++编译器,Microsoft C编译器也会损害C函数。当用__stdcall将函数输出时,C编译器会将函数改为的形式。在这里需要在.def文件中加入EXPORTS节来输出函数: EXPORTS
func 这样,dll将用func函数名来输出函数。
另一种方式是用#pragma (linker, “/exports:func=_func@1”),告诉编译器输出函数func,这种方式没有前一种好。

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

2016-01-21
; lib.def : 导出DLL函数
LIBRARY dllTest EXPORTS
add @ 1 .def文件的规则为:
  (1)LIBRARY语句说明.def文件相应的DLL;   (2)EXPORTS语句后列出要导出函数的名称。可以在.def文件中的导出函数名后加@n,表示要导出函数的序号为n(在进行函数调用时,这个序号将发挥其作用);
  (3).def 文件中的注释由每个注释行开始处的分号 (;) 指定,且注释不能与语句共享一行。   由此可以看出,例子中lib.def文件的含义为生成名为“dllTest”的动态链接库,导出其中的add函数,并指定add函数的序号为1。
2.1.2 Dll的调用方式: DLL的调用分为两种方式:动态和静态
(1) 动态调用:typedef int(*lpAddFun)(int, int); //宏定义函数指针类型 lpAddFun add;//函数指针
HINSTANCE hDll=LoadLibrary(“path”); add=(lpAddFun)GetProcAddress(hDll, "add");//获得dll中的add函数指针
FreeLibrary(hDll); 在从dll调用中返回的函数、指针或者类都是以指针的方式会的,即返回的是函数、变量或类的地址。这里一定要注意,不能简单的用函数名来赋值。

2016-01-21
(2) 静态调用:将生成的.dll和.lib文件拷入到调用dll的工程中,用命令
#pragma comment(lib,"dllTest.lib"),实现静态调用,即把该dll在编译的时候也编译到exe文件中去,而后在工程中调用时用下面的代码: #pragma comment(lib,"dllTest.lib")
//.lib文件中仅仅是关于其对应DLL文件中函数的重定位信息 extern "C" __declspec(dllimport) add(int x,int y);
int main(int argc, char* argv[]) {
int result = add(2,3); printf("%d",result);
return 0; }
  由上述代码可以看出,静态调用方式的顺利进行需要完成两个动作:   (1)告诉编译器与DLL相对应的.lib文件所在的路径及文件名,#pragma comment(lib,"dllTest.lib")就是起这个作用。
  程序员在建立一个DLL文件时,连接器会自动为其生成一个对应的.lib文件,该文件包含了DLL 导出函数的符号名及序号(并不含有实际的代码)。在应用程序里,.lib文件将作为DLL的替代文件参与编译。 另外一种显式调用的方式是设置vc中的目录和includefiles来实现
  (2)声明导入函数,extern "C" __declspec(dllimport) add(int x,int y)语句中的__declspec(dllimport)发挥这个作用。   静态调用方式不再需要使用系统API来加载、卸载DLL以及获取DLL中导出函数的地址。这是因为,当程序员通过静态链接方式编译生成应用程序时,应用程序中调用的与.lib文件中导出符号相匹配的函数符号将进入到生成的EXE 文件中,.lib文件中所包含的与之对应的DLL文件的文件名也被编译器存储在 EXE文件内部。当应用程序运行过程中需要加载DLL文件时,Windows将根据这些信息发现并加载DLL,然后通过符号名实现对DLL 函数的动态链接。这样,EXE将能直接通过函数名调用DLL的输出函数,就象调用程序内部的其他函数一样。
2.1.3 DllMain函数 Windows在加载dll的时候,会首先需要一个入口函数DllMain。当在dll中不定义DllMain的时候,windows会从其他运行库中调用一个不做任何操作的DllMain函数,直接返回true。DllMain是dll内部的函数,这意味着在调用dll的程序中不能显式的调用。它是在 dll被调用时自动被调用的。
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{ switch (ul_reason_for_call)
{ case: DLL_PROCESS_ATTACH:
break; case: DLL_THREAD_ATTACH:
break; case: DLL_THREAD_DETACH:
break; case: DLL_PROCESS_DETACH:
break; return TRUE;
} }

2016-03-13
2.2 在dll中导出变量
1、在dll中定义变量 extern int global; 2、在.def中定义输出 EXPORTS:
global DATA 3、 在应用程序中调用:#pragma comment(lib,"dllTest.lib")
extern int global; 注意在此引入的变量global,是一个地址,在使用时需要强制转化为指针后再用,才能得到其值。
(int *)global=10; 在应用工程中引用DLL中全局变量的一个更好方法是:
extern int _declspec(dllimport) global; //用_declspec(dllimport)导入 通过_declspec(dllimport)方式导入的就是DLL中全局变量本身而不再是其地址了,建议在一切可能的情况下使用这种方式。

2016-03-13
2.3 dll导出类
在定义的时候用 class _declspec(dllexport) classname{ }
在类中引用的时候用 加入类定义头文件:#include “classname.h”
Class _declspec(dllimport) classname 来导入类

2016-03-13
3、 MFC规则Dll
MFC规则DLL的概念体现在两方面:   (1) 它是MFC的
  “是MFC的”意味着可以在这种DLL的内部使用MFC;   (2) 它是规则的
  “是规则的”意味着它不同于MFC扩展DLL,在MFC规则DLL的内部虽然可以使用MFC,但是其与应用程序的接口不能是MFC。而MFC扩展DLL与应用程序的接口可以是MFC,可以从MFC扩展DLL中导出一个MFC类的派生类。

2016-03-13
Regular DLL能够被所有支持DLL技术的语言所编写的应用程序调用,当然也包括使用MFC的应用程序。在这种动态连接库中,包含一个从CWinApp继承下来的类,DllMain函数则由MFC自动提供。
(1)静态链接到MFC 的规则DLL   静态链接到MFC的规则DLL与MFC库(包括MFC扩展 DLL)静态链接,将MFC库的代码直接生成在.dll文件中。在调用这种DLL的接口时,MFC使用DLL的资源。因此,在静态链接到MFC 的规则DLL中不需要进行模块状态的切换。
  使用这种方法生成的规则DLL其程序较大,也可能包含重复的代码。 (2)动态链接到MFC 的规则DLL
  动态链接到MFC 的规则DLL 可以和使用它的可执行文件同时动态链接到 MFC DLL 和任何MFC扩展 DLL。在使用了MFC共享库的时候,默认情况下,MFC使用主应用程序的资源句柄来加载资源模板。这样,当DLL和应用程序中存在相同ID的资源时(即所谓的资源重复问题),系统可能不能获得正确的资源。因此,对于共享MFC DLL的规则DLL,我们必须进行模块切换以使得MFC能够找到正确的资源模板。 我们可以在Visual C++中设置MFC规则DLL是静态链接到MFC DLL还是动态链接到MFC DLL。如图8,依次选择Visual C++的project -> Settings -> General菜单或选项,在Microsoft Foundation Classes中进行设置。

2016-03-13
.1规则DLL的创建;
与非MFCdll不同的是,在其定义里面可以引入MFC类,其他与非MFC一样 3.2规则DLL的调用
(1)显示方式LoadLibrary , GetProcAdress , FreeLibrary (2) 我们照样可以在EXE程序中隐式调用MFC规则DLL,只需要将DLL工程生成的.lib文件和.dll文件拷入当前工程所在的目录,并在RegularDllCallDlg.cpp文件(图12所示对话框类的实现文件)的顶部添加:
#pragma comment(lib,"RegularDll.lib")

2016-03-13
3.3共享MFC DLL的规则DLL的模块切换
应用程序进程本身及其调用的每个DLL模块都具有一个全局唯一的HINSTANCE句柄,它们代表了DLL或EXE模块在进程虚拟空间中的起始地址。进程本身的模块句柄一般为0x400000,而DLL模块的缺省句柄为0x10000000。如果程序同时加载了多个DLL,则每个DLL模块都会有不同的 HINSTANCE。应用程序在加载DLL时对其进行了重定位。   共享MFC DLL(或MFC扩展DLL)的规则DLL涉及到HINSTANCE句柄问题,HINSTANCE句柄对于加载资源特别重要。EXE和DLL都有其自己的资源,而且这些资源的ID可能重复,应用程序需要通过资源模块的切换来找到正确的资源。如果应用程序需要来自于DLL的资源,就应将资源模块句柄指定为 DLL的模块句柄;如果需要EXE文件中包含的资源,就应将资源模块句柄指定为EXE的模块句柄。
模块的切换有三种方式: (1)在DLL函数中调用:AFX_MANAGE_STATE(AfxGetStaticModuleState());(推荐使用,最简单)
void ShowDlg(void) {
//方法1:在函数开始处变更,在函数结束时恢复 //将AFX_MANAGE_STATE(AfxGetStaticModuleState());作为接口函数的第一//条语句进行模块状态切换
AFX_MANAGE_STATE(AfxGetStaticModuleState()); CDialog dlg(IDD_DLL_DIALOG);//打开ID为2000的对话框
dlg.DoModal(); }
(2)在DLL函数中调用AfxGetResourceHandle(); AfxSetResourceHandle(HINSTANCE xxx);
(3)由应用程序自身切换(不推荐,最麻烦)

2016-03-13
、扩展MFCDLL
MFC扩展DLL的内涵为MFC的扩展,用户使用MFC扩展DLL就像使用MFC本身的DLL一样。除了可以在MFC扩展DLL的内部使用MFC以外, MFC扩展DLL与应用程序的接口部分也可以是MFC。我们一般使用MFC扩展DLL来包含一些MFC的增强功能,譬如扩展MFC的CStatic、 CButton等类使之具备更强大的能力。 导出一个类,直接在类声明头文件中使用AFX_EXT_CLASS即可,最后别忘了在调用dll的程序中加入class的头文件

2016-03-13
5、总结:
综上所述:以上几种dll主要由以下几种区别: 1、动态链接库是将exe程序在程序执行的时候动态加载的,而静态链接库是在编译的时 将其编译在代码之中的
2、动态链接库可以输出变量、函数和类。其中每种输出的方式与调用方式不尽相同: (1)变量:在dll中定义 extern int global;
在.def文件中输出 EXPORTS global DATA
或extern _declspec(dllexport)int global(不用输出文件了) 在程序中调用:
静态调用: #pragma comment(lib,"dllTest.lib") extern int _declspec(dllimport) global;
动态调用: (2)函数:在dll中定义
extern “C” __declspec(dllexport) int add(int a, int b); 也可以在.def文件中输出该函数
EXPORTS add @ 1
在程序中调用: 静态调用:
#pragma comment(lib,"dllTest.lib") extern "C" __declspec(dllimport) add(int x,int y);
动态调用: typedef int(*lpAddFun)(int, int); //宏定义函数指针类型
lpAddFun add;//函数指针 HINSTANCE hDll=LoadLibrary(“path”);
add=(lpAddFun)GetProcAddress(hDll, "add");//获得dll中的add函数指针 FreeLibrary(hDll);
在从dll调用中返回的函数、指针或者类都是以指针的方式会的,即返回的是函数、变量或类的地址。这里一定要注意,不能简单的用函数名来赋值。 (3)类:在dll中定义:
在定义的时候用 class _declspec(dllexport) classname{ }
在类中引用的时候用 加入类定义头文件:#include “classname.h”
Class _declspec(dllimport) classname 来导入类 3、除了扩展MFC的dll外,其他的dll均可被其他语言编写的应用程序来调用。

2016-03-13
PS:
动态链接库中定义有两种函数:导出函数(export function)和内部函数(internal function)。导出函数可以被其它模块调用,内部函数在定义它们的DLL程序内部使用。
输出函数的方法有以下几种:
1、传统的方法
在模块定义文件的EXPORT部分指定要输入的函数或者变量。语法格式如下: entryname[=internalname] [@ordinal[NONAME]] [DATA] [PRIVATE]
其中:
entryname是输出的函数或者数据被引用的名称;
internalname同entryname;
@ordinal表示在输出表中的顺序号(index);
NONAME仅仅在按顺序号输出时被使用(不使用entryname);
DATA表示输出的是数据项,使用DLL输出数据的程序必须声明该数据项为_declspec(dllimport)。
上述各项中,只有entryname项是必须的,其他可以省略。
对于“C”函数来说,entryname可以等同于函数名;但是对“C++”函数(成员函数、非成员函数)来说,entryname是修饰名。可以从.map映像文件中得到要输出函数的修饰名,或者使用DUMPBIN /SYMBOLS得到,然后把它们写在.def文件的输出模块。DUMPBIN是VC提供的一个工具。
如果要输出一个“C++”类,则把要输出的数据和成员的修饰名都写入.def模块定义文件。
2、在命令行输出
对链接程序LINK指定/EXPORT命令行参数,输出有关函数。
3、使用MFC提供的修饰符号_declspec(dllexport)
在要输出的函数、类、数据的声明前加上_declspec(dllexport)的修饰符,表示输出。__declspec(dllexport)在C调用约定、C编译情况下可以去掉输出函数名的下划线前缀。extern "C"使得在C++中使用C编译方式成为可能。在“C++”下定义“C”函数,需要加extern “C”关键词。用extern "C"来指明该函数使用C编译方式。输出的“C”函数可以从“C”代码里调用。
例如,在一个C++文件中,有如下函数:
extern "C" {void __declspec(dllexport) __cdecl Test(int var);} 其输出函数名为:Test
MFC提供了一些宏,就有这样的作用。
AFX_CLASS_IMPORT:__declspec(dllexport)
AFX_API_IMPORT:__declspec(dllexport)
AFX_DATA_IMPORT:__declspec(dllexport)
AFX_CLASS_EXPORT:__declspec(dllexport)
AFX_API_EXPORT:__declspec(dllexport)
AFX_DATA_EXPORT:__declspec(dllexport)
AFX_EXT_CLASS: #ifdef _AFXEXT
AFX_CLASS_EXPORT #else
AFX_CLASS_IMPORT
AFX_EXT_API:#ifdef _AFXEXT AFX_API_EXPORT
#else AFX_API_IMPORT
AFX_EXT_DATA:#ifdef _AFXEXT
AFX_DATA_EXPORT #else
AFX_DATA_IMPORT

2016-03-13
像AFX_EXT_CLASS这样的宏,如果用于DLL应用程序的实现中,则表示输出(因为_AFX_EXT被定义,通常是在编译器的标识参数中指定该选项/D_AFX_EXT);如果用于使用DLL的应用程序中,则表示输入(_AFX_EXT没有定义)。
要输出整个的类,对类使用_declspec(_dllexpot);要输出类的成员函数,则对该函数使用_declspec(_dllexport)。如:
class AFX_EXT_CLASS CTextDoc : public CDocument
{ …
}
extern "C" AFX_EXT_API void WINAPI InitMYDLL();
这几种方法中,最好采用第三种,方便好用;其次是第一种,如果按顺序号输出,调用效率会高些;最次是第二种。

2016-03-13
调用方式
1、静态调用方式:由编译系统完成对 DLL 的加载和应用程序结束时 DLL 卸载的编码(如还有其它程序使用该 DLL,则 Windows 对 DLL 的应用记录减1,直到所有相关程序都结束对该 DLL 的使用时才释放它,简单实用,但不够灵活,只能满足一般要求。 隐式的调用:需要把产生动态连接库时产生的 .LIB 文件加入到应用程序的工程中,想使用 DLL 中的函数时,只须说明一下。隐式调用不需要调用 LoadLibrary() 和 FreeLibrary()。程序员在建立一个 DLL 文件时,链接程序会自动生成一个与之对应的 LIB 导入文件。该文件包含了每一个 DLL 导出函数的符号名和可选的标识号,但是并不含有实际的代码。LIB 文件作为 DLL 的替代文件被编译到应用程序项目中。
当程序员通过静态链接方式编译生成应用程序时,应用程序中的调用函数与 LIB 文件中导出符号相匹配,这些符号或标识号进入到生成的 EXE 文件中。LIB 文件中也包含了对应的 DL L文件名(但不是完全的路径名),链接程序将其存储在 EXE 文件内部。 当应用程序运行过程中需要加载 DLL 文件时,Windows 根据这些信息发现并加载 DLL,然后通过符号名或标识号实现对 DLL 函数的动态链接。所有被应用程序调用的 DLL 文件都会在应用程序 EXE 文件加载时被加载在到内存中。可执行程序链接到一个包含 DLL 输出函数信息的输入库文件(.LIB文件)。操作系统在加载使用可执行程序时加载 DLL。可执行程序直接通过函数名调用 DLL 的输出函数,调用方法和程序内部其 它的函数是一样的。

2016-03-13
2、动态调用方式:是由编程者用 API 函数加载和卸载 DLL 来达到调用 DLL 的目的,使用上较复杂,但能更加有效地使用内存,是编制大型应用程序时的重要方式。
显式的调用:是指在应用程序中用 LoadLibrary 或 MFC 提供的 AfxLoadLibrary 显式的将自己所做的动态连接库调进来,动态连接库的文件名即是上面两个函数的参数,再用 GetProcAddress() 获取想要引入的函数。自此,你就可以象使用如同本应用程序自定义的函数一样来调用此引入函数了。在应用程序退出之前,应该用 FreeLibrary 或 MFC 提供的 AfxFreeLibrary 释放动态连接库。直接调用 Win32 的 LoadLibary 函数,并指定 DLL 的路径作为参数。LoadLibary 返回 HINSTANCE 参数,应用程序在调用 GetProcAddress 函数时使用这一参数。GetProcAddress 函数将符号名或标识号转换为 DLL 内部的地址。程序员可以决定 DLL 文件何时加载或不加载,显式链接在运行时决定加载哪个 DLL 文件。使用 DLL 的程序在使用之前必须加载(LoadLibrary)加载DLL从而得到一个DLL模块的句柄,然后调用 GetProcAddress 函数得到输出函数的指针,在退出之前必须卸载DLL(FreeLibrary)。

2016-03-13
MFC中的DLL
Non-MFC DLL:指的是不用 MFC 的类库结构,直接用 C 语言写的 DLL,其输出的函数一般用的是标准 C 接口,并能被 非 MFC 或 MFC 编写的应用程序所调用。 Regular DLL:和下述的 Extension DLLs 一样,是用 MFC 类库编写的。明显的特点是在源文件里有一个继承 CWinApp 的类。其又可细分成静态连接到 MFC 和动态连接到 MFC 上的。
静态连接到 MFC 的动态连接库只被 VC 的专业 版和企业版所支持。该类 DLL 应用程序里头的输出函数可以被任意 Win32 程序使用,包括使用 MFC 的应用程序。输入函数有如下形式: extern "C" EXPORT YourExportedFunction();
如果没有 extern "C" 修饰,输出函数仅仅能从 C++ 代码中调用。 DLL 应用程序从 CWinApp 派生,但没有消息循环。

2016-03-13
动态链接到 MFC 的 规则 DLL 应用程序里头的输出函数可以被任意 Win32 程序使用,包括使用 MFC 的应用程序。但是,所有从 DLL 输出的函数应该以如下语句开始:
1. AFX_MANAGE_STATE(AfxGetStaticModuleState( ))
此语句用来正确地切换 MFC 模块状态。 Regular DLL能够被所有支持 DLL 技术的语言所编写的应用程序所调用。在这种动态连接库中,它必须有一个从 CWinApp 继承下来的类,DLLMain 函数被 MFC 所提供,不用自己显式的写出来。
Extension DLL:用来实现从 MFC 所继承下来的类的重新利用,也就是说,用这种类型的动态连接库,可以用来输出一个从 MFC 所继承下来的类。它输出的函数仅可以被使用 MFC 且动态链接到 MFC 的应用程序使用。可以从 MFC 继承你所想要的、更适于你自己用的类,并把它提供给你的应用程序。你也可随意的给你的应用程序提供 MFC 或 MFC 继承类的对象指针。Extension DLL使用 MFC 的动态连接版本所创建的,并且它只被用 MFC 类库所编写的应用程序所调用。Extension DLLs 和 Regular DLLs 不一样,它没有从 CWinApp 继承而来的类的对象,所以,你必须为自己 DLLMain 函数添加初始化代码和结束代码。 和规则 DLL 相比,有以下不同:
1、它没有从 CWinApp 派生的对象; 2、它必须有一个 DLLMain 函数;
3、DLLMain 调用 AfxInitExtensionModule 函数,必须检查该函数的返回值,如果返回0,DLLMmain 也返回 0; 4、如果它希望输出 CRuntimeClass 类型的对象或者资源,则需要提供一个初始化函数来创建一个 CDynLinkLibrary 对象。并且,有必要把初始化函数输出;
5、使用扩展 DLL 的 MFC 应用程序必须有一个从 CWinApp 派生的类,而且,一般在InitInstance 里调用扩展 DLL 的初始化函数。

2016-03-13
DLL入口函数
1、每一个 DLL 必须有一个入口点,DLLMain 是一个缺省的入口函数。DLLMain 负责初始化和结束工作,每当一个新的进程或者该进程的新的线程访问 DLL 时,或者访问 DLL 的每一个进程或者线程不再使用DLL或者结束时,都会调用 DLLMain。但是,使用 TerminateProcess 或 TerminateThread 结束进程或者线程,不会调用 DLLMain。 DLLMain的函数原型:
01. BOOL APIENTRY DLLMain(HANDLE hModule,DWORD ul_reason_for_call,LPVOID
02. lpReserved)
03. {
04. switch(ul_reason_for_call)
05. {
06. case DLL_PROCESS_ATTACH:
07. .......
08. case DLL_THREAD_ATTACH:
09. .......
10. case DLL_THREAD_DETACH:
11. .......
12. case DLL_PROCESS_DETACH:
13. .......
14. return TRUE;
15. }
16. }
参数: hMoudle:是动态库被调用时所传递来的一个指向自己的句柄(实际上,它是指向_DGROUP段的一个选择符);ul_reason_for_call:是一个说明动态库被调原因的标志。当进程或线程装入或卸载动态连接库的时候,操作系统调用入口函数,并说明动态连接库被调用的原因。它所有的可能值为:
DLL_PROCESS_ATTACH: 进程被调用; DLL_THREAD_ATTACH: 线程被调用;
DLL_PROCESS_DETACH: 进程被停止; DLL_THREAD_DETACH: 线程被停止;
lpReserved:是一个被系统所保留的参数;

2016-03-13
、_DLLMainCRTStartup
为了使用 "C" 运行库 (CRT,C Run time Library) 的 DLL 版本(多线程),一个 DLL 应用程序必须指定 _DLLMainCRTStartup 为入口函数,DLL 的初始化函数必须是 DLLMain。 _DLLMainCRTStartup 完成以下任务:当进程或线程捆绑(Attach) 到 DLL 时为 "C" 运行时的数据 (C Runtime Data) 分配空间和初始化并且构造全局 "C++"对象,当进程或者线程终止使用DLL(Detach) 时,清理 C Runtime Data 并且销毁全局 "C++" 对象。它还调用 DLLMain 和 RawDLLMain 函数。
RawDLLMain 在 DLL 应用程序动态链接到 MFC DLL 时被需要,但它是静态链接到 DLL 应用程序的。在讲述状态管理时解释其原因。

2016-03-14
关于调用约定
动态库输出函数的约定有两种:调用约定和名字修饰约定。 1)调用约定(Calling convention):决定函数参数传送时入栈和出栈的顺序,由调用者还是被调用者把参数弹出栈,以及编译器用来识别函数名字的修饰约定。
函数调用约定有多种,这里简单说一下: 1、__stdcall 调用约定相当于16位动态库中经常使用的 PASCAL 调用约定。在32位的 VC++5.0 中PASCAL 调用约定不再被支持(实际上它已被定义为__stdcall。除了__pascal 外,__fortran 和__syscall也不被支持),取而代之的是 __stdcall 调用约定。两者实质上是一致的,即函数的参数自右向左通过栈传递,被调用的函数在返回前清理传送参数的内存栈,但不同的是函数名的修饰部分(关于函数名的修饰部分在后面将详细说明)。
_stdcall 是 Pascal 程序的缺省调用方式,通常用于 Win32 API 中,函数采用从右到左的压栈方式,自己在退出时清空堆栈。VC 将函数编译后会在函数名前面加上下划线前缀,在函数名后加上 "@" 和参数的字节数。

2016-03-14
2、C 调用约定(即用__cdecl 关键字说明)按从右至左的顺序压参数入栈,由调用者把参数弹出栈。对于传送参数的内存栈是由调用者来维护的(正因为如此,实现可变参数的函数只能使用该调用约定)。另外,在函数名修饰约定方面也有所不同。
_cdecl 是 C 和 C++ 程序缺省的调用方式。每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用 _stdcall 函数的大。函数采用从右到左的压栈方式。VC 将函数编译后会在函数名前面加上下划线前缀。 它是 MFC 缺省调用约定。

2016-03-14
3、__fastcall 调用约定是 "人" 如其名,它的主要特点就是快,因为它是通过寄存器来传送参数的(实际上,它用 ECX 和 EDX 传送前两个双字(DWORD)或更小的参数,剩下的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈),在函数名修饰约定方面,它和前两者均不同。
_fastcall方式的函数采用寄存器传递参数,VC 将函数编译后会在函数名前面加上"@"前缀,在函数名后加上"@"和参数的字节数。

2016-03-14
4、thiscall 仅仅应用于 "C++" 成员函数。this 指针存放于 CX 寄存器,参数从右到左压。thiscall 不是关键词,因此不能被程序员指定。
5、naked call采用 1-4 的调用约定时,如果必要的话,进入函数时编译器会产生代码来保存ESI,EDI,EBX,EBP寄存器,退出函数时则产生代码恢复这些寄存器的内容。 naked call不产生这样的代码。naked call不是类型修饰符,故必须和_declspec 共同使用。
关键字 __stdcall、__cdecl 和 __fastcall 可以直接加在要输出的函数前,也可以在编译环境的 Setting...\C/C++ \Code Generation 项选择。当加在输出函数前的关键字与编译环境中的选择不同时,直接加在输出函数前的关键字有效。它们对应的命令行参数分别为/Gz、/Gd 和 /Gr。缺省状态为/Gd,即__cdecl。 要完全模仿 PASCAL 调用约定首先必须使用 __stdcall 调用约定,至于函数名修饰约定,可以通过其它方法模仿。还有一个值得一提的是 WINAPI 宏,Windows.h 支持该宏,它可以将出函数翻译成适当的调用约定,在 WIN32 中,它被定义为 __stdcall。使用 WINAPI 宏可以创建自己的 APIs。

2016-03-15
2)名字修饰约定
1、修饰名(Decoration name) "C" 或者 "C++" 函数在内部(编译和链接)通过修饰名识别。修饰名是编译器在编译函数定义或者原型时生成的字符串。有些情况下使用函数的修饰名是必要的,如在模块定义文件里头指定输出"C++"重载函数、构造函数、析构函数,又如在汇编代码里调用"C""或"C++"函数等。
修饰名由函数名、类名、调用约定、返回类型、参数等共同决定。

2016-03-15
2、名字修饰约定随调用约定和编译种类(C或C++)的不同而变化。函数名修饰约定随编译种类和调用约定的不同而不同,下面分别说明。
a、C编译时函数名修饰约定规则: __stdcall 调用约定在输出函数名前加上一个下划线前缀,后面加上一个"@"符号和其参数的字节数,格式为 _functionname@number。
__cdecl调用约定仅在输出函数名前加上一个下划线前缀,格式为 _functionname。 __fastcall调用约定在输出函数名前加上一个"@"符号,后面也是一个"@"符号和其参数的字节数,格式为@functionname@number。
它们均不改变输出函数名中的字符大小写,这和PASCAL调用约定不同,PASCAL约定输出的函数名无任何修饰且全部大写。 b、C++编译时函数名修饰约定规则:
__stdcall调用约定: 1、以"?"标识函数名的开始,后跟函数名;
2、函数名后面以"@@YG"标识参数表的开始,后跟参数表; 3、参数表以代号表示:
X――void, D――char,
E――unsigned char, F――short,
H――int, I――unsigned int,
J――long, K――unsigned long,
M――float, N――double,
_N――bool, ....
PA――表示指针,后面的代号表明指针类型,如果相同类型的指针连续出现,以"0"代替,一个"0"代表一次重复; 4、参数表的第一项为该函数的返回值类型,其后依次为参数的数据类型,指针标识在其所指数据类型前;
5、参数表后以"@Z"标识整个名字的结束,如果该函数无参数,则以"Z"标识结束。 其格式为"?functionname@@YG*****@Z"或?functionname@@YG*XZ,
例如 1.
int Test1(char *var1,unsigned long)-----“?Test1@@YGHPADK@Z” 2.
void Test2() -----“?Test2@@YGXXZ”
__cdecl调用约定: 规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的"@@YG"变为"@@YA"。
__fastcall调用约定: 规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的"@@YG"变为"@@YI"。
VC++对函数的省缺声明是"__cedcl",将只能被C/C++调用。

2016-03-15
关于DLL的函数
动态链接库中定义有两种函数:导出函数(export function)和内部函数(internal function)。导出函数可以被其它模块调用,内部函数在定义它们的DLL程序内部使用。 输出函数的方法有以下几种:
1、传统的方法 在模块定义文件的 EXPORT 部分指定要输入的函数或者变量。语法格式如下:
1. entryname[=internalname] [@ordinal[NONAME]] [DATA] [PRIVATE]
其中: entryname 是输出的函数或者数据被引用的名称;
internalname 同 entryname; @ordinal 表示在输出表中的顺序号(index);
NONAME 仅仅在按顺序号输出时被使用(不使用 entryname ); DATA 表示输出的是数据项,使用 DLL 输出数据的程序必须声明该数据项为 _declspec(DLLimport)。
上述各项中,只有 entryname 项是必须的,其他可以省略。 对于"C"函数来说,entryname 可以等同于函数名;但是对 "C++" 函数(成员函数、非成员函数)来说,entryname 是修饰名。可以从 .map 映像文件中得到要输出函数的修饰名,或者使用DUMPBIN /SYMBOLS 得到,然后把它们写在 .def 文件的输出模块。DUMPBIN 是VC提供的一个工具。
如果要输出一个 "C++" 类,则把要输出的数据和成员的修饰名都写入 .def 模块定义文件。

2016-03-15
2、在命令行输出
对链接程序 LINK 指定 /EXPORT 命令行参数,输出有关函数。 3、使用 MFC 提供的修饰符号 _declspec(DLLexport)
在要输出的函数、类、数据的声明前加上 _declspec(DLLexport) 修饰符表示输出。__declspec(DLLexport) 在 C 调用约定、C 编译情况下可以去掉输出函数名的下划线前缀。extern "C" 使得在 C++ 中使用 C 编译方式成为可能。在"C++"下定义"C"函数需要加 extern "C" 关键词。用 extern "C" 来指明该函数使用 C 编译方式。输出的 "C" 函数可以从 "C" 代码里调用。 例如,在一个 C++ 文件中,有如下函数:
1. extern "C" {void __declspec(DLLexport) __cdecl Test(int var);}
其输出函数名为:Test MFC提供了一些宏,就有这样的作用。
01. AFX_CLASS_IMPORT:__declspec(DLLexport)
02. AFX_API_IMPORT:__declspec(DLLexport)
03. AFX_DATA_IMPORT:__declspec(DLLexport)
04. AFX_CLASS_EXPORT:__declspec(DLLexport)
05. AFX_API_EXPORT:__declspec(DLLexport)
06. AFX_DATA_EXPORT:__declspec(DLLexport)
07. AFX_EXT_CLASS: #ifdef _AFXEXT
08. AFX_CLASS_EXPORT
09. #else
10. AFX_CLASS_IMPORT
11.
12. AFX_EXT_API:#ifdef _AFXEXT
13. AFX_API_EXPORT
14. #else
15. AFX_API_IMPORT
16.
17. AFX_EXT_DATA:#ifdef _AFXEXT
18. AFX_DATA_EXPORT
19. #else
20. AFX_DATA_IMPORT
像 AFX_EXT_CLASS 这样的宏,如果用于 DLL 应用程序的实现中,则表示输出(因为_AFX_EXT被定义,通常是在编译器的标识参数中指定该选项 /D_AFX_EXT);如果用于使用DLL的应用程序中,则表示输入(_AFX_EXT没有定义)。

2016-03-15
要输出整个的类,对类使用_declspec(_DLLexpot);要输出类的成员函数,则对该函数使用_declspec(_DLLexport)。如:
1. class AFX_EXT_CLASS CTextDoc : public CDocument
2. {
3. …
4. }
5.
6. extern "C" AFX_EXT_API void WINAPI InitMYDLL();
这几种方法中,最好采用第三种,方便好用;其次是第一种,如果按顺序号输出,调用效率会高些;最次是第二种。

2016-03-15
模块定义文件(.DEF)
模块定义文件(.DEF)是一个或多个用于描述 DLL 属性的模块语句组成的文本文件,每个DEF文件至少必须包含以下模块定义语句: 第一个语句必须是LIBRARY语句,指出DLL的名字;
EXPORTS 语句列出被导出函数的名字;将要输出的函数修饰名罗列在 EXPORTS 之下,这个名字必须与定义函数的名字完全一致,如此就得到一个没有任何修饰的函数名了。 可以使用DESCRIPTION语句描述DLL的用途(此句可选);
";"对一行进行注释(可选)。

2016-03-15
DLL程序和调用其输出函数的程序的关系
1、DLL与进程、线程之间的关系 DLL模块被映射到调用它的进程的虚拟地址空间。
DLL使用的内存从调用进程的虚拟地址空间分配,只能被该进程的线程所访问。 DLL的句柄可以被调用进程使用;调用进程的句柄可以被DLL使用。
DLL使用调用进程的栈。

2016-03-15
2、关于共享数据段
DLL定义的全局变量可以被调用进程访问;DLL可以访问调用进程的全局数据。使用同一DLL的每一个进程都有自己的DLL全局变量实例。如果多个线程并发访问同一变量,则需要使用同步机制;对一个DLL的变量,如果希望每个使用DLL的线程都有自己的值,则应该使用线程局部存储(TLS,Thread Local Strorage)。 在程序里加入预编译指令,或在开发环境的项目设置里也可以达到设置数据段属性的目的.必须给这些变量赋初值,否则编译器会把没有赋初始值的变量放在一个叫未被初始化的数据段中。


多看笔记 来自多看阅读 for Android
duokanbookid:lcb7191f2g73c3685b4b031cd3c763fd

《DLL用法》的笔记相关推荐

  1. 《js中原型和原型链的深入理解》的笔记

    前言:在微信公众号前端大全上看过<js中原型和原型链的深入理解>,个人认为这是我看过的js原型链的文章中,在思维结构上理解最清楚的一个文章了,本着温故而知新,有害怕找不到这个文章,我就把文 ...

  2. JS 中原型和原型链深入理解

    作者: erdu segmentfault.com/a/1190000014717972 首先要搞明白几个概念: 函数(function) 函数对象(function object) 本地对象(nat ...

  3. 前端开发:JS中原型和原型链的介绍

    前言 在前端开发过程中,涉及到JS原理相关的内容也就是常用的几大模块,不仅常用而且很重要,但是涉及到原理的话会有点难懂,尤其是对JS接触不太久的开发者来讲.本篇博文就来分享一下关于JS的原型和原型链相 ...

  4. JS中的原型链(超清晰理解)

    什么是原型链 原型链,所有的原型构成了一个链条,这个链条我们称之为原型链(prototype chain). 原型链的案例 如果我们执行下面这段代码,因为没有定义address这个属性,程序结果理所当 ...

  5. JS原型链的一些理解

    关于原型链我的理解是一个构造函数的原型作为另一个构造函数的实例形成的继承关系 在JS高级程序设计中有这样一个图 当我们定义一个函数时会有一个原型,即图中的SuperType Prototype,这时原 ...

  6. JS题目总结:原型链/new/json/MVC/Promise

    JS题目总结:原型链/new/json/MVC/Promise 1原型链相关 解读: 上图中,Object,Function,Array,Boolean都是构造函数 第一个框: object是实例对象 ...

  7. 【JS继承】JS继承之原型链继承

    自我介绍:大家好,我是吉帅振的网络日志:微信公众号:吉帅振的网络日志:前端开发工程师,工作4年,去过上海.北京,经历创业公司,进过大厂,现在郑州敲代码. JS继承专栏 1[JS继承]什么是JS继承? ...

  8. [js] js怎样避免原型链上的对象共享?

    [js] js怎样避免原型链上的对象共享? 组合继承 优势 公有的写在原型 私有的卸载构造函数 可以向父类传递参数 劣势 需要手动绑定constructor 封装性一般 重复调用父类性能损耗

  9. 理解js中的原型链,prototype与__proto__的关系

    说到prototype,就不得不先说下new的过程. 我们先看看这样一段代码: 1 <script type="text/javascript"> 2 var Pers ...

  10. JS三座大山之原型链

    在JS中原型链的概念刚开始可能一直迷惑着大多数人,我也一样,不过花点时间仔细的梳理梳理,还是很容易理解的.本文就着重介绍一下原型链,如有不对之处,欢迎指正,共同探讨,共同进步. 在面试时,面试官可能会 ...

最新文章

  1. linux之父密码,Linux之父十大名言
  2. 16个免费和开源商业智能工具
  3. ROS 使用参数以及相应的 .launch 文件编写
  4. 绝对Linux服务器管理利器webmin
  5. iis授权mysql验证_ASP.NET Web API身份验证和授权
  6. linux 手动配置ip地址方法
  7. 判断一个路径串是否为有效目录
  8. Python都被用在哪?都有哪些人在用Python呢?
  9. [JavaWeb-JavaScript]JavaScript_Data日期对象
  10. java阿里系学习经历的小小领悟
  11. 最小公倍数一些性质定理及证明
  12. 在没有插件的情况下为Chrome设置Proxy
  13. 《Linux命令行与shell脚本编程大全》第十八章 图形化桌面环境中的脚本编程
  14. Hi3520d 网卡驱动源码分析
  15. web 前端签名插件_手写签名插件—jSignature
  16. 【leetcode刷题】[简单]441. 排列硬币(arranging coins)-java
  17. 2022下半年软件设计师中级考试通过
  18. CSMA/CD协议总结最短帧长计算
  19. 织梦dedecms橙色响应式月嫂保姆家政服务公司网站模板
  20. 人工智能( AI )将如何颠覆项目管理?看看这六大关键领域

热门文章

  1. 1G流量看视频阅读打游戏能用多久
  2. dplayer安装php_DPlayer for Z-BlogPHP
  3. 你的电脑如何与别的电脑通信
  4. UA:User-agent是什么?
  5. 我的firefox插件
  6. 解决mysql插入中文出现错误Incorrect string value: '\xE7\xA8\x8B\xE5\xBA\x8F...' for column 'course' at row 1
  7. python做圆柱绕流_OpenFOAM-圆柱绕流
  8. HTML与CSS基础
  9. 多元宇宙量子计算机进展,斯蒂芬霍金的最后一篇论文提出了无限的多元宇宙理论...
  10. 拉普拉斯矩阵和拉普拉斯二次型