在调用进程退出后,是否可以将DLL保留在内存中?

时间:2012-06-04 16:01:49

标签: c++ qt dll

我有一个需要5到10秒才能加载的DLL,这意味着每次编译和运行使用它的可执行文件时我都要等很长时间。有没有办法将DLL加载到内存中,以便每次编译相应的可执行文件时都可以立即访问它?我正在编译QT MinGW上的程序,如果这是相关的。

编辑:到目前为止没有运气。在另一个程序上加载DLL似乎没有任何效果(原始程序仍然加载DLL,并且需要这么长时间)。我猜我需要加载DLL及其功能,如果它们已被加载到另一个程序,但我不知道如何做到这一点。现在我正在使用LoadLibrary和GetProcAddress。

4 个答案:

答案 0 :(得分:2)

我不是MinGW开发人员,但您的问题很常见,并不依赖于您如何创建DLL。通常使用三种技术解决这些问题:

  • 选择DLL的唯一基址
  • 绑定DLL和exe(在安装应用程序期间)
  • DLL延迟加载技术的使用
  • 稍微改善DLL_PROCESS_ATTACH DllMain --image-base部分--enable-auto-image-base内部DisableThreadLibraryCalls来电的加载时间

您可以使用的链接器或其他工具的确切开关取决于您的开发环境。

要了解问题,您应该知道如何加载可执行文件或DLL。首先,EXE或DLL将映射到内存中。将创建指向EXE / DLL的Memory mapped file(section)。因此,您将在进程中拥有一些访问权限对应于EXE / DLL文件的地址。如果链接DLL,则可以选择基址。如果在进程地址空间中未使用该地址,则不会执行任何操作。如果将使用第一行代码(从DLL调用某些函数),则使用的地址附近的内存页8K将从文件加载到内存中。如果两个进程使用相同的DLL,那么代码的物理内存将在进程之间共享。即使您持有初始化变量,包含变量的页面也会被共享,直到变量的第一次更改为止。在修改时,将进行修改过程的内存页面的副本。

在进程中加载​​DLL之后,必须修改调用者的一些小部分(例如EXE)以包含DLL中使用的函数的实际地址。使用来自另一个DLL的函数的DLL也将完成相同的操作。

一切听起来都很完美,但是如果你在DLL编译期间没有设置任何链接器选项(如果你不使用TPOSDSVC.dllHKVOLKEY.dll链接器选项)你将拥有相同的所有DLL基址(链接器的默认值)。所以第一个DLL可以在地址加载。在加载与相同(或某些重叠地址)链接的第二DLL期间,将完成DLL的重定位。在重定位期间,DLL的代码将被修改,因此1)DLL的加载将是缓慢的2)代码的修改副本将在进程中进行(包括DLL使用的内存)3)修改后的副本将不会在DLL的多个实例之间共享(即使所有实例都将以相同的方式进行修改)。

我建议您首先使用Process Explorer来验证哪些DLL将在您的应用程序中重新定位。您应该在“视图”/“下部疼痛视图”菜单中选择“DLLs”选项,然后在“选项”菜单的“配置突出显示”中选择“重定位DLL”复选框。您还可以自定义将显示有关每个DLL的信息。下面的信息越多,您将看到加载程序的速度越慢,应用程序实例之间或使用相同DLL的不同应用程序之间不会共享的地址空间越多:

enter image description here

在上面的示例中,您会看到树联想DLL TPLHMM.dll0x10000000TPOSDSVC.dll与相同的基地--image-base和只有一个DLL({{1这里将加载到地址。另外两个DLL必须重新定位。

我不能在这里写一本关于这个主题的书。我建议您检查有关重定位问题的应用程序。您可以使用链接器选项(--enable-auto-image-basebind.exe似乎是您所需要的)。您可以使用dumpbin.exe工具(来自免费版的Visual Studio)来检查PE图像。

在所有DLL都具有唯一的基址之后,您可以使用另一个带有选项-u的工具IMAGE_DIRECTORY_ENTRY_IMPORT将EXE和DLL绑定到其依赖的DLL。它还将减少内存大小并改善应用程序的启动时间。它会更新您的DLL和EXE的IMAGE_DIRECTORY_ENTRY_DELAY_IMPORTBind.exe部分(请参阅the answer)。 BindImageEx在内部使用Delayimp.lib API。许多Windows Installer设置使用BindImage操作和BindImage表在EXE和DLL安装结束时进行绑定。

您可以考虑使用其他技术(请参阅here)来减小DLL和EXE的大小。

我不确切知道如何在MinGW中使用延迟加载技术,但它应该是绝对可能的。在Visual Studio中,您需要执行两个步骤:将/DELAYLOAD作为附加库并使用{{1}}选项(请参阅here)指定在第一次使用时应从DLL加载哪个而不是直。使用非常有用的工具Dependency Walker,您可以看到大多数标准Microsoft DLL都使用该技术。如果您也使用该技术,则可以改善应用程序的开始时间并减少已用内存。

答案 1 :(得分:1)

创建一个显式安装的系统服务,以保持DLL的加载。这种方式初始化在启动时发生,而不再发生。我建议不要使用大多数其他答案中概述的方法。虽然他们看起来像是在工作,但他们觉得我对你的软件行为不好。从用户和维护者的角度来看,我更喜欢显式安装的系统服务而不是将某些内容挂钩到winlogin.exe中。更诚实的是,您使用Windows API和环境,您在不同版本和版本升级时所具有的破坏性变化就越小。

答案 2 :(得分:1)

如果我没有错,Windows会在内存中保留一个DLL实例,因此保持这个活动应该有效:

#include <conio.h>
#include <windows.h>

int main()
    {
    HMODULE handle=LoadLibrary("yourdll.dll");
//  Make shure Windows resolves the DLL
    FARPROC dummy=GetProcAddress(handle,"functionInDll");
//  The process will now just wait for keyboard input.
    getch();
    CloseHandle(handle);
    return 0;
    }

答案 3 :(得分:1)

最简单的解决方案是(假设MSVC ++)使DLL延迟加载。权衡当然是初始化仍然必须发生,但这将不再延迟你的程序的其他部分。例如。你可以在后台线程上做到这一点。