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

WiX V5 MSI无法自动关闭通知区域应用,如何解决?

问题:WiX V5 MSI无法自动关闭托盘应用

我正在将一款通知区域(托盘)应用升级为使用WiX V5 MSI安装,希望MSI在安装前自动关闭运行中的应用,但util:CloseApplication元素不起作用。

配置细节

在Package.wxs中添加了以下配置:

<util:CloseApplication Id="CloseMyApp" ElevatedEndSessionMessage="yes" 
                       Target="MyTestTrayApp.exe" RebootPrompt="no" />

<InstallExecuteSequence>
  <Custom Action="override Wix4CloseApplications_$(sys.BUILDARCHSHORT)" After="InstallInitialize" />
</InstallExecuteSequence>

应用端实现

创建了CloseMessageFilter类处理关闭消息,但PreFilterMessage从未接收到预期的WM_ENDSESSION消息:

using System.Windows.Forms;

static class Program
{
    private class CloseMessageFilter : IMessageFilter
    {
        const int WM_CLOSE           = 0x0010;  // 当CloseMessage="yes"时应发送
        const int WM_QUERYENDSESSION = 0x0011;  // 当CloseMessage≠"yes"时应发送
        const int WM_ENDSESSION      = 0x0016;  // 当ElevatedEndSessionMessage="yes"时应发送
        const int VK_CONTROL         = 0x0011;  // 但日志显示尝试发送消息ID 17

        public bool PreFilterMessage(ref Message m)
        {
            if (m.Msg == WM_CLOSE || m.Msg == WM_QUERYENDSESSION || m.Msg == WM_ENDSESSION || m.Msg == VK_CONTROL)
            {
                MessageBox.Show(String.Format("PreFilterMessage: m.Msg = {0}", m.Msg));
                Program.Exit();
            }

            return false;
        }

        private static MyTestTrayAppContext appContext = null;

        [STAThread]
        public static void Main()
        {
            AddApplicationToStartup();
            // ...其他初始化操作
            Application.AddMessageFilter(new CloseMessageFilter());
            appContext = new MyTestTrayContext();
            Application.Run(appContext);
        }

        public static void Exit()
        {
            appContext.myConnectorTray.Stop();
            appContext.trayIcon.Visible = false;
            appContext = null;
            Application.Exit();
        }

        public static void AddApplicationToStartup()
        {
            // ...启动项添加逻辑
        }
    }
}

public class MyTestTrayContext : ApplicationContext
{
    public MyTestTrayContext()
    {
        // ...上下文初始化
    }
}

安装日志信息

启用详细日志后,发现MSI尝试发送消息ID 17(而非预期的WM_ENDSESSION),发送6次后放弃:

WixCloseApplicationsDeferred:  Checking for App: MyTestTrayApp.exe Attributes: 16
WixCloseApplicationsDeferred:  Checking App: MyTestTrayApp.exe 
WixCloseApplicationsDeferred:  App: MyTestTrayApp.exe found running, 1 processes, attempting to send message.
WixCloseApplicationsDeferred:  Attempting to send process id 0x209c message id: 17
WixCloseApplicationsDeferred:  Sending message to process id 0x209c
WixCloseApplicationsDeferred:  Result 0x1
WixCloseApplicationsDeferred:  Sending message to process id 0x209c
WixCloseApplicationsDeferred:  Result 0x1
WixCloseApplicationsDeferred:  Sending message to process id 0x209c
WixCloseApplicationsDeferred:  Result 0x1
WixCloseApplicationsDeferred:  Sending message to process id 0x209c
WixCloseApplicationsDeferred:  Result 0x1
WixCloseApplicationsDeferred:  Sending message to process id 0x209c
WixCloseApplicationsDeferred:  Result 0x1
WixCloseApplicationsDeferred:  Sending message to process id 0x209c
WixCloseApplicationsDeferred:  Result 0x1

解决方案

1. 修正WiX配置的动作名称

WiX V5中,override Wix4CloseApplications_$(sys.BUILDARCHSHORT)动作名称不正确,应直接使用WixCloseApplications动作,同时调整util:CloseApplication属性,改用更适合托盘应用的CloseMessage="yes":

<util:CloseApplication Id="CloseMyApp" Target="MyTestTrayApp.exe" 
                       CloseMessage="yes" RebootPrompt="no" />

<InstallExecuteSequence>
  <Custom Action="WixCloseApplications" After="InstallInitialize" />
</InstallExecuteSequence>

2. 用隐藏顶层窗口捕获系统消息

托盘应用无可见主窗口时,IMessageFilter无法可靠捕获全局消息,需创建隐藏窗口并重写WndProc处理关闭消息:

public class MyTestTrayContext : ApplicationContext
{
    private CustomHiddenWindow hiddenWindow;

    public MyTestTrayContext()
    {
        // 创建隐藏窗口接收系统消息
        hiddenWindow = new CustomHiddenWindow();
        hiddenWindow.ShowInTaskbar = false;
        hiddenWindow.WindowState = FormWindowState.Minimized;
        hiddenWindow.Visible = false;
    }

    private class CustomHiddenWindow : Form
    {
        protected override void WndProc(ref Message m)
        {
            const int WM_CLOSE = 0x0010;
            const int WM_QUERYENDSESSION = 0x0011;
            const int WM_ENDSESSION = 0x0016;

            if (m.Msg == WM_CLOSE || m.Msg == WM_QUERYENDSESSION || m.Msg == WM_ENDSESSION)
            {
                Program.Exit();
            }

            base.WndProc(ref m);
        }
    }
}

同时移除Application.AddMessageFilter的调用,依赖隐藏窗口的WndProc处理消息。

3. 修复Exit方法的空引用问题

调整Exit方法的执行顺序,避免空引用异常:

public static void Exit()
{
    if (appContext != null)
    {
        appContext.myConnectorTray.Stop();
        appContext.trayIcon.Visible = false;
        appContext.trayIcon.Dispose();
        appContext = null;
    }
    Application.Exit();
}

4. 理解日志中的消息ID 17

日志中的消息ID 17对应WM_QUERYENDSESSION(0x0011=17),说明ElevatedEndSessionMessage="yes"配置未生效,改用CloseMessage="yes"触发WM_CLOSE更适合托盘应用场景,逻辑更直接。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 13:27:00