封送带字符串指针的结构体参数到非托管函数
在NET项目开发过程中,经常会遇到向非托管代码封送结构体的情况,如果结构体中仅包含blittable类型/字符串/字符数组类型字段,仅需要在C#中重新声明该结构体并将该结构体作为参数传递到非托管函数即可。但若结构体中包含了指向字符串的指针,情况会稍微复杂些。
非托管结构体代码如下:
struct ParamType { wchar_t* JobBond; //字符串数组的个数 int Size; //字符串数组 wchar_t** NameList; };
extern "C" __declspec(dllexport) void WINAPI Report(ParamType ParamList[], int Size) { for(int i = 0; i < Size; i ++) { ParamType Param = ParamList[i]; wprintf(_T("%s\n"), Param.JobBond); for(int j = 0; j < Param.Size; j ++) { wprintf(_T("\t%s\n"), Param.NameList[j]); } } }
C#中以string代表wchar_t*,如非托管函数参数直接为wchar_t**,可以用ref string或out string表示指向字符串的指针。但上述示例wchar_t**存在于结构体中,对于这类指针在C#中采用IntPtr。
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct ParamType { public string JobBond; public int Size; public IntPtr NameList; }
下面需要获取字符串数组的首地址,C#中有两种方式可以获取数组的首地址:GCHandle的AddrOfPinnedObject和Marshal.UnsafeAddrOfPinnedArrayElement方法。但GCHandle无法Pinned引用类型数组,MSDN中又指明Marshal.UnsafeAddrOfPinnedArrayElement方法调用前需Pinned指定的数组。看来必须设法将字符串数组转换为值类型数组,完整代码如下:
List<ParamType> paramList = new List<ParamType>(); ParamType param1; param1.JobBond = "Engineer"; IntPtr[] namePtr1 = new IntPtr[3]; //将字符串存储到非托管内存中并返回字符串地址 namePtr1[0] = Marshal.StringToCoTaskMemUni("Desmond, Wang"); namePtr1[1] = Marshal.StringToCoTaskMemUni("Jerry, Lin"); namePtr1[2] = Marshal.StringToCoTaskMemUni("Penny"); //将namePtr1对象固定在内存中,防止垃圾收集器调整内存以致指针失效 GCHandle gch1 = GCHandle.Alloc(namePtr1, GCHandleType.Pinned); param1.NameList = gch1.AddrOfPinnedObject(); param1.Size = namePtr1.Length; paramList.Add(param1); ParamType param2; param2.JobBond = "Expert"; IntPtr[] namePtr2 = new IntPtr[2]; namePtr2[0] = Marshal.StringToCoTaskMemUni("Petter"); namePtr2[1] = Marshal.StringToCoTaskMemUni("Jordan"); GCHandle gch2 = GCHandle.Alloc(namePtr2, GCHandleType.Pinned); param2.NameList = gch2.AddrOfPinnedObject(); param2.Size = namePtr2.Length; paramList.Add(param2); ParamType[] test = paramList.ToArray(); try { Report(test, test.Length); } finally { //释放固定对象,否则会导致内存泄漏,导致程序性能下降 gch1.Free(); gch2.Free(); //释放非托管内存,避免内存泄漏 foreach (var ptr in namePtr1) Marshal.FreeCoTaskMem(ptr); foreach (var ptr in namePtr2) Marshal.FreeCoTaskMem(ptr); }
运行结果:
转载请注明出处:
http://www.cnblogs.com/desmondwang/archive/2011/12/18/MarshalStructWithStrPtr.html
posted on 2011-12-18 22:59 音符、时间、走走停停 阅读(2013) 评论(1) 编辑 收藏 举报