How to make my program search specific folders for dependencies?
When my program opens, before any of my code actually runs, it will automatically attempt to load various DLLs whose funct开发者_如何学Goions it imports. It looks in the folder that the app is in, and then in a few specific places like \Windows and \Windows\System32.
If I want to use some custom DLLs, but I don't want to clutter up the app's folder with them, is there a way to install them to a subfolder and then put something into the EXE that tells it where to look?
You must change PATH
environment variable. Try using SetDllDirectory()
function. Otherwise, you will have to dynamically load your DLLs.
Also see this question to avoid some more possible problems.
Here's everything you need to know about DLL search order. Be careful that you don't introduce a security problem. If you search an insecure location, an attacker can put a malicious DLL there and get your program to load and execute it.
I actually like dynamically loaded DLLS, and just have a wrapper unit so I can call the DLL functions as if they were compiled right in.
I avoid the performance hit of loading/unloading all the time by having every wrapper function call my local LoadDLLLibrary, like this:
function LoadDLLLibrary: Boolean;
begin
if MyDLLLib = 0 then
MyDLLLib := LoadLibrary('path to my dll'); // Only load it once.
Result := MyDLLLib <> 0;
end;
Each of the wrappers calls LoadDLLLibrary (which only actually does anything one time) and then calls GetProcAddress. It's like this:
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;
And way down at the bottom.....
initialization
MyDLLLib := 0;
finalization
UnLoadDLLLibrary; // Final unload, as we let it stick around instead of freeing it all the time.
end.
So the end result is that I only load the DLL once, and unload it once. Very handy for DLLs that are loaded dynamically, but are executed a lot.
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;
精彩评论