C#和記憶體指標
來源:qqread 作者: 出處:巧巧讀書 2007-03-16 進入討論群組
關 鍵 詞: serverword.net
雖然到了.net 的runtime 時代,C 仍然是不可磨滅的主要支柱,包括在MSDN 裡見到的API 大部都是有指標類型存在,那麼在以C 冠首的C# 裡應當如何去使用指標呢?
其實,C# 裡可以直接使用結構型的變數引用進API,但是畢竟它不是指標在對一些涉及以結構數組存放的記憶體區操作可能就力有不及了,我舉一個例子要使用到結構數組的一個API(對這個API 不必加以瞭解,我們不是主說API):
net_api_status netshareenum(
LPWSTR servername,
DWORD level,
LPBYTE* bufptr,
DWORD prefmaxlen,
LPDWORD entriesread,
LPDWORD totalentries,
LPDWORD resume_handle
);
及它要用到的結構 (設 level 為1):
typedef struct _share_info_1 {
LPWSTR shi1_netname;
DWORD shi1_type;
LPWSTR shi1_remark;
} SHARE_INFO_1, *PSHARE_INFO_1, *LPSHARE_INFO_1;
在第三個參數 LPBYTE* bufptr 它就是一個指標,而非C 型傳統語言的數組的做法,就可以直接聲明成 ref SHARE_INFO_1[] bufptr,但我不推薦這麼做,原因之一在C# 似乎還不支援結構數組塊,而且它並沒有存在數組型結構參數的重載。暫且不說如何去實現它,我希望以 C 的操作方式-指標去實現,所以我轉成 C# 聲明如下:
[structlayout(layoutkind.sequential)]
protected struct SHARE_INFO_1 {
[MarshalAs(UnmanagedType.LPWStr)] public string shi1_netname;
[MarshalAs(UnmanagedType.U4)] public uint shi1_type;
[MarshalAs(UnmanagedType.LPWStr)] public string shi1_remark;
}
[dllimport("netapi32.dll", entrypoint="netshareenum")]
protected static extern int NetShareEnum(
[MarshalAs(UnmanagedType.LPWStr)] string servername,
[MarshalAs(UnmanagedType.U4)] uint level,
ref IntPtr bufptr,
[MarshalAs(UnmanagedType.U4)] uint prefmaxlen,
[MarshalAs(UnmanagedType.U4)] ref uint entriesread,
[MarshalAs(UnmanagedType.U4)] ref uint totalentries,
[MarshalAs(UnmanagedType.U4)] ref uint resume_handle
);
在上面的函數執行完成後,會在指標 bufptr 所指的位置存在一個資料區塊,此記憶體塊的資料格式是以 share_info_1 結構排列的(假設level為1),並且在 entriesread 中指示該塊的結構個數,那麼我們要如何將它轉成真正可用的類型到 share_info_1[] shareinfo; 變數中去呢,其實 c# 提供了一種 marshal.ptrtostructure 的方法,它就是專門將指標所指的非託管記憶體塊搬到託管記憶體中的指定的結構中去,可它不支援結構數組(目前.net
1.1 還沒有該重載實現過),所以只好也最好一個一個的搬(而且一個一個搬更形象些),例子如下:
int32 ptr = bufptr.toint32();
for (int i=0; i<entriesread; i++) {
//開始將ptr 所指的記憶體塊中搬一個SHARE_INFO_1 結構大小並按SHARE_INFO_1 格式排列的資料到 SHARE_INFO_1 型變數 shareInfo 中去
SHARE_INFO_1 shareInfo = (SHARE_INFO_1)Marshal.PtrToStructure(new IntPtr(ptr), typeof(SHARE_INFO_1));
ptr += Marshal.SizeOf(shareInfo); // 這是將指標向後移一個結構位,因為已經移完一個結構了
//todo: 操作當前的 shareinfo
}
上面的例子很明顯了,bufptr 就是指標,該例子就是將記憶體中的指標所指的記憶體塊(非託管記憶體)以一個指定結構的大小和排列格式移動.net 記憶體(託管記憶體)中的一個變數去,然後將指標向後移一個位置(ptr += marshal.sizeof(shareinfo)),此位置由指標當前位置加上 marshal.sizeof(shareinfo) 也就是已經移完的大小獲得,它的意思其實就是將指標移到已經搬完的資料的後面去準備搬一下結構塊
這個例子只是說明指標的用法,並不是主要說api 對該api 及它的參數不必細加研究