如何让我的程序搜索特定文件夹的依赖项?

时间:2010-10-27 17:27:10

标签: windows delphi dll search-path

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

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

4 个答案:

答案 0 :(得分:3)

您必须更改PATH环境变量。尝试使用SetDllDirectory()功能。否则,您必须dynamically load your DLLs

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

答案 1 :(得分:3)

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

答案 2 :(得分:1)

我实际上喜欢动态加载的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非常方便。

答案 3 :(得分:1)

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

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

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

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

BTW:代码示例摘自2001年大会期间Mark Miller(CodeRush的开发人员/架构师)在动态应用程序研讨会期间开发的插件系统。代码曾经在线,但我再也找不到了......

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;