0

当面の問題は解決されましたが、クラスの vtable を構築するためにどのデータが使用され、vtable のレイアウトがどこに保存されるかについて、少し混乱しています。誰かが説明を提供してくれたり、私の好奇心を満たしてくれる情報を教えてくれたりできれば、とてもありがたいです。

バックグラウンド

  1. 2 つの個別の VC6.0 プロジェクト: 1 つは exe 用、もう 1 つは dll 用です。
  2. アプリケーションには、dll プロジェクト リリースの .lib、.dll、および .h ファイルが含まれています。
  3. 新しいリリースの準備が整うと、.lib、.dll、および .h ファイルがdll プロジェクトから exe プロジェクトにコピーされます。
  4. この仕組みを受け継いでいます。

問題:

最近、DLL に変更を加え、.lib と .dll をコピーしましたが、ヘッダー ファイルをコピーするのを忘れていました。いくつかの階層の変更があり、その結果、1 つの特定のクラス ( と呼びますInternalClass) の vtable が変更されました。

exe は のメソッドを直接呼び出しませんInternalClass。代わりに、オブジェクトInterfaceClassへのポインターをカプセル化し、そのポインターに対してさまざまなメソッドを呼び出す別のクラスのインスタンスを作成します (それを呼び出します) 。InternalClass

実行時に、メソッド内からInterfaceClassメソッドへInternalClassの呼び出しが、実際には間違ったメソッドを呼び出していました(つまり、InterfaceClass呼び出しInternalClass::AInternalClass::B実際に実行されていました)。asmを見ると、フィックスアップまたはサンク (専門用語が間違っていたらすみません!) がvtable への正しいオフセットを使用していたことがわかります。ただし、vtable 自体には、古いヘッダー ファイルから期待されるポインタのリストが含まれていました。

実際の質問:

私は自分の間違いに気づき、ヘッダー ファイルをコピーして再コンパイルしましたが、すべて問題ありませんでした。ただし、1 つのしつこい質問が残っています。

vtables のレイアウトはいつ決定され、実行時にこれらのクラスの vtable を構築するためにどのような情報が使用されますか? つまり、dll がコンパイルされたときに適切なvtable がアセンブルされている必要があり、オフセットなどを からInterfaceClassへの呼び出しに使用できるように思われInternalClassます。では、なぜ実行時に古い vtable が使用されたのでしょうか? exeが持っているヘッダーを使用してコンパイルされたときに、レイアウトも個別に決定されますか?

これがまったく明確かどうかはわかりません。私が求めていることが複雑すぎる場合はお知らせください。ありがとう!

4

2 に答える 2

2

うーん、12 年前のコンパイラのヘビーデューティな実装の詳細。クラスで __declspec(dllexport) を使用すると、リンカーはクラスのメンバーをエクスポートします。vtable ではありません。これは、コンパイラがヘッダー ファイル内のクラス宣言を解析するときに、クラスのクライアントで再構築されます。リンカーは、そのローカル vtable に、エクスポートされたメンバー アドレスを入力します。

コンパイラが単純に vtable を DLL からエクスポートした方がうまくいくでしょうか? そうかもしれませんが、そのような実装の詳細をエクスポートするのは非常に脆弱です。下位互換性のために抜け出せない隅に自分を追い込むことへの恐怖は、強い動機になると思います。

于 2010-08-10T21:35:59.573 に答える
1

これら2つのクラスの相互作用を説明する方法は、1つの非常に重要な詳細について完全に明確ではありませんInternalClass.dllからエクスポートされているのでしょうか? または、別の言い方をすれば、これらのコピーされたヘッダー ファイルのコードは、そのクラスへのポインターを使用していますか、InternalClassそれともそのクラスのすべての知識は dll 内にのみ隠されていますか?

問題のクラスが実際に dll 内に完全に隠されている場合、この問題がどのように発生するかはわかりません。しかし、あなたが説明したことからInternalClass、dllによって公開されているように聞こえます。

コピーされるヘッダーに、概念が次のようなコードが含まれている場合:

class InterfaceClass
{
    InternalClass* Internal;

    void DoSomething()
    {
        Internal->DoSomething();
    }
};

そうしInternalClassないと、exe のプロジェクトがヘッダーのコンパイルに失敗します。(つまり、結局のところ、実際には内部的ではありません。)

その場合は、問題を簡単に説明できます。InternalClassexe は、dll がビルドされたものとは異なる宣言に対してビルドされています。つまり、それぞれが独自のバージョンの仮想テーブルをコンパイルし、それらのバージョンは異なります。

次に、実行時に、dll 内から作成されたオブジェクトが exe コードに渡され、その vtable ポインターが vtable の dll のバージョンを指します。次に、exe コードは、vtable の exe のバージョンを指しているかのように、その vtable ポインターを処理しようとします。そして明らかにそれは問題を引き起こします。

于 2010-08-10T22:11:06.277 に答える