Я сталкиваюсь с неожиданным поведением при использовании сообщения WM_CLOSE для закрытия приложения C #. В моем сценарии мне нужно убедиться, что приложение C # закрыто, когда работает его установочный пакет MSI. MSI не имеет соответствующих действий для этого, поэтому я писал DLL-файл с настраиваемым действием, которая встроена в пакет MSI и предоставляет функцию EnsureApplicationClosed. Связанный код DLL ниже:
#include "stdafx.h"#include <iostream>
#include <vector>
#include <string>
#include "TlHelp32.h"
std::vector<DWORD> processesList;
//This method is the main point of interest
BOOL CALLBACK enumWindowsProc(__in HWND hWnd, __in LPARAM lParam)
{
if(processesList.size()==0) return FALSE;
DWORD processId;
GetWindowThreadProcessId(hWnd, &processId);
int index=0;
while(index<processesList.size())
{
if(processesList.at(index)==processId)
{
//Remove process id from the list
processesList.erase(processesList.begin()+index);
//Should close main windows of the process found.
SendMessage(hWnd, WM_CLOSE, (LPARAM)0, (WPARAM)0);
}
else
{
index++;
}
}
return TRUE;
}
std::vector<DWORD> FindProcessesId(const LPCWSTR processName)
{
std::vector<DWORD> result;
PROCESSENTRY32 processInfo;
processInfo.dwSize = sizeof(processInfo);
HANDLE processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if ( processesSnapshot == INVALID_HANDLE_VALUE ) return result;
Process32First(processesSnapshot, &processInfo);
if (wcscmp(processName, processInfo.szExeFile)==0)
{
result.push_back(processInfo.th32ProcessID);
}
while ( Process32Next(processesSnapshot, &processInfo) )
{
if ( wcscmp(processName, processInfo.szExeFile)==0 )
{
result.push_back(processInfo.th32ProcessID);
}
}
CloseHandle(processesSnapshot);
return result;
}
void InnerEnsureApplicationClosed(const LPCWSTR processName)
{
processesList=FindProcessesId(processName);
BOOL enumeratingWindowsSucceeded = ::EnumWindows( enumWindowsProc, NULL );
}
//Function exported by the DLL
UINT __stdcall EnsureApplicationClosed ( MSIHANDLE hModule )
{
InnerEnsureApplicationClosed(L"SomeApplication.exe");
return ERROR_SUCCESS;
}
И я использую простое консольное приложение для проверки этой DLL:
#include "stdafx.h"#include <SomeApplicationCustomActions.h>
int _tmain(int argc, _TCHAR* argv[])
{
EnsureApplicationClosed(NULL);
return 0;
}
Приложение C # в большинстве случаев невидимо для пользователя, так как должно отображать только уведомления от сторонних приложений. Вот почему он имеет собственный обработчик FormClosing:
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
Console.Beep();
if (e.CloseReason != CloseReason.WindowsShutDown && e.CloseReason!= CloseReason.TaskManagerClosing)
{
e.Cancel = true;
this.Hide();
}
}
Я использую SendMessage (hWnd, WM_CLOSE, (LPARAM) 0, (WPARAM) 0), чтобы закрыть приложение C #. Проверка причины закрытия TaskManagerClosing необходима для закрытия при получении этого сообщения WM_CLOSE, поскольку я обнаружил, что SendMessage (hWnd, WM_CLOSE, (LPARAM) 0, (WPARAM) 0) создает TaskManagerClosing причину закрытия в приложении C #. Console.Beep () используется для тестирования, чтобы создать звуковой сигнал, чтобы я мог услышать, что введен код обработчика.
Проблема в том, что это работает только тогда, когда я запускаю тестовое приложение DLL во второй раз. Я не слышу звуковой сигнал, и процесс C # остается в диспетчере задач при первом запуске тестового приложения. Поэтому событие FormClosing приложения C # не запускается. Я пытался заменить SendMessage на PostMessage, но безуспешно. GetLastError () всегда возвращает 18 в обоих случаях. Кто-нибудь сталкивался с такой же проблемой и знает, как ее решить?
Заранее спасибо.
Проиграв с ним целый день, я нашел причину, по которой возникает эта проблема. DLL пользовательских действий в порядке. Проблема связана с управлением .NET ListView. Если свойство Visible формы установлено в false в обработчике события Shown, и если форма имеет элемент управления ListView, то она становится невосприимчивой к сообщению WM_CLOSE, отправляемому из другого приложения в первый раз. WM_CLOSE просто не появляется в методе WndProc. Я исправил это, переопределив SetVisibleCore формы, но в любом случае эта проблема выглядит очень странно и кажется ошибкой в .NET.
Других решений пока нет …