代码改变世界

.net 互操作之p/invoke- 数据封送(结构体传参,返回值,内存管理)(3)

2010-08-26 23:47  Clingingboy  阅读(1263)  评论(0编辑  收藏  举报

除了简单的数据类型传值之外,还可以传递自定义的结构体

传参

一.同时定义非托管和托管的结构体

//typedef struct _SIMPLESTRUCT
 //{
 //    int    intValue;
 //    short  shortValue;
 //    float  floatValue;
 //    double doubleValue;
 //} SIMPLESTRUCT, *PSIMPLESTRUCT;
 [StructLayout(LayoutKind.Sequential)]
 private struct ManagedSimpleStruct
 {
     public int intValue;
     public short shortValue;
     public float floatValue;
     public double doubleValue;
 }


在托管代码中,需要注意一下几点

1.以StructLayout 来标记此结构体,以Sequential来指定结构体内存布局是相同的
2.字段定义的顺序
3.字段类型
4.字段在内存中的大小
5.非托管与托管结构名称可以不同

二.在非托管和托管代码输出结构体

1.定义托管函数

// void __cdecl TestStructArgumentByVal(SIMPLESTRUCT simpleStruct);
 [DllImport(_dllName, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
 private extern static void TestStructArgumentByVal(ManagedSimpleStruct argStruct);

2.非托管代码

void __cdecl TestStructArgumentByVal(SIMPLESTRUCT simpleStruct)
 {
     ShowNativeStructSize(sizeof(SIMPLESTRUCT));
     wprintf(L"\n结构体原数据?: int = %d, short = %d, float = %f, double = %f\n", 
         simpleStruct.intValue, simpleStruct.shortValue, simpleStruct.floatValue, simpleStruct.doubleValue);
 
     simpleStruct.intValue += 10;
 }


3.测试

ManagedSimpleStruct simpleStruct = new ManagedSimpleStruct();
 simpleStruct.intValue = 10;
 simpleStruct.shortValue = 20;
 simpleStruct.floatValue = 3.5f;
 simpleStruct.doubleValue = 6.8f;
 
 TestStructArgumentByVal(simpleStruct);
 
 Console.WriteLine("\n结构体新数据?:?int = {0}, short = {1}, float = {2:f6}, double = {3:f6}",
     simpleStruct.intValue, simpleStruct.shortValue, simpleStruct.floatValue, simpleStruct.doubleValue);

输出

image_2

二.以指针传递

1.定义托管函数和非托管函数

还是用原来的结构体

void __cdecl TestStructArgumentByRef(PSIMPLESTRUCT pStruct)
 {
     ShowNativeStructSize(sizeof(SIMPLESTRUCT));
 
     if( NULL != pStruct)
     {
         // 打印初始数据
         wprintf(L"\n结构体原数据: int = %d, short = %d, float = %f, double = %f\n", 
             pStruct->intValue, pStruct->shortValue, pStruct->floatValue, pStruct->doubleValue);
         
         // 修改数据
         pStruct->intValue++;
         pStruct->shortValue++;
         pStruct->floatValue += 1;
         pStruct->doubleValue += 1;
     }
 }

测试

ManagedSimpleStruct simpleStruct = new ManagedSimpleStruct();
 simpleStruct.intValue = 10;
 simpleStruct.shortValue = 20;
 simpleStruct.floatValue = 3.5f;
 simpleStruct.doubleValue = 6.8f;
 TestStructArgumentByVal(simpleStruct);
 
 Console.WriteLine("\n结构体新数据:int = {0}, short = {1}, float = {2:f6}, double = {3:f6}",
     simpleStruct.intValue, simpleStruct.shortValue, simpleStruct.floatValue, simpleStruct.doubleValue);

输出结果

image_6

结构体返回值

主要是注意内存的释放方式,若不以CoTaskMemAlloc方法申请内存,则释放的时候,需从托管代码释放.CoTaskMemAlloc方法申请的内存则调用FreeCoTaskMem方法来释放

1.托管函数

// PSIMPLESTRUCT __cdecl TestStructAsResult(void);
 [DllImport(_dllName, CallingConvention = CallingConvention.Cdecl)]
 private extern static IntPtr TestReturnStruct();
 
 // PSIMPLESTRUCT __cdecl TestReturnNewStruct(void)
 [DllImport(_dllName, CallingConvention = CallingConvention.Cdecl)]
 private extern static IntPtr TestReturnNewStruct();
 
 // void __cdecl FreeStruct(PSIMPLESTRUCT pStruct)
 [DllImport(_dllName, CallingConvention = CallingConvention.Cdecl)]
 private extern static void FreeStruct(IntPtr pStruct);


2.非托管函数

PSIMPLESTRUCT __cdecl TestReturnNewStruct(void)
 {
     // 使用new分配内存
     PSIMPLESTRUCT pStruct = new SIMPLESTRUCT();
 
     pStruct->intValue = 5;
     pStruct->shortValue = 4;
     pStruct->floatValue = 3.0;
     pStruct->doubleValue = 2.1;
 
     return pStruct;
 }
 //******************************************************************
 
 void __cdecl FreeStruct(PSIMPLESTRUCT pStruct)
 {
     if(NULL != pStruct)
     {
         delete pStruct;
         pStruct = NULL;
     }
 }

PSIMPLESTRUCT __cdecl TestReturnStruct(void)
 {
     // 使用CoTaskMemAlloc分配内存
     PSIMPLESTRUCT pStruct = (PSIMPLESTRUCT)CoTaskMemAlloc(
         sizeof(SIMPLESTRUCT));
 
     pStruct->intValue = 5;
     pStruct->shortValue = 4;
     pStruct->floatValue = 3.0;
     pStruct->doubleValue = 2.1;
 
     return pStruct;
 }


3.测试代码

private static void TestReturnStructByNew()
 {
     IntPtr pStruct = TestReturnNewStruct();
     ManagedSimpleStruct retStruct =
         (ManagedSimpleStruct)Marshal.PtrToStructure(pStruct, typeof(ManagedSimpleStruct));
 
        FreeStruct(pStruct);
 
     Console.WriteLine("\nint = {0}, short = {1}, float = {2:f6}, double = {3:f6}",
         retStruct.intValue, retStruct.shortValue, retStruct.floatValue, retStruct.doubleValue);
  
 }

private static void TestReturnStructByCoTaskMemAlloc()
 {
     IntPtr pStruct = TestReturnStruct();
     ManagedSimpleStruct retStruct =
         (ManagedSimpleStruct)Marshal.PtrToStructure(pStruct, typeof(ManagedSimpleStruct));
 
     Marshal.FreeCoTaskMem(pStruct);
 
     Console.WriteLine("\nint = {0}, short = {1}, float = {2:f6}, double = {3:f6}",
         retStruct.intValue, retStruct.shortValue, retStruct.floatValue, retStruct.doubleValue);
 }

指针传递

二级指针传递,同理,也需要用FreeCoTaskMem方法释放

1.托管函数

// void __cdecl TestReturnStructFromArg(PSIMPLESTRUCT* ppStruct)
 [DllImport(_dllName, CallingConvention = CallingConvention.Cdecl)]
 private extern static void TestReturnStructFromArg(ref IntPtr pStruct);

2.非托管函数

void __cdecl TestReturnStructFromArg(PSIMPLESTRUCT* ppStruct)
 {
     if( NULL != ppStruct)
     {
         *ppStruct = (PSIMPLESTRUCT)CoTaskMemAlloc(
             sizeof(SIMPLESTRUCT));
 
         (*ppStruct)->intValue = 5;
         (*ppStruct)->shortValue = 4;
         (*ppStruct)->floatValue = 3.0;
         (*ppStruct)->doubleValue = 2.1;
     }
     return;
 }


3.测试

private static void TestReturnStructByArg()
 {
     IntPtr ppStruct = IntPtr.Zero;
 
     TestReturnStructFromArg(ref ppStruct);
 
     ManagedSimpleStruct retStruct =
         (ManagedSimpleStruct)Marshal.PtrToStructure(ppStruct, typeof(ManagedSimpleStruct));
 
     Marshal.FreeCoTaskMem(ppStruct);
 
     Console.WriteLine("\nint = {0}, short = {1}, float = {2:f6}, double = {3:f6}",
         retStruct.intValue, retStruct.shortValue, retStruct.floatValue, retStruct.doubleValue);
 
 }