如何实现用户点击取消后立即终止带异步下载的Wix安装自定义操作
解决方案
问题根因
你当前的代码存在两个核心问题导致取消无效:
while (client.IsBusy) { }是无任何等待的死循环,完全占满当前自定义操作的线程,Wix的UI取消信号无法传递更新到Session.Cancel属性- 没有监听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
相关产品推荐
相关产品推荐

