異なるコンパイラバージョンでC ++ DLLを使用する
-
11-07-2019 - |
質問
この質問は、"一貫性を保つ方法に関連していますVSバージョン間のdllバイナリ?"
- アプリケーションとDLLが構築されています VC6と新しいアプリケーションが構築された VC9で。 VC9-アプリは使用する必要があります VC6でコンパイルされたDLL、ほとんど Cと1で書かれています C ++。
- C ++ライブラリは、 名前の装飾/マングリングの問題。
- VC9ですべてをコンパイルするのは 現在、そこにはオプションがありません いくつかの副作用があるようです。 これらの解決にはかなりの時間がかかります 消費します。
- C ++ライブラリを変更できますが、VC6でコンパイルする必要があります。
- C ++ libは、本質的に別のCライブラリのオブジェクト指向ラッパーです。 VC9-appは、いくつかの静的関数と非静的関数を使用します。
静的関数は次のようなもので処理できます
// Header file
class DLL_API Foo
{
int init();
}
extern "C"
{
int DLL_API Foo_init();
}
// Implementation file
int Foo_init()
{
return Foo::init();
}
非静的メソッドではそれほど簡単ではありません。
わかりましたが、クリス・ベッケ COMのようなインターフェイスを使用するという提案は、インターフェイスメンバ名がまだ装飾されているため、別のコンパイラで作成されたバイナリからアクセスできないため、役に立ちません。 私はそこにいますか
唯一の解決策は、オブジェクトへのハンドラーを使用してCスタイルのDLLインターフェイスを作成することですか、それとも何か不足していますか? その場合、ラップされたCライブラリを直接使用することで、おそらく労力が少なくなると思います。
解決
インターフェイスメンバ名は装飾されません。 は、vtable内の単なるオフセットです。ヘッダーファイルでインターフェイスを(COM" interface"ではなくC構造体を使用して)定義することができます:
struct IFoo {
int Init() = 0;
};
その後、マングルせずにDLLから関数をエクスポートできます:
class CFoo : public IFoo { /* ... */ };
extern "C" IFoo * __stdcall GetFoo() { return new CFoo(); }
互換性のあるvtableを生成するコンパイラを使用している場合、これは正常に機能します。 Microsoft C ++は、(少なくとも、DOS用のMSVC6.1以降)と同じ形式のvtableを生成しました。vtableは、関数へのポインターの単純なリストです(多重継承の場合はサンクを使用)。 GNU C ++(正しく思い出せば)は、関数ポインターと相対オフセットを持つvtableを生成します。これらは相互に互換性がありません。
他のヒント
呼び出し元のEXEとは異なるC ++コンパイラでコンパイルされたDLLを使用する場合に考慮すべき最大の問題は、メモリの割り当てとオブジェクトの有効期間です。
名前のマングリング(および呼び出し規約)を通り抜けることができると仮定していますが、互換性のあるマングリング(VC6はVS2008と広く互換性があると思います)を使用する場合、またはexternを使用する場合は難しくありません" C"。
問題が発生するのは、DLLから new
(または malloc
)を使用して何かを割り当て、それを呼び出し元に返すときです。呼び出し元の delete
(または free
)は、別のヒープからオブジェクトを解放しようとします。これは恐ろしく間違って行きます。
COMスタイルの IFoo :: Release
を実行するか、 MyDllFree()
を実行できます。これらはどちらもDLLにコールバックするため、 delete
(または free()
)の正しい実装を使用するため、正しいオブジェクトが削除されます。
または、 LocalAlloc
(たとえば)を使用して、EXEとDLLが同じヒープを使用していることを確認できます。
まあ、 Chris Beckeの提案は問題ありません。インターフェイスを使用するロジャーの最初のソリューションは使用しません名前だけで、彼が言及したように、抽象クラスと仮想メソッドの互換性のないコンパイラ処理の問題に遭遇する可能性があります。ロジャーは、彼のフォローで魅力的なCOM一貫性のある事例を指摘していますa>。
-
問題点:少なくともIUnknown:AddRefとIUnknown:Releaseに依存して、COMインターフェイスリクエストを作成し、IUnknownを適切に処理することを学ぶ必要があります。インターフェイスの実装が複数のインターフェイスをサポートできる場合、またはメソッドがインターフェイスを返すこともできる場合、IUnknown:QueryInterfaceに慣れる必要があるかもしれません。
-
重要なアイデアは次のとおりです。インターフェイスの実装を使用する(ただし、実装しない)すべてのプログラムは、共通の#include" *。h"を使用します。インターフェースを構造体(C)またはC / C ++クラス(VC ++)または構造体(VC ++ではなくC ++)として定義するファイル。 * .hファイルは、C言語プログラムまたはC ++言語プログラムのどちらをコンパイルしているかに応じて、自動的に適切に適応します。 * .hファイルを使用するだけで、その部分について知る必要はありません。 * .hファイルが行うことは、インターフェイスの構造体または型、たとえばIFooをその仮想メンバー関数で定義することです(このアプローチでは関数のみであり、この方法ではデータメンバーに直接表示されません)。
-
ヘッダーファイルは、Cで動作し、使用されるC ++コンパイラに関係なくC ++で動作するように、COMバイナリ標準を尊重するように構築されています。 (Java JNIの人々はこれを理解しました。)これは、関数エントリポインタ(vtable)で完全に構成される構造体がすべて同じメモリにマップされる限り、任意の起源の別々にコンパイルされたモジュール間で動作することを意味します(したがって、すべてx86 32ビット、またはすべてx64などである必要があります。)
-
ある種のラッパークラスを介してCOMインターフェイスを実装するDLLでは、ファクトリエントリポイントのみが必要です。
のようなものextern" C" HRESULT MkIFooImplementation(void ** ppv);
(これらについても学習する必要があります)また、IFooインターフェイスポインターを受け取るために指定した場所に* pvを返します。 (私はざっと目を通しているので、ここで必要になる詳細な注意事項があります。構文を信用しないでください)これに使用する実際の関数ステレオタイプは、*。hファイルでも宣言されています。
-
重要なのは、ファクトリーエントリ(常に装飾されていないextern" C"必要なすべてのラッパークラスの作成を行い、指定した場所へのIfooインターフェイスポインターを配信します。これは、クラスを作成するためのすべてのメモリ管理、およびクラスを完成させるためのすべてのメモリ管理などが、ラッパーをビルドするDLLで行われることを意味します。これらの詳細を処理する必要がある唯一の場所です。
-
ファクトリ関数からOKの結果を取得すると、インターフェイスポインターが発行され、既に予約されています(インターフェイスポインターに代わって既に暗黙的なIFoo:Addref操作が実行されています)配信されました)。
-
インターフェイスを使い終わったら、インターフェイスのIFoo:Releaseメソッドを呼び出してインターフェイスを解放します。ファクトリDLLのクラスとそのインターフェイスサポートを破棄するのは、最終リリースの実装です(AddRefをさらにコピーした場合)。これが、interfの背後にある一貫した動的なストレージ割り当てとリリースへの正しい依存を得るものです。
さまざまなライブラリで使用されているランタイムなど、他にも考慮する必要があるものがあります。オブジェクトが共有されていない場合は問題ありませんが、一見しただけではまったく考えられません。
Chris Beckerの提案はかなり正確です。実際の COMインターフェースを使用すると、必要なバイナリ互換性を得るのに役立ちます。走行距離は異なる場合があります:)
面白くない、男。あなたは多くのフラストレーションを感じています。おそらくこれを与えるべきでしょう:
唯一の解決策は、 ハンドラーを使用したCスタイルのDLLインターフェイス オブジェクトにまたは私は行方不明です 何か?その場合、私は推測する、私は おそらくより少ない労力で ラップされたCライブラリを直接使用します。
本当によく見ます。幸運を祈ります。