由於CF是一個精簡(Compact)的.NET Framework,因此在開發CF應用時有一部分功能MS並沒有在CF中實現,需要直接調用Win32的API來實現。由於Win32的API是面向native代碼的,一般會提供一個DLL庫以及相應header文件。使用C++是十分容易和簡單調用這些API,但是使用C# .NET就沒那麼幸運了。C#調用Win32的API往往要借助於P/Invoke的幫助,下面講述一下P/Invoke 的使用。
API的引入
調用Win32的API需要引入相應的DLL,Wince下大部分的API存在於coredll.dll裡面,同時DllImport不僅僅支持Win32 API,他可以支持任何native 的DLL的引入。
[DllImport("coredll.dll", CharSet = CharSet.Auto)]
錯誤情況處理
調用Win32的API一般通過返回值表示調用結果,推薦使用Exception代替返回值的方式表達錯誤和異常情況。
int lStatus = RasHangUp(rasSession);
if (lStatus != 0)
{
throw new ApplicationException("RasHangUp(" + rasSession + ") failed with error code: " + lStatus);
}
參數的轉換
調用Win32的API大部分工作就是在對調用參數進行類型轉換,這些參數基於.NET的角度叫做unmanaged type(native type),對應於.NET built-in 的managed type.例如調用查詢所有WiFi網卡信息的接口,入口參數為LPWSTR和PINTFS_KEY_TABLE,LPWSTR是字符串指針( Long Pointer to Wide String),而PINTFS_KEY_TABLE是一個結構體。
DWORD WZCEnumInterfaces(
LPWSTR pSrvAddr,
PINTFS_KEY_TABLE pIntfs
);
typedef struct{ DWORD dwNumIntfs; PINTF_KEY_ENTRY pIntfs;} INTFS_KEY_TABLE, *PINTFS_KEY_TABLE;
在CF.NET中會轉化成下面接口,其中INTFS_KEY_TABLE為一個自定義的結構體。
[DllImport("wzcsapi.dll")]
public static extern uint WZCEnumInterfaces(string pSrvAddr, ref INTFS_KEY_TABLE pIntfs);
public struct INTFS_KEY_TABLE : IDisposable
{
private uint dwNumIntfs;
public IntPtr pIntfs;
public INTFS_KEY_TABLE(uint size)
{
this.dwNumIntfs = size;
if (size != 0)
{
this.pIntfs = Marshal.AllocHGlobal((int)(Marshal.SizeOf(typeof(IntPtr)) * size));
}
else
{
this.pIntfs = IntPtr.Zero;
}
}
public string this[uint i]
{
get
{
return Marshal.PtrToStringUni((IntPtr)Marshal.ReadInt32(this.pIntfs, (int)(i * 4)));
}
}
public uint Count
{
get
{
return this.dwNumIntfs;
}
}
public void Dispose()
{
if (this.pIntfs != IntPtr.Zero)
{
Marshal.FreeHGlobal(this.pIntfs);
}
}
}
所有類型轉換都是基於基本類型,所以要進行類型轉換要從基本類型入手。
c++ int類型(int, short, long, uint)直接轉
c++ float類型(double, float)直接轉
byte轉成uint
bool要轉成int
enum轉成uint
public enum INTF_FLAGS : uint
{
INTF_ALL = 0xffffffff,
INTF_ALL_FLAGS = 0x0000ffff,
/// <summary>
/// mask for the configuration mode (NDIS_802_11_NETWORK_INFRASTRUCTURE value)
/// </summary>
INTF_CM_MASK = 0x00000007,
}
char不能轉,因為c++裡面的char是1byte,而.NET是2bytes
對於結構體的轉換可以自定義一個結構,例如上列子的INTFS_KEY_TABLE。如果結構體裡面還包含結構體可以使用byte[]或者IntPtr來表示,因為c++裡面的struct就是內存加上offset。
這些我做P/Invoke找到的一些規律,但是發現不同API,轉換還是有點不一樣,上面只是列出一般情況,特殊情況還要根據列子進行調整。