Вот что я делаю в своем консольном приложении (работающем в Windows 7):
я. Создайте скрытое окно для ожидания уведомления о закрытии / выходе. Важное замечание: назначить собственный поток для цикла сообщений
void interrupt::start()
{
WNDCLASSEX wc = {};
HINSTANCE hi = GetModuleHandle(NULL);
wc.cbSize = sizeof(WNDCLASSEX);
wc.lpfnWndProc = WndProc;
// . . . etc
if(!RegisterClassEx(&wc))
return;
hwnd_ = CreateWindowEx(WS_EX_CLIENTEDGE, class_name, "Waiting for user logoff event",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 240, 120, NULL, NULL, hi , NULL);
ShowWindow(hwnd_, SW_HIDE);
UpdateWindow(hwnd_);
MSG msg = {};
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// call internal function for sending "stop" notification to rest of program
ctrl.stop(CTRL_CLOSE_EVENT);
}
II. Реализуйте обработку «специальных событий» в вашем обработчике сообщений окна
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_ENDSESSION:
if (lParam)
{
// call internal function for sending "stop" notification to rest of program
ctrl.stop(lParam == ENDSESSION_LOGOFF ? (int) interrupt::logoff : CTRL_CLOSE_EVENT);
}
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
III. Реализовать внутреннюю функцию для обработки «стоп» запросов. Он должен обрабатывать 2 специальных условия:
а. когда не вызывается из потока сообщений окна, отправьте WM_CLOSE в окно и дождитесь выхода его потока
б. когда не вызывается из основного потока, дождитесь завершения статических переменных (хотя бы одной).
Это потому, что после выхода из CtrlHandler Windows безоговорочно прервет ваш процесс, не предоставляя вашему коду никакой возможности для очистки. Статические переменные уничтожаются в главном потоке, поэтому это ожидание дает вам хотя бы гарантию, что int main()
завершился. Вы можете захватить идентификатор потока основного потока в конструкторе статической переменной (возможно, той же самой, которая запустила «теневое» окно).
Вот как я это сделал в своем коде:
void interrupt::stop(int signal)
{
// . . .
// Set exit signal
InterlockedExchange(&stage_, 2L);
// Close shadow window if notification is from elsewhere
if (hwnd_ && GetCurrentThreadId() != thread_.id())
{
PostMessage(hwnd_, WM_CLOSE, 0, 0);
thread_.wait();
}
// Wait for completion of own destructor on main thread
if (GetCurrentThreadId() != main_thread_id_)
while(stage_ != 3L)
Sleep(10);
}
// My static variable to wait for
interrupt ctrl;