【问题标题】:How to make my program search specific folders for dependencies?如何让我的程序在特定文件夹中搜索依赖项?
【发布时间】:2011-05-01 11:00:20
【问题描述】:

当我的程序打开时,在我的任何代码实际运行之前,它会自动尝试加载它所导入函数的各种 DLL。它会先查看应用所在的文件夹,然后再查看一些特定的位置,例如 \Windows 和 \Windows\System32。

如果我想使用一些自定义 DLL,但我不想将应用程序的文件夹与它们混在一起,有没有办法将它们安装到子文件夹中,然后在 EXE 中放入一些东西来告诉它在哪里查看?

【问题讨论】:

  • 梅森我错过了什么?您已经在自己寻找不同的位置。多一个应该不是问题吧?毕竟大多数插件系统都要求插件编写者将他们的 dll 安装在主应用程序的指定文件夹中?还是您希望文件夹名称是免费的?在那种情况下,所有“正常”的配置选项方式都对您开放?为什么要“将某些内容放入 EXE”?迷惑了... :-)
  • @Marjan:您缺少的是,这些是内置的依赖项,由 Windows 在加载时加载, 在 DPR 的第一行之前,而不是动态加载-在应用程序启动后被加载的插件。
  • 在这种情况下,您仅限于 windows 路径,并且必须将您的 dll 放在那里的文件夹中,或者将您的 dll 文件夹添加到 windows 的环境路径中...如果您不想弄乱机器的 Windows 路径:您可以从 bat/cmd 文件运行您的应用程序并在启动应用程序之前更改路径,这应该限制对环境设置的更改(持续时间)我认为是 cmd 实例。

标签: windows delphi dll search-path


【解决方案1】:

您必须更改PATH 环境变量。尝试使用SetDllDirectory() 函数。否则,您将不得不dynamically load your DLLs

另请参阅this 问题以避免更多可能的问题。

【讨论】:

    【解决方案2】:

    这里是everything you need to know about DLL search order。请注意不要引入安全问题。如果您搜索不安全的位置,攻击者可以将恶意 DLL 放在那里并让您的程序加载并执行它。

    【讨论】:

      【解决方案3】:

      我实际上喜欢动态加载的 DLLS,并且只有一个包装单元,所以我可以调用 DLL 函数,就好像它们是直接在其中编译的一样。
      我通过让每个包装函数调用我的本地 LoadDLLLibrary 来避免一直加载/卸载的性能损失,如下所示:

      function LoadDLLLibrary: Boolean;
      begin
        if MyDLLLib = 0 then
          MyDLLLib := LoadLibrary('path to my dll');   // Only load it once.
        Result := MyDLLLib <> 0;
      end;
      

      每个包装器都调用 LoadDLLLibrary(它实际上只执行一次),然后调用 GetProcAddress。是这样的:

      procedure DoSomeDLLStuff;
      var
        DLLProc: TExecuteDoSomeDLLStuffProc;
      begin
        LoadDLLLibrary;
        try
          if MyDLLLib <> 0 then
          begin
            DLLProc := GetProcAddress(MyDLLLib , PROC_SomeDLLSTuff);
            DLLProc;  // run it
          end;
        finally
          // No need to unload, it'll get unloaded in finalization.
        end;
      end;
      

      在底部......

      initialization
        MyDLLLib := 0;
      
      finalization
        UnLoadDLLLibrary;  // Final unload, as we let it stick around instead of freeing it all the time.
      end.
      

      所以最终结果是我只加载了一次 DLL,然后卸载了一次。对于动态加载但执行次数较多的 DLL 非常方便。

      【讨论】:

      • 是的,但是当有问题的 DLL 是 BPL 运行时包时,它的效果就不那么好了。
      • @Mason,那么您应该重新提出您的问题。唉,我有其他问题的完美答案......
      • 好吧,我确实在问题中指定了这些是在加载时加载的,在应用程序启动之前......
      • @Mason:实际上,使用运行时包编译的 bpl 也可以动态加载。试图挖掘一个旧的例子......
      • 从什么时候开始将 BPL:s 视为 DLL:s?我想@Mason 在一般意义上将“DLL”读作“动态加载的库”,而我(可能还有其他一些人)将其解释为“Windows *.DLL 文件”......
      【解决方案4】:

      正如我在 cmets 中对问题所说,如果您依赖静态依赖项并因此由 Windows 加载,那么您将无法使用 Windows 搜索 dll 的标准方式。如果您不想永久更改 Windows 的路径,您可以尝试从 bat/cmd 文件运行您的应用程序,并在启动您的应用程序之前更改路径。应该限制​​路径更改到 cmd 实例(持续时间)开始执行 bat/cmd 文件的 AFAIK。

      如果您能够更改为使用动态依赖项(从所需列表中删除您的 bpls?),则可以获得更大的灵活性。与 LoadLibrary 一样,编译为使用运行时包的 bpls 也可以动态加载。这是大多数基于 delphi bpl 的插件系统所依赖的。

      (Un)Loading bpls 是使用 (Un)LoadPackage 动态完成的。 LoadPackage 加载由 Name 参数指定的包(使用 SafeLoadLibrary),检查重复单元,并调用包中包含的所有单元的初始化块。

      为确保调用动态加载的 bpl 中的所有 Register 过程,您需要使用提供回调函数的 GetPackageInfo 调用枚举单元。

      顺便说一句:代码示例摘自 Mark Miller(CodeRush 的开发人员/架构师)在 2001 年会议期间在动态应用程序研讨会期间开发的插件系统。以前网上有代码,但是现在找不到了……

      var
        localModuleHandle: HModule;
      begin
        try
          localModuleHandle := LoadPackage(packageName);
      
          //GetPackageInfo accesses the given package's info table and enumerates
          //  all the contained units and required packages 
          Flags := ufAllUnits;
          GetPackageInfo(localModuleHandle, Pointer(localModuleHandle), Flags, PackageIsLoadingProc);
        except
          on e: Exception do
            Application.MessageBox(PChar(e.Message), PChar(sError), MB_OK + MB_ICONWARNING);
        end;  
      end;
      
      procedure PackageIsLoadingProc(const Name: string; NameType: TNameType;
                                     Flags: Byte; Param: Pointer);
      type
        TRegisterProc = procedure;
      var
        RegisterProc: TRegisterProc;
        localName: String;
      begin
      //  Flags:
      //  ufMainUnit = $01;
      //  ufPackageUnit = $02;
      //  ufWeakUnit = $04;
      //  ufOrgWeakUnit = $08;
      //  ufImplicitUnit = $10;
      //  ufWeakPackageUnit = ufPackageUnit or ufWeakUnit;
      
        if NameType = ntContainsUnit then
          begin
            localName := LowerCase(Name);
            if Length(localName) > 0 then
              localName[1] := UpCase(localName[1]);
      
            @RegisterProc := GetProcAddress(HModule(Param), 
                                            PChar('@' + localName + '@Register$qqrv'));
            if @RegisterProc <> nil then
              RegisterProc;
          end;
      end;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-07-26
        • 2022-06-11
        相关资源
        最近更新 更多