- ベストアンサー
C++からC#のdllを参照する際、引数内に構造体があった場合の処理
- C++からC#のdllを参照する際、引数に構造体がある場合の処理について説明します。
- C#dll内の関数の引数が整数・文字列の場合は問題ありませんが、構造体を渡す場合は注意が必要です。
- 具体的な対応方法をまとめました。
- みんなの回答 (3)
- 専門家の回答
質問者が選んだベストアンサー
こんばんは。補足頂きました。 [StructLayout(LayoutKind.Sequential)] public struct ADUser { [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)] public string account; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)] public string name; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)] public string mail; } ではないでしょうか。念の為にもう一度掲載します。 //C#サイド using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; namespace ClassLibrary1 { [ClassInterface(ClassInterfaceType.AutoDual)] public class Class1 { [StructLayout(LayoutKind.Sequential)] public struct ADUser { [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)] public string account; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)] public string name; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)] public string mail; } [MethodImpl(MethodImplOptions.Synchronized)] public int fun(int intPtr) { System.IntPtr ptr = new System.IntPtr(intPtr); ADUser test = (ADUser)Marshal.PtrToStructure(ptr, typeof(ADUser)); test.account = "123-456-7890"; test.name = "c++ to c# struct pointer"; test.mail = "xxx@abc.def.jp"; Marshal.StructureToPtr(test, ptr, true); return 0; } } } //C++サイド typedef struct ADUSER{ char account[100]; char name[100]; char mail[100]; }ADUser; int main() { ::CoInitialize( NULL ); CLSID clsid; BSTR bstrDLL = _com_util::ConvertStringToBSTR( "ClassLibrary1.Class1" ); HRESULT hResult = ::CLSIDFromProgID(bstrDLL, &clsid); if(!SUCCEEDED(hResult)) return 0; IUnknown* pUnk = NULL; hResult = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void**)&pUnk); if(!SUCCEEDED(hResult)) return 0; IDispatch* pDisp = NULL; hResult = pUnk->QueryInterface( IID_IDispatch, (void**)&pDisp ); if(!SUCCEEDED(hResult)) return 0; DISPID dispid = 0; LPOLESTR fun = L"fun"; hResult = pDisp->GetIDsOfNames(IID_NULL, &fun, 1, LOCALE_SYSTEM_DEFAULT, &dispid); if(!SUCCEEDED(hResult)) return 0; ADUser test = {0}; DISPPARAMS params = {0}; params.cArgs = 1; params.cNamedArgs = 0; params.rgdispidNamedArgs = NULL; VARIANTARG* pVarg = new VARIANTARG[params.cArgs]; ::ZeroMemory(pVarg, sizeof(VARIANTARG) * params.cArgs); pVarg[0].vt = VT_INT; pVarg[0].intVal = (int)&test; params.rgvarg = pVarg; hResult = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, ¶ms, NULL, NULL, NULL); ::printf("[account : %s][name : %s][mail : %s]\n", test.account, test.name, test.mail); ::CoUninitialize(); ::SysFreeString(bstrDLL); delete []pVarg; return 0; }
その他の回答 (2)
- machongola
- ベストアンサー率60% (434/720)
こんにちは。 例えば、C++サイドが、 typedef struct { int a; int b; } TEST; であった場合、 TEST test = {0}; pVarg[0].vt = VT_INT; pVarg[0].intVal = (int)&test; で、C#サイドが、 public void fun(int intPtr) ではないでしょうか(本当はC#サイドでSystem.IntPtrを引く方が良いと思うのですが、何故か上手くいきません)。 ポインタをintで引く場合は以下で出来ています(VisualStudio2008にて確認)。 参考程度で。 //C#サイド using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; namespace ClassLibrary1 { [ClassInterface(ClassInterfaceType.AutoDual)] public class Class1 { [StructLayout(LayoutKind.Explicit)] public struct Test { [FieldOffset(0)] public int a; [FieldOffset(4)] public int b; } [MethodImpl(MethodImplOptions.Synchronized)] public void fun(int intPtr) { System.IntPtr ptr = new System.IntPtr(intPtr); Test test = (Test)Marshal.PtrToStructure(ptr, typeof(Test)); test.a = 15; test.b = 800; Marshal.StructureToPtr(test, ptr, true); } } } //C++サイド #pragma pack(push, 4) typedef struct { int a; int b; }TEST; #pragma pack(pop) int main() { ::CoInitialize( NULL ); CLSID clsid; BSTR bstrDLL = _com_util::ConvertStringToBSTR( "ClassLibrary1.Class1" ); HRESULT hResult = ::CLSIDFromProgID(bstrDLL, &clsid); if(!SUCCEEDED(hResult)) return 0; IUnknown* pUnk = NULL; hResult = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void**)&pUnk); if(!SUCCEEDED(hResult)) return 0; IDispatch* pDisp = NULL; hResult = pUnk->QueryInterface( IID_IDispatch, (void**)&pDisp ); if(!SUCCEEDED(hResult)) return 0; DISPID dispid = 0; LPOLESTR fun = L"fun"; hResult = pDisp->GetIDsOfNames(IID_NULL, &fun, 1, LOCALE_SYSTEM_DEFAULT, &dispid); if(!SUCCEEDED(hResult)) return 0; //このポインタを渡す TEST test = {0}; DISPPARAMS params = {0}; //バリアントの数 params.cArgs = 1; params.cNamedArgs = 0; params.rgdispidNamedArgs = NULL; //バリアントの割り当て VARIANTARG* pVarg = new VARIANTARG[params.cArgs]; ::ZeroMemory(pVarg, sizeof(VARIANTARG) * params.cArgs); //バリアントにポインタを設定 pVarg[0].vt = VT_INT; pVarg[0].intVal = (int)&test; //パラメータにバリアントを設定 params.rgvarg = pVarg; //PInvoke hResult = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, ¶ms, NULL, NULL, NULL); ::printf("[a : %d][b : %d]\n", test.a, test.b); //後始末 ::CoUninitialize(); ::SysFreeString(bstrDLL); delete []pVarg; return 0; }
補足
回答ありがとうございます。 なんか、行けそうな感じなのですが、 うまく行きません。。。。 構造体のメンバ変数は、public string(C#側)のみで、 C++側の構造体のメンバを、charやBSTRにしてみたのですが、 PtrToStructureでポインタを構造体へ変換する箇所で 落ちてるみたいです。 C#のソースは今回初めて触るので、 分からない箇所ばかりなのですが、 下記に記載したC#側の構造体または、 C++の構造体に問題があるか 分かりますでしょうか? C#構造体 [StructLayout(LayoutKind.Sequential)] public struct ADUser { [MarshalAs(UnmanagedType.LPStr)] public string account; // アカウント(sAMAccount) [MarshalAs(UnmanagedType.LPStr)] public string name; // 名前(displayName) [MarshalAs(UnmanagedType.LPStr)] public string mail; // メール(mail) } C++構造体(テストのためchar[100]で宣言してみました) typedef struct ADUSER{ char account[100]; // アカウント(sAMAccount) char name[100]; // 名前(displayName) char mail[100]; // メール(mail) }ADUser; よろしくお願いします
- YUI_AI
- ベストアンサー率45% (303/661)
&pVarg じゃないですか? 下記URL等も参考にしてください。 ※ポインタ渡し
補足
&pVargじゃないかとの意味が分からないのですが、 VARIANTARGは構造体で、vtに型の種類を設定し、 pbstrValやplValといった共用体に、データ自体を設定するのですが、 独自構造体を設定する場合、 どの共用体に渡せばいいのかが分からないのです。 (というか、渡すことが可能なのかも分からない)
お礼
うまくデータ取得出来るようになりました。 詳細なプログラムまで記載していただき、 とても分かりやすい説明ありがとうございました。