1

マルチスレッド COM アプリケーションの 1 つのスレッドで実行される次のコードがあるとします。

// Thread 1
struct foo {
    int (*DoSomething)(void ** parm);
};

HMODULE fooHandle = LoadLibrary("pathToFoo");
typedef int (*loadUpFooFP)(foo ** fooPtrPtr);
loadUpFooFP loadUpFoo;
loadUpFoo = (loadUpFooFP)GetProcAddress(fooHandle, "fooLoader");
foo * myFoo;
loadUpFoo(&myFoo);

これはすべてうまく機能し、電話をかけることができます

myFoo->DoSomething(&parmPtr);

これも機能します!ここで、別のスレッドがやって来て、その foo をロードします。

// Thread 2
foo * myFooInThread2;
loadUpFoo(&myFooInThread2);

そして、これもうまく機能します。スレッド 2 で DoSomething を呼び出すことができます。

// Thread 2
myFooInThread2->DoSomething(&anotherParmPtr);

さて、スレッド 1 が最終的になくなると、問題が発生します。DoSomething のアドレスが評価できなくなっていることを Visual Studio でデバッグしていることに気付きました。最初のスレッドが終了した後、私が呼び出すと:

myFooInThread2->DoSomething(&anotherParmPtr);

アクセス違反になります。myFooInThread2 ポインターは引き続き有効ですが、関数ポインターは有効ではありませんでした。この関数ポインタは、LoadLibrary によってロードされた dll にある loadUpFoo への呼び出しによって設定されました。

私の質問は次のとおりです。これが失敗する理由をどこから探し始めますか? 外部 DLL (LoadLibrary でロードする) が foo 構造体に関数ポインタを設定する方法に問題がありますか? それとも、同じライブラリを使用する異なるスレッドと関係がありますか? それとも、このアプリケーションでの COM の使用に関係している可能性があります (最初のスレッドで CoUninitialize を呼び出すと、このメモリまたはライブラリが何らかの形で解放されます)。

それが原因であると思われる場合は、COM セットアップの詳細を提供できます。ありがとう!

編集:これまでの提案に感謝します。foo 構造体は不透明です - 私はその実装についてあまり知りません。foo 構造体は、インポートするヘッダーで宣言されています。明示的に呼び出す参照カウント メソッドはなく、LoadLibrary で読み込まれたライブラリとの相互作用は他にありません。foo 構造体が一部の COM クラスにマップされたメモリではないことは確かですが、前述のように不透明であり、確かなことは言えません。

foo ポインターの有効期間は適切に管理されます (削除されません)。

foo 構造は暗号化ライブラリであるため、これ以上公開することはできません。この時点で、異なるスレッド間および COM アプリケーション内での LoadLibrary の使用に本質的に問題はないと確信しています (また、関数ポインター メモリのクリーンアップは、ライブラリ自体の制御外の何かによって引き起こされていると思います)。 )。

4

3 に答える 3

3

あなたが示したコードには、COMに関連するものは何もありません。LoadLibrary はスレッド固有ではないため、ライブラリへのハンドルを取得したら、すべてのスレッドから再利用できます。fooLoader メソッドへのポインタも同様です。

ただし、確かに fooLoader 内に COM 固有の何かが存在する可能性があります。また、ここで明確でないのは、foo インスタンスのライフタイム コントロールとは何かです。

あなたがCOMに言及し、あなたが見るファンキーな振る舞いから、fooは実際にはCOMオブジェクトのvtableのメモリマップであり、fooLoaderはDllGetClassObjectまたはCOMオブジェクトを作成する別のファクトリメソッドであるという卑劣な疑いがあります.. :-)

いずれにせよ、foo インスタンスが無効になる最も可能性の高い説明は、それが参照カウントされ、DoSomething が AddRef()/Release() を呼び出して自己破壊することです。

何が起こっているのかを正確に特定するには、fooLoader の機能と、コードが COM 関連であると考える理由について、もう少し情報を提供する必要があります。

于 2009-09-04T22:32:37.993 に答える
2

ひょっとして、スレッド 1 はシャットダウン時に呼び出しFreeLibrary(または) を行っていますか? ExitThreadAndFreeLibraryその場合、プロセスにマップされていないコードを呼び出そうとしています。インスタンス データはまだ存在するため、オブジェクトは問題なく表示されますが、そのメソッドのコードは失われます。

これが問題である場合は、ライブラリを解放しないようにスレッド 1 を変更できます。

または、2 番目のスレッドでも を呼び出すことができますLoadLibraryLoadLibrary参照カウントをFreeLibrary使用するため、DLL を 3 回ロードすると、3 回解放するまでアンロードされません。参照カウントはプロセスごとに行われるため、異なるスレッドから同じライブラリをロードできます。追加の負荷は非常に低コストです。

于 2009-09-07T21:09:57.693 に答える
0

の値はDoSomething、ロードするライブラリによって決まります。それがどこを指しているのかを判断できるはずです。Visual Studio でデバッグ出力を確認します。いつだけでなく、DLL がロードされた場所もわかります。あなたの関数は、それが指すべきだと思う DLL を本当に指していますか?

于 2009-09-07T11:06:18.083 に答える