zoukankan      html  css  js  c++  java
  • .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);

    // 返回整数数组中,所有元素之和。并将每个元素的值加10
    int __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);
  • 相关阅读:
    使用Redis的理由
    从输入网址到显示网页的全过程分析
    Node.js初识
    GET和POST的数据传递到底有何区别?
    第四五六周学习进度
    首尾相接整数数组中最大子数组的和
    网页版四则运算
    团队介绍及项目简介
    整数数组中最大子数组的和
    软件工程个人作业03
  • 原文地址:https://www.cnblogs.com/Clingingboy/p/1809624.html
Copyright © 2011-2022 走看看