1. 程式人生 > >C#——Marshal.StructureToPtr方法簡介

C#——Marshal.StructureToPtr方法簡介

迎廣大朋友指正!

Marshal.StructureToPtr方法簡介

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
{
    
publicstaticclass define  //define some constant
{        
        
publicconstint MAX_LENGTH_OF_IDENTICARDID =20;   
//maximum length of identicardid
publicconstint MAX_LENGTH_OF_NAME =50;           //maximum length of name
publicconstint MAX_LENGTH_OF_COUNTRY =50;        //maximum length of country
publicconstint MAX_LENGTH_OF_NATION =50;         //maximum length of nation
publicconstint MAX_LENGTH_OF_BIRTHDAY =8;        //maximum length of birthday
publicconstint MAX_LENGTH_OF_ADDRESS =200;       //maximum length of address
    }


    
publicstruct PERSON    //person structure
{
        
//MarshalAs:指示如何在託管程式碼和非託管程式碼之間封送資料
        
//UnmanagedType:指定如何將引數或欄位封送到非託管記憶體塊
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_IDENTICARDID)]
        
publicbyte[] identicardid;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst 
= define.MAX_LENGTH_OF_NAME)]
        
publicbyte[] name;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst 
= define.MAX_LENGTH_OF_COUNTRY)]
        
publicbyte[] country;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst 
= define.MAX_LENGTH_OF_NATION)]
        
publicbyte[] nation;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst 
= define.MAX_LENGTH_OF_BIRTHDAY)]
        
publicbyte[] birthday;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst 
= define.MAX_LENGTH_OF_ADDRESS)]
        
publicbyte[] address;
    }


    
class testProgram
    
{
        
privatestaticbyte _fillChar =0;      //the fill character

        
//convert string to byte array in Ascii with length is len        
publicstaticbyte[] CodeBytes(string str, int len)
        
{
            
if (string.IsNullOrEmpty(str))
            
{
                str 
=string.Empty;
            }

 
            
byte[] result =newbyte[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
publicstaticvoid 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));
        }


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

        }

    }

}
執行過程中的物件地址及其內容如下: intPtr指向的記憶體塊的內容就是程式中對person物件所賦的初值,如下圖所示,共計378個位元組: 物件person和another的地址及其identicardid成員的地址: 物件person的identicardid成員的內容,即程式中的值123456198001011111,最後的2個位元組為0,圖中顯示的是20個元素的ASCII碼的16進位制數值: 執行結果如下: