2012-03-24 6 views
3

私はこれを学習の瞬間としてやっています。私がここで何をやっているのか分かりません。このシナリオでは、私がC++についてよく知らないことに言及する価値があるかもしれません。C#からの呼び出しを意図したC++の記述ですか?

C#では、user32.dllや他のDLLの内容を取り込むのに、DllImportを使いましたが、他の半分(C++の半分)これを実現するために実装されています。私が持っている

C++コードはシンプルで、ちょうど呼び出しが正常に通過したことを確認する:

#include <iostream> 

using namespace std; 

__declspec(dllexport) void HelloWorld() { 
    cout << "Hello, World" << endl; 
} 

私は__declspec(dllexport)の重要性があるかわからないが、私はcouple上でそれを見てきましたwebsitesはその重要性にあまり触れていませんでした。私はC++ DLLをコンパイルし、C#プロジェクトでそれを入れて、それはbinフォルダにコピーしていますよ

[DllImport("TestDLL.dll")] 
static extern void HelloWorld(); 

static void Main(string[] args) { 
    HelloWorld(); 
} 

私のC#が前に私がやった前回DllImport s以下の非常に異なるものではありません。 C#プロジェクトを実行すると、HelloWorld()のメイン関数内でEntryPointNotFoundExceptionが呼び出されます。

私の推測では、C++コードまたはC++プロジェクトのコンパイルフラグを変更する必要があります。現在、 "Use of MFC"は "Use Standard Windows Libraries"に設定されており、ATLまたはCLRは使用されません。どんな助けでも大歓迎です。

答えて

5

C++はオーバーロードをサポートする言語です。言い換えれば、複数のバージョンのHelloWorld()を持つことができます。別のバージョンのHelloWorld(int)をエクスポートすることもできます。また、リンカーが必要な言語です。異なる関数の同じ名前についてリンカを混乱させないために、コンパイラは、関数の名前をに飾ります。別名「ネームマングリング」。

このような問題のトラブルシューティングに使用するツールは、Dumpbin.exeです。 Visual StudioのコマンドプロンプトからDLLの/ exportsオプションを使用して実行します。

​​

括弧内にはエクスポートされた名前が表示されます。 ?正面に@YAXXZという名前が付けられているため、CLRはエクスポートされた機能を見つけることができません。 int引数をとる関数は、?HelloWorld @@ YAXH @ Z(try it)としてエクスポートされます。

[DllImport]ディレクティブでこれをサポートしています.EntryPointプロパティを使用して、エクスポートされた名前を付けることができます。または、C++コンパイラに、Cコンパイラが使用できるコードを生成する必要があることを伝えることができます。宣言の前にextern "C"を置くと、C++コンパイラは名前の装飾を抑止します。もちろん、関数のオーバーロードはサポートされません。 DUMPBIN.EXEは今、このことを示しています。名前はまだないプレーンな「HelloWorldの」であることを

ordinal hint RVA  name 

     1 0 00011005 HelloWorld = @ILT+0(_HelloWorld) 

注、名前の前にアンダースコアがあります。それは呼び出し規約で間違いをキャッチするのに役立つ装飾です。 32ビットコードでは、関数を呼び出す5つの異なる方法があります。そのうちの3つは、DLL、__cdecl、__stdcall、および__thiscallに共通しています。通常のフリー関数の場合、C++コンパイラはデフォルトで__cdeclになります。

これは、CallingConventionプロパティの[DllImport]属性のプロパティです。指定されていない場合に使用されるデフォルトはCallingConvention.StdCallです。これは、多くのDLL、特にWindowsの呼び出し規約に一致しますが、C++コンパイラのデフォルトとは一致しないため、まだ問題があります。単純にプロパティを使用するか、このようなあなたのC++の関数宣言:

extern "C" __declspec(dllexport) 
void __stdcall HelloWorld() { 
    // etc.. 
} 

をそしてDUMPBIN.EXE出力は、現在のようになります。

ordinal hint RVA  name 

     1 0 000110B9 [email protected] = @ILT+180([email protected]) 

注0 @追加、それはスタックの活性化の大きさを説明フレーム。つまり、何バイト分の引数が渡されます。これは、リンク時に宣言ミスをキャッチするのに役立ちます。このような間違いは、実行時に診断することは非常に困難です。

[DllImport]属性はもともと持っていたとおりに使用できるようになりました.pvoke marshallerは実際の機能の装飾を整理するのに十分なほどスマートです。あなたはExactSpellingとEntryPointのプロパティでそれを助けることができます、それは少し速くなりますが、あなたが気づくことは何もありません。

最後の質問:__declspec(dllexport)は、DLLから関数をエクスポートすることをコンパイラーに伝えるヒントです。エクスポートされた関数呼び出しを高速化するための余分なコードが生成されます(CLRは何も使用しません)。関数をエクスポートする必要があるという指示をリンカに渡します。関数のエクスポートは.defファイルでも行うことができますが、それは困難な方法です。

1

これはおそらくそれを行うための最善の方法である:How to import and use a unmanaged C++ class from C#?

私はあなたが静的にあなたの純粋なC++とリンクC++/CLIのプロジェクトを作成することをお勧めします。 C++/CLIプロジェクトはDLLを生成し、C#の他のDLLと同様に使用します。再度、上のリンクを参照してください。

1

名前のマングリングに影響を及ぼす基本的な2つのことがあります。その理由は、関数定義の呼び出しと関数の呼び出し規約のあいだにextern "C"がある場合、関数のインポートに問題があります。

cdecl呼び出し規約でextern "C"を使用すると、簡単にインポートすることができますが、呼び出し規約をDllImportAttributeに追加する必要があります。

関連する問題