.net 互操作之p/invoke- 数据封送(嵌套结构体,类,数组)(5)

来源:互联网 发布:多少个杨永信 知乎 编辑:程序博客网 时间:2024/04/29 23:06

.net 互操作之p/invoke- 数据封送(嵌套结构体,类,数组)(5)


嵌套结构体

  复杂的数据类型往往结构体中还有结构体,即嵌套结构体,也可以说是复杂类型.如下定义

//typedef struct _PERSONNAME //{ //    char* first; //    char* last; //    char* displayName; //} PERSONNAME, *PPERSONNAME; [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct PersonName {     public string first;     public string last;     public string displayName; }  //typedef struct _PERSON //{ //    PPERSONNAME pName; //    int age;  //} PERSON, *PPERSON; [StructLayout(LayoutKind.Sequential)] public struct Person {     public IntPtr name;     public int age; }

Person中的name属性被定义成IntPtr,

测试代码

1.使用IntPtr 定义结构体

Console.WriteLine("\n结构体作为引用类型成员"); // 创?建?名?字? PersonName name = new PersonName(); name.last = "Cui"; name.first = "Xiaoyuan";  // 创?建?人? Person person = new Person(); person.age = 27;  IntPtr nameBuffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(name)); Marshal.StructureToPtr(name, nameBuffer, false);  person.name = nameBuffer;  Console.WriteLine("调?用?前?显?示?姓?名?为?:?{0}", name.displayName); TestStructInStructByRef(ref person);  PersonName newValue =     (PersonName)Marshal.PtrToStructure(person.name, typeof(PersonName));  // 释?放?在?非?托?管?代?码?中?分?配?的?PersonName实?例?内?存? Marshal.DestroyStructure(nameBuffer, typeof(PersonName));  Console.WriteLine("调?用?后?显?示?姓?名?为?:?{0}", newValue.displayName);

分析如下代码片段
//Marshal.SizeOf(name)计算此对象非托管内存所需大小,用AllocCoTaskMem申请内存IntPtr nameBuffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(name));//将对象封送到非托管内存中 Marshal.StructureToPtr(name, nameBuffer, false);

//将非托管对象封送到托管对象中PersonName newValue =     (PersonName)Marshal.PtrToStructure(person.name, typeof(PersonName));  // 释放在?非托管代码中分配的PersonName实例内存 Marshal.DestroyStructure(nameBuffer, typeof(PersonName));

2.使用结构体实例

这样内存即自动管理

Console.WriteLine("\n结?构?体?作?为?值?类?型?成?员?"); Person2 person = new Person2(); person.name.last = "Huang"; person.name.first = "Jizhou"; person.name.displayName = string.Empty; person.age = 26;  TestStructInStructByVal(ref person);

3.对象单一化

若非托管对象字段是嵌套结构体,托管代码可以声明为单一对象

[StructLayout(LayoutKind.Sequential)] public struct Person2_Flattened {     public string first;     public string last;     public string displayName;     public int age; }

3.1测试

Console.WriteLine("\n结?构?体?作?为?值?类?型?成?员?(?flattened)?"); Person2_Flattened person = new Person2_Flattened(); person.last = "Huang"; person.first = "Jizhou"; person.displayName = string.Empty; person.age = 26;  TestStructInStructByVal(ref person);

类引用传递


1.NonBlittlable引用类型

因为其为引用类型,所以无须使用ref修饰符
也是以StructLayout标记

//typedef struct _MSEMPLOYEE  //{  //    UINT  employeeID;  //    short employedYear;  //    char* displayName;   //    char* alias;   //} MSEMPLOYEE, *PMSEMPLOYEE;  [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]  private class MsEmployee  {      private uint _employeeID;      private short _employedYear;      private string _displayName;      private string _alias;       #region Properties      public uint EmployeeID      {          get { return _employeeID; }          set { _employeeID = value; }      }       public short EmployedYear      {          get { return _employedYear; }          set { _employedYear = value; }      }       public string DisplayName      {          get { return _displayName; }          set { _displayName = value; }      }       public string Alias      {          get { return _alias; }          set { _alias = value; }      }      #endregion  }

2.非托管代码

注意引用类型需要申请内存

void __cdecl GetEmployeeInfo(PMSEMPLOYEE pEmployee) {     if(NULL != pEmployee)     {         // 我?们?应?该?根?据?pEmployee->employeeID查?找?写?信?息?回?来?         pEmployee->employedYear = 2;         pEmployee->alias = (char*)CoTaskMemAlloc(255);         pEmployee->displayName = (char*)CoTaskMemAlloc(255);          strcpy_s(pEmployee->alias, 255, "xcui");         strcpy_s(pEmployee->displayName, 255, "Xiaoyuan Cui");     } }

因为是引用类型,所以注意这里指定了In,Out

[DllImport(_dllName, CallingConvention = CallingConvention.Cdecl,     CharSet = CharSet.Ansi)] private extern static void GetEmployeeInfo([In, Out] MsEmployee employee);

测试代码

MsEmployee employee = new MsEmployee(); employee.EmployeeID = 10001;  GetEmployeeInfo(employee);  Console.WriteLine("\n员?工?信?息?:"); Console.WriteLine("ID: {0}", employee.EmployeeID); Console.WriteLine("工?龄?:{0}", employee.EmployedYear); Console.WriteLine("Alias: {0}", employee.Alias); Console.WriteLine("姓?名?: {0}", employee.DisplayName);

3.Blittable引用类型

类内部全为值类型

//typedef struct _SIMPLESTRUCT //{ //    int    intValue; //    short  shortValue; //    float  floatValue; //    double doubleValue; //} SIMPLESTRUCT, *PSIMPLESTRUCT; [StructLayout(LayoutKind.Sequential)] private class ManagedClassBlittable {     private int _intValue;     private short _shortValue;     private float _floatValue;     private double _doubleValue;      #region Properties     public int IntValue     {         get { return _intValue; }         set { _intValue = value; }     }      public short ShortValue     {         get { return _shortValue; }         set { _shortValue = value; }     }      public float FloatValue     {         get { return _floatValue; }         set { _floatValue = value; }     }      public double DoubleValue     {         get { return _doubleValue; }         set { _doubleValue = value; }     }     #endregion }


[DllImport(_dllName, CallingConvention = CallingConvention.Cdecl)] private extern static void TestStructArgumentByRef(ManagedClassBlittable argClass);

非托管代码可直接修改,定义的方法也无需指定ref修饰符

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

测试

ManagedClassBlittable blittableObject = new ManagedClassBlittable(); blittableObject.IntValue = 1; blittableObject.ShortValue = 2; blittableObject.FloatValue = 3; blittableObject.DoubleValue = 4.5;  TestStructArgumentByRef(blittableObject);  Console.WriteLine("\n结?构?体?新?数?据?:?int = {0}, short = {1}, float = {2:f6}, double = {3:f6}",     blittableObject.IntValue, blittableObject.ShortValue, blittableObject.FloatValue, blittableObject.DoubleValue);

4.封送为指针

非托管代码

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


注意这里参数用out修饰符,表示安引用传递参数(ref修饰符对应 [In,Out]属性,out修饰符对应[Out]属性)
[DllImport(_dllName, CallingConvention = CallingConvention.Cdecl)] private extern static void TestReturnStructFromArg(out ManagedClassBlittable outObject);

测试

ManagedClassBlittable outObject;   TestReturnStructFromArg(out outObject);   Console.WriteLine("\n结?构?体?新?数?据?:?int = {0}, short = {1}, float = {2:f6}, double = {3:f6}",      outObject.IntValue, outObject.ShortValue, outObject.FloatValue, outObject.DoubleValue);

封送数组

1.当数组类型为blittable类型时,直接定义即可

定义函数

// int __cdecl TestArrayOfInt(int intArray[], int arraySize); [DllImport(_dllName, CallingConvention = CallingConvention.Cdecl)] private extern static int TestArrayOfInt(int[] intArray, int arraySize);

// 返回整数数组中,所有元素之和。并将每个元素的值加10int __cdecl TestArrayOfInt(int intArray[], int arraySize) {     int result = 0;     for(int i = 0; i < arraySize; i++)     {         result += intArray[i];         intArray[i] += 10;     }     return result; }

测试

int[] intArray = new int[] { 1, 2, 3, 4, 5, 6, 7 }; Console.WriteLine("\n\n调?用?前?整?数?数?组?元?素?为?:?"); foreach (int i in intArray) {     Console.Write("{0} ", i); }  int sum = TestArrayOfInt(intArray, intArray.Length);  Console.WriteLine("\n调?用?前?整?数?数?组?中?所?有?元?素?之?和?为?:?{0}", sum);  Console.WriteLine("\n调?用?后?整?数?数?组?元?素?为?:?"); foreach (int i in intArray) {     Console.Write("{0} ", i); }  Console.WriteLine();

2.若数组类型为非blittable类型时,则需要显示添加[In,Out]属性

定义函数


// UINT __cdecl TestArrayOfChar(char charArray[], int arraySize); [DllImport(_dllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] private extern static uint TestArrayOfChar([In, Out] char[] charArray, int arraySize);

// 返回字符数组中,数字字符的总数UINT __cdecl TestArrayOfChar(char charArray[], int arraySize) {     int result = 0;     for(int i = 0; i < arraySize; i++)     {         if (isdigit(charArray[i]))         {             result++;             charArray[i] = '@';         }     }     return result; }

测试代码

char[] charArray = new char[] { 'a', '1', 'b', '2', 'c', '3', '4' }; Console.WriteLine("\n调?用?前?字?符?数?组?元?素?为?:?"); foreach (char c in charArray) {     Console.Write("{0} ", c); }  uint digitCount = TestArrayOfChar(charArray, charArray.Length);  Console.WriteLine("\n调?用?前?字?符?数?组?中?数?字?个?数?为?:?{0}", digitCount);  Console.WriteLine("\n调?用?后?字?符?数?组?元?素?为?:?"); foreach (char c in charArray) {     Console.Write("{0} ", c); }

3.字符串数组(指针数组)



void __cdecl TestArrayOfString(char* ppStrArray[], int size) {     for(int i = 0; i < size; i++)     {         // 翻?转?字?符?串?         ReverseAnsiStringInPlace(ppStrArray[i]);     } }  void ReverseAnsiStringInPlace(char* rawString) {     int strLength = (int)strlen(rawString);      char tempChar;     for(int i = 0; i < strLength/2; i++)     {         tempChar = rawString[i];         rawString[i] = rawString[strLength - 1 - i];         rawString[strLength - 1 - i] = tempChar;     } }


// void __cdecl TestArrayOfString(char* ppStrArray[], int size) [DllImport(_dllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] private extern static void TestArrayOfString([In, Out] string[] charArray, int arraySize);

测试

private static void TestStringArray() {     string[] strings = new string[] {          "This is the first string.",         "Those are brown horse.",         "The quick brown fox jumps over a lazy dog." };      Console.WriteLine("\n原?始?字?符?串?数?组?中?的?元?素?为?:?");     foreach (string originalString in strings)     {         Console.WriteLine(originalString);     }      TestArrayOfString(strings, strings.Length);      Console.WriteLine("修?改?后?字?符?串?数?组?中?的?元?素?为?:?");     foreach (string reversedString in strings)     {         Console.WriteLine(reversedString);     } }

若将数组参数指定为IntPtr,则需要注意内存管理

// int __cdecl TestRefArrayOfString(void** strArray, int* size) [DllImport(_dllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] private extern static int TestRefArrayOfString(out IntPtr charArray, out int arraySize);

测试

IntPtr arrayPtr;  int arraySize; int returnCount = TestRefArrayOfString(out arrayPtr, out arraySize); IntPtr[] arrayPtrs = new IntPtr[returnCount]; Marshal.Copy(arrayPtr, arrayPtrs, 0, returnCount);  string[] strings = new string[returnCount]; for (int i = 0; i < returnCount; i++) {     strings[i] = Marshal.PtrToStringUni(arrayPtrs[i]);        Marshal.FreeCoTaskMem(arrayPtrs[i]);     Console.WriteLine("#{0}: {1}", i, strings[i]); } Marshal.FreeCoTaskMem(arrayPtr);
http://www.cnblogs.com/Clingingboy/archive/2010/08/26/1809624.html
原创粉丝点击