8

私が達成しようとしていることの長い物語

DLL をプラグインとして動的にロードするプログラムに取り組んでいます。Microsoft Visual C++ 2008 を使用してプログラムをコンパイルしています。それでも、Qt が動作するすべての Visual C++ バージョンをサポートする必要があると仮定しましょう。プログラム ディレクトリのレイアウトは次のとおりです。

| plugins/
|   plugin1.dll
|   plugin2.dll
| QtCore4.dll
| QtGui4.dll
| program.exe

program.exeすべてのプラグイン DLL ファイルを検出し、それらに対して LoadLibrary() を実行し、特定の署名関数を呼び出して、それが実際にプラグインであるかどうかを調べます。これは、MSVC90 用の vcredist がインストールされているコンピューターでうまく機能します。当然のことながら、プログラムをすべてのコンピューターで動作させるには、msvc*.dll ファイルと適切なマニフェスト ファイルを使用してプログラムを再配布する必要があります。Qt DLL を実行するには redist も必要です。

ここで、選択した Visual Studio のバージョンに応じて、適切な redist DLL とマニフェストを自動的にコピーするように cmake をセットアップしました。簡単にするために、私は MSVC90 を使用していると仮定します。redist がプログラム ディレクトリにコピーされると、レイアウトは次のようになります。

| plugins/
|   plugin1.dll
|   plugin2.dll
| QtCore4.dll
| QtGui4.dll
| msvcm90.dll
| msvcp90.dll
| msvcr90.dll
| Microsoft.VC90.CRT.manifest (I'm also aware that this file is bugged in VS2008)
| program.exe

マニフェスト ファイルのバグについて: http://www.cmake.org/pipermail/cmake/2008-September/023822.html

問題

このレイアウトのプログラムは、redist がインストールされていないコンピューターで動作するようになりましたが、プラグインは読み込まれません。プラグインをロードするには、次のいずれかを実行する必要があります。

  1. マニフェスト ファイルをplugins/ディレクトリにコピーします。msvc*.dll ファイルへのすべての参照をマニフェスト ファイルから削除します。これは機能しますが、使用されている MSVC のバージョンに応じて、編集されたマニフェスト ファイルの異なるバージョンをサポートする必要があるため、うまくいきません。また、2008 以外の Visual Studio でこれが壊れないかどうかもわかりません。
  2. redist 全体をplugins/ディレクトリにコピーします。これにはマニフェスト ファイルを変更する必要はありませんがprogram.exe、msvc*.dll ファイルをプラグインと見なして愚かにもロードしようとします。当然、これは正常に失敗するため、大きな害はありません。もう 1 つの欠点は、プログラム パッケージのサイズが 1 MB 以上大きくなることです。ただし、これらの問題はどちらも私が一緒に暮らすことができるものです。
  3. /MT スイッチを使用してプラグインをコンパイルします。program.exe簡単なテストでは、これが実際に機能することが示されていますが、Qt と/MDの両方が将来的に壊れないかどうかはわかりません。

質問)

最善の解決策は何ですか? 正しい解決策は何ですか?正しい解決策が複数ある場合、ベスト プラクティスはどれですか。これをやろうとしたのは私が初めてですか?

更新 1 (2012 年 11 月 18 日)

質問には答えられないままですが、私は最も頭痛の少ないルートを選ぶことにしました。今まで私は解決策 1 を使用してきましたが、それを使い続けることにしました。CMake は、ユーザーが 2008 以外のバージョンの MSVC を使用していることを検出すると、自動パッケージングが完全にはサポートされていないことを示す警告メッセージを表示します。

4

3 に答える 3

1

ターゲット OS に _WIN32_WINNT >= 0x0502 がある場合、関数を使用できます

SetDllDirectory()

プラグインをロードする前に。

メイン プログラム フォルダへのパスを入力します。

この呼び出しは、システムのロード順序をオーバーライドします。

  1. アプリケーションのロード元のディレクトリ。
  2. SetDllDirectory() 呼び出しのパスで指定されたディレクトリ。

そのため、アプリケーションの起動後に関数を呼び出すことができます。すべての場合において安全です。幸運を!

于 2012-11-07T11:00:04.567 に答える
1

「LoadLibrary」に完全なファイル パスを指定できるため、プラグインをそのパスでロードできます。この正確なレイアウトを使用して、Visual Studio 2005 の現在の dll のサブディレクトリから同じライブラリの複数のバージョンをロードしました。

最初に、次を使用して現在の dll の現在のパスを取得する必要があります。

static LPSTR strDLLPath1 = new TCHAR[_MAX_PATH+1];
::GetModuleFileName((HINSTANCE)&__ImageBase, strDLLPath1, _MAX_PATH);

ただし、program.exe がこれらのプラグイン ファイルを既に検出している場合は、それらの完全なパスに既にアクセスしていると思います。

于 2014-11-27T11:41:36.160 に答える
0

インストール プロセスで CreateHardLink() 関数を使用して、VC dll へのハードリンクを作成できます。あなたが説明した方法(1)では、VCRT dllの異なるコピーに問題がある可能性があります。Hardlinks または SetDllDirectory() が最適なソリューションのようです。

MSVCRT への単一プロセスの静的リンクと動的リンクを混在させないでください。常に問題が発生します。

于 2012-11-07T15:56:11.593 に答える