4

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

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

4

4 回答 4

3

您必须更改PATH环境变量。尝试使用SetDllDirectory()功能。否则,您将不得不动态加载您的 DLL

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

于 2010-10-27T17:38:03.293 回答
3

这是您需要了解的有关 DLL 搜索顺序的所有信息。请注意不要引入安全问题。如果您搜索不安全的位置,攻击者可以将恶意 DLL 放在那里并让您的程序加载并执行它。

于 2010-10-27T17:49:48.210 回答
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 非常方便。

于 2010-10-27T18:19:20.440 回答
1

As I said in my comments to the question, if you are relying on static dependencies and thus loading by Windows, then you are stuck with using the standard ways in which Windows searches for dlls. And if you do not want to change the windows' path permanently, you could try running your app from a bat/cmd file and change the path just before starting your app. AFAIK that should limit the change of the path to the (duration of the) cmd instance started to execute the bat/cmd file.

More flexibility can be obtained though if you are able to change to using dynamic dependencies (remove your bpls from the required list?). As with LoadLibrary, bpls compiled to use runtime packages can be loaded dynamically as well. It is what most delphi bpl based plugin systems rely on.

(Un)Loading bpls dynamically is done using (Un)LoadPackage. LoadPackage loads the package specified by the Name parameter (using SafeLoadLibrary), checks for duplicate units, and calls the initialization blocks of all units contained in the package.

To make sure all Register procedures in a dynamically loaded bpl are called, you need to enumerate the units using a GetPackageInfo call providing a call back function.

BTW: Code samples are excerpts from a plugin system developed during a dynamic applications workshop by Mark Miller (CodeRush's developer/architect) during a 2001 conference. The code used to be online, but I can no longer find it there...

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;
于 2010-10-27T19:35:34.287 回答