/* File utils.c Contains common utilities for the ras dialup server ui. Paul Mayfield, 9/30/97 */ #include "rassrv.h" // Remoteaccess parameters key const WCHAR pszregRasParameters[] = L"SYSTEM\\CurrentControlSet\\Services\\RemoteAccess\\Parameters"; // Registry key values const WCHAR pszregServerFlags[] = L"ServerFlags"; const WCHAR pszregShowIcon[] = L"Rassrv_EnableIconsInTray"; const WCHAR pszregPure[] = L"UsersConfiguredWithMMC"; const WCHAR pszregLogLevel[] = L"LoggingFlags"; // Here is the instance of the global variables RASSRVUI_GLOBALS Globals; DWORD gblInit( IN HINSTANCE hInstDll, OUT RASSRVUI_GLOBALS * pGlobs) { // Clear out the memory ZeroMemory(pGlobs, sizeof(RASSRVUI_GLOBALS)); // Record the module for use in future resource fuction calls. Globals.hInstDll = hInstDll; // Initialize the global variable lock InitializeCriticalSection(&(pGlobs->csLock)); // Create the global heap pGlobs->hPrivateHeap = HeapCreate(0, 4096, 0); // Register the context ID atom for use in the Windows XxxProp calls // which are used to associate a context with a dialog window handle. Globals.atmRassrvPageData = (LPCTSTR)GlobalAddAtom(TEXT("RASSRVUI_PAGE_DATA")); if (!Globals.atmRassrvPageData) { return GetLastError(); } Globals.atmRassrvPageId = (LPCTSTR)GlobalAddAtom(TEXT("RASSRVUI_PAGE_ID")); if (!Globals.atmRassrvPageId) { return GetLastError(); } return NO_ERROR; } DWORD gblCleanup( IN RASSRVUI_GLOBALS * Globs) { if (Globs->hRasServer != NULL) { MprAdminServerDisconnect(Globs->hRasServer); Globs->hRasServer = NULL; } if (Globs->hPrivateHeap) { HeapDestroy(Globs->hPrivateHeap); } GlobalDeleteAtom(LOWORD(Globals.atmRassrvPageData)); GlobalDeleteAtom(LOWORD(Globals.atmRassrvPageId)); DeleteCriticalSection(&(Globs->csLock)); return NO_ERROR; } // // Loads the machine flags // DWORD gblLoadMachineFlags( IN RASSRVUI_GLOBALS * pGlobs) { DWORD dwErr = NO_ERROR; PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pInfo = NULL; BOOL bEnabled, bDefault; // If we're already initialized, there's nothing to // do // if (pGlobs->dwMachineFlags & RASSRVUI_MACHINE_F_Initialized) { return NO_ERROR; } do { // Find out what kind of machine we are // dwErr = DsRoleGetPrimaryDomainInformation( NULL, DsRolePrimaryDomainInfoBasic, (LPBYTE *)&pInfo ); if (dwErr != NO_ERROR) { break; } if ((pInfo->MachineRole != DsRole_RoleStandaloneWorkstation) && (pInfo->MachineRole != DsRole_RoleMemberWorkstation)) { pGlobs->dwMachineFlags |= RASSRVUI_MACHINE_F_Server; } if ((pInfo->MachineRole != DsRole_RoleStandaloneWorkstation) && (pInfo->MachineRole != DsRole_RoleStandaloneServer)) { pGlobs->dwMachineFlags |= RASSRVUI_MACHINE_F_Member; } // Record that we've been initailized // pGlobs->dwMachineFlags |= RASSRVUI_MACHINE_F_Initialized; } while (FALSE); // Cleanup { if (pInfo) { DsRoleFreeMemory (pInfo); } } return dwErr; } // // Establishes communication with the ras server if // not already established // DWORD gblConnectToRasServer() { DWORD dwErr = NO_ERROR;; EnterCriticalSection(&(Globals.csLock)); if (Globals.hRasServer == NULL) { dwErr = MprAdminServerConnect(NULL, &Globals.hRasServer); } LeaveCriticalSection(&(Globals.csLock)); return NO_ERROR; } /* Enhanced list view callback to report drawing information. 'HwndLv' is ** the handle of the list view control. 'DwItem' is the index of the item ** being drawn. ** ** Returns the address of the draw information. */ LVXDRAWINFO* LvDrawInfoCallback( IN HWND hwndLv, IN DWORD dwItem ) { /* The enhanced list view is used only to get the "wide selection bar" ** feature so our option list is not very interesting. ** ** Fields are 'nCols', 'dxIndent', 'dwFlags', 'adwFlags[]'. */ static LVXDRAWINFO info = { 1, 0, 0, { 0 } }; return &info; } // // Allocates memory. If bZero is TRUE, it also zeros the memory. // PVOID RassrvAlloc ( IN DWORD dwSize, IN BOOL bZero) { PVOID pvRet = NULL; HANDLE hHeap = NULL; hHeap = (Globals.hPrivateHeap) ? Globals.hPrivateHeap : GetProcessHeap(); pvRet = HeapAlloc( hHeap, (bZero) ? HEAP_ZERO_MEMORY: 0, dwSize); return pvRet; } // // Frees memory allocated by RassrvAlloc // VOID RassrvFree ( IN PVOID pvBuf) { PVOID pvRet; HANDLE hHeap; hHeap = (Globals.hPrivateHeap) ? Globals.hPrivateHeap : GetProcessHeap(); if (pvBuf) { HeapFree(hHeap, 0, pvBuf); } } // // Adds a user to the local machine // DWORD RasSrvAddUser ( IN PWCHAR pszUserLogonName, IN PWCHAR pszUserComment, IN PWCHAR pszUserPassword) { NET_API_STATUS nStatus; WCHAR pszDomainUser[1024]; WCHAR pszCompName[1024]; LOCALGROUP_MEMBERS_INFO_3 meminfo; DWORD dwSize = 1024, dwErr; USER_INFO_2 * pUser2; RAS_USER_0 UserInfo; // Initialize the base user information USER_INFO_1 User = { pszUserLogonName, pszUserPassword, 0, USER_PRIV_USER, L"", L"", UF_SCRIPT | UF_DONT_EXPIRE_PASSWD | UF_NORMAL_ACCOUNT, L"" }; // Add the user nStatus = NetUserAdd( NULL, 1, (LPBYTE)&User, NULL); // If the user wasn't added, find out why if (nStatus != NERR_Success) { switch (nStatus) { case ERROR_ACCESS_DENIED: return ERROR_ACCESS_DENIED; case NERR_UserExists: return ERROR_USER_EXISTS; case NERR_PasswordTooShort: return ERROR_INVALID_PASSWORDNAME; case NERR_InvalidComputer: case NERR_NotPrimary: case NERR_GroupExists: default: return ERROR_CAN_NOT_COMPLETE; } } // Now that the user is added, add the user's full name nStatus = NetUserGetInfo(NULL, pszUserLogonName, 2, (LPBYTE*)&pUser2); if (nStatus == NERR_Success) { // Modify the full name in the structure pUser2->usri2_full_name = pszUserComment; NetUserSetInfo(NULL, pszUserLogonName, 2, (LPBYTE)pUser2, NULL); NetApiBufferFree((LPBYTE)pUser2); } return NO_ERROR; } // // Deletes a user from the system local user datbase // DWORD RasSrvDeleteUser( PWCHAR pszUserLogonName) { NET_API_STATUS nStatus; // Delete the user and return the status code. If the // specified user is not in the user database, consider // it a success nStatus = NetUserDel(NULL, pszUserLogonName); if (nStatus != NERR_Success) { switch (nStatus) { case ERROR_ACCESS_DENIED: return ERROR_ACCESS_DENIED; case NERR_UserNotFound: return NO_ERROR; } return nStatus; } return NO_ERROR; } // // Changes the full name and password of a user. If // either of pszFullName or pszPassword is null, it is // ignored. // DWORD RasSrvEditUser ( IN PWCHAR pszLogonName, IN OPTIONAL PWCHAR pszFullName, IN OPTIONAL PWCHAR pszPassword) { NET_API_STATUS nStatus; DWORD dwSize = 1024, dwErr = NO_ERROR, dwParamErr; USER_INFO_2 * pUser2; // if nothing to set, return if (!pszFullName && !pszPassword) { return NO_ERROR; } // First, get this user's data so that we can manipulate it. // nStatus = NetUserGetInfo( NULL, pszLogonName, 2, (LPBYTE*)(&pUser2)); if (nStatus != NERR_Success) { return nStatus; } dwErr = NO_ERROR; do { // Fill in the blanks accordingly if (pszFullName) { pUser2->usri2_full_name = pszFullName; } if (pszPassword) { pUser2->usri2_password = pszPassword; } // Add the user nStatus = NetUserSetInfo( NULL, // server name pszLogonName, // user name 2, // level (LPBYTE)pUser2, // buf &dwParamErr); // param error if (nStatus != NERR_Success) { dwErr = nStatus; break; } } while (FALSE); // Cleanup { NetApiBufferFree(pUser2); } return dwErr; } // Returns whether a dword registry value was set or not. If the named // value does not exist, the value of bDefault is assigned. DWORD RassrvRegGetDwEx( IN DWORD * lpdwFlag, IN DWORD dwDefault, IN CONST PWCHAR pszKeyName, IN CONST PWCHAR pszValueName, IN BOOL bCreate) { DWORD dwErr, dwVal, dwType = REG_DWORD, dwSize = sizeof(DWORD); HKEY hKey = NULL; if (!lpdwFlag) { return ERROR_INVALID_PARAMETER; } do { if (bCreate) { DWORD dwDisposition; dwErr = RegCreateKeyExW( HKEY_LOCAL_MACHINE, pszKeyName, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ, NULL, &hKey, &dwDisposition); if (dwErr != ERROR_SUCCESS) { break; } } else { // Open the registry key dwErr = RegOpenKeyExW( HKEY_LOCAL_MACHINE, pszKeyName, 0, KEY_READ, &hKey); if (dwErr != ERROR_SUCCESS) { break; } } // Read the value dwErr = RegQueryValueExW( hKey, pszValueName, 0, &dwType, (BYTE *)&dwVal, &dwSize); if (dwErr != ERROR_SUCCESS) { dwErr = NO_ERROR; dwVal = dwDefault; } // Return the value read *lpdwFlag = dwVal; } while (FALSE); // Cleanup { if (hKey) { RegCloseKey(hKey); } } return dwErr; } // Returns whether a dword registry value was set or not. If the named // value does not exist, the value of bDefault is assigned. DWORD RassrvRegGetDw( IN DWORD * lpdwFlag, IN DWORD dwDefault, IN CONST PWCHAR pszKeyName, IN CONST PWCHAR pszValueName) { return RassrvRegGetDwEx( lpdwFlag, dwDefault, pszKeyName, pszValueName, FALSE); } // // Sets a dword registry value. If the named value does not exist, // it is automatically created. // DWORD RassrvRegSetDwEx( IN DWORD dwFlag, IN CONST PWCHAR pszKeyName, IN CONST PWCHAR pszValueName, IN BOOL bCreate) { DWORD dwErr = NO_ERROR, dwVal, dwType = REG_DWORD; DWORD dwSize = sizeof(DWORD); HKEY hKey = NULL; dwVal = dwFlag; do { if (bCreate) { DWORD dwDisposition; dwErr = RegCreateKeyExW( HKEY_LOCAL_MACHINE, pszKeyName, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, &dwDisposition); if (dwErr != ERROR_SUCCESS) { break; } } else { // Open the registry key dwErr = RegOpenKeyExW( HKEY_LOCAL_MACHINE, pszKeyName, 0, KEY_WRITE, &hKey); if (dwErr != ERROR_SUCCESS) { break; } } // Set the value dwErr = RegSetValueExW( hKey, pszValueName, 0, dwType, (CONST BYTE *)&dwVal, dwSize); if (dwErr != ERROR_SUCCESS) { break; } } while (FALSE); // Cleanup { if (hKey) { RegCloseKey(hKey); } } return dwErr; } DWORD RassrvRegSetDw( IN DWORD dwFlag, IN CONST PWCHAR pszKeyName, IN CONST PWCHAR pszValueName) { return RassrvRegSetDwEx(dwFlag, pszKeyName, pszValueName, FALSE); } // // Warns the user that we are about to switch to MMC returning TRUE if // user agrees to this and FALSE otherwise // BOOL RassrvWarnMMCSwitch( IN HWND hwndDlg) { PWCHAR pszWarning, pszTitle; pszWarning = (PWCHAR) PszLoadString(Globals.hInstDll, WRN_SWITCHING_TO_MMC); pszTitle = (PWCHAR) PszLoadString(Globals.hInstDll, WRN_TITLE); if (MessageBox( hwndDlg, pszWarning, pszTitle, MB_YESNO | MB_ICONEXCLAMATION) == IDYES) { return TRUE; } return FALSE; } // // Switches to mmc based on the console identifier passed in // DWORD RassrvLaunchMMC ( IN DWORD dwConsoleId) { STARTUPINFOA startupinfo; PROCESS_INFORMATION procinfo; CHAR * pszConsole; CHAR pszBuf[1024], pszDir[1024]; // Set the command line accordingly switch (dwConsoleId) { case RASSRVUI_NETWORKCONSOLE: pszConsole = "netmgmt.msc"; break; case RASSRVUI_USERCONSOLE: pszConsole = NULL; break; case RASSRVUI_SERVICESCONSOLE: pszConsole = "compmgmt.msc"; break; case RASSRVUI_MPRCONSOLE: default: pszConsole = "rrasmgmt.msc"; break; } if (pszConsole) { GetSystemDirectoryA (pszDir, sizeof(pszDir)); sprintf (pszBuf, "mmc %s\\%s", pszDir, pszConsole); } else strcpy (pszBuf, "mmc.exe"); // Launch MMC ZeroMemory(&startupinfo, sizeof(startupinfo)); startupinfo.cb = sizeof(startupinfo); CreateProcessA( NULL, // name of executable module pszBuf, // command line string NULL, // process security attributes NULL, // thread security attributes FALSE, // handle inheritance flag NORMAL_PRIORITY_CLASS, // creation flags NULL, // new environment block NULL, // current directory name &startupinfo, // STARTUPINFO &procinfo); // PROCESS_INFORMATION return NO_ERROR; } // // Retrieve a string from the registry // DWORD RassrvRegGetStr( OUT PWCHAR pszBuf, IN PWCHAR pszDefault, IN CONST PWCHAR pszKeyName, IN CONST PWCHAR pszValueName) { DWORD dwErr = NO_ERROR, dwVal, dwType = REG_SZ, dwSize = 512; HKEY hKey = NULL; do { // Open the registry key dwErr = RegOpenKeyExW( HKEY_LOCAL_MACHINE, pszKeyName, 0, KEY_READ, &hKey); if (dwErr != ERROR_SUCCESS) { break; } // Read the value dwErr = RegQueryValueExW( hKey, pszValueName, 0, &dwType, (BYTE *)pszBuf, &dwSize); if (dwErr != ERROR_SUCCESS) { dwErr = NO_ERROR; wcscpy(pszBuf, pszDefault); } } while (FALSE); // Cleanup { if (hKey) { RegCloseKey(hKey); } } return NO_ERROR; } // // Save a string to the registry // DWORD RassrvRegSetStr( IN PWCHAR pszStr, IN CONST PWCHAR pszKeyName, IN CONST PWCHAR pszValueName) { DWORD dwErr = NO_ERROR, dwVal, dwType = REG_SZ, dwSize; HKEY hKey = NULL; dwSize = wcslen(pszStr)*sizeof(WCHAR) + sizeof(WCHAR); do { // Open the registry key dwErr = RegOpenKeyExW( HKEY_LOCAL_MACHINE, pszKeyName, 0, KEY_WRITE, &hKey); if (dwErr != ERROR_SUCCESS) { break; } // Set the value dwErr = RegSetValueExW( hKey, pszValueName, 0, dwType, (CONST BYTE *)pszStr, dwSize); if (dwErr != ERROR_SUCCESS) { break; } } while (FALSE); // Cleanup { if (hKey) { RegCloseKey(hKey); } } return dwErr; } // // Gets the machine flags // DWORD RasSrvGetMachineFlags( OUT LPDWORD lpdwFlags) { GBL_LOCK; gblLoadMachineFlags(&Globals); *lpdwFlags = Globals.dwMachineFlags; GBL_UNLOCK; return NO_ERROR; } // // Get multilink status // DWORD RasSrvGetMultilink( OUT BOOL * pbEnabled) { DWORD dwFlags = PPPCFG_NegotiateMultilink; if (!pbEnabled) { return ERROR_INVALID_PARAMETER; } // Read the flags RassrvRegGetDw( &dwFlags, PPPCFG_NegotiateMultilink, (const PWCHAR)pszregRasParameters, (const PWCHAR)pszregServerFlags); // Assign the enable state accordingly if (dwFlags & PPPCFG_NegotiateMultilink) { *pbEnabled = TRUE; } else { *pbEnabled = FALSE; } return NO_ERROR; } // // Private internal function that enables/disables multilink // DWORD RasSrvSetMultilink( IN BOOL bEnable) { DWORD dwFlags = PPPCFG_NegotiateMultilink; // Read the flags RassrvRegGetDw( &dwFlags, PPPCFG_NegotiateMultilink, (const PWCHAR)pszregRasParameters, (const PWCHAR)pszregServerFlags); // Assign the enable state accordingly if (bEnable) { dwFlags |= PPPCFG_NegotiateMultilink; } else { dwFlags &= ~PPPCFG_NegotiateMultilink; } // Set the flags RassrvRegSetDw( dwFlags, (CONST PWCHAR)pszregRasParameters, (CONST PWCHAR)pszregServerFlags); return NO_ERROR; } // // Initialize the show icon setting // DWORD RasSrvGetIconShow( OUT BOOL * pbEnabled) { DWORD dwErr = NO_ERROR, dwFlags = 0; BOOL bDefault = TRUE; // Get machine flags // dwErr = RasSrvGetMachineFlags(&dwFlags); if (dwErr != NO_ERROR) { *pbEnabled = FALSE; return dwErr; } // Always off for member server // if ((dwFlags & RASSRVUI_MACHINE_F_Server) && (dwFlags & RASSRVUI_MACHINE_F_Member)) { *pbEnabled = FALSE; return NO_ERROR; } // Set default // if (dwFlags & RASSRVUI_MACHINE_F_Server) { bDefault = FALSE; } else { bDefault = TRUE; } // Load the machine flags and return accordingly // *pbEnabled = bDefault; dwErr = RassrvRegGetDw( pbEnabled, bDefault, (CONST PWCHAR)pszregRasParameters, (CONST PWCHAR)pszregShowIcon); return dwErr; } // // Save the show icon setting // DWORD RasSrvSetIconShow( IN BOOL bEnable) { return RassrvRegSetDw( bEnable, (CONST PWCHAR)pszregRasParameters, (CONST PWCHAR)pszregShowIcon); } // // Save the log level // DWORD RasSrvSetLogLevel( IN DWORD dwLevel) { return RassrvRegSetDw( dwLevel, (CONST PWCHAR)pszregRasParameters, (CONST PWCHAR)pszregLogLevel); } // Calls WinHelp to popup context sensitive help. 'pdwMap' is an array // of control-ID help-ID pairs terminated with a 0,0 pair. 'UnMsg' is // WM_HELP or WM_CONTEXTMENU indicating the message received requesting // help. 'Wparam' and 'lparam' are the parameters of the message received // requesting help. DWORD RasSrvHelp ( IN HWND hwndDlg, IN UINT unMsg, IN WPARAM wparam, IN LPARAM lparam, IN const DWORD* pdwMap) { HWND hwnd; UINT unType; TCHAR pszHelpFile[] = TEXT("Netcfg.hlp"); // Validate parameters if (! (unMsg==WM_HELP || unMsg==WM_CONTEXTMENU)) { return ERROR_INVALID_PARAMETER; } // If no map is provided, no help will show if (!pdwMap) { return NO_ERROR; } // If an actual help topic is request... if (unMsg == WM_HELP) { LPHELPINFO p = (LPHELPINFO )lparam; TRACE4( "ContextHelp(WM_HELP,t=%d,id=%d,h=$%08x,s=$%08x)", p->iContextType, p->iCtrlId,p->hItemHandle ,hwndDlg ); if (p->iContextType != HELPINFO_WINDOW) { return NO_ERROR; } hwnd = p->hItemHandle; unType = HELP_WM_HELP; } // Standard Win95 method that produces a one-item "What's This?" // menu that user must click to get help. else { TRACE1( "ContextHelp(WM_CONTEXTMENU,h=$%08x)", wparam ); hwnd = (HWND )wparam; unType = HELP_CONTEXTMENU; } WinHelp( hwnd, pszHelpFile, unType, (ULONG_PTR)pdwMap ); return NO_ERROR; } BOOL CALLBACK WSDlgProc( HWND hwnd, UINT unMsg, WPARAM wParam, LPARAM lParam ) /* Standard Win32 dialog procedure. */ { if (unMsg == WM_INITDIALOG) { HMENU hmenu; RECT r1, r2; /* Remove Close from the system menu since some people think it kills ** the app and not just the popup. */ hmenu = GetSystemMenu( hwnd, FALSE ); if (hmenu && DeleteMenu( hmenu, SC_CLOSE, MF_BYCOMMAND )) { DrawMenuBar( hwnd ); } // Center the window GetWindowRect(hwnd, &r1); GetWindowRect(GetDesktopWindow(), &r2); MoveWindow( hwnd, (r2.right - r2.left)/2 - (r1.right - r1.left)/2, (r2.bottom - r2.top)/2 - (r1.bottom - r1.top)/2, r1.right - r1.left, r1.bottom - r1.top, TRUE); return TRUE; } return FALSE; } // // Bring up the start waiting for services dialog // DWORD RasSrvShowServiceWait( IN HINSTANCE hInst, IN HWND hwndParent, OUT HANDLE * phData) { // Set the hourglass cursor *phData = (HANDLE) SetCursor (LoadCursor (NULL, IDC_WAIT)); ShowCursor (TRUE); return NO_ERROR; } // // Bring down wait for services dialog // DWORD RasSrvFinishServiceWait ( IN HANDLE hData) { HICON hIcon = (HICON)hData; if (hIcon == NULL) { hIcon = LoadCursor (NULL, IDC_ARROW); } SetCursor (hIcon); ShowCursor (TRUE); return NO_ERROR; } //----------------------------------------------------------------------- // Function: EnableBackupPrivilege // // Enables/disables backup privilege for the current process. //----------------------------------------------------------------------- DWORD EnableRebootPrivilege( IN BOOL bEnable) { LUID luid; HANDLE hToken = NULL; TOKEN_PRIVILEGES tp; BOOL bOk; // We first have to try to get the token of the current // thread since if it is impersonating, adjusting the // privileges of the process will have no affect. bOk = OpenThreadToken( GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, TRUE, &hToken); if (bOk == FALSE) { // There is no thread token -- open it up for the // process instead. OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken ); } // Get the LUID of the privilege if (!LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &luid)) { DWORD dwErr = GetLastError(); if(NULL != hToken) { CloseHandle(hToken); } return dwErr; } // Adjust the token privileges tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; // Commit changes to the system if (!AdjustTokenPrivileges( hToken, !bEnable, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL )) { DWORD dwErr = GetLastError(); if(NULL != hToken) { CloseHandle(hToken); } return dwErr; } // Even if AdjustTokenPrivileges succeeded (see MSDN) you still // need to verify success by calling GetLastError. if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) { if(NULL != hToken) { CloseHandle(hToken); } return ERROR_NOT_ALL_ASSIGNED; } if(NULL != hToken) { CloseHandle(hToken); } return NO_ERROR; } // Pops up a warning with the given parent window and reboots // windows DWORD RasSrvReboot(HWND hwndParent) { DWORD dwOldState; INT iRet; PWCHAR pszWarn, pszTitle; // Load the strings pszWarn = (PWCHAR) PszLoadString(Globals.hInstDll, WRN_REBOOT_REQUIRED); pszTitle = (PWCHAR) PszLoadString(Globals.hInstDll, WRN_TITLE); // Display the warning iRet = MessageBoxW( hwndParent, pszWarn, pszTitle, MB_YESNO | MB_APPLMODAL); if (iRet != IDYES) { return ERROR_CANCELLED; } // Enable the reboot privelege EnableRebootPrivilege(TRUE); ExitWindowsEx(EWX_REBOOT, 0); // Restore the reboot privelege EnableRebootPrivilege(FALSE); return NO_ERROR; }