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

如何在C#中通过async/await调用原生重叠IO(P/Invoke)?

在C#中用async/await结合P/Invoke调用原生重叠IO方法

要在C#中以async/await友好的方式调用原生重叠IO方法,核心是将原生异步IO的完成通知与.NET的TaskCompletionSource桥接,让原生操作的完成触发Task的状态变更,从而支持await等待。下面以调用CfHydratePlaceholder为例,分步实现:

1. 定义P/Invoke签名与核心结构体

首先要匹配原生API的定义,包含重叠IO所需的OVERLAPPED结构体、目标方法签名,以及完成回调委托:

using System;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

public static class CloudFileApi
{
    // Windows错误码常量
    private const int ERROR_IO_PENDING = 997;
    private const int ERROR_SUCCESS = 0;

    // 对应原生OVERLAPPED结构体
    [StructLayout(LayoutKind.Sequential)]
    private struct OVERLAPPED
    {
        public IntPtr Internal;
        public IntPtr InternalHigh;
        public int Offset;
        public int OffsetHigh;
        public IntPtr hEvent;
    }

    // CfHydratePlaceholder的P/Invoke签名(参数需根据实际原生API调整)
    [DllImport("cfapi.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern bool CfHydratePlaceholder(
        IntPtr placeholderInfo,
        uint flags,
        ref OVERLAPPED overlapped);

    // 重叠IO完成回调委托
    private delegate void NativeOverlappedCompletionCallback(
        uint errorCode,
        uint numberOfBytesTransferred,
        IntPtr nativeOverlapped);

2. 包装异步方法,实现await支持

通过TaskCompletionSource跟踪IO操作状态,调用原生方法后,若返回ERROR_IO_PENDING则等待Task完成;若同步成功则直接结束:

public static async Task HydratePlaceholderAsync(IntPtr placeholderInfo, uint flags)
    {
        var completionSource = new TaskCompletionSource<bool>();
        var overlapped = new OVERLAPPED();

        // 将回调和TaskCompletionSource绑定到NativeOverlapped
        var nativeOverlapped = overlapped.Pack(CompletionCallback, completionSource);

        try
        {
            bool syncResult = CfHydratePlaceholder(placeholderInfo, flags, ref overlapped);
            if (!syncResult)
            {
                int win32Error = Marshal.GetLastWin32Error();
                if (win32Error != ERROR_IO_PENDING)
                {
                    // 同步调用失败,直接抛出Win32异常
                    throw new System.ComponentModel.Win32Exception(win32Error);
                }
                // 异步等待IO完成
                await completionSource.Task.ConfigureAwait(false);
            }
        }
        finally
        {
            // 必须释放NativeOverlapped资源,避免内存泄漏
            Overlapped.Unpack(nativeOverlapped);
            Overlapped.Free(nativeOverlapped);
        }
    }

3. 实现完成回调,更新Task状态

回调函数会在原生IO操作完成时被调用,这里取出绑定的TaskCompletionSource,根据操作结果设置Task的成功或失败状态:

private static void CompletionCallback(uint errorCode, uint bytesTransferred, IntPtr nativeOverlapped)
    {
        // 取出绑定的TaskCompletionSource
        var completionSource = (TaskCompletionSource<bool>)Overlapped.Unpack(nativeOverlapped).AsyncResult;

        try
        {
            if (errorCode != ERROR_SUCCESS)
            {
                completionSource.SetException(new System.ComponentModel.Win32Exception((int)errorCode));
            }
            else
            {
                completionSource.SetResult(true);
            }
        }
        catch (Exception ex)
        {
            completionSource.SetException(ex);
        }
    }
}

关键注意事项

  • 资源管理:必须确保NativeOverlapped被正确释放,即使同步调用成功也要执行释放操作,避免内存泄漏。
  • 线程安全:回调函数运行在IO完成端口线程上,需保证逻辑线程安全,避免跨线程访问UI控件等非线程安全资源。
  • 签名匹配:P/Invoke的参数类型、字符集、SetLastError属性必须与原生API严格一致,否则会导致调用失败或内存错误。
  • 上下文切换:使用ConfigureAwait(false)可以避免不必要的上下文切换,提升异步操作性能(如果不需要回到原上下文,比如UI线程)。

内容的提问来源于stack exchange,提问作者Matt Smith

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 23:16:01