Win32-приложение без окон - ожидание выхода из программы
У меня есть приложение без окон, единственная цель которого - установить 32-битный DLL-файл ловушки и дождаться выхода родительской программы (64-битной). 64-битная программа написана на C#, а приложение без окон написано на C++. Первоначально у меня был этот цикл GetMessage, который держал программу открытой:
while(GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Я закрывал приложение C++, используя метод Process.Kill в C#, но обнаружил, что это не позволяет закрывать приложение C++ без ошибок. Кроме того, в случае сбоя приложения C# приложение C++ останется открытым навсегда. Я сделал проверку приложения C++, чтобы проверить, запущено ли приложение C#, используя этот цикл:
while(true)
{
if(PeekMessage(&msg, NULL, 0, 0, true))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if(!isMainProgramRunning())
break;
Sleep(1000);
}
По какой-то причине сон вызывает проблемы. Хуки, установленные файлом DLL, - это WH_CBT и WH_KEYBOARD. Всякий раз, когда я нажимаю клавишу, когда приложение C++ работает с этим циклом, ключи просто съедаются. Удаление Sleep заставляет его работать нормально, но, как и ожидалось, он использует 100% CPU, что мне не нужно. Я попытался полностью удалить цикл сообщений и вместо этого использовать WaitForSingleObject с бесконечным таймаутом в потоке, который закончился бы, когда isMainProgramRunning вернул false. Это в основном блокирует весь компьютер.
Я не очень понимаю, почему GetMessage, который, насколько я видел, никогда не возвращался, но приостанавливал основной поток на неопределенный срок, пока не вызывал этих проблем. WaitForSingleObject заставляет каждое приложение зависать при нажатии на него. Как я могу заставить приложение C++ оставаться открытым, пока приложение C# не закроется?
Редактировать:
Поскольку мне было указано, что спать в насосе сообщений плохо, позвольте мне спросить: есть ли способ указать тайм-аут ожидания сообщения, чтобы программа не ожидала сообщения бесконечно, а скорее подождите около 250 мс, тайм-аут, позвольте мне запустить метод isMainProgramRunning, затем подождите еще немного?
Edit2:
Я попытался использовать MsgWaitForMultipleObjects, хотя и несколько иначе, чем предложил Лео. Это цикл, который я использовал:
while(MsgWaitForMultipleObjects (0, NULL, true, 250, QS_ALLPOSTMESSAGE) != WAIT_FAILED)
{
if(PeekMessage(&msg, NULL, 0, 0, true))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if(!isMainProgramRunning())
break;
}
Снова у меня была такая же проблема со сном. Я также попытался приостановить основной поток и заставить другой поток возобновить его. Та же проблема. Что делает GetMessage, что позволяет ему ждать, не вызывая этих проблем? Может быть, это должно быть темой другого поста, но почему, когда приложение C++, устанавливающее хуки, спит или приостанавливается, вся обработка в хуках, похоже, также приостанавливается?
Edit3:
Вот метод DLL, который приложение C++ вызывает для установки хуков:
extern "C" __declspec(dllexport) void install()
{
cbtHook = SetWindowsHookEx(WH_CBT, hookWindowEvents, hinst, NULL);
if(cbtHook == NULL)
MessageBox(NULL, "Unable to install CBT hook", "Error!", MB_OK);
keyHook = SetWindowsHookEx(WH_KEYBOARD, LowLevelKeyboardProc, hinst, NULL);
if(keyHook == NULL)
MessageBox(NULL, "Unable to install key hook", "Error!", MB_OK);
}
4 ответа
GetMessage никогда не возвращается, потому что у вас нет созданного окна!
Чтобы использовать очередь сообщений, у вас должен быть графический интерфейс. Вы можете, например, создать скрытое окно.
У вас есть две отдельные проблемы:
Как заставить программу C++ без окон автоматически завершать работу, если программа C# завершается (например, происходит сбой).
В программе C++ откройте дескриптор программы C#. Поскольку программа C# запускает программу C++, пусть программа C# передает свой собственный PID в качестве аргумента; затем программа C++ может открыть дескриптор этого процесса, используя OpenProcess.
Затем используйте MsgWaitForMultipleObjects в вашем цикле сообщений. Если программа C# выйдет из дескриптора, который вам нужен, он будет сигнализирован, и вы проснетесь. (Вы также можете использовать
WaitForSingleObject(hProcess,0)==WAIT_OBJECT_0
проверить, сигнализирует ли процесс или нет, например, чтобы проверить, почему вы проснулись, хотя результат MsgWaitForMultipleObjects также скажет вам об этом.)Вы должны закрыть дескриптор процесса при выходе (хотя операционная система сделает это за вас, когда вы выйдете, это хорошая практика, если вы повторно используете этот код в другом контексте). Обратите внимание, что дескриптор переживает процесс, который он представляет, и поэтому вы можете подождать его.
Как заставить программу на C# дать команду программе на C++ для выхода.
Это может вам не понадобиться, если вы #1 работаете, но вы можете просто заставить программу C# публиковать сообщение на C++, если хотите.
НЕ используйте PostQuitMessage и НЕ публикуйте и не отправляйте WM_QUIT между потоками или процессами.
Вместо этого опубликуйте какое-то другое сообщение, о котором договорились два приложения (например, WM_APP+1), используя PostThreadMessage.
Вы можете создать именованное событие и использовать:
MsgWaitForMultipleObjects
в вашем цикле сообщений.
Приложение C# должно только открыть и вызвать это событие, чтобы сообщить вашему приложению о выходе.
Это своего рода минимальное межпроцессное взаимодействие.
Вы должны выйти из процесса, отправив ему сообщение WM_QUIT и обработав его правильно, как указано в этой статье (Модальность), Раймондом Ченом. Не спите внутри цикла без обработки сообщений - это неправильно. Ваше приложение должно либо обрабатывать сообщение, либо ждать новых сообщений.