searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

动态link库编程深入浅出

2024-10-17 09:34:25
0
0
1.概论
  先来阐述一下DLL(Dynamic Linkable Library)的概念,你可以简单的把DLL看成一种仓库,它提供给你一些可以直接拿来用的变量、函数或类。在仓库的发展史上经历了“无库-静态link库-动态link库”的时代。
  静态link库与动态link库都是共享代码的方式,如果采用静态link库,则无论你愿不愿意,lib中的指令都被直接包含在最终生成的EXE文件中了。但是若使用DLL,该DLL不必被包含在最终EXE文件中,EXE文件执行时可以“动态”地引用和卸载这个与EXE独立的DLL文件。静态链接库和动态链接库的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。
  对动态link库,我们还需建立如下概念:
  (1)DLL 的编制与具体的编程语言及编译器无关
  只要遵循约定的DLL接口规范和调用方式,用各种语言编写的DLL都可以相互调用。譬如Windows提供的系统DLL(其中包括了Windows的API),在任何开发环境中都能被调用,不在乎其是Visual Basic、Visual C++还是Delphi。
     (2)动态link库随处可见
  我们在Windows目录下的system32文件夹中会看到kernel32.dll、user32.dll和gdi32.dll,windows的大多数API都包含在这些DLL中。kernel32.dll中的函数主要处理内存管理和进程调度;user32.dll中的函数主要控制用户界面; gdi32.dll中的函数则负责图形方面的操作。
  一般的程序员都用过类似MessageBox的函数,其实它就包含在user32.dll这个动态link库中。由此可见DLL对我们来说其实并不陌生。
      (3)VC动态link库的分类
    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的类,但其无message循环;MFC扩展DLL采用MFC的动态link版本创建,它只能被用MFC类库所编写的应用程序所调用。
动态link库,导出函数方式:
1、 在函数声明 中加上__declspec(dllexport);
2、 采用模块定义(.def) 文件声明,.def文件为link器提供了有关被link程序的导出、属性及其他方面的信息
 
下面的代码演示了怎样同.def文件将函数add声明为DLL导出函数(需在dllTest工程中添加lib.def文件):
lib.def : 导出DLL函数
{
    LIBRARY dllTest
    EXPORTS
    add @ 1
}
.def文件的规则为:
  (1)LIBRARY语句说明.def文件相应的DLL;
  (2)EXPORTS语句后列出要导出函数的名称。可以在.def文件中的导出函数名后加@n,表示要导出函数的序号为n(在进行函数调用时,这个序号将发挥其作用);
  (3).def 文件中的注释由每个注释行开始处的分号 (;) 指定,且注释不能与语句共享一行。
  由此可以看出,例子中lib.def文件的含义为生成名为“dllTest”的动态link库,导出其中的add函数,并指定add函数的序号为1。
 
DLL的调用方式: “LoadLibrary-GetProcAddress-FreeLibrary”系统Api提供的三位一体“DLL加载-DLL函数adress获取-DLL释放”方式,这种调用方式称为DLL的动态调用
   1、动态调用方式的特点是完全由编程者用 API 函数加载和卸载 DLL,程序员可以决定 DLL 文件何时加载或不加载,显式link在运行时决定加载哪个 DLL 文件。
   与动态调用方式相对应的就是静态调用方式,“有动必有静”,这来源于物质世界的对立统一。“动与静”,其对立与统一竟无数次在技术领域里得到验证,譬如 静态IP与DHCP、静态路由与动态路由等。从前文我们已经知道,库也分为静态库与动态库DLL,而想不到,深入到DLL内部,其调用方式也分为静态与动 态。“动与静”,无处不在。《周易》已认识到有动必有静的动静平衡观,《易.系辞》曰:“动静有常,刚柔断矣”。哲学意味着一种普遍的真理,因此,我们经 常可以在枯燥的技术领域看到哲学的影子。
    2、静态调用方式的特点是由编译系统完成对DLL的加载和应用程序结束时 DLL 的卸载。当调用某DLL的应用程序结束时,若系统中还有其它程序使用该 DLL,则Windows对DLL的应用记录减1,直到所有使用该DLL的程序都结束时才释放它。静态调用方式简单实用,但不如动态调用方式灵活。
  下面我们来看看静态调用的例子,将编译dllTest工程所生成的.lib和.dll文件拷入dllCall工程所在的路径,dllCall执行下列代码:
#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的替代文件参与编译。
  (2)声明导入函数,extern "C" __declspec(dllimport) add(int x,int y)语句中的__declspec(dllimport)发挥这个作用。
   静态调用方式不再需要使用系统API来加载、卸载DLL以及获取DLL中导出函数的adress。这是因为,当程序员通过静态link方式编译生成应用程序时,应用 程序中调用的与.lib文件中导出符号相匹配的函数符号将进入到生成的EXE 文件中,.lib文件中所包含的与之对应的DLL文件的文件名也被编译器存储在 EXE文件内部。当应用程序运行过程中需要加载DLL文件时,Windows将根据这些信息发现并加载DLL,然后通过符号名实现对DLL 函数的动态link。这样,EXE将能直接通过函数名调用DLL的输出函数,就象调用程序内部的其他函数一样。
 
__stdcall约定
  如果通过VC++编写的DLL欲被其他语言编写的程序调用,应将 函数的调用方式声明为__stdcall方式,WINAPI都采用这种方式,而C/C++缺省的调用方式却为__cdecl。__stdcall方式与 __cdecl对函数名最终生成符号的方式不同。若采用C编译方式(在C++中需将函数声明为extern "C"),__stdcall调用约定在输出函数名前面加下划线,后面加“@”符号和参数的字节数,形如_functionname@number;而 __cdecl调用约定仅在输出函数名前面加下划线,形如_functionname。
0条评论
0 / 1000
邱****谋
7文章数
0粉丝数
邱****谋
7 文章 | 0 粉丝
原创

动态link库编程深入浅出

2024-10-17 09:34:25
0
0
1.概论
  先来阐述一下DLL(Dynamic Linkable Library)的概念,你可以简单的把DLL看成一种仓库,它提供给你一些可以直接拿来用的变量、函数或类。在仓库的发展史上经历了“无库-静态link库-动态link库”的时代。
  静态link库与动态link库都是共享代码的方式,如果采用静态link库,则无论你愿不愿意,lib中的指令都被直接包含在最终生成的EXE文件中了。但是若使用DLL,该DLL不必被包含在最终EXE文件中,EXE文件执行时可以“动态”地引用和卸载这个与EXE独立的DLL文件。静态链接库和动态链接库的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。
  对动态link库,我们还需建立如下概念:
  (1)DLL 的编制与具体的编程语言及编译器无关
  只要遵循约定的DLL接口规范和调用方式,用各种语言编写的DLL都可以相互调用。譬如Windows提供的系统DLL(其中包括了Windows的API),在任何开发环境中都能被调用,不在乎其是Visual Basic、Visual C++还是Delphi。
     (2)动态link库随处可见
  我们在Windows目录下的system32文件夹中会看到kernel32.dll、user32.dll和gdi32.dll,windows的大多数API都包含在这些DLL中。kernel32.dll中的函数主要处理内存管理和进程调度;user32.dll中的函数主要控制用户界面; gdi32.dll中的函数则负责图形方面的操作。
  一般的程序员都用过类似MessageBox的函数,其实它就包含在user32.dll这个动态link库中。由此可见DLL对我们来说其实并不陌生。
      (3)VC动态link库的分类
    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的类,但其无message循环;MFC扩展DLL采用MFC的动态link版本创建,它只能被用MFC类库所编写的应用程序所调用。
动态link库,导出函数方式:
1、 在函数声明 中加上__declspec(dllexport);
2、 采用模块定义(.def) 文件声明,.def文件为link器提供了有关被link程序的导出、属性及其他方面的信息
 
下面的代码演示了怎样同.def文件将函数add声明为DLL导出函数(需在dllTest工程中添加lib.def文件):
lib.def : 导出DLL函数
{
    LIBRARY dllTest
    EXPORTS
    add @ 1
}
.def文件的规则为:
  (1)LIBRARY语句说明.def文件相应的DLL;
  (2)EXPORTS语句后列出要导出函数的名称。可以在.def文件中的导出函数名后加@n,表示要导出函数的序号为n(在进行函数调用时,这个序号将发挥其作用);
  (3).def 文件中的注释由每个注释行开始处的分号 (;) 指定,且注释不能与语句共享一行。
  由此可以看出,例子中lib.def文件的含义为生成名为“dllTest”的动态link库,导出其中的add函数,并指定add函数的序号为1。
 
DLL的调用方式: “LoadLibrary-GetProcAddress-FreeLibrary”系统Api提供的三位一体“DLL加载-DLL函数adress获取-DLL释放”方式,这种调用方式称为DLL的动态调用
   1、动态调用方式的特点是完全由编程者用 API 函数加载和卸载 DLL,程序员可以决定 DLL 文件何时加载或不加载,显式link在运行时决定加载哪个 DLL 文件。
   与动态调用方式相对应的就是静态调用方式,“有动必有静”,这来源于物质世界的对立统一。“动与静”,其对立与统一竟无数次在技术领域里得到验证,譬如 静态IP与DHCP、静态路由与动态路由等。从前文我们已经知道,库也分为静态库与动态库DLL,而想不到,深入到DLL内部,其调用方式也分为静态与动 态。“动与静”,无处不在。《周易》已认识到有动必有静的动静平衡观,《易.系辞》曰:“动静有常,刚柔断矣”。哲学意味着一种普遍的真理,因此,我们经 常可以在枯燥的技术领域看到哲学的影子。
    2、静态调用方式的特点是由编译系统完成对DLL的加载和应用程序结束时 DLL 的卸载。当调用某DLL的应用程序结束时,若系统中还有其它程序使用该 DLL,则Windows对DLL的应用记录减1,直到所有使用该DLL的程序都结束时才释放它。静态调用方式简单实用,但不如动态调用方式灵活。
  下面我们来看看静态调用的例子,将编译dllTest工程所生成的.lib和.dll文件拷入dllCall工程所在的路径,dllCall执行下列代码:
#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的替代文件参与编译。
  (2)声明导入函数,extern "C" __declspec(dllimport) add(int x,int y)语句中的__declspec(dllimport)发挥这个作用。
   静态调用方式不再需要使用系统API来加载、卸载DLL以及获取DLL中导出函数的adress。这是因为,当程序员通过静态link方式编译生成应用程序时,应用 程序中调用的与.lib文件中导出符号相匹配的函数符号将进入到生成的EXE 文件中,.lib文件中所包含的与之对应的DLL文件的文件名也被编译器存储在 EXE文件内部。当应用程序运行过程中需要加载DLL文件时,Windows将根据这些信息发现并加载DLL,然后通过符号名实现对DLL 函数的动态link。这样,EXE将能直接通过函数名调用DLL的输出函数,就象调用程序内部的其他函数一样。
 
__stdcall约定
  如果通过VC++编写的DLL欲被其他语言编写的程序调用,应将 函数的调用方式声明为__stdcall方式,WINAPI都采用这种方式,而C/C++缺省的调用方式却为__cdecl。__stdcall方式与 __cdecl对函数名最终生成符号的方式不同。若采用C编译方式(在C++中需将函数声明为extern "C"),__stdcall调用约定在输出函数名前面加下划线,后面加“@”符号和参数的字节数,形如_functionname@number;而 __cdecl调用约定仅在输出函数名前面加下划线,形如_functionname。
文章来自个人专栏
windows编程
7 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
0