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

WCF命名管道单向通信下,如何向WPF GUI返回文件复制进度?

解决WCF命名管道单向通信下文件复制进度回传至WPF GUI的问题

单向WCF通信默认仅支持客户端发起请求,服务端无法主动推送数据。要实现文件复制进度回传至WPF进度条,有以下几种可行方案:

方案1:改用WCF双工通信契约(推荐)

命名管道绑定NetNamedPipeBinding原生支持双工通信,通过定义回调契约,让服务端在复制过程中主动回调客户端更新进度,实时性最好。

步骤与代码示例

  1. 定义回调与服务契约
// 回调契约:定义服务端调用客户端的进度更新方法
public interface IFileCopyCallback
{
    [OperationContract(IsOneWay = true)]
    void UpdateCopyProgress(int percentage);
}

// 双工服务契约:关联回调契约
[ServiceContract(CallbackContract = typeof(IFileCopyCallback))]
public interface IFileCopyService
{
    [OperationContract]
    void CopyFile(string sourcePath, string destPath);
}
  1. 服务端实现文件复制与进度回调
public class FileCopyService : IFileCopyService
{
    public void CopyFile(string sourcePath, string destPath)
    {
        // 获取客户端回调通道
        var callback = OperationContext.Current.GetCallbackChannel<IFileCopyCallback>();
        var fileInfo = new FileInfo(sourcePath);
        long totalBytes = fileInfo.Length;
        long copiedBytes = 0;

        using (var sourceStream = new FileStream(sourcePath, FileMode.Open))
        using (var destStream = new FileStream(destPath, FileMode.Create))
        {
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = sourceStream.Read(buffer, 0, buffer.Length)) > 0)
            {
                destStream.Write(buffer, 0, bytesRead);
                copiedBytes += bytesRead;
                int progress = (int)((copiedBytes * 100) / totalBytes);
                // 回调客户端更新进度
                callback.UpdateCopyProgress(progress);
                // 避免过于频繁回调,添加短延迟
                Thread.Sleep(10);
            }
            // 通知复制完成
            callback.UpdateCopyProgress(100);
        }
    }
}
  1. WPF客户端实现回调并更新UI
    WPF UI控件仅能在UI线程更新,需通过Dispatcher切换线程:
// 回调处理器
public class FileCopyCallbackHandler : IFileCopyCallback
{
    private readonly Action<int> _updateProgressAction;

    public FileCopyCallbackHandler(Action<int> updateProgressAction)
    {
        _updateProgressAction = updateProgressAction;
    }

    public void UpdateCopyProgress(int percentage)
    {
        // 切换到UI线程更新进度条
        Application.Current.Dispatcher.Invoke(() => _updateProgressAction(percentage));
    }
}

// 按钮点击事件逻辑
private void CopyButton_Click(object sender, RoutedEventArgs e)
{
    var callbackHandler = new FileCopyCallbackHandler(progress => CopyProgressBar.Value = progress);
    var binding = new NetNamedPipeBinding();
    var endpoint = new EndpointAddress("net.pipe://localhost/FileCopyService");
    var factory = new DuplexChannelFactory<IFileCopyService>(callbackHandler, binding, endpoint);
    
    var serviceProxy = factory.CreateChannel();
    // 后台启动复制任务,避免阻塞UI
    Task.Run(() => serviceProxy.CopyFile(@"C:\SourceFile.txt", @"C:\DestFile.txt"));
}

方案2:客户端轮询服务端获取进度

如果不想修改为双工通信,可让客户端定期调用服务端的进度查询接口,适合对实时性要求不高的场景。

步骤与代码示例

  1. 服务端维护进度变量并提供查询方法
[ServiceContract]
public interface IFileCopyService
{
    [OperationContract]
    void CopyFile(string sourcePath, string destPath);
    [OperationContract]
    int GetCopyProgress();
}

public class FileCopyService : IFileCopyService
{
    private static int _currentProgress;
    private static readonly object _lockObj = new object(); // 保证线程安全

    public void CopyFile(string sourcePath, string destPath)
    {
        var fileInfo = new FileInfo(sourcePath);
        long totalBytes = fileInfo.Length;
        long copiedBytes = 0;

        using (var sourceStream = new FileStream(sourcePath, FileMode.Open))
        using (var destStream = new FileStream(destPath, FileMode.Create))
        {
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = sourceStream.Read(buffer, 0, buffer.Length)) > 0)
            {
                destStream.Write(buffer, 0, bytesRead);
                copiedBytes += bytesRead;
                lock (_lockObj)
                {
                    _currentProgress = (int)((copiedBytes * 100) / totalBytes);
                }
                Thread.Sleep(10);
            }
            lock (_lockObj)
            {
                _currentProgress = 100;
            }
        }
    }

    public int GetCopyProgress()
    {
        lock (_lockObj)
        {
            return _currentProgress;
        }
    }
}
  1. 客户端轮询更新UI
private void CopyButton_Click(object sender, RoutedEventArgs e)
{
    var binding = new NetNamedPipeBinding();
    var endpoint = new EndpointAddress("net.pipe://localhost/FileCopyService");
    var factory = new ChannelFactory<IFileCopyService>(binding, endpoint);
    var serviceProxy = factory.CreateChannel();

    // 后台启动复制任务
    Task.Run(() => serviceProxy.CopyFile(@"C:\SourceFile.txt", @"C:\DestFile.txt"));

    // 启动轮询任务
    Task.Run(async () =>
    {
        while (true)
        {
            int progress = serviceProxy.GetCopyProgress();
            Application.Current.Dispatcher.Invoke(() => CopyProgressBar.Value = progress);
            if (progress >= 100)
                break;
            await Task.Delay(100); // 每100ms查询一次进度
        }
    });
}

方案3:本地共享资源传递进度(备选)

通过本地共享文件或内存映射文件传递进度,适合无法修改WCF契约的场景,但需处理同步和资源清理:

  • 服务端在复制过程中,将进度写入指定本地文件(或内存映射区域)
  • WPF客户端定期读取该文件,解析进度并更新UI
  • 复制完成后,服务端删除或清空文件,客户端停止读取

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 12:53:26