2

アンマネージコードとの.Net相互運用性を必要とするプロジェクトに取り組んでいます。私は数週間前に.Netを使い始めましたが、C / C ++の経験は豊富で、CLRがP/Invokeをどのように処理するかに驚いています。詳細はこちらです。私の同僚がこの関数を書きました

__declspec(dllexport) int __stdcall ReadIPWSensor(unsigned int deviceClassId, void *buffer) {...}

そして私はそれをC#モジュールから呼び出さなければなりませんでした。関数を次のようにインポートしました

[DllImport("ipw", CallingConvention = CallingConvention.StdCall)]
extern static int ReadIPWSensor(uint deviceClassId, IntPtr buffer);

例外を見つけるためだけに(System.EntryPointNotFoundException、DLL'ipw'で'ReadIPWSensor'という名前のエントリポイントが見つかりません)。DependencyWalkerツールを使用したところ、関数が?ReadIPWSensor @@ YGHIPAX @ Zとしてエクスポートされていることがわかりました(同僚がDEFファイルにエクスポートするのを忘れていました)。簡単なテスト(アンマネージDLLのコンパイルが非常に遅い)のために、インポート定義を次のように変更しました。

[DllImport("ipw", EntryPoint = "#22", CallingConvention = CallingConvention.StdCall)]
extern static int ReadIPWSensor(uint deviceClassId, IntPtr buffer);

序数は22でした。新しいインポート定義でテストに合格しました。

私の最初の質問は、マングルされた関数のエクスポートを処理するときの良い習慣は何ですか?輸出序数を使用するのは良い習慣ですか?

私の場合、C ++ソースコードとDEFファイルにアクセスできたので、エクスポートを追加し、インポート定義をに戻しました。

[DllImport("ipw", CallingConvention = CallingConvention.StdCall)]
extern static int ReadIPWSensor(uint deviceClassId, IntPtr buffer);

プロジェクトですでに使用している別の関数があることを知っていたので、自分のコードを既存のコードと比較したいと思いました。関数は次のように定義されます

extern "C" __declspec(dllexport)int __stdcall LoadIPWData(void * buffer)

としてインポートされます

[DllImport("ipw", CallingConvention = CallingConvention.StdCall)]
extern static int LoadIPWData(IntPtr buffer);

驚いたことに、DependencyWalkerツールは、関数が_LoadIPWData @ 4としてエクスポートされていることを示しています(私の同僚は、DEFファイルに再度エクスポートするのを忘れていました)。ただし、この関数では、System.EntryPointNotFoundExceptionエラーは発生しません。明らかに、CLRはどういうわけか正しい名前を解決することができました。CLRが適切な機能を見つけることを可能にするある種のフォールバックメカニズムがあるようです。非常に単純に見えますが、パラメーターのサイズを合計し、「function_name@the_sum_of_all_parameter_sizes」を探していることは容易に想像できます。

私の2番目の質問は、CLRがP/Invoke中にエクスポートされた関数名とどのように一致するかです。

このシナリオでは、CLRは非常に賢いので、実際にはバグを隠していると思います。LoadIPWData関数は、他のアンマネージモジュールからその名前でアクセスできる必要があります。多分私は少しパラノイアですが、CLRが実際にどのように機能するかを知りたいです。残念ながら、そのトピックに関する私のグーグル検索はすべて無益でした。

4

1 に答える 1

3

pinvoke マーシャラーには、いくつかの一般的な DLL エクスポートの命名スキームに関する知識が組み込まれています。__cdecl 関数には多くの場合、先頭にアンダースコアがあり、32 ビット モードの __stdcall は先頭のアンダースコアと末尾の @x で装飾されていることがわかっています。ここで、x はスタックに渡される引数のバイト単位のサイズです。また、winapi 関数は末尾に余分な A または W を付けてエクスポートされることも認識しています。これは、文字列を受け入れ、ANSI バージョンと Unicode バージョンの両方がある関数を区別するための命名スキームです。対応する [DllImport] プロパティは CharSet です。一致するものが見つかるまで、すべてを試行します。

C++ コンパイラの名前装飾規則 (マングリングとも呼ばれます) については何も知らないためextern "C"、手動でそれを抑制するために使用する必要があります。

于 2012-05-06T19:35:13.167 に答える