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

在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);
        }
 

运行结果:

image

转载请注明出处:

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

转载于:https://www.cnblogs.com/desmondwang/archive/2011/12/18/MarshalStructWithStrPtr.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值