两种类型的链接:隐式链接和显式链接

两种类型的链接:隐式链接和显式链接。

隐式链接

应用程序的代码调用导出   DLL   函数时发生隐式链接。当调用可执行文件的源代码被编译或被汇编时,DLL   函数调用在对象代码中生成一个外部函数引用。若要解析此外部引用,应用程序必须与   DLL   的创建者所提供的导入库(.LIB   文件)链接。


导入库仅包含加载   DLL   的代码和实现   DLL   函数调用的代码。在导入库中找到外部函数后,会通知链接器此函数的代码在   DLL   中。要解析对   DLL   的外部引用,链接器只需向可执行文件中添加信息,通知系统在进程启动时应在何处查找   DLL   代码。


系统启动包含动态链接引用的程序时,它使用程序的可执行文件中的信息定位所需的   DLL。如果系统无法定位   DLL,它将终止进程并显示一个对话框来报告错误。否则,系统将   DLL   模块映射到进程的地址空间中。


如果任何   DLL   具有(用于初始化代码和终止代码的)入口点函数,操作系统将调用此函数。在传递到入口点函数的参数中,有一个指定用以指示   DLL   正在附带到进程的代码。如果入口点函数没有返回   TRUE,系统将终止进程并报告错误。


最后,系统修改进程的可执行代码以提供   DLL   函数的起始地址。


与程序代码的其余部分一样,DLL   代码在进程启动时映射到进程的地址空间中,且仅当需要时才加载到内存中。因此,由   .def   文件用来在   Windows   的早期版本中控制加载的   PRELOAD     LOADONCALL   代码属性不再具有任何意义。


显式链接

大部分应用程序使用隐式链接,因为这是最易于使用的链接方法。但是有时也需要显式链接。下面是一些使用显式链接的常见原因:


直到运行时,应用程序才知道需要加载的   DLL   的名称。例如,应用程序可能需要从配置文件获取   DLL   的名称和导出函数名。


如果在进程启动时未找到   DLL,操作系统将终止使用隐式链接的进程。同样是在此情况下,使用显式链接的进程则不会被终止,并可以尝试从错误中恢复。例如,进程可通知用户所发生的错误,并让用户指定   DLL   的其他路径。


如果使用隐式链接的进程所链接到的   DLL   中有任何   DLL   具有失败的   DllMain   函数,该进程也会被终止。同样是在此情况下,使用显式链接的进程则不会被终止。


因为   Windows   在应用程序加载时加载所有的   DLL,故隐式链接到许多   DLL   的应用程序启动起来会比较慢。为提高启动性能,应用程序可隐式链接到那些加载后立即需要的   DLL,并等到在需要时显式链接到其他   DLL


显式链接下不需将应用程序与导入库链接。如果   DLL   中的更改导致导出序号更改,使用显式链接的应用程序不需重新链接(假设它们是用函数名而不是序号值调用   GetProcAddress),而使用隐式链接的应用程序必须重新链接到新的导入库。


下面是需要注意的显式链接的两个缺点:


如果   DLL   具有   DllMain   入口点函数,则操作系统在调用   LoadLibrary   的线程上下文中调用此函数。如果由于以前调用了   LoadLibrary   但没有相应地调用   FreeLibrary   函数而导致   DLL   已经附加到进程,则不会调用此入口点函数。如果   DLL   使用   DllMain   函数为进程的每个线程执行初始化,显式链接会造成问题,因为调用   LoadLibrary(或   AfxLoadLibrary)时存在的线程将不会初始化。
 

如果   DLL   将静态作用域数据声明为   __declspec(thread),则在显式链接时   DLL   会导致保护错误。用   LoadLibrary   加载   DLL   后,每当代码引用此数据时   DLL   就会导致保护错误。(静态作用域数据既包括全局静态项,也包括局部静态项。)因此,创建   DLL   时应避免使用线程本地存储区,或者应(在用户尝试动态加载时)告诉   DLL   用户潜在的缺陷。



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


包含导出函数和/   C++   类的声明的头文件(.h   文件)。类、函数和数据均应具有   __declspec(dllimport),有关更多信息,请参见   dllexport,   dllimport


要链接的导入库(.LIB   files)。(生成   DLL   时链接器创建导入库。)


实际的   DLL.dll   文件)。


使用   DLL   的可执行文件必须包括头文件,此头文件包含每个源文件中的导出函数(或   C++   类),而这些源文件包含对导出函数的调用。从编码的角度讲,导出函数的函数调用与任何其他函数调用一样。


若要生成调用可执行文件,必须与导入库链接。如果使用的是外部生成文件,请指定导入库的文件名,此导入库中列出了要链接到的其他对象   (.obj)   文件或库。


操作系统在加载调用可执行文件时,必须能够定位   DLL   文件。



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


调用   LoadLibrary(或相似的函数)以加载   DLL   和获取模块句柄。


调用   GetProcAddress,以获取指向应用程序要调用的每个导出函数的函数指针。由于应用程序是通过指针调用   DLL   的函数,编译器不生成外部引用,故无需与导入库链接。


使用完   DLL   后调用   FreeLibrary


例如:
 

   
复制代码
 
typedef   UINT   (CALLBACK*   LPFNDLLFUNC1)(DWORD,UINT);
...

HINSTANCE   hDLL;                               //   Handle   to   DLL
LPFNDLLFUNC1   lpfnDllFunc1;         //   Function   pointer
DWORD   dwParam1;
UINT     uParam2,   uReturnVal;

hDLL   =   LoadLibrary( "MyDLL ");
if   (hDLL   !=   NULL)
{
      lpfnDllFunc1   =   (LPFNDLLFUNC1)GetProcAddress(hDLL,
                                                                                      "DLLFunc1 ");
      if   (!lpfnDllFunc1)
      {
            //   handle   the   error
            FreeLibrary(hDLL);              
            return   SOME_ERROR_CODE;
      }
      else
      {
            //   call   the   function
            uReturnVal   =   lpfnDllFunc1(dwParam1,   uParam2);
      }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值