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++代码不多,全部贴到这里:
再来看C#的结构体声明:
第二。C++导出函数和C# P-INVOKE函数的对应。
C++:
C#: 注意结构体作为返回值的P-INVOKE声明是不是很奇怪。不过运行很正常。下一篇结合反汇编说。
第三:看下C#如何调用,这里用到了Marshal.AllocHGlobal 方法,和Alloc功能基本一样,会造成内存泄露,使用完了记住使用Marshal.FreeHGlobal函数释放申请的内存。
总结一下:Marshal.StructureToPtr从托管类复制数据到未托管的内存中,Marshal.PtrToStructure恰好相反。Marshal.AllocHGlobal申请非托管内存,Marshal.FreeHGlobal函数释放非托管内存。使用Marshal.Read系列读写指针,使用Marshal.ReadIntPtr来读写二级指针。
其中有一个比较费解的是结构体作为返回值的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来读写二级指针。
相关文章推荐
- c#编程指南(十) 平台调用P-INVOKE完全掌握, 字符串和指针
- c#编程指南(十一) 平台调用P-INVOKE完全掌握, 指针大全
- c#编程指南(十) 平台调用P-INVOKE完全掌握, 字符串和指针
- c#编程指南(十二) 平台调用P-INVOKE完全掌握, 结构体边界对齐和内存布局
- c#编程指南(十) 平台调用P-INVOKE完全掌握, 字符串和指针
- c#编程指南(九) 平台调用P-INVOKE完全掌握,C#和C++互相调用
- 平台调用P-INVOKE完全掌握, 结构体和结构体指针
- 平台调用P-INVOKE完全掌握, 结构体和结构体指针
- c#编程指南(十五) 平台调用P-INVOKE完全掌握(完结篇),自定义Mashaler
- 平台调用P-INVOKE完全掌握, 指针大全
- c#编程指南——平台调用P-INVOKE完全掌握,C#和C++互相调用
- 平台调用P-INVOKE完全掌握, 字符串和指针
- 平台调用P-INVOKE完全掌握, 结构体边界对齐和内存布局
- c#编程指南(十四) 平台调用P-INVOKE完全掌握, 反汇编细解结构体作为返回值
- C#调用C++ 平台调用P/Invoke 结构体--含有内置数据类型的一维、二维数组、字符串指针【六】
- 平台调用 4000 P-INVOKE完全掌握,C#和C++互相调用
- 平台调用P-INVOKE完全掌握, 反汇编细解结构体作为返回值
- 平台调用P-INVOKE(三)--(封送结构体)
- C#调用C++ 平台调用P/Invoke 结构体--输入输出参数、返回值、返出值、结构体数组作为参数【五】
- 平台调用P-INVOKE高级篇(一)--(封送含有二维数组的结构体)