DLLによってエクスポートされるクラスの誤ったVtableレイアウト:ヘッダーとvtable構築に関する明確化の要求
質問
手元の問題はそうですが 解決した, 、クラスのvtableを構築するためにどのデータが使用されているか、およびvtableのレイアウトが保存される場所については、少し混乱しています。誰かが明確化を提供したり、私の好奇心を満たす可能性のある情報に私を向けたりすることができれば、私はそれを大いに感謝します。
バックグラウンド
- 2つの個別のVC6.0プロジェクト:1つはEXE用、もう1つはDLL用です。
- アプリケーションには、DLLプロジェクトリリースの.lib、.dll、および.hファイルが含まれます。
- 新しいリリースの準備ができたら、.lib、.dll、および.hファイルは コピー DLLプロジェクトからEXEプロジェクトに参加します。
- 私はこのスキームを継承しました。
問題:
私は最近、DLLに変更を加え、.libと.dllを介してコピーしましたが ヘッダーファイルをコピーするのを忘れました. 。いくつかの階層の変更があり、その結果、1つの特定のクラスのVtableがありました(それを呼び出します InternalClass
) 変わった。
exeはします メソッドを直接呼び出すことはありません InternalClass
. 。代わりに、別のクラスのインスタンスを作成します(呼び出します InterfaceClass
)anへのポインターをカプセル化します InternalClass
そのポインターに対してさまざまな方法をオブジェクトして呼び出します。
実行時に、内部から行われた通話 InterfaceClass
方法 InternalClass
方法は実際に呼び出していました 間違った方法 (すなわち InterfaceClass
呼び出します InternalClass::A
と InternalClass::B
実際に実行されます)。を見ています ASM, 、修正またはサンク(これが間違った専門用語である場合はごめんなさい!)が正しいものを使用していたことがわかります オフセット vtableに。 でも, 、vtable自体には、あなたが期待するポインターのリストが含まれていました 年 ヘッダーファイル。
実際の質問:
私は自分の間違いに気づき、ヘッダーファイルをコピーし、再コンパイルされ、すべてが順調でした。 でも, 、私は1つのしつこい質問が残されています:
VTABLESのレイアウトはいつ決定され、実行時にこれらのクラスのVTABLEを構築するために使用される情報は何ですか?つまり、私にはそうです ちゃんとした vtableは、オフセットなどをからの呼び出しに使用できるように、DLLがコンパイルされたときに組み立てられている必要があります InterfaceClass
に InternalClass
. 。それでは、なぜ時代遅れのvtableが実行時に使用されたのですか?レイアウトです また EXEが持っているヘッダーを使用してコンパイルされたときに個別に決定しますか?
これがまったく明確かどうかはわかりません。 私が求めていることが複雑すぎるかどうか教えてください。ありがとう!
解決
うーん、12歳のコンパイラのヘビーデューティの実装の詳細。クラスで__declspec(dllexport)を使用すると、リンカーはクラスのメンバーをエクスポートします。 vtableではありません。コンパイラがヘッダーファイルのクラス宣言を解析すると、クラスのクライアントで再構築されます。リンカーは、そのローカルVtableをエクスポートしたメンバーアドレスで埋めます。
コンパイラがDLLからvtableを単純にエクスポートした場合、それはよりうまく機能しますか?たぶん、しかし、そのような実装の詳細をエクスポートすることは非常に脆弱です。後ろ向きの互換性のために出ることができないコーナーに自分自身を描くことを恐れていることは、強い動機付けです、と私は想像します。
他のヒント
これらの2つのクラスの相互作用を説明する方法は、1つの非常に重要な詳細については完全に明確ではありません。 InternalClass
DLLからエクスポートされるかどうか?または、質問を別の方法で配置するには:コピーされたヘッダーファイルのコードはポインターを使用していますか InternalClass
またはそうです すべて DLLの中だけに隠されているそのクラスの知識は?
問題のクラスが実際にDLL内に完全に隠されている場合、この問題がどのように発生するかはわかりません。しかし、あなたが説明することから、それは本当にまるで聞こえます InternalClass
DLLによって公開されています。
コピーされたヘッダーがこれに類似したコードが含まれている場合:
class InterfaceClass
{
InternalClass* Internal;
void DoSomething()
{
Internal->DoSomething();
}
};
そうして InternalClass
露出する必要があります。そうしないと、EXEのプロジェクトがヘッダーのコンパイルに失敗します。 (だから、結局のところ、それは本当に内部ではありません。)
そして、もしそうなら、それはあなたの問題を簡単に説明します。 exeは、の別の宣言に対して構築されています InternalClass
DLLよりも構築されました。つまり、各バージョンの仮想テーブルをコンパイルし、それらのバージョンは異なります。
その後、実行時に、EXEコードはDLL内から作成されたオブジェクトを手渡され、したがって、そのVtableポインターがDLLのVtableバージョンを指しています。 EXEコードは、そのVtableのバージョンのVtableバージョンを指しているかのように、そのVtableポインターを介して作業しようとします。そして明らかにそれは問題を引き起こします。