Изящно закройте приложение перед установкой с помощью управляемого пользовательского действия Wix


В моем установщике 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.Убить, но код не работает, когда помещается в пользовательское действие-должна быть последовательность события?

править

Использование 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 2

1 ответ:

Если вы правильно поняли Пользовательское действие CloseApp из Wix, вы должны перечислить все окна в процессе.

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

Поэтому вам нужно будет реализовать EnumWindows (EnumCallBack, HANDLE processToClose) И в EnumCallBack вы на самом деле метод postMessage WM_CLOSE для каждого окна.