DLL动态库相关问题解析

来源:互联网 发布:美女网络主播 编辑:程序博客网 时间:2024/06/04 23:52

       动态链接库 (DLL) 是作为共享函数库的可执行文件。动态链接提供了一种方法,使进程可以调用不属于其可执行代码的函数。函数的可执行代码位于一个 DLL 中,该 DLL 包含一个或多个已被编译、链接并与使用它们的进程分开存储的函数。DLL 还有助于共享数据和资源。多个应用程序可同时访问内存中单个 DLL 副本的内容。

       动态链接与静态链接的不同之处在于:动态链接允许可执行模块(.dll 文件或 .exe 文件)仅包含在运行时定位 DLL 函数的可执行代码所需的信息。在静态链接中,链接器从静态链接库获取所有被引用的函数,并将库同代码一起放到可执行文件中。

       使用动态链接代替静态链接有若干优点。DLL 节省内存,减少交换操作,节省磁盘空间,更易于升级,提供售后支持,提供扩展 MFC 库类的机制,支持多语言程序,并使国际版本的创建轻松完成。


一、动态库后缀名有限制吗?

       没有限制,任意的后缀都可作为动态库的后缀名。Dll后缀无所谓,但是保持下图所示一致可以消除编译时提示后缀名不一致警告问题。

           


二、动态库编译、链接、生成都需要哪些文件

      下图所示即动态库编译、链接、生成都需要的文件:


三、导入导出方式有哪些?

隐式链接

       为隐式链接到 DLL,可执行文件必须从 DLL 的提供程序获取下列各项:

  1. 包含导出函数和/或 C++ 类的声明的头文件(.h 文件),类、函数和数据均应具有 __declspec(dllimport)
  2. 要链接的导入库(.LIB files)(生成 DLL 时链接器创建导入库)。
  3. 实际的 DLL(.dll 文件)。
  • 使用 DLL 的可执行文件必须包括头文件,此头文件包含每个源文件中的导出函数(或 C++ 类),而这些源文件包含对导出函数的调用。从编码的角度讲,导出函数的函数调用与任何其他函数调用一样。
  • 若要生成调用可执行文件,必须与导入库链接。如果使用的是外部生成文件,请指定导入库的文件名,此导入库中列出了要链接到的其他对象 (.obj) 文件或库。
  • 操作系统在加载调用可执行文件时,必须能够定位 DLL 文件。 Dll 导出的方式可以是 导出的方式可以是 导出的方式可以是 导出的方式可以是__declspec(dllexport),也可以是在.def文件中


   导入 DLL进行编译的时候 ,可以使用 __declspec(dllimport) 指定导入声明和#pragma comment(lib, "MyDllAPI.lib")指定库文件导入,或者仅使用__declspec(dllimport)指定声明,下图配置指定lib导入。

显式链接:

    在显式链接下,应用程序必须进行函数调用以在运行时显式加载 DLL。为显式链接到 DLL,应用程序必须:

  • 调用 LoadLibrary(或相似的函数)以加载 DLL 和获取模块句柄。
  • 调用 GetProcAddress,以获取指向应用程序要调用的每个导出函数的函数指针。由于应用程序是通过指针调用 DLL 的函数,编译器不生成外部引用,故无需与导入库链接。
  • 使用完 DLL 后调用 FreeLibrary


       函数FunC也可以这样调用:


       利用ordinal值(def文件中导出函数后面的@N)进行调用,但使用 GetProcAddress Function 时,有以下几点需要特别留意:

1. 第二个参数类型是 LPCSTR,不是 LPCTSTR

2. 用 __declspec(dllexport),按C 名称修饰(extern "C") 导出的函数名,对于 __stdcall 和 __fastcall 调用约定是相同的;对 __cdecl 是不同的(导出的函数名没有前面的下划线);

3. 即使返回值不是 NULL,也有可能发生错误。当 .def 模块不是连续地从 1 开始编号 ordinal 值,那么,如果用一个无函数对应的 ordinal 值调用 GetProcAddress,就会发生错误,返回一个无效的非 NULL 地址; 

4. 最好用函数名,而不是 ordinal 值调用 GetProcAddress,以避免不同版本 Dll 中某些函数不存在的情况。

注:确认 Dll 的导出函数名,可以用 DUMPBIN /EXPORTS dll_file_name.dll 命令,然后查看name 列。

5. MAKEINTRESOURCE宏定义如下


   如果由于UNICODE导致使用MAKEINTRESOURCEWINT转换为了LPWSTR,那么直接使用MAKEINTRESOURCEA即可。即


不要拘泥于现有的宏定义。


四、导入导出代码实现

    利用预编译器预先定义一个宏,作为条件语句实现导入导出在客户端和本地实现不同声明



五、def文件改名称问题

    如下图所示保持一致即可。


六、 纯资源DLL定义

    需要关闭入口,如下图所示:


七、EXPORTS关键字

    引入了一个由一个或多个 definitions(导出的函数或数据)组成的节。每个定义必须在单独一行上。

    EXPORTS 关键字可以在第一个定义所在的同一行或前一行上。.def 文件可以包含一个或多个 EXPORTS 语句。

    导出 definitions 的语法为:

    entryname 是要导出的函数名或变量名。这是必选项。如果导出的名称与 DLL 中的名称不同,则通过internalname指定 DLL 中导出的名称。例如,如果 DLL 导出函数 func1(),要将它用作 func2(),则应指定:

\

    @ordinal 允许指定是序号而不是函数名将进入 DLL 的导出表。这有助于最小化 DLL 的大小。.LIB 文件将包含序号与函数之间的映射,这使您得以像通常在使用 DLL 的项目中那样使用函数名。

    可选的 NONAME 关键字允许只按序号导出,并减小结果 DLL 中导出表的大小。但是,如果要在 DLL 上使用 GetProcAddress,则必须知道序号,因为名称将无效。

    可选的 PRIVATE 关键字禁止将 entryname 放到由 LINK 生成的导入库中。它对同样是由 LINK 生成的图像中的导出无效。

    可选的 DATA 关键字指定导出的是数据,而不是代码。例如,可以导出数据变量,如下所示:

    当对同一导出使用 PRIVATE 和 DATA 时,PRIVATE 必须位于 DATA 的前面。

    有三种导出定义的方法,按照建议的使用顺序依次为:

    1.源代码中的__declspec(dllexport) 关键字

    2..def 文件中的 EXPORTS 语句

    3.LINK 命令中的/EXPORT 规范

    所有这三种方法可以用在同一个程序中。LINK 在生成包含导出的程序时还创建导入库,除非生成中使用了 .exp 文件。

    以下是 EXPORTS 节的示例:

    注意,使用 .def 文件从 DLL 中导出变量时,不需要在变量上指定 __declspec(dllexport)。但是,在任何使用 DLL 的文件中,仍必须在数据声明上使用 __declspec(dllimport)。


八、后缀的问题

    默认.dll后缀的动态库,显式链接时可以只要名称,但是改过后缀的动态库则要全称,包括后缀。


九、 Depends查看DLL


    .def文件中导出的函数是c方式的声明,__declspec(dllexport)导出的是c++形式的函数声明。


原创粉丝点击