WedX - журнал о программировании и компьютерных науках

Безоконное приложение 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++ останется открытым навсегда. Я проверил приложение С++, чтобы убедиться, что приложение С# все еще работает, используя этот цикл:

while(true)
{
    if(PeekMessage(&msg, NULL, 0, 0, true))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    if(!isMainProgramRunning())
        break;

    Sleep(1000);
}

По какой-то причине Сон вызывает проблемы. Перехватчики, установленные файлом DLL, — это WH_CBT и WH_KEYBOARD. Всякий раз, когда я нажимаю клавишу, пока приложение C++ работает с этим циклом, клавиши просто съедаются. Удаление спящего режима заставляет его работать нормально, но, как и ожидалось, он использует 100% CPU, что я не хочу. Я попытался полностью удалить цикл сообщений и вместо этого использовать WaitForSingleObject с бесконечным тайм-аутом в потоке, который завершится, когда isMainProgramRunning вернет false. Это в основном блокирует весь компьютер.

Я действительно не понимаю, почему GetMessage, который, насколько я видел, никогда не возвращался, но приостанавливал основной поток на неопределенный срок, не вызывал этих проблем, а WaitForSingleObject заставляет каждое приложение зависать, когда я нажимаю на него. Как я могу заставить приложение С++ оставаться открытым, пока приложение С# не закроется?

Редактировать:

Поскольку мне было указано, что сон в насосе сообщений — это плохо, позвольте мне задать следующий вопрос: есть ли способ указать тайм-аут для ожидания сообщения, чтобы программа не ждала сообщения бесконечно долго, а, скорее, подождите около 250 мс, тайм-аут, позвольте мне запустить метод isMainProgramRunning, затем подождать еще немного?

Редактировать2:

Я попытался использовать 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++, устанавливающее хуки, спит или приостанавливается, кажется, что вся обработка в хуках также приостанавливается?

Редактировать3:

Вот метод 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);
}
20.12.2010

Ответы:


1

GetMessage никогда не возвращается, потому что у вас не создано окно!

Чтобы использовать очередь сообщений, у вас должен быть некоторый графический интерфейс. Например, вы можете создать скрытое окно.

20.12.2010
  • Я как-то подозревал это. Как обычные приложения без окон остаются открытыми? 20.12.2010
  • Я сдался и решил сделать именно это, так как GetMessage казался единственной функцией ожидания, которая ничего не ломала. 22.12.2010
  • -1: GetMessage можно использовать без окна. Это не ответ, ИМО. Если MsgWaitForMultipleObject используется правильно, он также не вернется (если только не было причины для пробуждения потока). MSDN в PostThreadMessage явно упоминает GetMessage без окна: поток, в который отправляется сообщение, извлекает сообщение, вызывая функцию GetMessage или PeekMessage. Элемент hwnd возвращаемой структуры MSG имеет значение NULL. 22.12.2010
  • @Telanor: Как обычные приложения без окон остаются открытыми? Либо выполняя много работы, но, скорее всего, ожидая Object-Events. Если вы запускаете свою С#-программу с помощью CreateProcess(...), вы можете дождаться завершения этого процесса, используя дескрипторы, которые CreateProcess возвращает в параметре PROCESS_INFORMATION. 22.12.2010
  • @Telanor: только что обнаружил, что ваша программа на С# является родителем, поэтому игнорируйте вторую часть моего последнего комментария. 22.12.2010

  • 2

    У вас есть две отдельные проблемы:

    1. Как сделать так, чтобы программа на C++ без окон автоматически завершала работу, если завершалась программа на C# (например, при сбое).

      В программе C++ откройте дескриптор программы C#. Поскольку программа на C# запускает программу на C++, пусть программа на C# передает свой собственный PID в качестве аргумента; затем программа C++ может открыть дескриптор этого процесса, используя ОткрытьПроцесс.

      Затем используйте MsgWaitForMultipleObjects в своем цикле сообщений. . Если программа C# выходит из дескриптора, который вам нужен, это будет сигнализировано, и вы проснетесь. (Вы также можете использовать WaitForSingleObject(hProcess,0)==WAIT_OBJECT_0, чтобы проверить, сигнализируется процесс или нет, например, чтобы проверить, почему вы были разбужены, хотя результат MsgWaitForMultipleObjects также сообщит вам об этом.)

      Вы должны закрыть дескриптор процесса при выходе (хотя ОС сделает это за вас при выходе, это хорошая практика, если вы повторно используете этот код в другом контексте). Обратите внимание, что дескриптор переживает процесс, который он представляет, и поэтому вы можете подождать его.

    2. Как заставить программу C# дать команду программе C++ завершить работу.

      Вам может не понадобиться это, если вы заработаете # 1, но вы можете просто отправить программу С# сообщение на С++, если хотите.

      НЕ используйте PostQuitMessage и НЕ публикуйте и НЕ отправляйте WM_QUIT между потоками или процессами.

      Вместо этого опубликуйте какое-либо другое сообщение, с которым согласны два приложения (например, WM_APP+1), используя PostThreadMessage.

    20.12.2010
  • Я попробовал что-то подобное, используя MsgWaitForMultipleObjects, и столкнулся с той же проблемой. Взгляните на мою вторую правку. 21.12.2010
  • @Telanor, попробуйте использовать QS_ALLEVENTS вместо QS_ALLPOSTMESSAGE 21.12.2010
  • Я попробовал, то же самое произошло 21.12.2010
  • @Telanor, можешь показать нам, как ты устанавливаешь хук? Кстати, вызов MsgWaitForMultipleObjects (если он есть) должен принимать дескриптор процесса в качестве параметра, чтобы цикл автоматически просыпался, если процесс завершается, хотя я бы не стал беспокоиться об этом, пока не заработает базовый цикл. 21.12.2010

  • 3

    Вы можете создать именованное событие и использовать:

    MsgWaitForMultipleObjects

    в вашем цикле сообщений.

    Приложению C# нужно только открыть и вызвать это событие, чтобы сообщить вашему приложению о выходе.

    Это своего рода минимальное межпроцессное взаимодействие.

    20.12.2010
  • Если приложение C# выйдет из строя или будет завершено, это событие никогда не будет установлено. Лучше дождаться самого дескриптора процесса (см. мой ответ). 20.12.2010
  • @ Лео Да, ты прав. И, возможно, оба они полезны в случае, если приложению C# необходимо завершить работу приложения C++, не выходя из него. 20.12.2010

  • 4

    Вы должны выйти из процесса, отправив ему сообщение WM_QUIT и правильно обработав его, как указано в эта статья (Модальность), автор Рэймонд Чен. Не спите внутри цикла без обработки сообщений — это неправильно. Ваше приложение должно либо обрабатывать сообщение, либо ожидать новых сообщений.

    20.12.2010
  • Проблема в том, что функция GetMessage никогда не возвращается. Как только я вызываю его, он просто сидит и ждет вечно. Мне не удалось заставить приложение С# успешно отправить сообщение WM_QUIT, но даже если бы я это сделал, в случае, если приложение С# никогда не отправляет WM_QUIT (в случае сбоя), приложение С++ будет ждать вечно 20.12.2010
  • Новые материалы

    Как проанализировать работу вашего классификатора?
    Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

    Работа с цепями Маркова, часть 4 (Машинное обучение)
    Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

    Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
    Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

    Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
    Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..

    Учебные заметки: создание моего первого пакета Node.js
    Это мои обучающие заметки, когда я научился создавать свой самый первый пакет Node.js, распространяемый через npm. Оглавление Глоссарий I. Новый пакет 1.1 советы по инициализации..

    Забудьте о Matplotlib: улучшите визуализацию данных с помощью умопомрачительных функций Seaborn!
    Примечание. Эта запись в блоге предполагает базовое знакомство с Python и концепциями анализа данных. Привет, энтузиасты данных! Добро пожаловать в мой блог, где я расскажу о невероятных..

    ИИ в аэрокосмической отрасли
    Каждый полет – это шаг вперед к великой мечте. Чтобы это происходило в их собственном темпе, необходима команда астронавтов для погони за космосом и команда технического обслуживания..


    Для любых предложений по сайту: [email protected]