将可执行文件链接到 DLL

⌚Time: 2023-09-24 21:25:01

👨‍💻Author: Jack Ge

资料来自MSDN

将可执行文件链接到 DLL

可执行文件通过以下两种方式之一链接到(或加载)DLL:

可执行文件可以使用任一链接方法链接到同一个 DLL。 而且,这些方法并不相互排斥;一个可执行文件可以隐式链接到 DLL,另一个可执行文件可以显式附加到它。

确定要使用的链接方法

是使用隐式链接还是显式链接是必须为应用程序进行的体系结构决策。 每种方法都各有优缺点。

隐式链接

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

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

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

如果任何 DLL 的初始化和终止代码(如 DllMain)有入口点函数,则操作系统将调用该函数。 传递给入口点函数的一个参数指定用于指示 DLL 附加到进程的代码。 如果入口点函数不返回 TRUE,则系统将终止进程并报告错误。

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

与程序代码的其余部分一样,在进程启动时,加载程序会将 DLL 代码映射到进程的地址空间中。 操作系统仅在需要时才将它加载到内存中。 因此,.def 文件在以前 Windows 版本中用于控制加载的 PRELOAD 和 LOADONCALL 代码特性不再有意义。

显式链接

大多数应用程序使用隐式链接,因为这是可使用的最简单链接方法。 但是,有时需要显式链接。 下面是使用显式链接的一些常见原因:

下面是需要注意的显式链接方面的两个风险:

如何使用隐式链接

若要通过隐式链接使用 DLL,客户端可执行文件必须从 DLL 的提供程序获取以下文件:

若要通过隐式链接使用 DLL 中的数据、函数和类,任何客户端源文件都必须包含声明它们的头文件。 从编码的角度来看,对导出函数的调用与任何其他函数调用一样。

若要生成客户端可执行文件,必须与 DLL 的导入库链接。 如果使用外部生成文件或生成系统,请将导入库与链接的其他对象文件或库一起指定。

当操作系统加载调用可执行文件时,它必须能够找到 DLL 文件。 这意味着必须在安装应用程序时部署 DLL 或验证 DLL 是否存在。

如何显式链接到 DLL

若要通过显式链接使用 DLL,应用程序必须在运行时进行函数调用以显式加载 DLL。 若要显式链接到 DLL,应用程序必须:

例如,此示例函数调用 LoadLibrary 以加载名为“MyDLL”的 DLL,调用 GetProcAddress 以获取指向名为“DLLFunc1”的函数的指针,调用该函数并保存结果,然后调用 FreeLibrary 以卸载 DLL。


#include "windows.h"



typedef HRESULT (CALLBACK* LPFNDLLFUNC1)(DWORD,UINT*);



HRESULT LoadAndCallSomeFunction(DWORD dwParam1, UINT * puParam2)

{

    HINSTANCE hDLL;               // Handle to DLL

    LPFNDLLFUNC1 lpfnDllFunc1;    // Function pointer

    HRESULT hrReturnVal;



    hDLL = LoadLibrary("MyDLL");

    if (NULL != hDLL)

    {

        lpfnDllFunc1 = (LPFNDLLFUNC1)GetProcAddress(hDLL, "DLLFunc1");

        if (NULL != lpfnDllFunc1)

        {

            // call the function

            hrReturnVal = lpfnDllFunc1(dwParam1, puParam2);

        }

        else

        {

            // report the error

            hrReturnVal = ERROR_DELAY_LOAD_FAILED;

        }

        FreeLibrary(hDLL);

    }

    else

    {

        hrReturnVal = ERROR_DELAY_LOAD_FAILED;

    }

    return hrReturnVal;

}

与此示例不同,在大多数情况下,只应在应用程序中对给定 DLL 调用 LoadLibrary 和 FreeLibrary 一次。 如果要调用 DLL 中的多个函数,或重复调用 DLL 函数,则更是如此。