您的位置:首页 > 编程语言 > C#

c#编程指南(十三) 平台调用P-INVOKE完全掌握, 结构体和结构体指针

2010-08-26 16:52 531 查看
这篇讲关于结构体和结构体指针的P-INVOKE,关键有4个P-INVOKE类型,结构体作为输入输出参数。结构体指针作为输入输出参数。还有结构体内的成员类型分为:数组,指针,指针数组,结构体,结构体指针,结构体数组,结构体指针数组。当然还有类继承(这里只介绍了单继承)。
其中有一个比较费解的是结构体作为返回值的P-INVOKE的奇怪现象,下一篇结合反汇编讲解。

第一:C++结构体和C#结构体对应关系,看下面。这里提到一点C# 声明结构体中的成员是数组的必须像下面那样声明:使用[MarshalAs(UnmanagedType.ByValArray, SizeConst = N)]

C++代码不多,全部贴到这里:

struct Base
{
int BaseInt;
};

struct Test : Base
{
int  TestIntArray[2];
//
int * TestIntPointer;
int * TestIntPointerArray[2];
//
Base TestBase;
Base * TestBasePoint;
Base TestBaseArray[2];
Base * TestBasePointerArray[2];
};


再来看C#的结构体声明:

[StructLayout(LayoutKind.Sequential)]
public struct Base
{
public int BaseInt;
}

[StructLayout(LayoutKind.Sequential)]
public struct Test
{
public Base _base;//把继承的基类放在第一个元素的位置。
//
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public int[] TestIntArray;
//
public IntPtr TestIntPointer;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public IntPtr[] TestIntPointerArray;
//
public Base TestBase;
public IntPtr TestBasePoint;
//
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public Base[] TestBaseArray;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public IntPtr[] TestBasePointerArray;
}


第二。C++导出函数和C# P-INVOKE函数的对应。
C++:

static Test _test;
void SetTest(Test test)
{
_test = test;
PrintTest();
}

void SetTestPointer(Test * lptest)
{
_test = * lptest;
PrintTest();
}

Test GetTest()
{
return _test;
}

Test * GetTestPointer()
{
return &_test;
}


C#: 注意结构体作为返回值的P-INVOKE声明是不是很奇怪。不过运行很正常。下一篇结合反汇编说。

[DllImport("TestDll")]
public static extern void SetTest(Test test);

[DllImport("TestDll")]
public static extern void SetTestPointer(IntPtr lptest);

[DllImport("TestDll")]
public static extern void GetTest(IntPtr lptest); //注意声明。

[DllImport("TestDll")]
public static extern IntPtr GetTestPointer();


第三:看下C#如何调用,这里用到了Marshal.AllocHGlobal 方法,和Alloc功能基本一样,会造成内存泄露,使用完了记住使用Marshal.FreeHGlobal函数释放申请的内存。

private Test _test = new Test();

public void Run()
{
InitTest();
//#########################
SetTest(_test);
Console.WriteLine("-------------------------------------------------------------\n");
//#########################
_test._base.BaseInt = 9999;
//Marshal.AllocHGlobal 和WIN32 API, Alloc功能基本一样,
//这个方法不要多用,可能造成内存泄露。
//记住使用Marshal.FreeHGlobal函数释放申请的内存
IntPtr p = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Test)));
Marshal.StructureToPtr(_test,p,false);
SetTestPointer(p);
Console.WriteLine("-------------------------------------------------------------\n");
//#########################
IntPtr pp = GetTestPointer();
Test temp = (Test)Marshal.PtrToStructure(pp, typeof(Test));
PrintTest(temp);
Console.WriteLine("-------------------------------------------------------------\n");

//#########################
IntPtr pp2 = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Test)));
GetTest(pp2);
Test temp2 = (Test)Marshal.PtrToStructure(pp2, typeof(Test));
PrintTest(temp2);

}


总结一下:Marshal.StructureToPtr从托管类复制数据到未托管的内存中,Marshal.PtrToStructure恰好相反。Marshal.AllocHGlobal申请非托管内存,Marshal.FreeHGlobal函数释放非托管内存。使用Marshal.Read系列读写指针,使用Marshal.ReadIntPtr来读写二级指针。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐