如何在C#中不使用DangerousGetHandle调用带HANDLE指针的非托管函数
在C#中调用期望
HANDLE*参数的非托管DLL函数(无需DangerousGetHandle?) 首先,我们先拆解你的非托管函数签名:
extern "C" __declspec(dllexport) BOOL __stdcall MySetEvent(HANDLE* handle, int size)
这个函数核心需求是接收一个指向HANDLE的指针,以及一个用于验证HANDLE内存大小的整数。下面一步步实现C#的调用逻辑:
第一步:正确声明DllImport签名
在C#中,HANDLE对应IntPtr,而HANDLE*(指向句柄的指针)对应**ref IntPtr**——因为我们需要传递IntPtr变量的地址,和非托管的指针类型完全匹配。同时要明确调用约定和返回值的类型转换:
using System; using System.Runtime.InteropServices; using System.Threading; class UnmanagedInterop { // 完整的DllImport声明 [DllImport("UnmanagedDll", SetLastError = true, CallingConvention = CallingConvention.StdCall)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool MySetEvent(ref IntPtr handle, int size); // 实际调用示例 public static void TriggerMySetEvent() { // 场景1:使用.NET原生的EventWaitHandle using (var autoResetEvent = new EventWaitHandle(false, EventResetMode.AutoReset)) { // 获取原生句柄:这里需要说明,.NET安全句柄(SafeWaitHandle)的底层句柄只能通过DangerousGetHandle获取 // 只要确保调用期间SafeWaitHandle未被Dispose,这个操作就是安全的 IntPtr nativeHandle = autoResetEvent.SafeWaitHandle.DangerousGetHandle(); // 计算HANDLE的大小,对应非托管的sizeof(HANDLE)(自动适配32/64位进程) int handleSize = Marshal.SizeOf(typeof(IntPtr)); // 调用非托管函数 bool success = MySetEvent(ref nativeHandle, handleSize); if (!success) { // 获取错误码并处理 int errorCode = Marshal.GetLastWin32Error(); Console.WriteLine($"调用失败,错误码:{errorCode}"); } } // 场景2:如果句柄是从其他非托管API获取的IntPtr IntPtr externalHandle = /* 比如从CreateEvent/OpenEvent获取的句柄 */ IntPtr.Zero; try { int handleSize = Marshal.SizeOf(typeof(IntPtr)); bool success = MySetEvent(ref externalHandle, handleSize); // 根据业务需求处理调用结果 } finally { // 记得释放外部获取的非托管句柄 if (externalHandle != IntPtr.Zero) { CloseHandle(externalHandle); } } } // 辅助API:释放非托管句柄 [DllImport("kernel32.dll", SetLastError = true)] private static extern bool CloseHandle(IntPtr hObject); }
关键细节说明
ref IntPtr的必要性:非托管函数需要的是HANDLE*(指向句柄的指针),ref IntPtr会把C#中IntPtr变量的地址传递给非托管,完全匹配HANDLE*的类型要求。size参数的正确性:Marshal.SizeOf(typeof(IntPtr))会自动获取当前进程架构下HANDLE的大小(32位为4字节,64位为8字节),能满足非托管函数里的assert(sizeof(*handle) == size)校验。- 关于
DangerousGetHandle:你提到不想使用这个方法,但实际上,.NET的SafeWaitHandle是为了安全管理句柄生命周期设计的,它的底层原生句柄只能通过这个方法获取。只要你确保调用MySetEvent期间SafeWaitHandle未被Dispose,这个操作就没有风险——并没有完全绕过安全机制,只是需要你自己保证句柄的有效性。 - 返回值与错误处理:
[return: MarshalAs(UnmanagedType.Bool)]能正确将非托管的BOOL(4字节整数)转换为C#的bool。开启SetLastError = true后,调用失败时可以用Marshal.GetLastWin32Error()获取Windows原生错误码。
内容的提问来源于stack exchange,提问作者AndiR
相关产品推荐
相关产品推荐

