プログラムを開くと、コードが実際に実行される前に、プログラムがインポートする関数を持つさまざまなDLLを自動的にロードしようとします。アプリが含まれているフォルダーを検索し、次に\Windowsや\Windows\System32などの特定の場所を検索します。
いくつかのカスタムDLLを使用したいが、アプリのフォルダーをそれらで乱雑にしたくない場合、それらをサブフォルダーにインストールしてから、どこを見ればよいかを指示する何かをEXEに入れる方法はありますか?
プログラムを開くと、コードが実際に実行される前に、プログラムがインポートする関数を持つさまざまなDLLを自動的にロードしようとします。アプリが含まれているフォルダーを検索し、次に\Windowsや\Windows\System32などの特定の場所を検索します。
いくつかのカスタムDLLを使用したいが、アプリのフォルダーをそれらで乱雑にしたくない場合、それらをサブフォルダーにインストールしてから、どこを見ればよいかを指示する何かをEXEに入れる方法はありますか?
PATH
環境変数を変更する必要があります。関数を使ってみてくださいSetDllDirectory()
。それ以外の場合は、DLL を動的にロードする必要があります。
また、この質問を参照して、さらに起こりうる問題を回避してください。
DLL の検索順序について知っておくべきことはすべてここにあります。セキュリティ上の問題を引き起こさないように注意してください。セキュリティで保護されていない場所を検索すると、攻撃者はそこに悪意のある DLL を配置し、プログラムにそれをロードして実行させることができます。
質問へのコメントで述べたように、静的な依存関係に依存しているためにWindowsでロードしている場合は、Windowsがdllを検索する標準的な方法を使用することに固執します。また、Windowsのパスを永続的に変更したくない場合は、bat / cmdファイルからアプリを実行して、アプリを起動する直前にパスを変更してみてください。cmdインスタンス(の期間)へのパスの変更を制限する必要があるAFAIKは、bat/cmdファイルの実行を開始しました。
ただし、動的依存関係の使用に変更できる場合は、より柔軟性を得ることができます(必要なリストからbplsを削除しますか?)。LoadLibraryと同様に、ランタイムパッケージを使用するようにコンパイルされたbplsも動的にロードできます。これは、ほとんどのdelphibplベースのプラグインシステムが依存しているものです。
(Un)bplsの動的なロードは、(Un)LoadPackageを使用して行われます。LoadPackageは、Nameパラメーターで指定されたパッケージを(SafeLoadLibraryを使用して)ロードし、重複するユニットをチェックして、パッケージに含まれるすべてのユニットの初期化ブロックを呼び出します。
動的にロードされたbpl内のすべてのRegisterプロシージャが呼び出されるようにするには、コールバック関数を提供するGetPackageInfo呼び出しを使用してユニットを列挙する必要があります。
ところで:コードサンプルは、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;
私は動的に読み込まれる DLL が実際に好きで、ラッパー ユニットがあるだけなので、DLL 関数をあたかもコンパイルされているかのように呼び出すことができます
。このような:
function LoadDLLLibrary: Boolean;
begin
if MyDLLLib = 0 then
MyDLLLib := LoadLibrary('path to my dll'); // Only load it once.
Result := MyDLLLib <> 0;
end;
各ラッパーは LoadDLLLibrary を呼び出し (実際には 1 回だけ実行します)、次に 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 を 1 回だけロードし、1 回アンロードします。動的に読み込まれるが、頻繁に実行される DLL に非常に便利です。