P/Invoke是.NET中托管代码调用非托管DLL函数的核心机制,通过DllImport属性声明外部方法,自动处理类型封送、字符集转换与错误码获取,支持回调、结构体对齐控制及跨语言互操作。

能调,但多数情况不该直接调——除非你明确知道 GetSystemMetrics 和 GetDpiForWindow 的行为差异,或必须绕过 .NET 封装获取原始句柄。
什么时候非得用 P/Invoke?
不是所有 Windows 功能都被 .NET 封装了。比如:
-
SetThreadExecutionState:防止系统休眠(屏保/电源策略下保持活跃) -
EnableWindow:禁用/启用某个 HWND 对应的窗口(WinForms/WPF 无直接等价 API) -
GetRawInputData:处理高精度、低延迟的鼠标/键盘原始输入(游戏、绘图工具场景) - 需要和第三方 C/C++ DLL 交互,且对方只暴露 C ABI 接口
如果只是读注册表、操作文件、发 HTTP 请求,Microsoft.Win32.Registry、System.IO、HttpClient 更安全、更易维护。
DllImport 声明里最容易错的三件事
写错一个参数,轻则返回 0 或 -1,重则引发 AccessViolationException 或静默崩溃。
-
CallingConvention 必须匹配:Windows API 绝大多数是
StdCall,但少数如GetProcAddress返回的函数指针可能是Cdecl;不显式指定时 .NET 默认用StdCall,但部分旧文档或头文件容易误导 -
字符编码别硬猜:用
CharSet = CharSet.Auto看似省事,实际在 x64 进程中会默认走 Unicode 版本(如MessageBoxW),而你传入的 ANSI 字符串可能被截断;明确写CharSet = CharSet.Unicode并确保字符串是 UTF-16 -
结构体字段顺序和内存对齐必须显式控制:C# 默认按字段声明顺序布局,但 Windows API 结构体常含
#pragma pack(1)或__declspec(align(8));必须加[StructLayout(LayoutKind.Sequential, Pack = 1)],否则POINTL或WINDOWPLACEMENT传过去会错位
字符串、句柄、回调函数怎么传才不崩
P/Invoke 最常翻车的是资源生命周期和内存所有权。
-
字符串参数:
string传入 API 是只读的,但如果 API 内部修改它(极少见),需改用StringBuilder并设足够容量;返回字符串时,若 API 分配内存(如FormatMessage配合FORMAT_MESSAGE_ALLOCATE_BUFFER),要用Marshal.PtrToStringUni+Marshal.FreeHGlobal手动释放 -
HANDLE / HWND / HINSTANCE:全都是
IntPtr,但语义不同;不要把Process.Handle直接当HWND传给SetParent—— 它们不是同一类句柄,类型混淆会导致无效句柄错误(ERROR_INVALID_HANDLE) -
回调函数(委托):必须用
[UnmanagedFunctionPointer(CallingConvention.StdCall)]标记委托类型,并将委托实例存为类字段(防止 GC 回收);传进SetWindowsHookEx后,还要自己管理UnhookWindowsHookEx,否则卸载时可能崩溃
调试 AccessViolationException 的实际路径
这类异常不是“代码写错了”,而是“内存访问越界了”,堆栈通常没用。真正有效的排查路径是:
- 用
Process Monitor检查是否因权限不足被拦截(比如尝试读取HKEY_LOCAL_MACHINE\SOFTWARE而进程没以管理员运行) - 用
Visual Studio 调试器 → 调试 → Windows → 显示反汇编,看崩溃指令地址是否落在你的托管代码段外 - 在 P/Invoke 声明上加
[SuppressUnmanagedCodeSecurity]前先确认:这是性能优化手段,不是解决崩溃的补丁;滥用它会让安全检查失效,反而掩盖真实问题 - 把调用封装成独立方法,用
try/catch (SEHException ex)捕获结构化异常(比AccessViolationException更早一层),再结合ex.ExceptionInformation查具体地址
真正难的从来不是“怎么写那一行 [DllImport]”,而是确认你传进去的 IntPtr 确实指向一个当前线程有权访问、尚未被释放、且格式完全匹配的内存块。


















