请阐述在 .NET 平台下,通过 P/Invoke 机制调用非托管代码的具体步骤与关键注意事项。
考察说明
考察对 P/Invoke 工作原理及互操作边缘情况的理解。
回答思路
- 【回答框架 1】P/Invoke 是 .NET 提供的平台调用服务,允许托管代码调用非托管动态链接库(如 Win32 API、C/C++ Dll)。核心是 DllImport 特性,通过静态 extern 方法声明。
- 【回答框架 2】关键步骤:本机函数的 DllImport 声明,包括库名、入口点、调用约定(CallingConvention)、字符集(CharSet)和 SetLastError。
- 【回答框架 3】数据类型映射:需对应非托管类型,如 C 的 int 对应 System.Int32,指针对应 IntPtr,字符串考虑 CharSet 和 Encoding。
- 【回答框架 4】内存管理:若函数分配内存返回,需在托管侧释放,或使用 Marshal 类操作,如 AllocHGlobal、FreeHGlobal;对于结构体,注意布局(LayoutKind.Sequential)和大小。
- 【回答框架 5】错误处理:利用 SetLastError 和 Marshal.GetLastWin32Error 获取错误代码;异常需包装为托管异常。
- 【关键点 1】DllImport 特性是入口,需指定库名和正确的入口点。
- 【关键点 2】数据类型和内存布局必须精确匹配,否则可能导致数据损坏或崩溃。
- 【关键点 3】调用约定(cdecl 与 stdcall)不一致会导致栈不平衡,引发异常。
- 【关键点 4】及时释放非托管内存,避免内存泄漏。
- 【易错点 1】字符集(CharSet)设置不当会导致字符串编码错误(如 ANSI vs Unicode)。
- 【易错点 2】结构体布局(如对齐)可能因编译器而不同,需显式指定。
- 【易错点 3】回调函数需用委托并防止 GC 回收,否则可能访问已释放的内存。