.NET Compact Framework (CF) 是 .NET Framework 的精簡版,許多功能未內建實作,必須直接呼叫 Win32 API (位於 coredll.dll) 來完成。C++ 呼叫原生 DLL 直觀簡單,C# 則需透過 **P/Invoke (Platform Invocation Services)** 進行跨界呼叫。
[DllImport("coredll.dll", CharSet = CharSet.Auto)]
// 或指定進入點、呼叫慣例、設定 LastError
[DllImport("coredll.dll", EntryPoint = "FunctionName", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
CharSet.Auto:依平台自動選擇 ANSI/Unicode (Windows CE 為 Unicode)。SetLastError = true:允許透過 Marshal.GetLastWin32Error() 取得錯誤碼。Win32 API 多以回傳值表示成功/失敗。建議封裝為例外拋出,而非單純檢查回傳值:
int result = RasHangUp(rasSession);
if (result != 0)
{
throw new ApplicationException($"RasHangUp({rasSession}) failed with error code: {result}");
}
| C/C++ 型別 | C# 對應型別 | 說明 |
|---|---|---|
int, short, long, uint | int, short, long, uint | 直接對應 |
float, double | float, double | 直接對應 |
BYTE | byte / uint | 單位元組 |
BOOL | int / [MarshalAs(UnmanagedType.Bool)] bool | Win32 BOOL 為 4 bytes |
enum | enum : uint | 指定底層型別為 uint |
char | 不可直接對應 | C++ char=1 byte, C# char=2 bytes (Unicode) |
LPWSTR / LPCWSTR | string / StringBuilder | Unicode 字串指標 |
LPSTR / LPCSTR | string (配合 CharSet.Ansi) | ANSI 字串指標 |
| 結構體 | struct + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] | 記憶體配置需完全一致 |
| 結構體指標 | ref Struct / out Struct / IntPtr (手動 Marshal) | 視 API 是否需配置記憶體 |
| 回呼函數 | delegate + GCHandle.Alloc 防止被 GC 回收 | 委派實例必須保持存活 |
DWORD WZCEnumInterfaces(LPWSTR pSrvAddr, PINTFS_KEY_TABLE pIntfs);
typedef struct {
DWORD dwNumIntfs;
PINTF_KEY_ENTRY pIntfs;
} INTFS_KEY_TABLE, *PINTFS_KEY_TABLE;
[DllImport("wzcsapi.dll")]
public static extern uint WZCEnumInterfaces(string pSrvAddr, ref INTFS_KEY_TABLE pIntfs);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct INTFS_KEY_TABLE : IDisposable
{
private uint dwNumIntfs;
public IntPtr pIntfs; // 指向 INTF_KEY_ENTRY 陣列
public INTFS_KEY_TABLE(uint size)
{
dwNumIntfs = size;
if (size != 0)
{
// 配置非受控記憶體:指標陣列大小 = IntPtr.Size * size
pIntfs = Marshal.AllocHGlobal((int)(IntPtr.Size * size));
}
else pIntfs = IntPtr.Zero;
}
// 索引器:讀取第 i 個介面名稱
public string this[uint i]
{
get
{
// 讀取指標陣列中第 i 個 IntPtr
IntPtr entryPtr = (IntPtr)Marshal.ReadInt32(pIntfs, (int)(i * IntPtr.Size));
// 假設 INTF_KEY_ENTRY 第一欄位為 LPWSTR (介面名稱指標)
return Marshal.PtrToStringUni(entryPtr);
}
}
public uint Count => dwNumIntfs;
public void Dispose()
{
if (pIntfs != IntPtr.Zero)
{
Marshal.FreeHGlobal(pIntfs);
pIntfs = IntPtr.Zero;
}
}
}
若結構體包含另一結構體,可用以下任一種方式:
IntPtr + 手動 Marshal:外層只存指標,執行時 Marshal.PtrToStructure 讀取內部結構。byte[]:預留足夠位元組陣列,序列化/反序列化時手動處理。本質上 C++ 結構體就是「記憶體 + Offset」,掌握這點即可靈活應對。
StdCall (C# 預設),但 C 執行期函式庫為 Cdecl。CharSet.Auto 或 CharSet.Unicode。Marshal.SizeOf<T>() 而非 sizeof (C# 不支援)。AllocHGlobal 必須配對 FreeHGlobal,實作 IDisposable 並搭配 using。GCHandle.Alloc(delegate)。IntPtr / UIntPtr 確保可移植性。System.Runtime.InteropServices 命名空間coredll.dll / wzcsapi.dll 等 Windows CE 系統 DLL 匯出函數表