Изящно закройте приложение перед установкой с помощью управляемого пользовательского действия 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.