zoukankan      html  css  js  c++  java
  • C#将结构体和指针互转的方法

    1. 功能及位置
        将数据从托管对象封送到非托管内存块,属于.NET Framework 类库
        命名空间:System.Runtime.InteropServices
        程序集:mscorlib(在 mscorlib.dll 中)
                                                                                           
    2. 语法                                                                                      
        C#:
            [ComVisibleAttribute(true)] public static void StructureToPtr (Object structure,IntPtr ptr,bool fDeleteOld);
        C++:
            [ComVisibleAttribute(true)]public: static void StructureToPtr (Object^ structure, IntPtr ptr, bool fDeleteOld);
                                                                                            
    3. 参数说明                                          
        structure:托管对象,包含要封送的数据。该对象必须是格式化类的实例。
        ptr:指向非托管内存块的指针,必须在调用此方法之前分配该指针。
        fDeleteOld:设置为 true 可在执行Marshal.DestroyStructure方法前对 ptr 参数调用此方法。请注意,传递 false 可导致内存泄漏。
                                                                                            
    4. 异常                                           
        异常类型:ArgumentException
        条件:structrue参数是泛型类型
                                                                                            
    5. 备注                                           
        StructureToPtr将结构的内容复制到 ptr 参数指向的预分配内存块。如果 fDeleteOld 参数为 true,则使用嵌入指
        针上适当的删除 API 来删除最初由 ptr 指向的缓冲区,但该缓冲区必须包含有效数据。此方法为在镜像托管类中指
        定的每个引用字段执行清理工作。
                                                    
        假设 ptr 指向非托管内存块。此内存块的布局由相应的托管类 structure 描述。StructureToPtr将字段值从结构封
        送到指针。假设 ptr 块包含引用字段,该字段指向当前包含“abc”的字符串缓冲区。假设托管端上相应的字段是包含“vwxyz”的字符串。如果不另行通知它,StructureToPtr将分配一个新的非托管缓冲区来保存“vwxyz”,并将它挂钩到 ptr 块。这将丢弃旧缓冲区“abc”使之漂移而不将其释放回非托管堆。最后,您将得到一个孤立的缓冲区,它表示在代码中存在内存泄漏。如果将 fDeleteOld 参数设置为真,则 StructureToPtr 在继续为“vwxyz”分配新缓冲区之前释放保存“abc”的缓冲区。
                                                                                           
    6. 举例    
        定义PERSON结构,并将该结构的一个变量拷贝到非托管内存,再将该内存中的PERSON还原为PERSON对象,观察其内容的变化。  
    
        
    源代码如下:
             
    using System;
    using System.Text;
    using System.Runtime.InteropServices;
    
    namespace testStructureToPtr
    {
        public static class define  //define some constant
        {        
            public const int MAX_LENGTH_OF_IDENTICARDID = 20;   //maximum length of identicardid
            public const int MAX_LENGTH_OF_NAME = 50;           //maximum length of name
            public const int MAX_LENGTH_OF_COUNTRY = 50;        //maximum length of country
            public const int MAX_LENGTH_OF_NATION = 50;         //maximum length of nation
            public const int MAX_LENGTH_OF_BIRTHDAY = 8;        //maximum length of birthday
            public const int MAX_LENGTH_OF_ADDRESS = 200;       //maximum length of address
        }
    
        public struct PERSON    //person structure
        {
            //MarshalAs:指示如何在托管代码和非托管代码之间封送数据
            //UnmanagedType:指定如何将参数或字段封送到非托管内存块
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_IDENTICARDID)]
            public byte[] identicardid;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_NAME)]
            public byte[] name;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_COUNTRY)]
            public byte[] country;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_NATION)]
            public byte[] nation;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_BIRTHDAY)]
            public byte[] birthday;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_ADDRESS)]
            public byte[] address;
        }
    
        class testProgram
        {
            private static byte _fillChar = 0;      //the fill character
    
            //convert string to byte array in Ascii with length is len        
            public static byte[] CodeBytes(string str, int len)
            {
                if (string.IsNullOrEmpty(str))
                {
                    str = string.Empty;
                }
     
                byte[] result = new byte[len];
                byte[] strBytes = Encoding.Default.GetBytes(str);
    
                //copy the array converted into result, and fill the remaining bytes with 0
                for (int i = 0; i < len; i++)
                    result[i] = ((i < strBytes.Length) ? strBytes[i] : _fillChar);
                
                return result;
            }
    
            //show the person information
            public static void ShowPerson(PERSON person)
            {
                Console.WriteLine("cardid   :" + Encoding.ASCII.GetString(person.identicardid));
                Console.WriteLine("name     :" + Encoding.ASCII.GetString(person.name));
                Console.WriteLine("country  :" + Encoding.ASCII.GetString(person.country));
                Console.WriteLine("nation   :" + Encoding.ASCII.GetString(person.nation));
                Console.WriteLine("birthday :" + Encoding.ASCII.GetString(person.birthday));
                Console.WriteLine("address  :" + Encoding.ASCII.GetString(person.address));
            }
    
            static void Main(string[] args)
            {
                PERSON person;
                person.identicardid = CodeBytes("123456198001011111", define.MAX_LENGTH_OF_IDENTICARDID);
                person.name = CodeBytes("jackson", define.MAX_LENGTH_OF_NAME);
                person.country = CodeBytes("China", define.MAX_LENGTH_OF_COUNTRY);
                person.nation = CodeBytes("HanZu", define.MAX_LENGTH_OF_NATION);
                person.birthday = CodeBytes("19800101", define.MAX_LENGTH_OF_BIRTHDAY);
                person.address = CodeBytes("Luoshan Road, Shanghai", define.MAX_LENGTH_OF_ADDRESS);
    
                int nSizeOfPerson = Marshal.SizeOf(person);
                IntPtr intPtr = Marshal.AllocHGlobal(nSizeOfPerson);
                
                Console.WriteLine("The person infomation is as follows:");
                ShowPerson(person);
    
                try
                {
                    //将数据从托管对象封送到非托管内存块,该内存块开始地址为intPtr
                    Marshal.StructureToPtr(person, intPtr, true);
    
                    //将数据从非托管内存块封送到新分配的指定类型的托管对象anotherPerson
                    PERSON anotherPerson = (PERSON)Marshal.PtrToStructure(intPtr, typeof(PERSON));
    
                    Console.WriteLine("The person after copied is as follows:");
                    ShowPerson(anotherPerson);
                }
                catch (ArgumentException)
                {
                    throw;
                }
                finally
                {
                    Marshal.FreeHGlobal(intPtr);    //free tha memory
                }
            }
        }
    }
    
    7. 分析及扩展
        结构体:
        public struct PERSON    //person structure
        {
            //MarshalAs:指示如何在托管代码和非托管代码之间封送数据
            //UnmanagedType:指定如何将参数或字段封送到非托管内存块
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_IDENTICARDID)]
            public byte[] identicardid;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_NAME)]
            public byte[] name;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_COUNTRY)]
            public byte[] country;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_NATION)]
            public byte[] nation;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_BIRTHDAY)]
            public byte[] birthday;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_ADDRESS)]
            public byte[] address;
        }
        
        A. 通过获取Person信息的函数返回的指针,再将该指针转换为结构体,并显示结构体中的内容
            a. 获取Person信息的函数
                //获取 Person信息
                [DllImport("person.dll", CallingConvention = CallingConvention.Cdecl)]
                    public static extern IntPtr GetPerson(IntPtr pEngine);
                
            b. 使用指针定义一个变量,来获取一个返回值为该指针的函数    
                IntPtr Person = GetPerson(pEngine);
            
            c. 将指针变量装换为结构体,操作如下:
               PERSON anotherPerson = (PERSON)Marshal.PtrToStructure(intPtr, typeof(PERSON)); 
               
            d. 通过ShowPerson(PERSON person)函数,用来打印输出信息
               Console.WriteLine("cardid   :" + Encoding.ASCII.GetString(anotherPerson.identicardid));
               
              
        
        B. 通过定义结构体,并赋值结构体中的数据,再将结构体转为指针数据,并将指针传入到SetPerson信息的函数中
            a. 设置Person信息的函数
                //获取 Person信息
                [DllImport("person.dll", CallingConvention = CallingConvention.Cdecl)]
                    public static extern int SetPerson(IntPtr pEngine);
        
            b. 定义结构体,并赋值结构体中的数据    
                PERSON person = new PERSON();
                person.identicardid = CodeBytes("123456198001011111", define.MAX_LENGTH_OF_IDENTICARDID);
                person.name = CodeBytes("jackson", define.MAX_LENGTH_OF_NAME);
                person.country = CodeBytes("China", define.MAX_LENGTH_OF_COUNTRY);
                person.nation = CodeBytes("HanZu", define.MAX_LENGTH_OF_NATION);
                person.birthday = CodeBytes("19800101", define.MAX_LENGTH_OF_BIRTHDAY);
                person.address = CodeBytes("Luoshan Road, Shanghai", define.MAX_LENGTH_OF_ADDRESS);
            
            c. 将结构体转为指针数据
            
                int nSizeOfPerson = Marshal.SizeOf(person);                 //定义指针长度
                IntPtr personX  = Marshal.AllocHGlobal(nSizeOfPerson);        //定义指针
                Marshal.StructureToPtr(person, personX, true);                //将结构体person转为personX指针
                
            d. 将指针传入到SetPerson函数中    
                SetPerson(personX);    

     

     

     

     

     
  • 相关阅读:
    AC日记——Little Elephant and Numbers codeforces 221b
    AC日记——Little Elephant and Function codeforces 221a
    AC日记——Mice and Holes codeforces 797f
    AC日记——Sliding Window poj 2823
    Poj 2976 Dropping tests(01分数规划 牛顿迭代)
    Bzoj 1968: [Ahoi2005]COMMON 约数研究
    洛谷 P2424 约数和
    Hdu Can you find it?(二分答案)
    SPOJ GSS1
    Bzoj 2243: [SDOI2011]染色(树链剖分+线段树)
  • 原文地址:https://www.cnblogs.com/hbtmwangjin/p/9060482.html
Copyright © 2011-2022 走看看