4

私のコードは、Visual Studio 8 を使用して C++ で記述された特定のアプリケーションのプラグインです。外部プロバイダーからの 2 つの DLL を使用します。残念ながら、DLL が見つからないため、プラグインを起動できません (プラグイン自体と同じディレクトリに配置しました)。

DLL をホスト アプリケーション ディレクトリに手動で移動またはコピーすると、プラグインが正常に読み込まれます。この移動はエンド ユーザーにとって容認できないほど面倒であると見なされたため、プラグインがその DLL を透過的にロードする方法を探しています。私に何ができる?

関連する詳細:

  • ホスト アプリケーション プラグインは、ホスト アプリケーションによって指定されたディレクトリにあります。そのディレクトリは DLL 検索パスになく、私はそれを制御していません。
  • プラグイン自体は、プラグイン ディレクトリのサブディレクトリとしてパッケージ化され、プラグイン コード自体だけでなく、プラグインに関連付けられたリソース (画像、構成ファイルなど) も保持します。「バンドル」と呼ばれるそのサブディレクトリ内の内容は制御しますが、それがどこにあるかは制御しません。
  • そのアプリの一般的なプラグイン インストール イディオムは、エンド ユーザーがプラグイン バンドルをプラグイン ディレクトリにコピーすることです。

このプラグインは、プラグインの Macintosh バージョンからの移植です。Mac では、各バイナリに独自の動的ライブラリ検索パスが含まれているため、問題はありません。これは、プラグイン バイナリに対して必要に応じて設定しました。Mac でこれを設定するには、Xcode IDE でプロジェクトを設定するだけです。これが、Visual Studio で同様のものを期待する理由ですが、関連するものは見つかりませんでした。さらに、Visual Studio のヘルプはまったく役に立たず、Google も役に立ちませんでした。

考えられる回避策は、私のコードが DLL を見つける場所を Windows に明示的に伝えることですが、その方法がわかりません。いずれにせよ、コードが開始されていないため、そうする機会がありません。

Mac 開発者として、私は非常に基本的なことを求めている可能性があることを認識しています。その場合は申し訳ありませんが、抜く毛がなくなりました。

4

4 に答える 4

7

あなたは非常に初歩的なことを求めているわけではありません。Windows は単にあなたが望むものをサポートしていません。

この問題を回避するには、いくつかのオプションがあります。

  • 2 つの DLL を作成します。必要な他の dll に対して静的にリンクするプラグイン実装 dll。そして、ホスティング アプリによって読み込まれる単純な「ファサード」DLL です。ファサード dll は、SetDllDirectory を呼び出してから LoadLibrary を呼び出して、実装 dll に必要な検索パスをロードします。次に、プラグインのエクスポートされた関数ごとに、GetProcAddress を使用して呼び出しを実装 dll に直接渡すスタブ関数を実装します。

プラグイン インターフェイスが複雑で、使用している dll インターフェイスがそうでない場合:

  • あきらめて、LoadLibrary (明示的なパスを指定) と GetProcAddress を使用して、サテライト dll の機能にアクセスします。痛み。

  • 最後のオプションは、最も文書化されておらず、Windows プログラマーに最もよく理解されていません。基本的に、.NET: Side by Side アセンブリをサポートするために構築されたテクノロジの Windows バージョンを使用します。怖がらないでください。「サイド バイ サイド アセンブリ」は非常に単純な通常の古い dll ですが、それに関する追加情報を提供する .manifest ファイルが付随しています。

これを行う理由は、SxS テクノロジを介してリンクされている dll の検索順序が通常の dll 検索順序と異なるためです。つまり、c:\windows\WinSxS を検索した後、windows は同じフォルダーを検索します。 exeのフォルダーではなく、dllを参照するdll。

まず、プラグイン dll がリンクする必要があるすべてのサテライト dll のインベントリを取得し、それらから「アセンブリ」を作成します。つまり、多数の file= ノードを含む .manifest ファイルを作成します。アセンブリに名前を付ける必要があります。「MyAssembly」と呼びましょう。

次のような内容で、dll のフォルダーにファイル "MyAssembly.manifest" を作成します (含める必要がある各 dll をリストします)。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <assemblyIdentity name="MyAssembly" processorArchitecture="*" type="win32" version="1.0.0.1"/>
    <file name="firstrequireddll.dll"/>
    <file name="2ndrequireddll.dll"/>
</assembly>

これで、アセンブリマニフェストが作成されました。半分完了しました。

次の半分は、実際に dll にアセンブリを使用させることです。そのためには、DLL ファイルにマニフェスト リソースを追加する必要があります。そのマニフェストには、最終的に次のコンテンツを含める必要があります:-

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <dependency>
        <dependentAssembly>
            <assemblyIdentity type="win32" name="MyAssembly" version="1.0.0.1" processorArchitecture="*"/>
        </dependentAssembly>
    </dependency>
</assembly>

どうやらアプリケーション マニフェスト (dll に埋め込まれている場合は紛らわしい名前です) も<file>ノードを使用できるようになっているため、アセンブリの作成をスキップしてそのまま使用することも可能です。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <file name="firstrequireddll.dll"/>
    <file name="2ndrequireddll.dll"/>
</assembly>

dll のマニフェストとして。私はまだその繰り返しをいじっていないので、それが通常のdll検索パスをどのように変更するかはわかりません(もしあったとしても)。


開発環境を知らなければ、マニフェストを dll に追加する方法をアドバイスする方法を知るのは困難です。.rc ファイルを編集して手動でマニフェストを入力する場合、DLL で使用するリソース ID は 1 ではなく 2 であることに注意してください。これは通常、exe の例で使用されます。

DevStudio 2005 以降を使用している場合は、便利な #pragma ディレクティブがあり、すべてが魔法のように正しい ID を持ち、正しい場所に配置されます。


プロジェクト設定がデフォルトの場合、VS2005 以降は自動的に生成され、必要に応じてマニフェストが埋め込まれます。この #pragma は、生成されたマニフェストに追加のアセンブリ依存関係を追加します:-

#if _MSC_VER >= 1400 // VS2005 added this directive
#pragma comment(linker, \
    "\"/manifestdependency:type='Win32' "\
    "name='Company.Product.Subsystem' "\
    "version='6.0.0.0' "\
    "processorArchitecture='*' "\
    "language='*'\"")
#endif
于 2010-04-14T14:18:59.073 に答える
2

この状況では、遅延ロードされた DLLが役に立ちます。しばらく前にまったく同じ問題に直面しましたが、実際にはかなり単純です。どのモジュールが遅延ロードされるかをリンカー (/DELAYLOADフラグ) に指定し、基本的にそれらのモジュールは PE ヘッダーに明示的なインポートとしてリストされていないため、ローダーは、前述のモジュールとそれらからの関数へのすべての呼び出しが見つからない場合に文句を言いません。モジュールは、モジュールがロードされ、関数が見つかることを保証するスタブにラップされます。

たとえば、XmlLite ライブラリの読み込みを遅らせたいとします。まず/DELAYLOAD:XmlLite.dll、リンカー フラグで指定します。次に、モジュールの初期化関数 (できればDllMain) で、XmlLite DLL を一時フォルダーに解凍し、それを呼び出しますLoadLibrary。そこから、XmlLite.dll によってエクスポートされた関数への各呼び出しが自動的に解決されます。

于 2010-04-14T21:21:39.070 に答える
0

Assuming native code and that you can use explicit run-time dynamic link (rather than any form of implicit link), use GetModuleHandle and GetModuleFileName to find out where your dll is running from.

HMODULE hModule = GetModuleHandleW(L"RunningDll.dll");
WCHAR path[MAX_PATH];
GetModuleFileNameW(hModule, path, MAX_PATH);

Then replace the base name of the dll with the name of the plugin.dll you want to load.

CString plugin(path);
int pos = plugin.Find(L"RunningDll.dll");
plugin = plugin.Left(pos);
plugin += L"pluginName.dll";

Call LoadLibrary on the generated string.

于 2010-04-14T14:45:46.760 に答える
0

GetModuleFileName () を使用して、dll が配置されているパスを見つけます。次に、SetDllDirectory () を使用して、そのパスを dll 検索パスに追加します。

于 2010-04-14T14:17:30.670 に答える