class Program { // Win32 MessageBox関数の宣言 [DllImport("user32.dll", CharSet = CharSet.Unicode)] public static extern int MessageBox(IntPtr hWnd, String text, String caption, uint type); static void Main(string[] args) { MessageBox(new IntPtr(0), "text", "caption", 0); } }使用例 - DllImportAttribute クラス (System.Runtime.InteropServices) | MSDN
このようにアンマネージドの関数を呼び出すと、コード分析で「P/Invoke を NativeMethods クラスに移動します」としてCA1060で警告されます。これに対処するには関数宣言をNativeMethodsクラス内に移動して、
internal static class NativeMethods { [DllImport("user32.dll", CharSet = CharSet.Unicode)] public static extern int MessageBox(IntPtr hWnd, String text, String caption, uint type); }
とします。このときクラス名をNativeMethodsとしないと、この警告は抑制できません。またメソッドが外部からアクセス可能だと「P/Invoke 'NativeMethods.***' の外部から可視できないように、アクセシビリティを変更します。」としてCA1401で警告されるため、internalとします。
関数名で呼び出せるようにextern "C"
を、外部から参照できるように__declspec(dllexport)
を指定して宣言します。またC#側の呼び出し規約と一致するように__stdcall
も指定します。
extern "C" __declspec(dllexport) int __stdcall Add(int a, int b);
定義では__stdcal
だけ指定します。
int __stdcall Add(int a, int b) { return a + b; }
これらの指定がなくC#から関数を見つけられないと、C#からの呼び出し時に「DLL '***.dll' の '***' というエントリ ポイントが見つかりません。」としてEntryPointNotFoundExceptionが投げられます。
DllImport属性を使用して宣言します。
class Program { [DllImport("sample.dll")] public static extern int Add(int a, int b); static void Main(string[] args) { int result = Add(2, 3); } }
C++の関数呼び出し時にMDAのPInvokeStackImbalanceが検出されるときには、呼び出し規約が一致するようにC++の関数に__stdcallを指定するか、DllImportの引数でCallingConvention.Cdeclを指定します。.net - Why do I get "PInvokeStackImbalance was detected" for this simple example? - Stack Overflow
≫C#から利用できるC++のDLLを作成する方法 | Windows Mobile アプリケーション開発
対象のクラスの生成やそのメンバを呼び出す関数をC++側で定義しておき、C#からはその関数を介して対象のクラスを利用できるようにします。using a class defined in a c++ dll in c# code - Stack Overflow
たとえば対象のクラスが、次のように定義されているとします。
// 宣言 class __declspec(dllexport) MyClass { public: MyClass(); ~MyClass(); int Func(int); }; // 定義 MyClass::MyClass(){} MyClass::~MyClass(){} int MyClass::Func(int a) { return a * 2; }
C#側からはこのクラスを生成できないため、それを仲介する関数を定義します。
// 宣言 extern "C" { extern __declspec(dllexport) MyClass* Create(); extern __declspec(dllexport) void Dispose(MyClass* obj); extern __declspec(dllexport) int Func(MyClass* obj, int a); } // 定義 MyClass* Create() { return new MyClass(); } void Dispose(MyClass* obj) { if (obj != NULL) { delete obj; obj = NULL; } } int Func(MyClass* obj, int a) { int result = 0; if (obj != NULL) { result = obj->Func(a); } return result; }
そしてC#側からは、その関数を介してC#のクラスのメンバを呼び出します。
[DllImport("sample.dll")] static public extern IntPtr Create(); [DllImport("sample.dll")] static public extern void Dispose(IntPtr obj); [DllImport("sample.dll")] static public extern int Func(IntPtr obj, int a); static void Main(string[] args) { IntPtr myClass = Create(); int result = Func(myClass, 2); Dispose(myClass); }
COMとして実装されたクラスならば、ラッパーを介せず呼び出せます。How do I DllExport a C++ Class for use in a C# Application - Stack Overflow
C++から例外が投げられると、C#側ではその型にかかわらず「外部コンポーネントが例外をスローしました。」としてSEHExceptionが発生します。そのときErrorCodeプロパティから得られるHRESULTはつねに0x80004005であり、E_FAILの意味です。