封送带字符串指针的结构体参数到非托管函数

在NET项目开发过程中,经常会遇到向非托管代码封送结构体的情况,如果结构体中仅包含blittable类型/字符串/字符数组类型字段,仅需要在C#中重新声明该结构体并将该结构体作为参数传递到非托管函数即可。但若结构体中包含了指向字符串的指针,情况会稍微复杂些。

非托管结构体代码如下:

?
1
2
3
4
5
6
7
8
struct ParamType
{
     wchar_t * JobBond;
     //字符串数组的个数
     int Size;
     //字符串数组
     wchar_t ** NameList;
};
?
1
2
3
4
5
6
7
8
9
10
11
12
13
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。

?
1
2
3
4
5
6
7
[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指定的数组。看来必须设法将字符串数组转换为值类型数组,完整代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
List paramList = new List();
 
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);
}
?
1
  

运行结果:

封送带字符串指针的结构体参数到非托管函数_第1张图片

转载请注明出处:

 http://www.cnblogs.com/desmondwang/archive/2011/12/18/MarshalStructWithStrPtr.html

你可能感兴趣的:(C/C++/C#)