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);    
相關文章
相關標籤/搜索