NTSTATUS UserClientConnect( PCSR_PROCESS Process, PVOID ConnectionInformation, PULONG pulConnectionLen) { PHANDLE pHandle = Process->ServerDllPerProcessData[USERSRV_SERVERDLL_INDEX]; int i; /* * Pass the api port to the kernel. Do this early so the kernel * can send a datagram to CSR to activate a debugger. */ if (CsrApiPort == NULL) { CsrApiPort = CsrQueryApiPort(); NtUserSetInformationThread( NtCurrentThread(), UserThreadCsrApiPort, &CsrApiPort, sizeof(HANDLE)); } /* * Initialize cached handles to NULL */ for (i = 0; i < CHANDLES; ++i) pHandle[i] = NULL; return NtUserProcessConnect(Process->ProcessHandle, (PUSERCONNECT)ConnectionInformation, *pulConnectionLen); }
ULONG SrvDeviceEvent( IN OUT PCSR_API_MSG m, IN OUT PCSR_REPLY_STATUS ReplyStatus) { NTSTATUS Status = STATUS_SUCCESS; PDEVICEEVENTMSG a = (PDEVICEEVENTMSG)&m->u.ApiMessageData; USERTHREAD_USEDESKTOPINFO utudi; UNREFERENCED_PARAMETER(ReplyStatus); try { // // Set the desktop to the active desktop before sending the // message. // utudi.hThread = NULL; utudi.drdRestore.pdeskRestore = NULL; Status = NtUserSetInformationThread(NtCurrentThread(), UserThreadUseActiveDesktop, &utudi, sizeof(utudi)); if (!NT_SUCCESS(Status)) { #if DBG KdPrint(("--> SrvDeviceEvent: NtUserSetInformationThread failed %d\n", Status)); #endif goto Clean1; } // // Verify the window handle is still valid, if not, let the caller know // so it can be purged from the notification window hand list that the // user-mode pnp manager keeps. // if ((a->hWnd != HWND_BROADCAST) && !IsWindow(a->hWnd)) { Status = STATUS_INVALID_HANDLE; goto Clean0; } if (a->dwFlags) { // // This is a query so we have to send the message but use // timeouts so an app can't stall us forever. // RIPMSG3(RIP_VERBOSE, "--> SrvDeviceEvent: Sending WM_DEVICECHANGE to 0x%x, w 0x%x, l 0x%x", (ULONG_PTR)a->hWnd, a->wParam, a->lParam); if (!SendMessageTimeout(a->hWnd, WM_DEVICECHANGE, a->wParam, a->lParam, SMTO_ABORTIFHUNG | SMTO_NORMAL, PNP_NOTIFY_TIMEOUT, &a->dwResult)) { Status = STATUS_UNSUCCESSFUL; } } else { // // It's not a query so just post it and return, we don't // care what the app returns. // RIPMSG3(RIP_VERBOSE, "--> SrvDeviceEvent: Posting WM_DEVICECHANGE to 0x%x, w 0x%x, l 0x%x", (ULONG_PTR)a->hWnd, a->wParam, a->lParam); if (!PostMessage(a->hWnd, WM_DEVICECHANGE, a->wParam, a->lParam)) { Status = STATUS_UNSUCCESSFUL; } } Clean0: // // Reset this thread's desktop back to NULL before returning. This // decrements the desktop's reference count. // NtUserSetInformationThread(NtCurrentThread(), UserThreadUseDesktop, &utudi, sizeof(utudi)); Clean1: ; } except (EXCEPTION_EXECUTE_HANDLER) { KdPrint(("SrvDeviceEvent generated an exception (%d)\n", GetExceptionCode())); Status = STATUS_UNSUCCESSFUL; } return Status; } // SrvDeviceEvent
VOID HandleMediaChangeEvent( UINT uidCdRom ) { /* * Local variables */ HANDLE hDevice; ULONG id; DWORD cb; DWORD dwLogicalDrives; DWORD dwDriveMask; DWORD dwDriveCount; DWORD dwRecipients; BOOL bResult; INT nCurrentTry; NTSTATUS Status; UNICODE_STRING ustrCdRom; UNICODE_STRING ustrCdRomId; UNICODE_STRING ustrAnyCdRom; UNICODE_STRING ustrNtPath; DEV_BROADCAST_VOLUME dbcvInfo; LPWSTR lpszCdRom = TEXT("\\Device\\CdRom"); WCHAR szDrive[] = TEXT("A:\\"); WCHAR szDevice[] = TEXT("\\\\.\\A:"); WCHAR wcDrive; WCHAR szCdRom[32]; WCHAR szBuff[256]; UserAssert(uidCdRom >= 0 && uidCdRom < NUM_MEDIA_EVENTS); // at most 24 cd-rom drives in system /* * Some initializations */ RtlInitUnicodeString( &ustrAnyCdRom, lpszCdRom ); wcDrive = UNICODE_NULL; /* * Form the \Device\CdRomX name based on uidCdRom */ wsprintfW( szCdRom, L"\\Device\\CdRom%d", uidCdRom ); RtlInitUnicodeString( &ustrCdRom, szCdRom ); /* * The uidCdRom parameter tells us which CD-ROM device generated the * MediaChange event. We need to map this device back to it's logical * drive letter because WM_DEVICECHANGE is based on drive letters. * * To avoid always searching all logical drives, we cache the last * associated drive letter. We still need to check this every time * we get notified because WinDisk can remap drive letters. */ if (wcDriveCache[uidCdRom]) { /* * Convert our DOS path name to a NT path name */ ustrNtPath.MaximumLength = sizeof(szBuff); ustrNtPath.Length = 0; ustrNtPath.Buffer = szBuff; bResult = GetDeviceObject(wcDriveCache[uidCdRom], &ustrNtPath); if (bResult) { /* * Check to see if this drive letter is the one that maps * to the CD-ROM drive that just notified us. */ if (RtlEqualUnicodeString(&ustrCdRom, &ustrNtPath, TRUE)) { /* * Yes, we found a match */ wcDrive = wcDriveCache[uidCdRom]; } } } if (!wcDrive) { /* * Either the cache wasn't initialized, or we had a re-mapping * of drive letters. Scan all drive letters looking for CD-ROM * devices and update the cache as we go. */ RtlZeroMemory(wcDriveCache, sizeof(wcDriveCache)); szDrive[0] = L'A'; szDevice[4] = L'A'; dwDriveCount = 26; //Max number of drive letters dwDriveMask = 1; //Max number of drive letters dwLogicalDrives = GetLogicalDrives(); while (dwDriveCount) { /* * Is this logical drive a CD-ROM? */ // // JOHNC - Remove after GetDriveType() is fixed if ((dwLogicalDrives & dwDriveMask) && GetDriveType(szDrive) == DRIVE_CDROM) { /* * For this CD-ROM drive, find it's NT path. */ ustrNtPath.MaximumLength = sizeof(szBuff); ustrNtPath.Length = 0; ustrNtPath.Buffer = szBuff; bResult = GetDeviceObject(szDrive[0], &ustrNtPath); if (bResult) { /* * Make sure the string is in the form \Device\CdRom */ if (RtlPrefixUnicodeString(&ustrAnyCdRom, &ustrNtPath, TRUE)) { /* * Now find it's id. We have a string that looks like * \Device\CdRom??? where ??? is the unit id */ RtlInitUnicodeString(&ustrCdRomId, (PWSTR)((PSTR)(ustrNtPath.Buffer)+ustrAnyCdRom.Length)); RtlUnicodeStringToInteger(&ustrCdRomId, 10, &id); UserAssert(id >= 0 && id < NUM_MEDIA_EVENTS); wcDriveCache[id] = szDrive[0]; //Initially set State to Unknown aDriveState[id] = DS_UNKNOWN; /* * If this is the device that notified us, remember its * drive letter so we can broadcase WM_DEVICECHANGE */ if (uidCdRom == id) { wcDrive = szDrive[0]; } } } } /* * Try the next drive */ szDrive[0] = szDrive[0] + 1; szDevice[4] = szDevice[4] + 1; dwDriveMask <<= 1; --dwDriveCount; } } /* * If we found a logical drive, determine the media state for the drive * and broadcast the WM_DEVICECHANGE notification. */ if (wcDrive) { /* * Get the Media status of this drive. Assume media is not * present in the case of an error. */ szDevice[4] = wcDrive; hDevice = CreateFile(szDevice, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hDevice == INVALID_HANDLE_VALUE) { #ifdef DEBUG KdPrint((" CreateFile( '%ls' ) Failed. Error %lx\n", szDevice,GetLastError())); #endif return; } /* * Loop and check the CD-ROM to see if media is available. We need * this loop because some CD-ROM drives notify us that media has * arrived before the drive has recognized that it had new media. */ for (nCurrentTry = 0; nCurrentTry < MAX_TRIES; nCurrentTry++) { /* * See if media is present */ bResult = (DWORD)DeviceIoControl(hDevice, IOCTL_DISK_CHECK_VERIFY, NULL, 0, NULL, 0, &cb, NULL); if (bResult) { /* * Media is present, change the state to Inserted. */ aDriveState[uidCdRom] = DS_INSERTED; break; } /* * Media wasn't present, so we need to look at GetLastError() to see * if DeviceIoControl() failed. If so we may want to try again. */ if (GetLastError() == ERROR_NOT_READY) { Sleep(500); // 1/2 second /* * We only want to retry if we the prev State was UNKNOWN or * EJECTED. If the previous State was INSERTED it means that * this event is the removal event */ if(aDriveState[uidCdRom]== DS_UNKNOWN || aDriveState[uidCdRom] == DS_EJECTED) { continue; } } /* * Call failed. Assume worst case and say the media has been removed. */ aDriveState[uidCdRom] = DS_EJECTED; break; } /* * Close the handle to the CD-ROM device */ CloseHandle(hDevice); /* * Initialize the structures used for BroadcastSystemMessage */ dbcvInfo.dbcv_size = sizeof(dbcvInfo); dbcvInfo.dbcv_devicetype = DBT_DEVTYP_VOLUME; dbcvInfo.dbcv_reserved = 0; dbcvInfo.dbcv_flags = DBTF_MEDIA; dbcvInfo.dbcv_unitmask = (1 << (wcDrive - L'A')); dwRecipients = BSM_ALLCOMPONENTS | BSM_ALLDESKTOPS; /* * Temporarily we must assign this thread to a desktop so we can * call USER's BroascastSystemMessage() routine. We call the * private SetThreadDesktopToDefault() to assign ourselves to the * desktop that is currently receiving input. */ Status = NtUserSetInformationThread(NtCurrentThread(), UserThreadUseActiveDesktop, NULL, 0); if (NT_SUCCESS(Status)) { /* * Broadcast the message */ BroadcastSystemMessage(BSF_FORCEIFHUNG, &dwRecipients, WM_DEVICECHANGE, // HACK: need to or 0x8000 in wParam // because this is a flag to let // BSM know that lParam is a pointer // to a data structure. 0x8000 | ((bResult) ? DBT_DEVICEARRIVAL : DBT_DEVICEREMOVECOMPLETE), (LPARAM)&dbcvInfo); #ifdef DEBUG KdPrint((" Message Broadcast for '%lc:'\n", wcDrive)); #endif /* * Set our thread's desktop back to NULL. This will decrement * the desktop's reference count. */ hDevice = NULL; NtUserSetInformationThread(NtCurrentThread(), UserThreadUseDesktop, &hDevice, // hDevice = NULL sizeof(HANDLE)); } } }
LONG APIENTRY EndTaskDlgProc( HWND hwndDlg, UINT msg, UINT wParam, LONG lParam) { ENDDLGPARAMS* pedp; LARGE_INTEGER li; WCHAR achFormat[CCHBODYMAX]; WCHAR achText[CCHBODYMAX]; DWORD dwData; USERTHREAD_FLAGS Flags; switch (msg) { case WM_INITDIALOG: pedp = (ENDDLGPARAMS*)lParam; /* * Save this for later revalidation. */ SetWindowLong(hwndDlg, GWL_USERDATA, (DWORD)pedp->h); SetWindowLong(hwndDlg, DWL_USER, (DWORD)pedp->type); SetWindowText(hwndDlg, pedp->pszTitle); /* * Update text that says how long we'll wait. */ GetDlgItemText(hwndDlg, IDIGNORE, achFormat, CCHBODYMAX); wsprintf(achText, achFormat, pedp->cSeconds); SetDlgItemText(hwndDlg, IDIGNORE, achText); /* * Make this dialog top most and foreground. */ Flags.dwFlags = TIF_ALLOWFOREGROUNDACTIVATE; Flags.dwMask = TIF_ALLOWFOREGROUNDACTIVATE; NtUserSetInformationThread(NtCurrentThread(), UserThreadFlags, &Flags, sizeof(Flags)); SetWindowPos(hwndDlg, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); /* * Set this timer so every 1/2 a second we can see if this app * has gone away. */ SetTimer(hwndDlg, 5, 500, NULL); return TRUE; case WM_TIMER: /* * If shutdown has been cancelled, bring down the dialog. */ li.QuadPart = 0; if (NtWaitForSingleObject(heventCancel, FALSE, &li) == 0) { EndDialog(hwndDlg, IDCANCEL); break; } dwData = GetWindowLong(hwndDlg, GWL_USERDATA); if (GetWindowLong(hwndDlg, DWL_USER) == TYPE_CONSOLE_ID) { /* * If it's the console calling us, check if the thread or process * handle is still valid. If not, bring down the dialog. */ if (WaitForSingleObject((HANDLE)dwData, 0) != 0) break; } else if (!(((PCSR_THREAD)dwData)->Flags & CSR_THREAD_DESTROYED)) { /* * If the thread is marked as destroyed, bring down the dialog. */ break; } /* * This'll cause the dialog to go away and the wait for this app to * close to return. */ EndDialog(hwndDlg, IDRETRY); break; case WM_CLOSE: /* * Assume WM_CLOSE means cancel shutdown */ wParam = IDCANCEL; /* * falls through... */ case WM_COMMAND: EndDialog(hwndDlg, LOWORD(wParam)); break; } return FALSE; }
NTSTATUS _ExitWindowsEx( PCSR_THREAD pcsrt, UINT dwFlags, DWORD dwReserved) { BOOL fDoEndSession = FALSE; LUID luidCaller; NTSTATUS Status = STATUS_SUCCESS; UNREFERENCED_PARAMETER(dwReserved); if ((dwFlags & EWX_REBOOT) || (dwFlags & EWX_POWEROFF)) { dwFlags |= EWX_SHUTDOWN; } /* * 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; } try { while (1) { LARGE_INTEGER li; LeaveCrit(); Status = NtUserSetInformationThread( pcsrt->ThreadHandle, UserThreadInitiateShutdown, &dwFlags, sizeof(dwFlags)); EnterCrit(); 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 */ li.QuadPart = 0; if (NtWaitForSingleObject(heventCancel, FALSE, &li) == 0) { Status = STATUS_PENDING; goto fastexit; } /* * Cancel the old shutdown */ NtClearEvent(heventCancelled); NtSetEvent(heventCancel, NULL); /* * Wait for the other guy to be cancelled */ LeaveCrit(); NtWaitForSingleObject(heventCancelled, FALSE, NULL); EnterCrit(); /* * This signals that we are no longer trying to cancel a * shutdown */ NtClearEvent(heventCancel); 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)) goto fastexit; } break; } gdwFlags = dwFlags; dwThreadEndSession = (DWORD)pcsrt->ClientId.UniqueThread; fDoEndSession = TRUE; /* * Sometimes the console calls the dialog box when not in shutdown * if now is one of those times cancel the dialog box. */ while (gcInternalDoEndTaskDialog > 0) { LARGE_INTEGER li; NtPulseEvent(heventCancel, NULL); LeaveCrit(); li.QuadPart = (LONGLONG)-10000 * CMSSLEEP; NtDelayExecution(FALSE, &li); EnterCrit(); } /* * Call csr to loop through the processes shutting them down. */ LeaveCrit(); Status = CsrShutdownProcesses(&luidCaller, dwFlags); NtUserSetInformationThread( pcsrt->ThreadHandle, UserThreadEndShutdown, &Status, sizeof(Status)); EnterCrit(); fastexit:; } finally { /* * Only turn off dwThreadEndSession if this is the * thread doing shutdown. */ if (fDoEndSession) { dwThreadEndSession = 0; NtSetEvent(heventCancelled, NULL); } } CsrRevertToSelf(); return Status; }
DWORD MySendEndSessionMessages( HWND hwnd, PCSR_THREAD pcsrt, BOOL fEndTask, DWORD dwClientFlags) { HWND hwndOwner; LARGE_INTEGER li; DWORD dwRet; int cLoops; int cSeconds; WCHAR achName[CCHBODYMAX]; BOOL fPostedClose; BOOL fDialogFirst; DWORD dwFlags; DWORD dwHungApp; HANDLE hNull = NULL; NTSTATUS Status; /* * We've got a random top level window for this application. Find the * root owner, because that's who we want to send the WM_CLOSE to. */ while ((hwndOwner = GetWindow(hwnd, GW_OWNER)) != NULL) hwnd = hwndOwner; /* * We expect this application to process this shutdown request, * so make it the foreground window so it has foreground priority. * This won't leave the critical section. */ SetForegroundWindow(hwnd); /* * Send the WM_CLIENTSHUTDOWN message for end-session. When the app * receives this, it'll then get WM_QUERYENDSESSION and WM_ENDSESSION * messages. */ if (!fEndTask) { USERTHREAD_FLAGS Flags; Flags.dwFlags = 0; Flags.dwMask = (TIF_SHUTDOWNCOMPLETE | TIF_ALLOWSHUTDOWN); LeaveCrit(); Status = NtUserSetInformationThread(pcsrt->ThreadHandle, UserThreadFlags, &Flags, sizeof(Flags)); EnterCrit(); if (!NT_SUCCESS(Status)) return CMDEND_APPSAYSOK; SendNotifyMessage(hwnd, WM_CLIENTSHUTDOWN, dwClientFlags, 0); } /* * If the main window is disabled, bring up the end-task window first, * right away, only if this the WM_CLOSE case. */ fDialogFirst = FALSE; if (fEndTask && (GetWindowLong(hwnd, GWL_STYLE) & WS_DISABLED)) fDialogFirst = TRUE; fPostedClose = FALSE; while (TRUE) { if (fEndTask) { cLoops = (CMSHUNGAPPTIMEOUT / CMSSLEEP); cSeconds = (CMSHUNGAPPTIMEOUT / 1000); } else { cLoops = (CMSWAITTOKILLTIMEOUT / CMSSLEEP); cSeconds = (CMSWAITTOKILLTIMEOUT / 1000); } /* * If end-task and not shutdown, must give this app a WM_CLOSE * message. Can't do this if it has a dialog up because it is in * the wrong processing loop. We detect this by seeing if the window * is disabled - if it is, we don't send it a WM_CLOSE and instead * bring up the end task dialog right away (this is exactly compatible * with win3.1 taskmgr.exe). */ if (fEndTask) { if (!fPostedClose && IsWindow(hwnd) && !(GetWindowLong(hwnd, GWL_STYLE) & WS_DISABLED)) { PostMessage(hwnd, WM_CLOSE, 0, 0L); fPostedClose = TRUE; } } /* * Every so often wake up to see if the app is hung, and if not go * back to sleep until we've run through our timeout. */ while (cLoops--) { /* * If a WM_QUERY/ENDSESSION has been answered to, return. */ if (!fEndTask) { LeaveCrit(); NtUserQueryInformationThread(pcsrt->ThreadHandle, UserThreadFlags, &dwFlags, sizeof(DWORD), NULL); EnterCrit(); if (dwFlags & TIF_SHUTDOWNCOMPLETE) { if (dwFlags & TIF_ALLOWSHUTDOWN) return CMDEND_APPSAYSOK; return CMDEND_APPSAYSNOTOK; } } /* * If the thread is gone, we're done. */ if (pcsrt->Flags & CSR_THREAD_DESTROYED) { return CMDEND_APPSAYSOK; } /* * If the dialog should be brought up first (because the window * was initially disabled), do it. */ if (fDialogFirst) { fDialogFirst = FALSE; break; } /* * if we we're externally cancelled get out */ li.QuadPart = 0; if (NtWaitForSingleObject(heventCancel, FALSE, &li) == 0) { /* * !!! JimA - We may want to call the kernel to * set TIF_SHUTDOWNCOMPLETE in this case. */ return CMDEND_USERSAYSCANCEL; } /* * If hung, bring up the endtask dialog right away. */ dwHungApp = (fEndTask ? CMSHUNGAPPTIMEOUT : CMSWAITTOKILLTIMEOUT); LeaveCrit(); Status = NtUserQueryInformationThread(pcsrt->ThreadHandle, UserThreadHungStatus, &dwHungApp, sizeof(dwHungApp), NULL); EnterCrit(); if (!NT_SUCCESS(Status) || dwHungApp == TRUE) break; /* * Sleep for a second. */ LeaveCrit(); li.QuadPart = (LONGLONG)-10000 * CMSSLEEP; NtDelayExecution(FALSE, &li); EnterCrit(); } achName[0] = 0; if (IsWindow(hwnd)) { GetWindowText(hwnd, achName, CCHMSGMAX); } /* * If there's a hard error, put it on top. */ BoostHardError((DWORD)pcsrt->ClientId.UniqueProcess, FALSE); if (achName[0] == 0) { /* * If the thread is gone, we're done. */ if (pcsrt->Flags & CSR_THREAD_DESTROYED) { return CMDEND_APPSAYSOK; } /* * pti is valid right now. Use the name in the pti. */ LeaveCrit(); NtUserQueryInformationThread(pcsrt->ThreadHandle, UserThreadTaskName, achName, CCHMSGMAX * sizeof(WCHAR), NULL); EnterCrit(); } /* * Set this thread to use the desktop of the * thread being shutdown. */ if (NT_SUCCESS(NtUserSetInformationThread(NtCurrentThread(), UserThreadUseDesktop, &pcsrt->ThreadHandle, sizeof(HANDLE)))) { /* * Bring up the dialog */ dwRet = DoEndTaskDialog(achName, pcsrt, TYPE_THREADINFO, cSeconds); /* * Release the desktop that was used. */ NtUserSetInformationThread(NtCurrentThread(), UserThreadUseDesktop, &hNull, sizeof(HANDLE)); } else { /* * We were unable to get the thread's desktop. All we * can do is kill the task. */ dwRet = IDABORT; } switch(dwRet) { case IDCANCEL: /* * Cancel the shutdown process... Get out of here. Signify that * we're cancelling the shutdown request. * * !!! JimA - We may want to call the kernel to * set TIF_SHUTDOWNCOMPLETE in this case. */ return CMDEND_USERSAYSCANCEL; break; case IDABORT: /* * End this guy's task... */ BoostHardError((DWORD)pcsrt->ClientId.UniqueProcess, TRUE); /* * !!! JimA - We may want to call the kernel to * set TIF_SHUTDOWNCOMPLETE in this case. */ return CMDEND_USERSAYSKILL; break; case IDRETRY: /* * Just continue to wait. Reset this app so it doesn't think it's * hung. This'll cause us to wait again. */ if (!(pcsrt->Flags & CSR_THREAD_DESTROYED)) { LeaveCrit(); NtUserSetInformationThread(pcsrt->ThreadHandle, UserThreadHungStatus, NULL, 0); EnterCrit(); } fPostedClose = FALSE; break; } } }
BOOL _EndTask( HWND hwnd, BOOL fShutdown, BOOL fMeanKill) { PCSR_THREAD pcsrt = CSR_SERVER_QUERYCLIENTTHREAD(); PCSR_THREAD pcsrtKill; DWORD dwThreadId; DWORD dwProcessId; LPWSTR lpszMsg; BOOL fAllocated; DWORD dwCmd; /* * Note: fShutdown isn't used for anything in this routine! * They are still there because I haven't removed them: the old endtask * code relied on them. */ UNREFERENCED_PARAMETER(fShutdown); /* * Get the process and thread that owns hwnd. */ dwThreadId = GetWindowThreadProcessId(hwnd, &dwProcessId); if (dwThreadId == 0) return TRUE; /* * If this is a console window, then just send the close message to * it, and let console clean up the processes in it. */ if ((HANDLE)GetWindowLong(hwnd, GWL_HINSTANCE) == hModuleWin) { PostMessage(hwnd, WM_CLOSE, 0, 0); return TRUE; } /* * Find the CSR_THREAD for the window. */ LeaveCrit(); CsrLockThreadByClientId((HANDLE)dwThreadId, &pcsrtKill); EnterCrit(); if (pcsrtKill == NULL) return TRUE; CsrReferenceThread(pcsrtKill); CsrUnlockThread(pcsrtKill); /* * If this is a WOW app, then shutdown just this wow application. */ if (!fMeanKill) { /* * Find out what to do now - did the user cancel or the app cancel, * etc? Only allow cancelling if we are not forcing the app to * exit. */ dwCmd = MySendEndSessionMessages(hwnd, pcsrtKill, TRUE, 0); switch (dwCmd) { case CMDEND_APPSAYSNOTOK: /* * App says not ok - this'll let taskman bring up the "are you sure?" * dialog to the user. */ CsrDereferenceThread(pcsrtKill); return FALSE; case CMDEND_USERSAYSCANCEL: /* * User hit cancel on the timeout dialog - so the user really meant * it. Let taskman know everything is ok by returning TRUE. */ CsrDereferenceThread(pcsrtKill); return TRUE; } } /* * Kill the application now. If the thread has not been destroyed, * nuke the task. If WowExitTask returns that the thread is not * a WOW task, terminate the process. */ if (!(pcsrtKill->Flags & CSR_THREAD_DESTROYED) && !WowExitTask(pcsrtKill)) { /* * Calling ExitProcess() in the app's context will not always work * because the app may have .dll termination deadlocks: so the thread * will hang with the rest of the process. To ensure apps go away, * we terminate the process with NtTerminateProcess(). * * Pass this special value, DBG_TERMINATE_PROCESS, which tells * NtTerminateProcess() to return failure if it can't terminate the * process because the app is being debugged. */ if (!NT_SUCCESS(NtTerminateProcess(pcsrtKill->Process->ProcessHandle, DBG_TERMINATE_PROCESS))) { /* * If the app is being debugged, don't close it - because that can * cause a hang to the NtTerminateProcess() call. */ lpszMsg = ServerLoadString(hModuleWin, STR_APPDEBUGGED, NULL, &fAllocated); if (lpszMsg) { if (NT_SUCCESS(NtUserSetInformationThread(NtCurrentThread(), UserThreadUseDesktop, &pcsrt->ThreadHandle, sizeof(HANDLE)))) { HANDLE hNull = NULL; LeaveCrit(); MessageBoxEx(NULL, lpszMsg, NULL, MB_OK | MB_SETFOREGROUND, 0); EnterCrit(); NtUserSetInformationThread(NtCurrentThread(), UserThreadUseDesktop, &hNull, sizeof(HANDLE)); } LocalFree(lpszMsg); } } else { pcsrtKill->Process->Flags |= CSR_PROCESS_TERMINATED; } } CsrDereferenceThread(pcsrtKill); return TRUE; }