You need to enable JavaScript to run this app. 如何实现用户点击取消后立即终止带异步下载的Wix安装自定义操作
优惠活动
大模型
产品
解决方案
定价
更多

如何实现用户点击取消后立即终止带异步下载的Wix安装自定义操作

解决方案

问题根因

你当前的代码存在两个核心问题导致取消无效:

  1. while (client.IsBusy) { } 是无任何等待的死循环,完全占满当前自定义操作的线程,Wix的UI取消信号无法传递更新到Session.Cancel属性
  2. 没有监听Wix的取消触发事件,也没有调用WebClient.CancelAsync()主动终止下载请求,DownloadFileCompleted的e.Cancelled自然永远不会被触发

修改方案

1. 调整CustomAction.cs代码

修改后的代码如下:

public class CustomActions
{
    [CustomAction]
    public static ActionResult DownloadImage(Session session)
    {
        InstallerLog.Log(session, "DownloadImage", "Entry Point :", InstallerLog.LoggerLevel.Info);
        string url = session["DOWNLOAD_URL"];
        string fileName = Path.GetFileName(new UriBuilder(url).Path);
        string downloadPath = "SomePath";
        string downloadFile = Path.Combine(downloadPath, fileName);

        DownloadImageFile DIF = null;
        try
        {
            DIF = new DownloadImageFile(session);
            var downloadSuccess = DIF.DownloadFile(url, downloadFile);
            // 用户主动取消的场景返回UserExit
            if (DIF.IsUserCancelled)
            {
                return ActionResult.UserExit;
            }
            if (!downloadSuccess)
            {
                return ActionResult.Failure;
            }
        }
        catch (Exception exc)
        {
            InstallerLog.Log(session, "DownloadImage", "Error: " + exc.Message, InstallerLog.LoggerLevel.Error);
            session.Message(InstallMessage.Error, new Record { FormatString = exc.Message });
            // 清理残留的未完成下载文件
            if (File.Exists(downloadFile))
            {
                File.Delete(downloadFile);
            }
            return ActionResult.Failure;
        }
        return ActionResult.Success;
    }
}

class DownloadImageFile : IDisposable
{
    private volatile bool _completed;
    private volatile bool _isCancelled;
    private Session session;
    private WebClient client;
    private string downloadFilePath;

    public bool IsUserCancelled => _isCancelled;
    public bool DownloadCompleted => _completed;

    public static double previousBytes = 0;

    public DownloadImageFile(Session sess)
    {
        session = sess;
        client = new WebClient();
    }

    public bool DownloadFile(string address, string location)
    {
        downloadFilePath = location;
        Uri Uri = new Uri(address);
        _completed = false;
        _isCancelled = false;
        InstallerLog.Log(session, "DownloadFile", "Starting file download");

        client.DownloadFileCompleted += new AsyncCompletedEventHandler(Completed);
        client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(DownloadProgress);
        client.Headers.Add("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0)");
        client.DownloadFileAsync(Uri, location);

        // 替换原来的死循环,定期检查取消信号
        while (client.IsBusy && !_isCancelled)
        {
            // 检测到用户点击Wix取消按钮
            if (session.Cancel)
            {
                _isCancelled = true;
                client.CancelAsync();
                break;
            }
            // 让出线程时间片,允许Wix更新取消状态,同时避免CPU占用100%
            Thread.Sleep(100);
        }

        return _completed && !_isCancelled;
    }

    private void DownloadProgress(object sender, DownloadProgressChangedEventArgs e)
    {
        if (((double)e.BytesReceived - previousBytes) / e.TotalBytesToReceive * 100 > 0.5)
        {
            previousBytes = e.BytesReceived;
            InstallerLog.Log(session, "DownloadFile", "File downloaded: " + e.ProgressPercentage + "%");
        }
    }

    private void Completed(object sender, AsyncCompletedEventArgs e)
    {
        if (e.Cancelled == true)
        {
            InstallerLog.Log(session, "DownloadFile", "Download has been canceled");
            // 清理未完成的下载文件
            if (File.Exists(downloadFilePath))
            {
                File.Delete(downloadFilePath);
            }
        }
        else if (e.Error != null)
        {
            InstallerLog.Log(session, "DownloadFile", "Error encountered while downloading image file:" + e.Error.ToString());
            if (File.Exists(downloadFilePath))
            {
                File.Delete(downloadFilePath);
            }
        }
        else
        {
            InstallerLog.Log(session, "DownloadFile", "Download completed");
        }

        _completed = true;
    }

    // 释放WebClient资源
    public void Dispose()
    {
        client?.Dispose();
    }
}

2. 自定义操作配置无需修改

你当前Product.wxs里的CustomAction配置可以保持不变,不需要额外调整。


额外优化说明

  • 新增了IDisposable接口实现,避免WebClient资源泄漏
  • 增加了取消/下载失败后的临时文件清理逻辑,避免残留无效文件
  • 调整了取消场景的返回值为ActionResult.UserExit,符合Wix安装取消的标准返回逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.18 22:36:04