最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C# 調(diào)用 Win32 API的實現(xiàn)示例

 更新時間:2026年01月04日 11:03:09   作者:工程師007  
Win32 API是微軟為 Windows 操作系統(tǒng)提供的底層編程接口,包含了操作系統(tǒng)的核心功能,本文就來詳細(xì)的介紹一下C# 調(diào)用 Win32 API的實現(xiàn)示例,感興趣的可以了解一下

一、核心概念解析

1. 什么是 Win32 API?

Win32 API(Windows 32-bit Application Programming Interface)是微軟為 Windows 操作系統(tǒng)提供的底層編程接口,包含了操作系統(tǒng)的核心功能(如窗口管理、文件操作、進(jìn)程控制、內(nèi)存管理、系統(tǒng)信息獲取等),本質(zhì)上是一組用 C/C++ 編寫的原生函數(shù)。

2. C# 為什么能調(diào)用 Win32 API?

C# 運行在 .NET 運行時(CLR)中,屬于托管代碼;而 Win32 API 是非托管代碼(直接運行在操作系統(tǒng)層面)。.NET 提供了 P/Invoke(Platform Invocation Services,平臺調(diào)用服務(wù)) 機(jī)制,這是 CLR 提供的核心功能,允許托管代碼調(diào)用非托管的函數(shù)(如 Win32 API)。

3. P/Invoke 核心要素

要在 C# 中調(diào)用 Win32 API,必須滿足以下條件:

  • 函數(shù)簽名匹配:C# 中聲明的函數(shù)必須和 Win32 API 的原生簽名(返回值、參數(shù)類型、調(diào)用約定)一致;
  • DLL 導(dǎo)入:指定 Win32 API 所在的系統(tǒng) DLL(如 kernel32.dll、user32.dlladvapi32.dll 等);
  • 數(shù)據(jù)類型映射:C/C++ 的原生類型(如 DWORDHANDLE、LPCSTR)需要映射到 C# 對應(yīng)的類型(如 uintIntPtr、string)。

二、Win32 API 調(diào)用的語法規(guī)則

1. 基礎(chǔ)聲明格式

在 C# 中,通過 DllImport 特性(位于 System.Runtime.InteropServices 命名空間)聲明 Win32 API 函數(shù),核心語法如下:

using System.Runtime.InteropServices; // 必須引入此命名空間

class Win32Api
{
    // DllImport 特性指定 API 所在的 DLL
    [DllImport("DLL名稱", 
               CharSet = CharSet.ANSI/Unicode, // 字符集(匹配 API 要求)
               SetLastError = true/false,      // 是否捕獲系統(tǒng)錯誤碼
               CallingConvention = CallingConvention.StdCall)] // 調(diào)用約定(Win32 幾乎都是 StdCall)
    // 方法聲明:必須是 static extern,返回值+方法名+參數(shù)列表(類型要匹配)
    public static extern 返回值類型 方法名(參數(shù)類型1 參數(shù)1, 參數(shù)類型2 參數(shù)2, ...);
}

2. 關(guān)鍵參數(shù)說明

特性參數(shù)作用
DllNameWin32 API 所在的系統(tǒng) DLL 名稱(如 kernel32.dll、user32.dll)
CharSet字符編碼:CharSet.Ansi(ANSI)、CharSet.Unicode(UTF-16)、CharSet.Auto(自動)
SetLastError設(shè)為 true 時,可通過 Marshal.GetLastWin32Error() 獲取系統(tǒng)錯誤碼
CallingConvention調(diào)用約定:Win32 API 默認(rèn)為 StdCall(C# 默認(rèn)為 Winapi,等價于 StdCall)

3. 常見類型映射(Win32 → C#)

Win32 API 的原生類型和 C# 類型必須嚴(yán)格映射,否則會導(dǎo)致調(diào)用失敗甚至程序崩潰:

Win32 類型
C# 等效類型說明
DWORDuint32 位無符號整數(shù)
HANDLEIntPtr句柄(指針類型,用 IntPtr 兼容 32/64 位)
LPCSTRstringANSI 字符串(常量指針)
LPWSTRstringUnicode 字符串(可變指針)
BOOLbool/intWin32 的 BOOL 是 int(0 / 非 0),C# 可用 bool 兼容
int/long
int/long直接映射
VOIDvoid無返回值

三、控制臺實戰(zhàn)案例(多個場景)

環(huán)境準(zhǔn)備

  • 開發(fā)工具:Visual Studio(任意版本)或 VS Code + .NET SDK
  • 創(chuàng)建項目:控制臺應(yīng)用(.NET Framework/.NET Core/.NET 5+ 均可,示例用 .NET 8)
  • 核心命名空間:System.Runtime.InteropServices(必須)

案例 1:獲取系統(tǒng)目錄(簡單無參數(shù) / 返回值)

需求:調(diào)用 kernel32.dll 中的 GetSystemDirectory 函數(shù),獲取 Windows 系統(tǒng)目錄(如 C:\Windows\System32)。

步驟 1:查看 Win32 API 原生簽名

// Win32 原生聲明(C/C++)
UINT GetSystemDirectoryA(
  LPSTR lpBuffer,  // 接收目錄的緩沖區(qū)
  UINT uSize       // 緩沖區(qū)大小
);
  • 返回值:實際復(fù)制到緩沖區(qū)的字符數(shù)(不含終止符);
  • 字符集:A 后綴表示 ANSI,W 后綴表示 Unicode(推薦用 Unicode)。

步驟 2:C# 聲明并調(diào)用

using System;
using System.Runtime.InteropServices; // 核心命名空間

namespace Win32ApiDemo
{
    class Program
    {
        // 1. 聲明 Win32 API(使用 Unicode 版本 GetSystemDirectoryW)
        [DllImport("kernel32.dll",           // API 所在 DLL
                   CharSet = CharSet.Unicode, // 匹配 W 后綴的 Unicode 版本
                   SetLastError = true)]      // 啟用錯誤碼捕獲
        // static extern 是固定寫法,返回值 uint 對應(yīng) Win32 的 UINT
        private static extern uint GetSystemDirectoryW(
            char[] lpBuffer,  // 字符數(shù)組作為緩沖區(qū)(替代 C 的 char*)
            uint uSize        // 緩沖區(qū)大小
        );

        static void Main(string[] args)
        {
            try
            {
                // 2. 準(zhǔn)備緩沖區(qū)(系統(tǒng)目錄最長不超過 260 字符,預(yù)留冗余)
                char[] buffer = new char[256];
                // 3. 調(diào)用 Win32 API
                uint result = GetSystemDirectoryW(buffer, (uint)buffer.Length);

                // 4. 處理結(jié)果
                if (result == 0)
                {
                    // 調(diào)用失敗,獲取錯誤碼
                    int errorCode = Marshal.GetLastWin32Error();
                    Console.WriteLine($"調(diào)用失敗,錯誤碼:{errorCode}");
                }
                else
                {
                    // 將字符數(shù)組轉(zhuǎn)為字符串(去掉空字符)
                    string systemDir = new string(buffer).TrimEnd('\0');
                    Console.WriteLine($"Windows 系統(tǒng)目錄:{systemDir}");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"異常:{ex.Message}");
            }

            Console.ReadKey();
        }
    }
}

運行結(jié)果

Windows 系統(tǒng)目錄:C:\Windows\System32

案例 2:彈出系統(tǒng)消息框(調(diào)用 user32.dll)

需求:調(diào)用 user32.dll 中的 MessageBox 函數(shù),彈出 Windows 原生消息框(控制臺程序也能調(diào)用 GUI 相關(guān) API)。

using System;
using System.Runtime.InteropServices;

namespace Win32ApiDemo
{
    class Program
    {
        // 1. 聲明 MessageBoxW(Unicode 版本)
        [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        private static extern int MessageBoxW(
            IntPtr hWnd,          // 父窗口句柄(控制臺無窗口,傳 IntPtr.Zero)
            string lpText,        // 消息內(nèi)容
            string lpCaption,     // 標(biāo)題
            uint uType            // 消息框類型(按鈕+圖標(biāo))
        );

        // 定義消息框類型常量(對應(yīng) Win32 的宏)
        private const uint MB_OK = 0x00000000;          // 僅 OK 按鈕
        private const uint MB_ICONINFORMATION = 0x00000040; // 信息圖標(biāo)
        private const uint MB_OKCANCEL = 0x00000001;     // OK + 取消按鈕

        static void Main(string[] args)
        {
            try
            {
                // 2. 調(diào)用 MessageBoxW
                int ret = MessageBoxW(
                    IntPtr.Zero,                  // 無父窗口
                    "這是 C# 調(diào)用 Win32 API 彈出的消息框!", // 消息內(nèi)容
                    "Win32 API 演示",              // 標(biāo)題
                    MB_OK | MB_ICONINFORMATION    // 組合類型:OK 按鈕 + 信息圖標(biāo)
                );

                // 3. 處理返回值(用戶點擊的按鈕)
                switch (ret)
                {
                    case 1:
                        Console.WriteLine("用戶點擊了【確定】按鈕");
                        break;
                    case 2:
                        Console.WriteLine("用戶點擊了【取消】按鈕");
                        break;
                    default:
                        Console.WriteLine($"返回值:{ret}(調(diào)用失?。?);
                        break;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"異常:{ex.Message}");
            }

            Console.ReadKey();
        }
    }
}

關(guān)鍵說明

  • MessageBoxW 的返回值:1 = 確定,2 = 取消,3 = 終止,4 = 重試,5 = 忽略等;
  • 消息框類型可以通過 | 組合(如 MB_OK | MB_ICONWARNING 表示 OK 按鈕 + 警告圖標(biāo));
  • 控制臺程序調(diào)用 GUI API 時,hWnd 傳 IntPtr.Zero 即可。

案例 3:獲取進(jìn)程 ID(調(diào)用 GetCurrentProcessId)

需求:調(diào)用 kernel32.dll 的 GetCurrentProcessId 獲取當(dāng)前控制臺程序的進(jìn)程 ID。

using System;
using System.Runtime.InteropServices;

namespace Win32ApiDemo
{
    class Program
    {
        // 聲明 GetCurrentProcessId(無參數(shù),返回 DWORD)
        [DllImport("kernel32.dll", SetLastError = false)] // 此函數(shù)不會失敗,無需捕獲錯誤碼
        private static extern uint GetCurrentProcessId();

        static void Main(string[] args)
        {
            // 調(diào)用 API
            uint pid = GetCurrentProcessId();
            Console.WriteLine($"當(dāng)前控制臺程序的進(jìn)程 ID:{pid}");

            // 驗證:可以在任務(wù)管理器中查看控制臺程序的 PID 是否一致
            Console.WriteLine("按任意鍵退出...");
            Console.ReadKey();
        }
    }
}

運行結(jié)果

當(dāng)前控制臺程序的進(jìn)程 ID:12345
按任意鍵退出...

案例 4:讀寫 INI 文件

需求:調(diào)用 kernel32.dll 的 WritePrivateProfileString 和 GetPrivateProfileString 讀寫 INI 配置文件(Win32 原生 INI 操作)。

using System;
using System.Runtime.InteropServices;

namespace Win32ApiDemo
{
    class Program
    {
        // 1. 聲明寫 INI 的 API
        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        private static extern bool WritePrivateProfileStringW(
            string lpAppName,  // 節(jié)名(INI 的 [Section])
            string lpKeyName,  // 鍵名
            string lpString,   // 鍵值
            string lpFileName  // INI 文件路徑
        );

        // 2. 聲明讀 INI 的 API
        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        private static extern uint GetPrivateProfileStringW(
            string lpAppName,  // 節(jié)名
            string lpKeyName,  // 鍵名
            string lpDefault,  // 默認(rèn)值(讀取失敗時返回)
            char[] lpReturnedString, // 接收值的緩沖區(qū)
            uint nSize,        // 緩沖區(qū)大小
            string lpFileName  // INI 文件路徑
        );

        static void Main(string[] args)
        {
            string iniPath = $"{Environment.CurrentDirectory}\\demo.ini";

            try
            {
                // 步驟 1:寫入 INI 文件
                bool writeSuccess = WritePrivateProfileStringW(
                    "UserInfo",    // 節(jié)名
                    "UserName",    // 鍵名
                    "張三",        // 鍵值
                    iniPath        // 文件路徑
                );

                if (writeSuccess)
                {
                    Console.WriteLine($"成功寫入 INI 文件:{iniPath}");
                }
                else
                {
                    int errorCode = Marshal.GetLastWin32Error();
                    Console.WriteLine($"寫入失敗,錯誤碼:{errorCode}");
                }

                // 步驟 2:讀取 INI 文件
                char[] buffer = new char[1024];
                uint readLen = GetPrivateProfileStringW(
                    "UserInfo",    // 節(jié)名
                    "UserName",    // 鍵名
                    "默認(rèn)值",      // 默認(rèn)值
                    buffer,        // 緩沖區(qū)
                    (uint)buffer.Length, // 緩沖區(qū)大小
                    iniPath        // 文件路徑
                );

                if (readLen > 0)
                {
                    string value = new string(buffer).TrimEnd('\0');
                    Console.WriteLine($"讀取到的值:{value}");
                }
                else
                {
                    Console.WriteLine("讀取失敗或鍵不存在");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"異常:{ex.Message}");
            }

            Console.ReadKey();
        }
    }
}

運行結(jié)果

成功寫入 INI 文件:D:\Win32ApiDemo\bin\Debug\net8.0\demo.ini
讀取到的值:張三

生成的 INI 文件內(nèi)容

[UserInfo]
UserName=張三

四、常見問題與避坑指南

1. 調(diào)用失敗的常見原因

  • 類型不匹配:如 Win32 的 DWORD 用了 C# 的 int(雖然有時能運行,但 64 位系統(tǒng)會出問題);
  • 字符集錯誤:調(diào)用 A 后綴的 API 卻用 CharSet.Unicode,或反之;
  • 調(diào)用約定錯誤:Win32 API 幾乎都是 StdCall,若設(shè)為 Cdecl 會導(dǎo)致棧溢出;
  • 緩沖區(qū)大小不足:如獲取系統(tǒng)目錄時緩沖區(qū)太小,返回值為 0;
  • 權(quán)限問題:部分 Win32 API 需要管理員權(quán)限(如修改系統(tǒng)設(shè)置),需右鍵以管理員運行程序。

2. 如何調(diào)試 Win32 API 調(diào)用?

  • 啟用 SetLastError = true,調(diào)用后通過 Marshal.GetLastWin32Error() 獲取錯誤碼,對照 Windows 錯誤碼表 排查;
  • 檢查 API 名稱是否正確(如是否漏寫 W/A 后綴);
  • 用 IntPtr 替代所有句柄類型(避免 32/64 位兼容性問題);
  • 在 try-catch 中捕獲 EntryPointNotFoundException(API 名稱錯誤)、AccessViolationException(內(nèi)存訪問錯誤)等異常。

到此這篇關(guān)于C# 調(diào)用 Win32 API的實現(xiàn)示例的文章就介紹到這了,更多相關(guān)C# 調(diào)用 Win32 API內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家! 

相關(guān)文章

  • C#委托與事件初探

    C#委托與事件初探

    事件是委托的一種特殊形式,當(dāng)發(fā)生有意義的事情時,事件處理對象通知過程。接下來通過本文給大家介紹C#委托與事件初探,感興趣的朋友一起學(xué)習(xí)吧
    2016-02-02
  • C#中括號報錯“應(yīng)輸入標(biāo)識符”的常見原因及解決方案

    C#中括號報錯“應(yīng)輸入標(biāo)識符”的常見原因及解決方案

    C# 中在中括號 [] 上提示應(yīng)輸入標(biāo)識符錯誤,通常是因為編譯器期望在中括號內(nèi)找到一個有效的標(biāo)識符(如變量名、屬性名、索引參數(shù)等),但實際提供的語法不符合規(guī)范,本文通過具體代碼示例和場景分析,詳細(xì)闡述導(dǎo)致此錯誤的常見原因及解決方案,需要的朋友可以參考下
    2026-05-05
  • WPF自定義路由事件的實例教程

    WPF自定義路由事件的實例教程

    WPF中有兩種事件模型,一種是在WinForm時代就存在的CLR事件,另一種是WPF時代的路由事件,這篇文章主要給大家介紹了關(guān)于WPF自定義路由事件的相關(guān)資料,需要的朋友可以參考下
    2021-09-09
  • C# VB 實現(xiàn)10進(jìn)制 16進(jìn)制之間互相轉(zhuǎn)換

    C# VB 實現(xiàn)10進(jìn)制 16進(jìn)制之間互相轉(zhuǎn)換

    如何將10進(jìn)制轉(zhuǎn)成16進(jìn)制,又如何將16進(jìn)制數(shù)轉(zhuǎn)成10進(jìn)制,本文將介紹C#和VB實現(xiàn)代碼,需要了解的朋友可以參考下
    2012-11-11
  • C#字符串?dāng)?shù)組轉(zhuǎn)換為整形數(shù)組的方法

    C#字符串?dāng)?shù)組轉(zhuǎn)換為整形數(shù)組的方法

    這篇文章主要介紹了C#字符串?dāng)?shù)組轉(zhuǎn)換為整形數(shù)組的方法,涉及C#數(shù)組遍歷與轉(zhuǎn)換的相關(guān)技巧,需要的朋友可以參考下
    2015-06-06
  • C#微信公眾號與訂閱號接口開發(fā)示例代碼

    C#微信公眾號與訂閱號接口開發(fā)示例代碼

    這篇文章主要介紹了C#微信公眾號與訂閱號接口開發(fā)示例代碼,結(jié)合實例形式簡單分析了C#針對微信接口的調(diào)用與處理技巧,需要的朋友可以參考下
    2016-06-06
  • 一個可逆加密的類(使用3DES加密)

    一個可逆加密的類(使用3DES加密)

    表示三重數(shù)據(jù)加密標(biāo)準(zhǔn)算法的基類,TripleDES 的所有實現(xiàn)都必須從此基類派生。是從 SymmetricAlgorithm 類里繼承出來。
    2011-07-07
  • C# 獲取數(shù)據(jù)庫中所有表名、列名的示例代碼

    C# 獲取數(shù)據(jù)庫中所有表名、列名的示例代碼

    這篇文章主要介紹了C# 獲取數(shù)據(jù)庫中所有表名、列名,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-06-06
  • C#訪問PostGreSQL數(shù)據(jù)庫的方法

    C#訪問PostGreSQL數(shù)據(jù)庫的方法

    這次的項目中的一個環(huán)節(jié)要求我把PostGreSQL數(shù)據(jù)取出來,然后放到SqlServer里,再去處理分析。
    2013-04-04
  • C#實現(xiàn)拼圖游戲

    C#實現(xiàn)拼圖游戲

    這篇文章主要為大家詳細(xì)介紹了C#實現(xiàn)拼圖游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-07-07

最新評論

吉林省| 左权县| 郯城县| 石景山区| 泾阳县| 新民市| 光泽县| 泽州县| 江西省| 阜南县| 获嘉县| 肇庆市| 通海县| 正镶白旗| 突泉县| 收藏| 洪洞县| 陈巴尔虎旗| 德兴市| 南京市| 海门市| 斗六市| 桦南县| 子长县| 罗田县| 顺义区| 花莲县| 望都县| 缙云县| 遂平县| 商河县| 乐昌市| 闽清县| 濮阳县| 会宁县| 错那县| 宜兴市| 新邵县| 吉林市| 吉林市| 枣强县|