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