MochiuWiki : SUSE, EC, PCB
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
検索
個人用ツール
ログイン
Toggle dark mode
名前空間
ページ
議論
表示
閲覧
ソースを閲覧
履歴を表示
ライブラリの基礎 - C++DLLのソースを表示
提供: MochiuWiki : SUSE, EC, PCB
←
ライブラリの基礎 - C++DLL
あなたには「このページの編集」を行う権限がありません。理由は以下の通りです:
この操作は、次のグループのいずれかに属する利用者のみが実行できます:
管理者
、new-group。
このページのソースの閲覧やコピーができます。
== 概要 == C# EXEからC++ DLLへ様々なデータ型の変数を渡したいときがある。<br> <br><br> == DllImport属性 == <code>DLLImport</code>属性は、DLLエントリポイントを定義する関数を記述することで、DLLファイルに定義された関数を呼び出すことができる。<br> <source lang="csharp"> [ DllImport( "DLL名" ) ] </source> <br> また、DLLファイルに定義された関数を、関数名または序数で指定するには、<code>DllImport</code>属性の<code>EntryPoint</code>フィールドを使用する。<br> メソッド定義内の関数の名前がDLLエントリポイントと同じである場合は、その関数を<code>EntryPoint</code>フィールドで明示的に識別する必要はない。<br> 同じではない場合は、次のいずれかの記述で名前または序数を指定する。<br> <br> 以下の例では、<code>EntryPoint</code>フィールドを使用して、<code>MessageBoxA</code>をMsgBoxAに置き換える方法を示している。<br> <source lang="csharp"> using System; using System.Runtime.InteropServices; internal static class NativeMethods { [DllImport("user32.dll", EntryPoint = "MessageBoxA")] internal static extern int MsgBoxA(IntPtr hWnd, string lpText, string lpCaption, uint uType); } </source> <br> また、以下の例では、DLLファイルに定義された関数を序数で指定している。<br> <source lang="csharp"> [DllImport("DllName", EntryPoint = "Functionname")] [DllImport("DllName", EntryPoint = "#123")] // 序数を使用するには、序数値の前にシャープ記号#を付ける必要がある </source> <br> <code>DllImport</code>属性には、DLLファイル名を指定する以外にも、下表のような引数を与えることができる。<br> <center> {| class="wikitable" |- ! 名称 !! 説明 |- | EntryPoint || 呼び出すDLLエントリポイントの名前または序数を指定する。<br>DLLの関数名とC#上で使用する関数名を異なる名前にする時に指定する。 |- | CharSet || 文字列パラメータをメソッドにマーシャリングして、名前マングルを制御する方法を指定する。<br>文字コードの相互変換する時に指定する。<br>指定なしの場合は、<code>CharSet.Auto</code>となる。 |- | SetLastError || Win32エラー情報を維持するかどうか指定する。<br>指定なしの場合は、<code>false</code>となる。 |- | ExactSpelling || エントリポイントの関数名を厳密に一致させるかどうか指定する。<br>指定なしの場合は、<code>false</code>となる。 |- | PreserveSig || 定義通りのメソッドのシグネチャを維持するかどうか指定する。 |- | CallingConvention || アンマネージドコードを呼び出すための<u>エントリポイントの呼び出し規約</u>を、明示的に指定できる。<br>指定なしの場合は、<code>__stdcall</code>(StdCall)となる。<br>詳細は、下表を参照すること。 |} </center> <br> 詳細は、以下のMSDNのWebサイトを参照すること。<br> [https://msdn.microsoft.com/ja-jp/library/system.runtime.interopservices.dllimportattribute.aspx DllImportAttribute クラス (System.Runtime.InteropServices) | MSDN]<br> <br> * 呼び出し規約(Calling Convention) *: CallingConvention列挙型で、アンマネージドコードを呼び出すための呼び出し規約を指定する。<br> *: 下表に、CallingConvention列挙型を示す。<br> *: <br> *: また、実行時に指定のDLLを読み込めない場合、<u>モジュールが見つからない</u>として、<code>FileNotFoundException</code>や<code>DllNotFoundException</code>等の例外が投げられる。<br> System.IO.FileNotFoundException はハンドルされませんでした。 Message: 型 'System.IO.FileNotFoundException' のハンドルされていない例外が mscorlib.dll で発生しました 追加情報:ファイルまたはアセンブリ '**.dll'、またはその依存関係の 1 つが読み込めませんでした。 指定されたモジュールが見つかりません。 これは参照しているDLLがない、またはそのDLLが依存しているDLLが適切に配置されていない場合に発生します。 これは問題のDLLをdumpbinで調べ、それを配置することで解決できます。 <br> <center> {| class="wikitable" |- ! 列挙子 !! 説明 |- | Cdecl || 呼び出し元がスタックを消去する。<br>これを使用すると、<code>varargs</code>で関数を呼び出すことができる。<br>これは、<code>printf</code>関数等の可変長引数のメソッドの呼び出しで使用する。 |- | StdCall || 呼び出し先がスタックを消去する。<br>アンマネージド関数を呼び出す既定値内のテキスト。 |- | ThisCall || 最初の引数がthisポインタで、その他の引数はスタックにプッシュされる。<br>アンマネージドDLLからエクスポートしたクラスのメソッドを呼び出すために使用する。 |- | Winapi || プラットフォームに応じた既定の呼び出し規約を使用する。<br>例えば、WindowsではStdCall、Windows CE.NETではCdecl、LinuxではCdeclである。<br>(正確には、この設定は呼び出し規約ではない) |- | FastCall || この呼び出し規約はサポートされていない。 |} </center> <br> 以下の例では、呼び出し規約を適用する方法をCdeclとしている。<br> これは、呼び出し元がスタックをクリーンアップするために使用する必要がある。<br> <source lang="csharp"> using System; using System.Runtime.InteropServices; internal static class NativeMethods { // C#は可変長引数をサポートしていないため、全ての引数を明示的に定義する必要がある。 // スタックは呼び出し元によってクリーンアップされるため、CallingConvention.Cdeclを使用する必要がある。 // int printf( const char *format [, argument]... ) [DllImport("msvcrt.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] internal static extern int printf(String format, int i, double d); [DllImport("msvcrt.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] internal static extern int printf(String format, int i, String s); } public class App { public static void Main() { NativeMethods.printf("\nPrint params: %i %f", 99, 99.99); NativeMethods.printf("\nPrint params: %i %s", 99, "abcd"); } } </source> 詳細は、以下のMSDNのWebサイトを参照すること。<br> [https://msdn.microsoft.com/ja-jp/library/system.runtime.interopservices.callingconvention.aspx CallingConvention 列挙型 (System.Runtime.InteropServices) | MSDN]<br> <br><br> == StructLayout属性 == StructLayout属性とは、クラスまたは構造体のデータメンバを、メモリ内でどのように配置するかを表す。<br> <source lang="csharp"> [ StructLayout( LayoutKind列挙 ) ] </source> <br> 下表に、LayoutKind列挙子を示す。<br> <center> {| class="wikitable" |- ! LayoutKind列挙子 !! 説明 |- | Sequential || 宣言される順番に従って並べる。 |- | Explicit || <code>FieldOffsetAttribute</code>で独自のオフセットを指定して並べる。 |- | Auto || 適切なレイアウトで並べる。<br>(これを指定すると、マネージドコード外からアクセスできない) |} </center> <br> 下表に、StructLayout属性のパラメータを示す。<br> <center> {| class="wikitable" |- ! StructLayout属性のパラメータ !! 説明 !! 既定値 |- | Pack || パックサイズを指定するint値である。<br>指定可能な値は、1、2、4、8、16のいずれかである。 || 8 |- | CharSet || 文字列のマーシャリング方法を示すCharSet列挙である。 || CharSet.Auto |- | Size || 構造体またはクラスのサイズを指定する。 || |} </center> <br> <source lang="csharp"> [StructLayout( LayoutKind.Sequential )] public struct Position { public double x; public double y; public double z; } </source> <br> 詳細は、以下のMSDNのWebサイトを参照すること。<br> [https://msdn.microsoft.com/ja-jp/library/system.runtime.interopservices.structlayoutattribute.aspx StructLayoutAttribute クラス (System.Runtime.InteropServices) | MSDN]<br> <br><br> == CLSCompliant属性 == CLSCompliant属性は、CLSへの準拠を検証するかどうかをコンパイラに指示する。<br> <br> 外部から参照できない型やメンバに対しては、この属性を指定する必要は無い。<br> 指定する場合、<code>このアセンブリの外から認識できないため、CLS準拠の確認は '型' で実行されません</code>と警告が表示される。<br> <br> 以下の例では、UInt32型はCLSに準拠しないため、<code>CLSCompliant(false)</code>と指定する必要がある。<br> <source lang="csharp"> [CLSCompliant( false )] public int SetValue( UInt32 value ); </source> <br> 以下の例では、CLSCompliantAttribute属性をアセンブリ全体に適用する。<br> <source lang="csharp"> using System; [assembly: CLSCompliant(true)] </source> <br> 詳細は、以下のMSDNのWebサイトを参照すること。<br> [https://msdn.microsoft.com/ja-jp/library/system.clscompliantattribute.aspx CLSCompliantAttribute クラス (System) | MSDN]<br> <br><br> == SuppressUnmanagedCodeSecurity属性 == SuppressUnmanagedCodeSecurity属性は、アンマネージドコードの呼び出し時に実行されたスタックウォークを、実行時に省いて効率を大幅に向上させる。<br> <br> 詳細は、以下のMSDNのWebサイトを参照すること。<br> [https://msdn.microsoft.com/ja-jp/library/system.security.suppressunmanagedcodesecurityattribute.aspx SuppressUnmanagedCodeSecurityAttribute クラス | MSDN]<br> <br><br> == MarshalAs属性 == MarshalAs属性は、マネージドコードとアンマネージドコードの間で、データをマーシャリングする方法を指定する。<br> <br> 詳細は、以下のMSDNのWebサイトを参照すること。<br> [https://msdn.microsoft.com/ja-jp/library/system.runtime.interopservices.marshalasattribute.aspx MarshalAsAttribute クラス (System.Runtime.InteropServices) | MSDN]<br> <source lang="csharp"> // パラメータへの適用 public void M1([MarshalAs(UnmanagedType.LPWStr)]string msg) { } // クラスのフィールドへの適用 class MsgText { [MarshalAs(UnmanagedType.LPWStr)] public string msg = "Hello World"; } // 戻り値への適用 [return: MarshalAs(UnmanagedType.LPWStr)] public string GetMessage() { return "Hello World"; } </source> <br><br> == In属性 / Out属性 == In属性 / Out属性は、データの渡し方を指示する。<br> <center> {| class="wikitable" |- ! !! 説明 |- | In属性 || 呼び出し側にデータをマーシャリングして渡すことを示す。 |- | Out属性 || 呼び出し元にデータをマーシャリングして戻すことを示す。 |} </center> <br> <source lang="csharp"> void Method([in] int[] array); </source> <br><br> == C#のデータ型とWindows APIのデータ型 == <center> {| class="wikitable" |- ! Windows APIのデータ型<br>(括弧内は対応するC言語の型) !! 対応するC#のデータ型<br>(括弧内は.NET Frameworkでの型名) !! 備考 |- | HANDLE (void *) || System.IntPtr<br>System.UIntPtr || x86は4バイト<br>x64は8バイト |- | BYTE (unsigned char) || byte (System.Byte) || |- | SHORT (short) || short (System.Int16) || |- | WORD (unsigned short) || ushort (System.UInt16) || |- | INT (int)<br>LONG (long) || int (System.Int32) || |- | UINT (unsigned int)<br>DWORD, ULONG (unsigned long) || uint (System.UInt32) || |- | BOOL (long) || bool (System.Boolean) || |- | CHAR (char) || 文字を渡すとき<br>char (System.Char)<br>文字を受け取るとき<br>StringBuilder || |- | WCHAR(wchar_t) || 文字を渡すとき<br>char (System.Char)<br>文字を受け取るとき<br>StringBuilder || |- | LPSTR (char *, char[])<br>LPWSTR (wchar_t *, wchar_t[]) || 文字を渡すとき<br>string (System.String)<br>文字を受け取るとき<br>System.Text.StringBuilder || |- | LPCSTR (const char *, const char[])<br>LPCWSTR (const wchar_t *, const wchar_t[]) || 文字を渡すとき<br>string (System.String)<br>文字を受け取るとき<br>System.Text.StringBuilder || |- | FLOAT (float) || float (System.Single) || |- | DOUBLE (double) || double (System.Double) || |} </center> <br><br> == サンプルコード == ==== ポインタのマーシャリング ==== 例えば、C++ DLLから次のような関数がエクスポートされているとする。<br> <source lang="c++"> void WINAPI ConvertToShort(char *pstr, short *pret); </source> <br> 上記の関数において、C# EXEから使用する時は、第1引数の<code>char*</code>型は文字列なので、<code>string</code>型または<code>StringBuilder</code>型を渡す。<br> 第2引数の<code>short*</code>型は、<code>IntPtr</code>型を渡す。(<code>IntPtr</code>型は汎用ポインタを表す型であり、<code>void*</code>型とほぼ同義)<br> <br> ただし、C#は厳しい型付け言語なので、曖昧さを解決するために変換メソッドを経由する必要がある。<br> <br> 具体的には、<code>IntPtr</code>型の変数に<code>Marshal.AllocHGlobal</code>関数で必要なサイズのメモリを確保して、それをC++ DLLに渡した後、<br> <code>Marshal.ReadInt16</code>関数(型によって異なる)等で変換した後、確保したメモリを<code>Marshal.FreeHGlobal</code>関数で解放するというプロセスを経る必要がある。<br> <br> 以下の例では、C++ DLLを呼ぶC# EXEのソースコードを記述している。<br> <source lang="c#"> // DllImportを使用するために必要 using System.Runtime.InteropServices; // 呼び出し元の関数名を変更する [DllImport("DrsUtil.dll", EntryPoint = "ConvertToShort")] extern static void _ConvertToShort(string pstr, IntPtr pret); public static short ConvertToShort(string str) { // 2バイトのメモリ確保 IntPtr buffer = new IntPtr(); buffer = Marshal.AllocHGlobal(2); // C++ DLLの関数を呼ぶ _ConvertToShort(str, buffer); // 2バイトのメモリをshort型に変換 short sval = Marshal.ReadInt16(buffer); // メモリの開放 Marshal.FreeHGlobal(buffer); return sval; } </source> <br> <code>IntPtr</code>型の変数は様々なものが入る。<br> ただし、構造体を取得することもできるが、C# EXEで構造体を定義しなければならない。<br> ネイティブコードと.NET Frameworkでは型の管理方法が違うため、実際には型の相互変換(マーシャリング)が行われる。<br> <br> なお、Windows APIではBOOL型の実体はLONG型なので、.NET Frameworkではboolの代わりにintを指定することも可能である。<br> <br> ==== 配列のマーシャリング ==== このセクションでは、C# EXEからC++ DLLへ配列を渡す方法を記載する。<br> <br> まず、以下にC++ DLLを記述する。<br> <source lang="c++"> // SampleDLL.h #pragma once int __stdcall SampleArray(int array[], int length); </source> <br> <source lang="c++"> // SampleDll.cpp #include <stdio.h> #include <string.h> #include "SampleDll.h" void __stdcall SampleArray(int array[], int length) { for (int i = 0; i < length; i++) { std::cout << array[i] << std::endl; } } </source> <br> <source lang="c++"> // モジュール定義ファイル // SampleDll.def LIBRARY SampleDll EXPORTS ; 公開する関数名をリストアップ SampleArray @1 </source> <br> 次に、C# EXEからC++ DLLを呼び出す方法を記述する。<br> <br> 手順としては、以下の処理の流れとなる。<br> # C# EXEでマネージド配列(C# EXEの配列)を定義する。 # C# EXEにおいて、アンマネージド配列(C++ DLLの配列)のメモリを確保する。 # マネージド配列の内容を、上記で確保したアンマネージド配列のメモリにコピーする。 # C++ DLLの関数を実行する時、ポインタを渡す。 # 使用したアンマネージド配列のメモリを解放する。 <br> <source lang="c#"> namespace SampleEXE { [DllImport("SampleDll.dll", CallingConvention = CallingConvention.Cdecl)] static extern void SampleArray(IntPtr array, int length); static void Main() { // 配列の定義 var array = new int[Length] { 0, 1, 2, 3, 4 }; // アンマネージド配列のメモリの確保 IntPtr ptrRet = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(int)) * array.Length); // マネージド配列をアンマネージドにコピーする Marshal.Copy(array, 0, ptrRet, array.Length); // C++ DLLに配列を渡す(ポインタを渡す) SampleArray(ptrRet, array.Length); // アンマネージドメモリの解放 Marshal.FreeCoTaskMem(ptrRet); } } </source> <br> ==== 構造体のマーシャリング ==== C++ DLLの作成方法は[[ライブラリの基礎 - DLLの作成(C/C++/MFC)|ライブラリの基礎 - DLLの作成(C/C++/MFC)]]を参照する。<br> <br> 下記にもC++ DLLを記述する。<br> <source lang="c++"> SampleDLL.h double __stdcall SampleFunc01(int a); void __stdcall SampleFunc02(int a, char *pstr) void __stdcall SampleFunc03(int a, char *pstr) void __stdcall SampleFunc04(SampleStruct st) void __stdcall SampleFunc05(SampleStruct *pStructure) </source> <br> <source lang="c++"> SampleDll.cpp #include <stdio.h> #include <string.h> #include "SampleDll.h" typedef struct tagSampleStruct { int index; char name[128]; int data[50]; } SampleStruct, *pSampleStruct; typedef struct tagSampleStruct2 { int length; double *data; } SampleStruct2, *pSampleStruct2; double __stdcall SampleFunc01(int a) { printf(--<SampleDll:SampleFunc01>--\r\n"); printf("a = %d\r\n", a); printf("---------------------------\r\n"); return 3.14; } void __stdcall SampleFunc02(int a, char *pstr) { printf("--<SampleDll:SampleFunc02>--\r\n"); printf("[%d] %s\r\n", a, str); printf("-----------------------------\r\n"); } void __stdcall SampleFunc03(int a, char *pstr) { printf("--<SampleDll:SampleFunc03>--\r\n"); printf("[%d] %s\r\n", a, str); sprintf_s(str, 256, "C++ DLL側から文字列を返す場合は、StringBuilderクラスを使用する"); printf("------------------------\r\n"); } void __stdcall SampleFunc04(SampleStruct st) { printf("--<SampleDll:Sample04>--\r\n"); printf("index = %d\r\n", st.index); printf("name = %s\r\n", st.name); printf("data[0] = %d, data[1] = %d, data[2] = %d, data[3] = %d\r\n", st.data[0], st.data[1], st.data[2], st.data[3]); printf("------------------------\r\n"); } void __stdcall SampleFunc05(SampleStruct2 *pStructure) { dData[256] = {0}; printf("--<SampleDll:Sample05>--\r\n"); memset(pStructure, 0, sizeof(SampleStruct2)); pStructure->length = 10; pStructure->data = dData; for(int i = 0; i < pStructure->length; i++) { dData[i] = (i + 1) / 10.0; } printf("------------------------\r\n"); } </source> <source lang="c++"> SampleDll.def // モジュール定義ファイル LIBRARY SampleDll EXPORTS ; 公開する関数名をリストアップ SampleFunc01 @1 SampleFunc02 @2 SampleFunc03 @3 SampleFunc04 @4 SampleFunc05 @5 </source> <br> 次に、C# EXEからC++ DLLを呼び出す方法を記述する。<br> 文字列をC++ DLL側に渡す場合は、string型を使用する。<br> 文字列をC++ DLL側から返す場合は、string型ではなくStringBuilderクラスを使用する必要がある。<br> StringBuilderクラスは受け渡しの両方が可能なので、文字列はStringBuilderクラスを使用すべきである。<br> <br> C++では構造体のサイズはコンパイル時に決定されるが、C#では実行時に決定される。<br> したがって、C#側で構造体のサイズを予め指定する必要がある。<br> この場合、構造体は固定長サイズとなるため、配列などを定義する場合は異なるサイズの配列を後からインスタンス化することができなくなる。<br> <br> 構造体をC++ DLL側から返す場合、IntPtr型からdouble型の配列を取得するときは、一度Int64型に変換し、これをBitConverter.Int64BitsToDouble()メソッドでdouble型に変換する。<br> <source lang="c#"> using System; using System.Text; using System.Runtime.InteropServices; namespace SampleEXE { class Program { /// <summary> /// 最も基本的な関数のインポート例 /// </summary> /// <param name="a">4 バイト符号付き整数を指定します。</param> /// <returns>倍精度浮動小数を返します。</returns> [DllImport("SampleDLL.dll")] private static extern double SampleFunc01(int a); [DllImport("SampleDLL.dll", CharSet = CharSet.Unicode)] // C++ DLL側の文字コードがUnicodeの場合は"CharSet = CharSet.Unicode"と明示的に指定する private static extern void SampleFunc02(int a, string str); [DllImport("SampleDLL.dll", CharSet = CharSet.Unicode)] // C++ DLL側の文字コードがUnicodeの場合は"CharSet = CharSet.Unicode"と明示的に指定する private static extern void SampleFunc03(int a, StringBuilder str); /// <summary> /// 構造体を引数に持つ関数のインポート例 /// </summary> /// <param name="st">DLL 側に渡す構造体を指定します</param> [DllImport("SampleDLL.dll")] private static extern void SampleFunc04(SampleStruct st); /// <summary> /// DLL側からメンバにポインタを含む構造体を受け取る関数のインポート例 /// </summary> /// <param name="pst">受け渡す構造体の先頭アドレスを示すポインタを指定する</param> [DllImport("SampleDLL.dll")] private static extern void SampleFunc05(IntPtr pst); /// <summary> /// DLLとの取り合いのために定義する構造体 /// LayoutKind.Sequentialを指定することで、C/C++同様、変数の宣言順通りにメモリに配置されるようになる /// </summary> [StructLayout(LayoutKind.Sequential)] private struct SampleStruct { /// <summary> /// 4バイト符号付整数 /// </summary> [MarshalAs(UnmanagedType.I4)] public int index; /// <summary> /// 固定長文字配列(SizeConstは配列のサイズを示す) /// </summary> [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] public string name; /// <summary> /// 固定長配列(SizeConstは配列の要素数を示す) /// </summary> [MarshalAs(UnmanagedType.ByValArray, SizeConst = 50)] public int[] data; } /// <summary> /// DLLとの取り合いのために定義する構造体 /// LayoutKind.Sequentialを指定することで、C/C++同様、変数の宣言順通りにメモリに配置されるようにする /// </summary> [StructLayout(LayoutKind.Sequential)] private struct SampleStruct2 { public int length; public IntPtr data; } static void Main(string[] args) { var dRet= SampleFunc01(1); Console.WriteLine(dRet); Console.WriteLine(); var str = "string型で文字列を渡すことができます。"; SampleFunc02(2, str); var strb = new System.Text.StringBuilder(256); strb.Append("文字列のバッファを渡す場合は StringBuilder クラスで受け渡します。"); SampleFunc03(3, strb); Console.WriteLine(strb); var structHoge = new SampleStruct() { index = 4, name = "構造体サンプル", data = new int[50], }; structHoge.data[0] = 11; structHoge.data[1] = 22; structHoge.data[2] = 33; SampleFunc04(structHoge); // SampleStruct2構造体のサイズを取得する // 指定サイズ分だけメモリ領域を確保して、その先頭アドレスをstructPiyoに格納する var structPiyo = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SampleStruct2))); try { SampleFunc05(structPiyo); // 受け取ったstructPiyoからSampleStruct2構造体の情報に構築し直す var structFuga = (SampleStruct2)Marshal.PtrToStructure(structPiyo, typeof(SampleStruct2)); for (int i = 0; i < structFuga.length; i++) { // IntPtr型からdouble型の数値を取得するときは、一度Int64型に変換して、これをBitConverter.Int64BitsToDoubleメソッドでdouble型に変換する var v = Marshal.ReadInt64(structFuga.data, i * sizeof(double)); Console.WriteLine("data[{0}] = {1}", i, BitConverter.Int64BitsToDouble(v)); } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } finally { // 必ずメモリを解放する Marshal.FreeHGlobal(sample06_a); } Console.ReadKey(); } } } </source> <br> ==== 関数ポインタのマーシャリング ==== このセクションでは、C# EXEからC++ DLLへコールバック関数を渡す方法を記載する。<br> <br> まず、以下にC++ DLLを記述する。<br> <source lang="c++"> // SampleDLL.h #pragma once int __stdcall SampleCallback(int handle, int lPalam); </source> <br> <source lang="c++"> // SampleDll.cpp #include <stdio.h> #include <string.h> #include "SampleDll.h" using UnManagedCallback = bool (*)(int); int __stdcall SampleCallback(UnManagedCallback CallbackFunc, int lPalam) { int iRet = 0; bool bRet = CallbackFunc(lPalam); if(bRet) { iRet = 1; } else { iRet = -1; } return iRet; } </source> <br> <source lang="c++"> // モジュール定義ファイル // SampleDll.def LIBRARY SampleDll EXPORTS ; 公開する関数名をリストアップ SampleCallback @1 </source> <br> 次に、C# EXEからC++ DLLを呼び出す方法を記述する。<br> <br> マネージドコールバック関数を定義する。<br> 以下の例では、ManagedCallBackというデリゲートを宣言している。<br> C++ DLLのSampleCallBack関数にデリゲートを引数として渡すことで、既知のコールバック形式に自動的に変換される。<br> <source lang="c#"> using System; using System.Runtime.InteropServices; namespace ConsoleSample01 { delegate bool ManagedCallBack(int x); class Program { [DllImport("SampleDLL.dll")] private static extern int SampleCallback(ManagedCallBack CallBack, int lPalam); static void Main(string[] args) { var CallBack = new ManagedCallBack(CallBackFunction); var iRet = SampleCallback(CallBack, 0); Console.WriteLine($"{iRet}"); Console.ReadKey(); } public static bool CallBackFunction(int x) { Console.WriteLine($"{x}"); return true; } } } </source> == トラブル対処 == 可能な限り、デバッグビルドしたDLLとそのPDBファイルを用意する。<br> <br> ==== C++DLLのデバッグ ==== C++DLLにステップインできない場合、<br> プロジェクトの[プロパティ] - [デバッグ] - [アンマネージ コード デバッグを有効にする]または[ネイティブ コードのデバッグを有効にする]にチェックを入力する。<br> [https://msdn.microsoft.com/ja-jp/library/2wcdezs5.aspx#Anchor_3 [デバッガーを有効にする<nowiki>]</nowiki> - [デバッグ<nowiki>]</nowiki> ページ (プロジェクト デザイナー) | MSDN ネイティブ コードのデバッグ | MSDN]<br> <br> この設定をしていない場合、C++DLLからのエラーにより、アプリケーションが終了することがある。<br> <br><br> __FORCETOC__ [[カテゴリ:C_Sharp]]
ライブラリの基礎 - C++DLL
に戻る。
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
Collapse