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);
  • 相关阅读:
    uva 11294 Wedding
    uvalive 4452 The Ministers’ Major Mess
    uvalive 3211 Now Or Later
    uvalive 3713 Astronauts
    uvalive 4288 Cat Vs. Dog
    uvalive 3276 The Great Wall Game
    uva 1411 Ants
    uva 11383 Golden Tiger Claw
    uva 11419 SAM I AM
    uvalive 3415 Guardian Of Decency
  • 原文地址:https://www.cnblogs.com/Clingingboy/p/1809624.html
Copyright © 2011-2022 走看看