.NET 框架雖內建 `MessageBox`,但大量 Win32 API 需 P/Invoke 調用。本文深入探討 `DllImport` 修飾符、調用約定陷阱,並示範正確包裝 C++ 類的模式。
P/Invoke 關鍵細節
- 句柄類型:務必用 `IntPtr` 對應 `HWND`/`HANDLE`,自動適配 32/64 位。
- CharSet.Auto:根據目標 OS 自動選 Unicode/ANSI。
- CallingConvention:默認 `Winapi`(Windows 上為 `StdCall`,CE 為 `CDecl`)。C++ 成員函數使用 `ThisCall`(ECX 傳 this),直接 P/Invoke 極易導致堆疊錯誤(如 `ESP not properly saved`)。
為何不直接 P/Invoke C++ 成員函數
- 調用約定不匹配(`StdCall` vs `ThisCall`)
- C++ 名稱修飾支援函數重載,需 `extern "C"` 抑制(但成員函數無法這樣做)
- 即使用完整修飾名與 `CallingConvention.ThisCall`,極不穩定、不可維護
正確做法:托管包裝器模式
public __gc class Widget {
private:
CWidget* m_pObj; // 原生指標
public:
Widget() { m_pObj = new CWidget; }
~Widget() { delete m_pObj; }
int Method(int n) { return m_pObj->Method(n); }
// 僅暴露需要的方法
};
包裝器標準流程
- 建立 `__gc` 托管類,持有原生類指標
- 建構子/解構子管理原生物件生命週期
- 編寫對應原生成員函數的包裝方法
- 可轉換為 .NET 風格屬性(如 `GetName`/`SetName` → `Name` 屬性)
互操作路徑總結

- 托管→非托管:P/Invoke(C 函數) / 包裝器(C++ 類)
- 非托管→托管:Managed Extensions / C++/CLI
- COM 互操作另闢專題