Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

742 lines
19 KiB

  1. ////////////////////////////////////////////////////////////////
  2. //
  3. // Refgp.cxx
  4. //
  5. // Refresh Group Policy exe
  6. //
  7. //
  8. ////////////////////////////////////////////////////////////////
  9. #include "refgp.h"
  10. #define USER_POLICY_APPLIED_EVENT TEXT("userenv: User Group Policy has been applied")
  11. #define MACHINE_POLICY_APPLIED_EVENT TEXT("Global\\userenv: Machine Group Policy has been applied")
  12. #define USER_POLICY_DONE_EVENT TEXT("userenv: User Group Policy Processing is done")
  13. #define MACHINE_POLICY_DONE_EVENT TEXT("Global\\userenv: Machine Group Policy Processing is done")
  14. #define USER_POLICY_REFRESH_NEEDFG_EVENT TEXT("userenv: User Group Policy ForcedRefresh Needs Foreground Processing")
  15. #define MACHINE_POLICY_REFRESH_NEEDFG_EVENT TEXT("Global\\userenv: Machine Group Policy ForcedRefresh Needs Foreground Processing")
  16. #define REFRESH_MACHINE 1
  17. #define REFRESH_USER 2
  18. HINSTANCE hInst;
  19. typedef enum _FAILSTATES {
  20. NO_FAILURE,
  21. REFRESH_FAILED,
  22. POLWAIT_FAILED,
  23. POLWAIT_TIMEDOUT
  24. } FAILSTATES;
  25. typedef struct _REFSTRUCT {
  26. BOOL bMachine;
  27. DWORD dwOption;
  28. DWORD dwTimeOut;
  29. DWORD dwError;
  30. FAILSTATES fState;
  31. BOOL bFGNeeded;
  32. } REFSTRUCT, *LPREFSTRUCT;
  33. REFSTRUCT refMach;
  34. REFSTRUCT refUser;
  35. WCHAR szUser[200];
  36. WCHAR szMach[200];
  37. WCHAR szErr[MAX_PATH*2];
  38. //
  39. // Below we define a mapping so that we can have user and computer
  40. // versions of certain messages. Note that we could parameterize
  41. // the messages such that they took the string 'user' or 'computer'
  42. // as an argument, but this can lead to problems with localization,
  43. // especially for languages in which nearby words affect the form
  44. // of other nearby words. For safety then, we simply have two
  45. // messages for each of these cases -- a user version and computer
  46. // version.
  47. //
  48. enum
  49. {
  50. ID_REFRESH_FAILED = 0,
  51. ID_POLWAIT_FAILED,
  52. ID_POLWAIT_TIMEDOUT,
  53. ID_REFRESH_BACKGND_SUCCESS,
  54. ID_REFRESH_BACKGND_TRIGGERED
  55. };
  56. const DWORD REFRESH_STATUS_IDS[][2] =
  57. {
  58. {
  59. IDS_REFRESH_FAILED_COMPUTER,
  60. IDS_REFRESH_FAILED_USER
  61. },
  62. {
  63. IDS_POLWAIT_FAILED_COMPUTER,
  64. IDS_POLWAIT_FAILED_USER
  65. },
  66. {
  67. IDS_POLWAIT_TIMEDOUT_COMPUTER,
  68. IDS_POLWAIT_TIMEDOUT_USER
  69. },
  70. {
  71. IDS_REFRESH_BACKGND_SUCCESS_COMPUTER,
  72. IDS_REFRESH_BACKGND_SUCCESS_USER
  73. },
  74. {
  75. IDS_REFRESH_BACKGND_TRIGGERED_COMPUTER,
  76. IDS_REFRESH_BACKGND_TRIGGERED_USER
  77. }
  78. };
  79. // Process arg. checks whether argument is present
  80. BOOL ProcessArg(int *pargc, LPWSTR **pargv, DWORD dwStringId, BOOL *bValue)
  81. {
  82. WCHAR szStr[200];
  83. LPWSTR *argv = *pargv;
  84. if (*pargc == 0)
  85. return TRUE;
  86. if (!LoadString (hInst, dwStringId, szStr, 200)) {
  87. return FALSE;
  88. }
  89. for (; (*argv); *argv++) {
  90. if (_wcsicmp(*argv, szStr) == 0) {
  91. *bValue = TRUE;
  92. (*pargc)--;
  93. return TRUE;
  94. }
  95. }
  96. return TRUE;
  97. }
  98. // Process arg. checks whether argument is present and what the value is after the ":" in string format
  99. BOOL ProcessArg(int *pargc, WCHAR ***pargv, DWORD dwStringId, WCHAR **szValue)
  100. {
  101. WCHAR szStr[200];
  102. LPWSTR *argv = *pargv, szJunk=NULL;
  103. if (*pargc == 0)
  104. return TRUE;
  105. if (!LoadString (hInst, dwStringId, szStr, 200)) {
  106. return FALSE;
  107. }
  108. for (; (*argv); *argv++) {
  109. if (_wcsnicmp(*argv, szStr, lstrlen(szStr)) == 0) {
  110. *szValue = (*argv)+lstrlen(szStr);
  111. (*pargc)--;
  112. return TRUE;
  113. }
  114. }
  115. *szValue = NULL;
  116. return TRUE;
  117. }
  118. // Process arg. checks whether argument is present and what the value is after the ":" in long format
  119. BOOL ProcessArg(int *pargc, WCHAR ***pargv, DWORD dwStringId, long *plValue)
  120. {
  121. WCHAR szStr[200];
  122. LPWSTR *argv = *pargv, szJunk=NULL;
  123. if (*pargc == 0)
  124. return TRUE;
  125. if (!LoadString (hInst, dwStringId, szStr, 200)) {
  126. return FALSE;
  127. }
  128. for (; (*argv); *argv++) {
  129. if (_wcsnicmp(*argv, szStr, lstrlen(szStr)) == 0) {
  130. *plValue = wcstol((*argv)+lstrlen(szStr), &szJunk, 10);
  131. (*pargc)--;
  132. return TRUE;
  133. }
  134. }
  135. return TRUE;
  136. }
  137. BOOL CompareOptions(WCHAR *szValue, DWORD dwOptionId)
  138. {
  139. WCHAR szStr[200];
  140. if (!szValue)
  141. return FALSE;
  142. if (!LoadString (hInst, dwOptionId, szStr, 200)) {
  143. return FALSE;
  144. }
  145. if (_wcsicmp(szValue, szStr) == 0)
  146. return TRUE;
  147. return FALSE;
  148. }
  149. BOOL GetValue(WCHAR *szValue, DWORD dwOptionId)
  150. {
  151. if (!LoadString (hInst, dwOptionId, szValue, 200)) {
  152. return FALSE;
  153. }
  154. return TRUE;
  155. }
  156. void PrintMsg(DWORD dwMsgId, ...)
  157. {
  158. WCHAR szFmt[200];
  159. WCHAR szMsg[200];
  160. va_list marker;
  161. if (!LoadString (hInst, dwMsgId, szFmt, 200)) {
  162. return;
  163. }
  164. va_start(marker, dwMsgId);
  165. wvnsprintf(szMsg, 200, szFmt, marker);
  166. va_end(marker);
  167. wprintf(szMsg);
  168. return;
  169. }
  170. void PrintUsage()
  171. {
  172. for (DWORD dwMsgId = IDS_USAGE_FIRST; dwMsgId <= IDS_USAGE_LAST; dwMsgId++) {
  173. PrintMsg(dwMsgId);
  174. }
  175. return;
  176. }
  177. BOOL PromptUserForFG(BOOL bMachine)
  178. {
  179. WCHAR tTemp, tChar;
  180. WCHAR Yes[20], No[20];
  181. if (!LoadString (hInst, IDS_YES, Yes, 20)) {
  182. return FALSE; // safe
  183. }
  184. if (!LoadString (hInst, IDS_NO, No, 20)) {
  185. return FALSE; // safe
  186. }
  187. for (;;) {
  188. if (bMachine)
  189. PrintMsg(IDS_PROMPT_REBOOT);
  190. else
  191. PrintMsg(IDS_PROMPT_LOGOFF);
  192. tChar = getwchar();
  193. tTemp = tChar;
  194. while (tTemp != TEXT('\n')) {
  195. tTemp = getwchar();
  196. }
  197. if (towupper(tChar) == towupper(Yes[0]))
  198. return TRUE;
  199. if (towupper(tChar) == towupper(No[0]))
  200. return FALSE;
  201. }
  202. return FALSE;
  203. }
  204. //***************************************************************************
  205. //
  206. // GetErrString
  207. //
  208. // Purpose: Calls FormatMessage to Get the error string corresp. to a error
  209. // code
  210. //
  211. //
  212. // Parameters: dwErr - Error Code
  213. // szErr - Buffer to return the error string (MAX_PATH)
  214. // is assumed.!!!
  215. //
  216. // Return: szErr
  217. //
  218. //***************************************************************************
  219. LPTSTR GetErrString(DWORD dwErr, LPTSTR szErr)
  220. {
  221. szErr[0] = TEXT('\0');
  222. FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_MAX_WIDTH_MASK,
  223. NULL, dwErr,
  224. MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
  225. szErr, MAX_PATH, NULL);
  226. return szErr;
  227. }
  228. void RefreshPolicyAndWait(LPREFSTRUCT lpRef)
  229. {
  230. HANDLE hNotifyEvent=NULL, hFGProcessingEvent=NULL;
  231. DWORD dwRet=0;
  232. lpRef->fState = REFRESH_FAILED;
  233. lpRef->dwError = E_FAIL;
  234. lpRef->bFGNeeded = FALSE;
  235. if (!RefreshPolicyEx(lpRef->bMachine, lpRef->dwOption)) {
  236. lpRef->fState = REFRESH_FAILED;
  237. lpRef->dwError = GetLastError();
  238. goto Exit;
  239. }
  240. if (lpRef->dwTimeOut != 0) {
  241. lpRef->fState = POLWAIT_FAILED;
  242. lpRef->dwError = E_FAIL;
  243. hNotifyEvent = OpenEvent(SYNCHRONIZE, FALSE, lpRef->bMachine ? MACHINE_POLICY_DONE_EVENT : USER_POLICY_DONE_EVENT );
  244. if (!hNotifyEvent) {
  245. lpRef->fState = POLWAIT_FAILED;
  246. lpRef->dwError = GetLastError();
  247. goto Exit;
  248. }
  249. hFGProcessingEvent = OpenEvent(SYNCHRONIZE, FALSE, lpRef->bMachine ? MACHINE_POLICY_REFRESH_NEEDFG_EVENT : USER_POLICY_REFRESH_NEEDFG_EVENT);
  250. if (!hNotifyEvent) {
  251. lpRef->fState = POLWAIT_FAILED;
  252. lpRef->dwError = GetLastError();
  253. goto Exit;
  254. }
  255. dwRet = WaitForSingleObject(hNotifyEvent, (lpRef->dwTimeOut == INFINITE) ? INFINITE : 1000*(lpRef->dwTimeOut));
  256. if (dwRet == WAIT_FAILED) {
  257. lpRef->fState = POLWAIT_FAILED;
  258. lpRef->dwError = GetLastError();
  259. goto Exit;
  260. }
  261. else if (dwRet == WAIT_ABANDONED) {
  262. lpRef->fState = POLWAIT_FAILED;
  263. lpRef->dwError = E_UNEXPECTED;
  264. goto Exit;
  265. }
  266. else if (dwRet == WAIT_TIMEOUT) {
  267. lpRef->fState = POLWAIT_TIMEDOUT;
  268. lpRef->dwError = 0;
  269. goto Exit;
  270. }
  271. lpRef->bFGNeeded = (lpRef->dwOption == RP_FORCE) && (WaitForSingleObject(hFGProcessingEvent, 0) == WAIT_OBJECT_0);
  272. }
  273. lpRef->fState = NO_FAILURE;
  274. Exit:
  275. if (hNotifyEvent)
  276. CloseHandle(hNotifyEvent);
  277. if (hFGProcessingEvent)
  278. CloseHandle(hFGProcessingEvent);
  279. return;
  280. }
  281. void PrintRefreshError(LPREFSTRUCT lpRef)
  282. {
  283. DWORD dwTarget;
  284. dwTarget = (lpRef->bMachine) ? COMPUTER_TARGET : USER_TARGET;
  285. switch (lpRef->fState) {
  286. case REFRESH_FAILED:
  287. PrintMsg(REFRESH_ID(ID_REFRESH_FAILED, dwTarget), GetErrString(lpRef->dwError, szErr));
  288. break;
  289. case POLWAIT_FAILED:
  290. PrintMsg(REFRESH_ID(ID_POLWAIT_FAILED, dwTarget), GetErrString(lpRef->dwError, szErr));
  291. break;
  292. case POLWAIT_TIMEDOUT:
  293. PrintMsg(REFRESH_ID(ID_POLWAIT_TIMEDOUT, dwTarget));
  294. case NO_FAILURE:
  295. if (lpRef->dwTimeOut == 0)
  296. PrintMsg(REFRESH_ID(ID_REFRESH_BACKGND_TRIGGERED, dwTarget));
  297. else
  298. PrintMsg(REFRESH_ID(ID_REFRESH_BACKGND_SUCCESS, dwTarget));
  299. break;
  300. default:
  301. break;
  302. }
  303. }
  304. void __cdecl main (int argc, char **argv)
  305. {
  306. DWORD uTarget=0;
  307. BOOL bArgValid = TRUE;
  308. LONG lTime = 600;
  309. DWORD dwTime = 600, dwRet = 0, dwOption = 0, dwThread = 0;
  310. HANDLE hNotifyEvent=NULL, hFGProcessingEvent=NULL, hToken = NULL;
  311. BOOL bNeedFG = FALSE;
  312. LPWSTR lpCommandLine=0, szTarget=0;
  313. int wargc=0;
  314. LPWSTR *wargv=NULL, *wargv1=NULL;
  315. BOOL bForce=FALSE, bOkToLogoff=FALSE, bOkToBoot=FALSE, bNextFgSync = FALSE;
  316. BOOL bNeedBoot = FALSE, bNeedLogoff = FALSE;
  317. BOOL bError = FALSE;
  318. HANDLE hThreads[2] = {0, 0};
  319. WCHAR achCodePage[13] = L".OCP";
  320. UINT CodePage = GetConsoleOutputCP();
  321. //
  322. // Set locale to the default
  323. //
  324. if ( 0 != CodePage )
  325. {
  326. _ultow( CodePage, achCodePage + 1, 10 );
  327. }
  328. _wsetlocale(LC_ALL, achCodePage);
  329. SetThreadUILanguage(0);
  330. lpCommandLine = GetCommandLine();
  331. wargv1 = CommandLineToArgvW(lpCommandLine, &wargc);
  332. wargv = (LPWSTR *)LocalAlloc(LPTR, (1+wargc)*sizeof(LPWSTR));
  333. if (!wargv) {
  334. PrintMsg(IDS_OUT_OF_MEMORY);
  335. goto Exit;
  336. }
  337. memcpy(wargv, wargv1, wargc*sizeof(LPWSTR));
  338. hInst = GetModuleHandle(wargv[0]);
  339. if ((!GetValue(szUser, IDS_USER)) || (!GetValue(szMach, IDS_MACHINE))) {
  340. // we cannot read the resource strings. no point continuing
  341. return;
  342. }
  343. //
  344. // Ignore the first argument
  345. //
  346. wargc--;
  347. wargv++;
  348. //
  349. // Get the args
  350. //
  351. bArgValid = bArgValid && ProcessArg(&wargc, &wargv, IDS_TARGET, &szTarget);
  352. bArgValid = bArgValid && ProcessArg(&wargc, &wargv, IDS_TIME, &lTime);
  353. bArgValid = bArgValid && ProcessArg(&wargc, &wargv, IDS_FORCE, &bForce);
  354. bArgValid = bArgValid && ProcessArg(&wargc, &wargv, IDS_LOGOFF, &bOkToLogoff);
  355. bArgValid = bArgValid && ProcessArg(&wargc, &wargv, IDS_BOOT, &bOkToBoot);
  356. bArgValid = bArgValid && ProcessArg(&wargc, &wargv, IDS_SYNC, &bNextFgSync);
  357. bArgValid = bArgValid && (wargc == 0);
  358. //
  359. // Get the target correctly
  360. //
  361. uTarget = 0;
  362. if (bArgValid ) {
  363. if (!szTarget) {
  364. uTarget |= REFRESH_MACHINE;
  365. uTarget |= REFRESH_USER;
  366. }
  367. else if ( CompareOptions(szTarget, IDS_MACHINE) )
  368. uTarget |= REFRESH_MACHINE;
  369. else if ( CompareOptions(szTarget, IDS_USER) )
  370. uTarget |= REFRESH_USER;
  371. else {
  372. bArgValid = FALSE;
  373. }
  374. }
  375. //
  376. // Get the options correctly
  377. //
  378. if (bArgValid) {
  379. if ( bForce )
  380. dwOption = RP_FORCE;
  381. else
  382. dwOption = 0;
  383. }
  384. if (lTime == -1)
  385. dwTime = INFINITE;
  386. else
  387. dwTime = lTime;
  388. if (!bArgValid) {
  389. PrintUsage();
  390. goto Exit;
  391. }
  392. if (bOkToBoot)
  393. bOkToLogoff = TRUE;
  394. if (bNextFgSync) {
  395. if (uTarget & REFRESH_MACHINE) {
  396. dwRet = ForceSyncFgPolicy( 0 );
  397. if (dwRet != ERROR_SUCCESS) {
  398. PrintMsg(IDS_SET_MODE_FAILED, GetErrString(dwRet, szErr));
  399. goto Exit;
  400. }
  401. }
  402. if (uTarget & REFRESH_USER) {
  403. if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
  404. PrintMsg(IDS_SET_MODE_FAILED, GetErrString(GetLastError(), szErr));
  405. goto Exit;
  406. }
  407. LPWSTR szSid = GetSidString( hToken );
  408. if (!szSid) {
  409. PrintMsg(IDS_SET_MODE_FAILED, GetErrString(GetLastError(), szErr));
  410. goto Exit;
  411. }
  412. dwRet = ForceSyncFgPolicy( szSid );
  413. if (dwRet != ERROR_SUCCESS) {
  414. LocalFree (szSid);
  415. PrintMsg(IDS_SET_MODE_FAILED, GetErrString(dwRet, szErr));
  416. goto Exit;
  417. }
  418. LocalFree (szSid);
  419. CloseHandle (hToken);
  420. hToken = 0;
  421. }
  422. }
  423. else {
  424. if (uTarget & REFRESH_MACHINE) {
  425. refMach.bMachine = TRUE;
  426. refMach.dwOption = dwOption;
  427. refMach.dwTimeOut = dwTime;
  428. if ((hThreads[dwThread] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)RefreshPolicyAndWait, &refMach, 0, 0)) == NULL) {
  429. PrintMsg(IDS_REFRESH_POLICY_FAILED, GetErrString(GetLastError(), szErr));
  430. goto Exit;
  431. }
  432. dwThread++;
  433. }
  434. if (uTarget & REFRESH_USER) {
  435. refUser.bMachine = FALSE;
  436. refUser.dwOption = dwOption;
  437. refUser.dwTimeOut = dwTime;
  438. if ((hThreads[dwThread] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)RefreshPolicyAndWait, &refUser, 0, 0)) == NULL) {
  439. PrintMsg(IDS_REFRESH_POLICY_FAILED, GetErrString(GetLastError(), szErr));
  440. goto Exit;
  441. }
  442. dwThread++;
  443. }
  444. PrintMsg(IDS_REFRESH_LAUNCHED);
  445. dwRet = WaitForMultipleObjects(dwThread, hThreads, TRUE, INFINITE);
  446. if ((dwRet != WAIT_OBJECT_0) && (dwRet != (WAIT_OBJECT_0 + 1))) {
  447. // our threads didn't terminate properly..
  448. PrintMsg(IDS_REFRESH_POLICY_FAILED, GetErrString(GetLastError(), szErr));
  449. goto Exit;
  450. }
  451. if (uTarget & REFRESH_USER) {
  452. PrintRefreshError(&refUser);
  453. if (refUser.fState != NO_FAILURE)
  454. bError = TRUE;
  455. }
  456. if (uTarget & REFRESH_MACHINE) {
  457. PrintRefreshError(&refMach);
  458. if (refMach.fState != NO_FAILURE)
  459. bError = TRUE;
  460. }
  461. if (NO_FAILURE == refMach.fState)
  462. {
  463. PrintMsg(IDS_LOOK_EVENT_LOG);
  464. }
  465. if (bError) {
  466. goto Exit;
  467. }
  468. }
  469. PrintMsg(IDS_SPACE);
  470. if ((uTarget & REFRESH_USER) && (bNextFgSync || refUser.bFGNeeded)) {
  471. if ( bNextFgSync )
  472. PrintMsg(IDS_NEED_LOGOFF_SYNC);
  473. else
  474. PrintMsg(IDS_NEED_LOGOFF);
  475. bNeedLogoff = TRUE;
  476. }
  477. if ((uTarget & REFRESH_MACHINE) && (bNextFgSync || refMach.bFGNeeded)) {
  478. if ( bNextFgSync )
  479. PrintMsg(IDS_NEED_REBOOT_SYNC);
  480. else
  481. PrintMsg(IDS_NEED_REBOOT);
  482. bNeedBoot = TRUE;
  483. }
  484. if ( !bNeedBoot && !bNeedLogoff) {
  485. goto Exit;
  486. }
  487. PrintMsg(IDS_SPACE);
  488. if (bNeedBoot && !bOkToBoot) {
  489. bOkToBoot = PromptUserForFG(TRUE);
  490. }
  491. if (bNeedBoot && bOkToBoot) {
  492. if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
  493. PrintMsg(IDS_COULDNT_REBOOT, GetErrString(GetLastError(), szErr));
  494. goto Exit;
  495. }
  496. BYTE bytesTokenPrivNew[sizeof(DWORD)+sizeof(LUID_AND_ATTRIBUTES)];
  497. PTOKEN_PRIVILEGES pTokenPrivNew = (PTOKEN_PRIVILEGES)bytesTokenPrivNew;
  498. BYTE bytesTokenPrivOld[sizeof(DWORD)+sizeof(LUID_AND_ATTRIBUTES)];
  499. PTOKEN_PRIVILEGES pTokenPrivOld = (PTOKEN_PRIVILEGES)bytesTokenPrivOld;
  500. DWORD dwSize=sizeof(DWORD)+sizeof(LUID_AND_ATTRIBUTES);
  501. DWORD dwRetSize=0;
  502. pTokenPrivNew->PrivilegeCount = 1;
  503. pTokenPrivNew->Privileges->Attributes = SE_PRIVILEGE_ENABLED;
  504. if (!LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &(pTokenPrivNew->Privileges->Luid))) {
  505. PrintMsg(IDS_COULDNT_REBOOT, GetErrString(GetLastError(), szErr));
  506. goto Exit;
  507. }
  508. if (!AdjustTokenPrivileges(hToken, FALSE, pTokenPrivNew, dwSize, pTokenPrivOld, &dwRetSize)) {
  509. PrintMsg(IDS_COULDNT_REBOOT, GetErrString(GetLastError(), szErr));
  510. goto Exit;
  511. }
  512. PrintMsg(IDS_NOTIFY_MACHINE_FG);
  513. if (!ExitWindowsEx(EWX_REBOOT, 0)) {
  514. PrintMsg(IDS_COULDNT_REBOOT, GetErrString(GetLastError(), szErr));
  515. }
  516. else {
  517. PrintMsg(IDS_REBOOTING);
  518. }
  519. if (!AdjustTokenPrivileges(hToken, FALSE, pTokenPrivOld, 0, NULL, 0)) {
  520. PrintMsg(IDS_COULDNT_REBOOT, GetErrString(GetLastError(), szErr));
  521. goto Exit;
  522. }
  523. // if we are rebooting no need to call logoff code
  524. goto Exit;
  525. }
  526. if (bNeedLogoff && !bOkToLogoff) {
  527. bOkToLogoff = PromptUserForFG(FALSE);
  528. }
  529. if (bNeedLogoff && bOkToLogoff) {
  530. PrintMsg(IDS_NOTIFY_USER_FG);
  531. if (!ExitWindowsEx(EWX_LOGOFF, 0)) {
  532. PrintMsg(IDS_COULDNT_LOGOFF, GetErrString(GetLastError(), szErr));
  533. }
  534. else {
  535. PrintMsg(IDS_LOGGING_OFF);
  536. }
  537. }
  538. Exit:
  539. if (hToken)
  540. CloseHandle(hToken);
  541. for (;dwThread;dwThread--)
  542. if (hThreads[dwThread-1]) {
  543. CloseHandle(hThreads[dwThread-1]);
  544. }
  545. if (wargv1)
  546. GlobalFree(wargv1);
  547. if (wargv)
  548. LocalFree(wargv);
  549. return;
  550. }