首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# Win32 API 互操作大全:P/Invoke 簽名庫建構與實戰

     編輯:關於C語言

前言:為何仍需 Win32 API?

.NET Framework 提供了豐富的類別庫,但部分系統層級功能(如底層視窗操作、裝置驅動程式互動、特定登錄操作)仍無對應的 Managed 封裝。此時,平台呼叫(P/Invoke)成為橋樑,讓 C# 直接呼叫 kernel32.dll、user32.dll、gdi32.dll 等原生 DLL 匯出函式。

本文記錄如何系統性整理 Win32 API 簽名、建構可重用的 Win32API 命名空間,並解決常見陷阱(如 DLL 命名衝突)。

專案架構規劃


Win32API.sln
├── Structs.Net.cs       // 所有結構體、列舉定義
├── Kernel32.Net.cs      // kernel32.dll 函式
├── User32.Net.cs        // user32.dll 函式
├── GDI32.Net.cs         // gdi32.dll 函式
├── OtherFnc.Net.cs      // advapi32, shell32, spoolss, winmm

命名空間與核心宣告


namespace Win32API
{
    using System;
    using System.Runtime.InteropServices;

    // 1. 結構體與列舉集中放置
    [StructLayout(LayoutKind.Sequential)]
    public class RECT { public int Left, Top, Right, Bottom; }

    public enum WindowStyles : uint { WS_OVERLAPPED = 0x00000000, ... }

    // 2. 各 DLL 對應一個 static class
    public static class Kernel32
    {
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, ...);
    }

    public static class User32
    {
        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern int MessageBox(IntPtr hWnd, string lpText, string lpCaption, uint uType);
    }
    // ... GDI32, Advapi32, Shell32, Spoolss, Winmm
}

編譯與使用

  1. 先編譯結構體庫:csc /t:library /r:System.dll Structs.Net.cs
  2. 再編譯各功能庫並參考結構體:csc /t:library /r:Structs.Net.dll Kernel32.Net.cs
  3. 專案參考產生的 DLL,即可直接呼叫:

using Win32API;

class Program
{
    static void Main()
    {
        User32.MessageBox(IntPtr.Zero, "Hello P/Invoke", "Title", 0);
    }
}

避坑指南:DLL 命名衝突

慘痛教訓:若將 C# 類別庫編譯為 User32.dll,執行時會載入「你的 DLL」而非系統 C:\Windows\System32\user32.dll,導致 MessageBox 找不到入口點或簽名不符。

解決:輸出檔名加上前綴(如 User32.Net.dll),或置於獨立目錄。編譯指令:csc /t:library /out:User32.Net.dll User32.Net.cs

常用簽名範例精選

1. 視窗操作


// 尋找視窗
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

// 設定前景視窗
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);

2. 檔案與路徑


[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern uint GetFullPathName(string lpFileName, uint nBufferLength, StringBuilder lpBuffer, out IntPtr lpFilePart);

3. 登錄操作


[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public static extern int RegOpenKeyEx(IntPtr hKey, string lpSubKey, uint ulOptions, int samDesired, out IntPtr phkResult);

最佳實踐建議

  1. 集中管理:所有 P/Invoke 簽名置於專屬專案,避免分散在業務程式碼中。
  2. 啟用 SetLastError = true:搭配 Marshal.GetLastWin32Error() 取得錯誤碼。
  3. 使用 SafeHandle 衍生類:包裝非受控控制碼,確保釋放時機正確。
  4. 參考 pinvoke.net:社群維護的權威簽名庫,複製貼上即用。

總結

建構一套乾淨、可維護的 Win32 API 互操作層,能讓 C# 應用程式在需要時突破 Managed 邊界,直達系統核心功能。關鍵在於結構化組織簽名、避免命名衝突、善用 SafeHandle 管理生命週期。將此庫視為基礎設施投資,長期回報極高。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved