通过Wix托管自定义操作安装之前,优雅地关闭应用程序

时间:2011-02-08 13:31:42

标签: c# wix pinvoke wix3 custom-action

在我的WiX安装程序中,我想优雅地关闭即将运行的即将更新的应用程序。我不想提示用户关闭,我不想杀死进程。在关闭申请之前,我需要有机会进行一些清理等工作。

该应用是一个在系统托盘中运行的WinForms应用。主窗体有一个标题,比方说“mainwindow”,但是隐藏了并且ShowInTaskbar = false。

通过尝试Process.Kill() Process.CloseMainWindow() FindWindow, SendMessage, PostMessage等一些不同的测试人员应用程序,我发现对我来说最好的方法是使用PostMessage

var hWnd = FindWindow(null, "mainwindowtitle");
PostMessage(hWnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);

这样我可以覆盖OnFormClosing并执行我需要的任何清理。这可以从我扔在一起的测试器应用程序中正常工作。问题是它在WiX安装程序中运行时不起作用。我有一个c#Custom Action CA.dll,安装程序肯定会调用自定义操作 - 我可以从msiexec日志中看到,如果我将自定义操作代码更改为Process.Kill(),它确实会正确停止应用程序。但是,当它使用PostMessage代码运行时,应用程序不会关闭,OnFormClosing永远不会被调用。

这是我的CustomAction代码

        private const int WM_CLOSE = 0x0010;

    [DllImport("user32.dll", SetLastError = true)]
    static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    [DllImport("user32.dll", SetLastError = true)]
    static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

    [CustomAction]
    public static ActionResult CloseApplicationGracefully(Session session)
    {
        session.Log("Starting the CloseApplicationGracefully Custom Action - attempting to stop DUC.");

        var hWnd = FindWindow(null, "mainwindowtitle");

        session.Log("Window handle found: " + hWnd);

        bool result = PostMessage(hWnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);

        session.Log("Result of calling app to close: " + result);

        if (result)
        {
            return ActionResult.Success;
        }
        return ActionResult.Failure;
    }

这是wx设置代码

<Binary Id="WixCustomAction.dll"
        SourceFile="$(var.WixCustomAction.TargetDir)$(var.WixCustomAction.TargetName).CA.dll" />
<CustomAction Id="WixCustomAction"
              BinaryKey="WixCustomAction.dll"
              DllEntry="CloseDeploymentUpdater" />
<InstallExecuteSequence>
  <Custom Action="WixCustomAction" After="FindRelatedProducts"></Custom>
</InstallExecuteSequence>

我试过用不同的顺序调用这个自定义动作但没有运气... 代码可以在测试器应用程序中运行,当我使用Process.Kill时,自定义操作可以工作,但是当放入自定义操作时代码不起作用 - 必须是事件序列吗?

修改

在答案中使用下面建议的WixCloseApplications CA会产生以下日志条目

WixCloseApplications:  App: DUC.exe found running, 1 processes, attempting to send close message.
WixCloseApplications:  Sending close message to process id 0x1978
WixCloseApplications:  Result 0x12
WixCloseApplications:  Sending close message to process id 0x1978
WixCloseApplications:  Result 0x0
WixCloseApplications:  Sending close message to process id 0x1978
WixCloseApplications:  Result 0x578
WixCloseApplications:  Sending close message to process id 0x1978
WixCloseApplications:  Result 0x0
.
.
.
MSI (s) (C8!D4) [15:00:47:985]: PROPERTY CHANGE: Adding WixCloseApplicationsDeferred property. Its value is 'DUC.exe5'.
MSI (s) (C8!D4) [15:00:48:000]: Doing action: WixCloseApplicationsDeferred
.
.
Action 15:00:48: WixCloseApplicationsDeferred. 
Action start 15:00:48: WixCloseApplicationsDeferred.
.
.
Action ended 15:00:48: WixCloseApplicationsDeferred. Return value 1.
Action ended 15:00:48: WixCloseApplications. Return value 1.

1 个答案:

答案 0 :(得分:2)

如果正确理解Wix中的CloseApp CustomAction,您应该枚举进程中的所有窗口。

http://wix.codeplex.com/SourceControl/changeset/view/175af30efe78#src%2fca%2fwixca%2fdll%2fCloseApps.cpp

所以你需要实现一个EnumWindows(EnumCallBack,HANDLE processToClose) 在EnumCallBack中,实际上每个窗口都有PostMessage WM_CLOSE。