我不是 MinGW 开发人员,但您的问题很常见,并不取决于您如何创建 DLL。此类问题通常使用以下三种技术来解决:
您可以使用的链接器或其他工具的确切切换取决于您的开发环境。
要了解问题,您应该知道如何加载可执行文件或 DLL。首先将EXE或DLL映射到内存中。将创建指向 EXE/DLL 的Memory mapped file(部分)。因此,您将在进程中拥有一些地址,这些地址将对应于 EXE/DLL 文件。如果链接 DLL,则可以选择基地址。如果该地址在进程地址空间中未使用,则不会执行任何操作。如果将使用第一行代码(您从 DLL 中调用某个函数),则使用地址附近的内存页面 8K 将从文件加载到内存中。如果两个进程使用相同的 DLL,则代码的物理内存将在进程之间共享。即使您持有已初始化的变量,带有变量的页面也将被共享,直到变量的第一次更改。修改时将为进行修改的进程制作内存页的副本。
在进程中加载 DLL 后,必须修改调用者的一些小部分(例如 EXE)以包含 DLL 中使用的函数的实际地址。使用来自另一个 DLL 的函数的 DLL 也会这样做。
一切听起来都很完美,但是如果您在 DLL 编译期间没有设置任何链接器选项(如果您不使用 --image-base 或 --enable-auto-image-base 链接器选项),您的所有 DLL 将具有相同的基地址(默认链接器的值)。所以第一个 DLL 可以在该地址加载。在加载与相同(或某些重叠地址)链接的第二个 DLL 期间,将完成 DLL 的重定位。在重定位期间,DLL 的代码会被修改,因此 1) DLL 的加载会很慢 2) 代码的修改副本将在进程中生成(包括 DLL 使用的内存) 3) 修改后的副本不会在 DLL 的多个实例之间共享(即使所有实例都将以相同的方式修改)。
我建议您首先使用Process Explorer 来验证哪些DLL 将在您的应用程序中重定位。您应该在“视图”/“下疼痛视图”菜单中选择“DLLs”选项,并在“选项”菜单的“配置突出显示”中选择“重定位 DLLs”复选框。您还可以自定义将显示每个 DLL 的哪些信息。像下面这样的信息越多,您将看到程序加载越慢,并且在您的应用程序实例之间或使用相同 DLL 的不同应用程序之间不会共享更多的地址空间:
在上面的示例中,您看到树 Lenovo DLL TPOSDSVC.dll、HKVOLKEY.dll 和 TPLHMM.dll 链接到相同的基地址 0x10000000,并且只有一个 DLL(此处为 TPOSDSVC.dll)将加载到该地址.其他两个 DLL 必须重新定位。
我不能在这里写一本关于这个主题的书。我建议您检查有关重定位问题的应用程序。您可以使用链接器选项(--image-base 或 --enable-auto-image-base 似乎是您所需要的)。您可以使用dumpbin.exe 工具(来自免费版的 Visual Studio)来检查 PE 映像。
在您的所有 DLL 将具有唯一的基地址之后,您可以使用另一个工具 bind.exe 和选项 -u 将 EXE 和您的 DLL 绑定到其依赖的 DLL。它将另外减少内存大小并改善应用程序的启动时间。它将更新您的 DLL 和 EXE 的 IMAGE_DIRECTORY_ENTRY_IMPORT 和 IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 部分(请参阅 the answer)。 Bind.exe 在内部使用 BindImageEx API。许多 Windows Installer 安装程序使用BindImage 操作和BindImage 表在 EXE 和 DLL 安装结束时进行绑定。
您可以考虑使用其他技术(请参阅here)来减小 DLL 和 EXE 的大小。
我不确切知道如何在 MinGW 中使用延迟加载技术,但它应该是绝对可行的。您需要在 Visual Studio 中执行两个步骤:包含 Delayimp.lib 作为附加库并使用 /DELAYLOAD 选项(请参阅 here)指定应在第一次使用时而不是直接加载 DLL 中的哪些。使用非常有用的工具Dependency Walker,您可以看到大多数标准 Microsoft DLL 都使用了该技术。如果您也使用该技术,您可以改善应用程序的启动时间并减少使用的内存。