Видимо, ничего. Скорее всего, можно функцию перехватить и не допускать ее выполнения, когда этого не хочется...
Nouzui "Внутреннее устройство W2000" Руссиновича утверждает следующее: 1. Csrss получает информацию о вызове ExitWindowsEx. 2. Тогда Csrss "в интересах инициатора" пересылает скрытому окну WinLogon-a Windows-сообщение. 3. Теперь уже WinLogon вызывает ExitWindowsEx. 4. Csrss опять получает сообщение, но видит что завершить работу хочет уже Winlogon. 5. Csrss начинает перебор процесов а дальше и сервисов, уведомляя их о завершении работы пользователя или системы. 6. Потом ExitWindowsEx вызываеться ещё, но это уже связано с сервисами. Вообще эти пляски с бубном(если правильно я понимаю) нужны лишь для того чтоб Csrss получила сеанс интерактивного пользователя(их может быть много) от Winlogon-а. Теперь выводы: 1. Ставить хуки на ExitWindowsEx. 2. Перехватывать это загадочное сообщение для Winlogon-a. 3. Делать вид что в танке на неприличные предложения системы: WM_QUERYENDSESSION, WM_ENDSESSION (или CTRL_LOGOFF_EVENT- для консоли). 4. etc Вообще извращаться можно долго. Например тупой патч с драйвера бинарного кода csrss.
да вопрос то больше теоретический WM_QUERYENDSESSION, помнится, начиная с 2k уже спасала.. csrss.. брр а вот 2k изнутри: ntos\w32\ntser\server\exitwin.c Код (Text): NTSTATUS _ExitWindowsEx( PCSR_THREAD pcsrt, UINT dwFlags, DWORD dwReserved) { LUID luidCaller; NTSTATUS Status = STATUS_SUCCESS; UNREFERENCED_PARAMETER(dwReserved); if ((dwFlags & EWX_REBOOT) || (dwFlags & EWX_POWEROFF)) { dwFlags |= EWX_SHUTDOWN; } // // Only winlogon gets to set the high flags: // if ( ( dwFlags & ( ~ ( EWX_VALID ) ) ) != 0 ) { if ( HandleToUlong(pcsrt->ClientId.UniqueProcess) != gIdLogon ) { KdPrint(( "Process %x tried to call ExitWindowsEx with flags %x\n", pcsrt->ClientId.UniqueProcess, dwFlags )); return STATUS_ACCESS_DENIED ; } } /* * Find out the callers sid. Only want to shutdown processes in the * callers sid. */ if (!CsrImpersonateClient(NULL)) { return STATUS_BAD_IMPERSONATION_LEVEL; } Status = CsrGetProcessLuid(NULL, &luidCaller); if (!NT_SUCCESS(Status)) { CsrRevertToSelf(); return Status; } /* * Loop until we can do the shutdown; if we cannot do it, * we'll go to fastexit and bail. */ while (TRUE) { LARGE_INTEGER li; Status = NtUserSetInformationThread(pcsrt->ThreadHandle, UserThreadInitiateShutdown, &dwFlags, sizeof(dwFlags)); switch (Status) { case STATUS_PENDING: /* * The logoff/shutdown is in progress and nothing * more needs to be done. */ goto fastexit; case STATUS_RETRY: /* * Another logoff/shutdown is in progress and we need * to cancel it so we can do an override. * * if someone else is trying to cancel shutdown, exit */ EnterCrit(); li.QuadPart = 0; if (NtWaitForSingleObject(gheventCancel, FALSE, &li) == WAIT_OBJECT_0) { Status = STATUS_PENDING; LeaveCrit(); goto fastexit; } /* * If no one will set gheventCancelled, don't wait. */ if (gdwThreadEndSession == 0) { LeaveCrit(); continue; } NtClearEvent(gheventCancelled); NtSetEvent(gheventCancel, NULL); LeaveCrit(); /* * Wait for the other guy to be cancelled */ NtWaitForSingleObject(gheventCancelled, FALSE, NULL); EnterCrit(); /* * This signals that we are no longer trying to cancel a * shutdown */ NtClearEvent(gheventCancel); /* * If someone managed to start a shutdown again, exit * Can this happen? Let's assert to check it out. */ if (gdwThreadEndSession != 0) { UserAssert(gdwThreadEndSession == 0); Status = STATUS_PENDING; LeaveCrit(); goto fastexit; } LeaveCrit(); continue; case STATUS_CANT_WAIT: /* * There is no notify window and the calling thread has * windows that prevent this request from succeeding. * The client handles this by starting another thread * to recall ExitWindowsEx. */ goto fastexit; default: if (!NT_SUCCESS(Status)) { SetLastError(RtlNtStatusToDosError(Status)); goto fastexit; } } break; } /* * This thread is doing the shutdown */ EnterCrit(); UserAssert(gdwThreadEndSession == 0); gdwThreadEndSession = HandleToUlong(pcsrt->ClientId.UniqueThread); LeaveCrit(); /* * Call csr to loop through the processes shutting them down. */ Status = CsrShutdownProcesses(&luidCaller, dwFlags); /* * Tell win32k.sys we're done. */ NtUserSetInformationThread(pcsrt->ThreadHandle, UserThreadEndShutdown, &Status, sizeof(Status)); EnterCrit(); gdwThreadEndSession = 0; NtSetEvent(gheventCancelled, NULL); LeaveCrit(); fastexit: CsrRevertToSelf(); return Status; } поиметь бы этот gheventCancel надо будет в pdbшниках поискать...
В ответ на WM_QUERYENDSESSION возвращяй 0 в wParam, выключение прервётся. Но это только если не используется флаг EWX_FORCE
похоже, единственный вариант кстати, почему я думал, что он не работает под Xp/2k: Код (Text): LRESULT CALLBACK WndProc(HWND, UINT uMsg, WPARAM, LPARAM) { if(uMsg==WM_QUERYENDSESSION || Msg==WM_ENDSESSION) return 0; return 1; } под 9x это помогало замечательно, а под 2k нет... так что надо: Код (Text): LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if(uMsg==WM_QUERYENDSESSION || Msg==WM_ENDSESSION) return 0; return DefWindowProc(hWnd, uMsg, wParam, lParam); } почему - непонятно
У меня с этим shutdown проблемы похлеще... Нужно дать прользователю время разобраться с моей прогой перед выключением, но если не возвращяться из WM_QUERYENDSESSION, вылазит окошко с пожеланием прибить неотвечающюю прогу, так что приходится сохранять флаг действия из lParam (выкл, перезагрузка, итд), возвращять 1, после действий самому инициализировать выключение с полученым флагом... Но охрененный баг в том, что какая-то комбинация флагов даёт в lParam одинаковое число! Вот и думай что там было...
ладно.. понятно, что ничего не понятно а какие есть способы отследить shutdown и узнать его тип? ну вот для оконных - мессаги для консольных - хэндлеры.. кстати, а кто их вызывает, и каким потоком и раз уж кто-то это делает, нельзя ли заставить его же проделоть это в gui-приложниях? и как, например, отследить это событие в сервисах: SERVICE_CONTROL_SHUTDOWN они получат, но как узнать тип shutdown'а или отследить, что юзел логоффится и какие еще есть способы? какие-нибудь события, объекты ядра итд