You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 10:42:04