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.

1272 lines
38 KiB

  1. // Copyright (c) 1997-1999 Microsoft Corporation
  2. #include "precomp.h"
  3. #ifdef EXT_DEBUG
  4. #undef THIS_FILE
  5. static char THIS_FILE[] = __FILE__;
  6. #endif
  7. #include "StartupPage.h"
  8. // avoid some warnings.
  9. #undef HDS_HORZ
  10. #undef HDS_BUTTONS
  11. #undef HDS_HIDDEN
  12. #include "resource.h"
  13. #include <stdlib.h>
  14. #include <TCHAR.h>
  15. #include "..\Common\util.h"
  16. #include <windowsx.h>
  17. #include <commctrl.h>
  18. #include <shellapi.h>
  19. #include "RebootPage.h"
  20. #include "helpid.h"
  21. #include "NetUtility.h"
  22. // Reboot switch for crashdump dlg
  23. #define RET_ERROR (-1)
  24. #define RET_NO_CHANGE 0x00
  25. #define RET_VIRTUAL_CHANGE 0x01
  26. #define RET_RECOVER_CHANGE 0x02
  27. #define RET_CHANGE_NO_REBOOT 0x04
  28. #define RET_CONTINUE 0x08
  29. #define RET_BREAK 0x10
  30. #define RET_VIRT_AND_RECOVER (RET_VIRTUAL_CHANGE | RET_RECOVER_CHANGE)
  31. #define FORMIN 0
  32. #define FORMAX 999
  33. // Length of WCHAR buffer needed to hold "Display startup list for..." value
  34. #define FOR_MAX_LENGTH 20
  35. // Default "Display startup list for..." value
  36. #define FORDEF 30
  37. #define NO_DUMP_OPTION 0
  38. #define COMPLETE_DUMP_OPTION 1
  39. #define KERNEL_DUMP_OPTION 2
  40. #define SMALL_DUMP_OPTION 3
  41. //
  42. // Help ID's
  43. //
  44. DWORD aStartupHelpIds[] = {
  45. IDC_STARTUP_SYS_OS, (IDH_STARTUP + 0),
  46. IDC_STARTUP_SYS_ENABLECOUNTDOWN, (IDH_STARTUP + 1),
  47. IDC_STARTUP_SYS_SECONDS, (IDH_STARTUP + 2),
  48. IDC_STARTUP_SYS_SECONDS_LABEL, (IDH_STARTUP + 2),
  49. IDC_STARTUP_CDMP_TXT1, (IDH_STARTUP + 3),
  50. IDC_STARTUP_CDMP_LOG, (IDH_STARTUP + 4),
  51. IDC_STARTUP_CDMP_SEND, (IDH_STARTUP + 5),
  52. IDC_STARTUP_CDMP_FILENAME, (IDH_STARTUP + 7),
  53. IDC_STARTUP_CDMP_OVERWRITE, (IDH_STARTUP + 13),
  54. IDC_STARTUP_CDMP_AUTOREBOOT, (IDH_STARTUP + 9),
  55. IDC_STARTUP_SYSTEM_GRP, (IDH_STARTUP + 10),
  56. IDC_STARTUP_SYS_SECSCROLL, (IDH_STARTUP + 11),
  57. IDC_STARTUP_CDMP_GRP, (IDH_STARTUP + 12),
  58. IDC_STARTUP_SYSTEM_GRP2, (IDH_STARTUP + 14),
  59. IDC_STARTUP_CDMP_OPTIONS, (IDH_STARTUP + 8),
  60. IDC_EDIT_BOOT_INI_LABEL, (IDH_STARTUP + 15),
  61. IDC_EDIT_BOOT_INI, (IDH_STARTUP + 16),
  62. IDC_REBOOT, IDH_WBEM_ADVANCED_STARTRECOVER_REMOTE_REBOOT,
  63. 0, 0
  64. };
  65. //----------------------------------------------------------------------------
  66. INT_PTR CALLBACK StaticStartupDlgProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam)
  67. {
  68. // if this is the initDlg msg...
  69. if(message == WM_INITDIALOG)
  70. {
  71. // transfer the 'this' ptr to the extraBytes.
  72. SetWindowLongPtr(hwndDlg, DWLP_USER, lParam);
  73. }
  74. // DWL_USER is the 'this' ptr.
  75. StartupPage *me = (StartupPage *)GetWindowLongPtr(hwndDlg, DWLP_USER);
  76. if(me != NULL)
  77. {
  78. // call into the DlgProc() that has some context.
  79. return me->DlgProc(hwndDlg, message, wParam, lParam);
  80. }
  81. else
  82. {
  83. return FALSE;
  84. }
  85. }
  86. //--------------------------------------------------------------
  87. StartupPage::StartupPage(WbemServiceThread *serviceThread)
  88. : WBEMPageHelper(serviceThread)
  89. {
  90. IWbemClassObject *pInst = NULL;
  91. m_WbemServices.SetPriv();
  92. if((pInst = FirstInstanceOf("Win32_ComputerSystem")) != NULL)
  93. {
  94. m_computer = pInst;
  95. }
  96. if((pInst = FirstInstanceOf("Win32_OperatingSystem")) != NULL)
  97. {
  98. m_OS = pInst;
  99. }
  100. if((pInst = FirstInstanceOf("Win32_OSRecoveryConfiguration")) != NULL)
  101. {
  102. m_recovery = pInst;
  103. }
  104. if((pInst = FirstInstanceOf("Win32_LogicalMemoryConfiguration")) != NULL)
  105. {
  106. m_memory = pInst;
  107. }
  108. m_WbemServices.ClearPriv();
  109. m_writable = TRUE;
  110. m_lBound = 1;
  111. m_bDownlevelTarget = TRUE; // Assume downlevel until proven otherwise.
  112. }
  113. //--------------------------------------------------------------
  114. INT_PTR StartupPage::DoModal(HWND hDlg)
  115. {
  116. return DialogBoxParam(HINST_THISDLL,
  117. (LPTSTR) MAKEINTRESOURCE(IDD_STARTUP),
  118. hDlg, StaticStartupDlgProc, (LPARAM)this);
  119. }
  120. //--------------------------------------------------------------
  121. StartupPage::~StartupPage()
  122. {
  123. }
  124. //--------------------------------------------------------------
  125. BOOL StartupPage::CheckVal( HWND hDlg, WORD wID, WORD wMin, WORD wMax, WORD wMsgID )
  126. {
  127. WORD nVal;
  128. BOOL bOK;
  129. HWND hVal;
  130. WCHAR szTemp[FOR_MAX_LENGTH];
  131. if( wMin > wMax )
  132. {
  133. nVal = wMin;
  134. wMin = wMax;
  135. wMax = nVal;
  136. }
  137. nVal = (WORD) GetDlgItemInt( hDlg, wID, &bOK, FALSE );
  138. //
  139. // This is a hack to make the null string act equivalent to zero
  140. //
  141. if (!bOK) {
  142. bOK = !GetDlgItemTextW( hDlg, wID, szTemp, FOR_MAX_LENGTH );
  143. }
  144. if( !bOK || ( nVal < wMin ) || ( nVal > wMax ) )
  145. {
  146. TCHAR megBuf[30] = {0};
  147. MsgBoxParam( hDlg, wMsgID, IDS_DISPLAY_NAME,
  148. MB_OK | MB_ICONERROR);
  149. SendMessage( hDlg, WM_NEXTDLGCTL,
  150. (WPARAM) ( hVal = GetDlgItem( hDlg, wID ) ), 1L );
  151. // SendMessage(hVal, EM_SETSEL, NULL, MAKELONG(0, 32767));
  152. SendMessage( hVal, EM_SETSEL, 0, 32767 );
  153. return( FALSE );
  154. }
  155. return( TRUE );
  156. }
  157. //--------------------------------------------------------------
  158. INT_PTR CALLBACK StartupPage::DlgProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam)
  159. {
  160. m_hDlg = hwndDlg;
  161. switch (message)
  162. {
  163. case WM_INITDIALOG:
  164. Init(hwndDlg);
  165. break;
  166. case WM_COMMAND:
  167. switch(HIWORD(wParam))
  168. {
  169. case EN_CHANGE:
  170. case BN_CLICKED:
  171. case CBN_SELCHANGE:
  172. PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
  173. break;
  174. }
  175. switch(LOWORD(wParam))
  176. {
  177. case IDC_STARTUP_SYS_ENABLECOUNTDOWN:
  178. if (HIWORD(wParam) == BN_CLICKED)
  179. {
  180. BOOL bChecking = (WORD) !IsDlgButtonChecked(m_hDlg, IDC_STARTUP_SYS_ENABLECOUNTDOWN);
  181. CheckDlgButton(m_hDlg, IDC_STARTUP_SYS_ENABLECOUNTDOWN, bChecking);
  182. EnableWindow(GetDlgItem(m_hDlg, IDC_STARTUP_SYS_SECONDS), bChecking);
  183. EnableWindow(GetDlgItem(m_hDlg, IDC_STARTUP_SYS_SECSCROLL), bChecking);
  184. if(bChecking)
  185. {
  186. Edit_SetText(GetDlgItem(m_hDlg, IDC_STARTUP_SYS_SECONDS), _T("30"));
  187. }
  188. else //unchecking it.
  189. {
  190. Edit_SetText(GetDlgItem(m_hDlg, IDC_STARTUP_SYS_SECONDS), _T("0"));
  191. }
  192. SendMessage((HWND) lParam, EM_SETSEL, 0, -1);
  193. }
  194. break;
  195. case IDC_STARTUP_SYS_SECONDS:
  196. if(HIWORD(wParam) == EN_UPDATE)
  197. {
  198. if(!CheckVal(m_hDlg, IDC_STARTUP_SYS_SECONDS, FORMIN, FORMAX, SYSTEM+4))
  199. {
  200. SetDlgItemInt(m_hDlg, IDC_STARTUP_SYS_SECONDS, FORDEF, FALSE);
  201. SendMessage((HWND) lParam, EM_SETSEL, 0, -1);
  202. } // endif (!CheckVal()
  203. } // endif
  204. break;
  205. case IDC_REBOOT:
  206. if(HIWORD(wParam) == BN_CLICKED)
  207. {
  208. RebootPage dlg(m_serviceThread);
  209. if(dlg.DoModal(hwndDlg) == IDOK)
  210. {
  211. EnableWindow(GetDlgItem(hwndDlg, IDC_REBOOT), FALSE);
  212. m_serviceThread->DisconnectServer();
  213. EndDialog(m_hDlg, CLOSE_SNAPIN);
  214. }
  215. }
  216. break;
  217. case IDOK:
  218. if(HIWORD(wParam) == BN_CLICKED)
  219. {
  220. if(Save())
  221. {
  222. EndDialog(m_hDlg, IDOK);
  223. }
  224. }
  225. break;
  226. case IDCANCEL:
  227. EndDialog(m_hDlg, IDCANCEL);
  228. break;
  229. case IDC_STARTUP_CDMP_OPTIONS:
  230. OnCDMPOptionUpdate();
  231. break;
  232. case IDC_EDIT_BOOT_INI:
  233. if (m_serviceThread && m_serviceThread->LocalConnection())
  234. {
  235. //
  236. // Local-only option. The button has been disabled but
  237. // perform this anyway.
  238. //
  239. OnBootEdit();
  240. }
  241. break;
  242. }
  243. break;
  244. case WM_HELP: // F1
  245. ::WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle,
  246. L"sysdm.hlp",
  247. HELP_WM_HELP,
  248. (ULONG_PTR)(LPSTR)aStartupHelpIds);
  249. break;
  250. case WM_CONTEXTMENU: // right mouse click
  251. WinHelp((HWND) wParam, HELP_FILE, HELP_CONTEXTMENU,
  252. (ULONG_PTR)(LPSTR) aStartupHelpIds);
  253. break;
  254. default:
  255. return FALSE;
  256. }
  257. return TRUE;
  258. }
  259. //--------------------------------------------------------------
  260. void StartupPage::OnCDMPOptionUpdate(void)
  261. {
  262. HWND ComboHwnd = GetDlgItem(m_hDlg, IDC_STARTUP_CDMP_OPTIONS);
  263. DWORD dwDumpOption = ComboBox_GetCurSel(ComboHwnd);
  264. EnableWindow(GetDlgItem(m_hDlg, IDC_STARTUP_CDMP_FILENAME),
  265. dwDumpOption != NO_DUMP_OPTION);
  266. EnableWindow(GetDlgItem(m_hDlg, IDC_STARTUP_CDMP_OVERWRITE),
  267. dwDumpOption != NO_DUMP_OPTION);
  268. bstr_t debugPath;
  269. if (dwDumpOption == SMALL_DUMP_OPTION)
  270. {
  271. debugPath = m_recovery.GetString("MiniDumpDirectory");
  272. }
  273. else
  274. {
  275. debugPath = m_recovery.GetString("DebugFilePath");
  276. }
  277. Edit_SetText(GetDlgItem(m_hDlg, IDC_STARTUP_CDMP_FILENAME), debugPath);
  278. }
  279. //--------------------------------------------------------------
  280. #define BOOT_INI _T("boot.ini")
  281. void StartupPage::OnBootEdit(void)
  282. {
  283. HKEY hReg;
  284. if (RegOpenKey(HKEY_LOCAL_MACHINE,
  285. _T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup"),
  286. &hReg) == ERROR_SUCCESS)
  287. {
  288. TCHAR szBootDir[4];
  289. DWORD dwType = REG_SZ;
  290. DWORD cbBootDir = sizeof(szBootDir);
  291. if (RegQueryValueEx(hReg,
  292. _T("BootDir"),
  293. NULL,
  294. &dwType,
  295. (LPBYTE)szBootDir,
  296. &cbBootDir) == ERROR_SUCCESS)
  297. {
  298. if (dwType == REG_SZ)
  299. {
  300. TCHAR szBootIni[ARRAYSIZE(szBootDir) + ARRAYSIZE(BOOT_INI)];
  301. lstrcpy(szBootIni, szBootDir);
  302. lstrcat(szBootIni, BOOT_INI);
  303. ShellExecute(m_hDlg,
  304. NULL, // Default verb.
  305. szBootIni, // boot.ini path.
  306. NULL, // No parameters.
  307. NULL, // Default working dir.
  308. SW_SHOWNORMAL);
  309. }
  310. }
  311. RegCloseKey(hReg);
  312. }
  313. }
  314. //--------------------------------------------------------------
  315. #define ONE_MEG 1048576
  316. long StartupPage::GetRAMSizeMB(void)
  317. {
  318. IWbemClassObject *pInst = NULL;
  319. CWbemClassObject memory;
  320. long RAMsize = 0;
  321. if((pInst = FirstInstanceOf("Win32_LogicalMemoryConfiguration")) != NULL)
  322. {
  323. memory = pInst;
  324. long dwTotalPhys = memory.GetLong("TotalPhysicalMemory");
  325. RAMsize = (dwTotalPhys / ONE_MEG) + 1;
  326. }
  327. return RAMsize;
  328. }
  329. //--------------------------------------------------------------
  330. bool StartupPage::IsWorkstationProduct()
  331. {
  332. bool retval = true;
  333. bstr_t name = m_OS.GetString("Name");
  334. if(name.length() > 0)
  335. {
  336. TCHAR sName[200] = {0};
  337. wcscpy(sName, name);
  338. if(wcsstr(sName, L"Server") != NULL)
  339. {
  340. retval = false;
  341. }
  342. }
  343. return retval;
  344. }
  345. //--------------------------------------------------------------
  346. TCHAR szCrashKey[] = TEXT("System\\CurrentControlSet\\Control\\CrashControl");
  347. void StartupPage::Init(HWND hDlg)
  348. {
  349. HWND ComboHwnd;
  350. variant_t array;
  351. DWORD dwDebugInfoType;
  352. // load the startup combobox.
  353. //
  354. // Must enable SE_SYSTEM_ENVIRONMENT_NAME privilege on ia64.
  355. //
  356. #if defined(_IA64_)
  357. m_WbemServices.SetPriv();
  358. #endif // IA64
  359. m_computer.Get("SystemStartupOptions", (variant_t &)array);
  360. #if defined(_IA64_)
  361. m_WbemServices.ClearPriv();
  362. #endif // IA64
  363. if(array.vt & VT_ARRAY)
  364. {
  365. SAFEARRAY *startupArray = V_ARRAY(&array);
  366. long uBound = 1;
  367. BSTR temp;
  368. ComboHwnd = GetDlgItem(hDlg, IDC_STARTUP_SYS_OS);
  369. SafeArrayGetLBound(startupArray, 1, &m_lBound);
  370. SafeArrayGetUBound(startupArray, 1, &uBound);
  371. for (long i = m_lBound; i <= uBound; i++)
  372. {
  373. SafeArrayGetElement(startupArray, &i, &temp);
  374. ComboBox_AddString(ComboHwnd, temp);
  375. }
  376. // the first one is the selection we want (watch out for 'lBound' values)
  377. long idx = m_computer.GetLong("SystemStartupSetting");
  378. ComboBox_SetCurSel(ComboHwnd, idx - m_lBound);
  379. // 3 chars in the second's edit box.
  380. Edit_LimitText(GetDlgItem(hDlg, IDC_STARTUP_SYS_SECONDS), 3);
  381. // limit spinner to 0 - 999.
  382. SendDlgItemMessage (hDlg, IDC_STARTUP_SYS_SECSCROLL,
  383. UDM_SETRANGE, 0, (LPARAM)MAKELONG(999,0));
  384. WCHAR buf[30] = {0};
  385. m_delay = 0;
  386. m_delay = (short)m_computer.GetLong("SystemStartupDelay");
  387. BOOL bChecked = (m_delay != 0);
  388. CheckDlgButton(m_hDlg, IDC_STARTUP_SYS_ENABLECOUNTDOWN, bChecked);
  389. EnableWindow(GetDlgItem (m_hDlg, IDC_STARTUP_SYS_SECONDS), bChecked);
  390. EnableWindow(GetDlgItem (m_hDlg, IDC_STARTUP_SYS_SECSCROLL), bChecked);
  391. Edit_SetText(GetDlgItem(hDlg, IDC_STARTUP_SYS_SECONDS), _itow(m_delay, buf, 10));
  392. }
  393. if( !(array.vt & VT_ARRAY) || !IsCurrentUserAdministrator())
  394. {
  395. EnableWindow(GetDlgItem (m_hDlg, IDC_STARTUP_SYS_OS), FALSE);
  396. EnableWindow(GetDlgItem (m_hDlg, IDC_STARTUP_SYS_ENABLECOUNTDOWN), FALSE);
  397. EnableWindow(GetDlgItem (m_hDlg, IDC_STARTUP_SYS_SECSCROLL), FALSE);
  398. EnableWindow(GetDlgItem (m_hDlg, IDC_STARTUP_SYS_SECONDS), FALSE);
  399. EnableWindow(GetDlgItem (m_hDlg, IDC_STARTUP_SYS_SECONDS_LABEL), FALSE);
  400. } // endif VT_ARRAY failure.
  401. // set all the recovery controls.
  402. // Special Case: Server Product does not want ability to disable logging
  403. // of crashdumps.
  404. WPARAM checkState;
  405. if(IsWorkstationProduct() == true)
  406. {
  407. checkState = (m_recovery.GetBool("WriteToSystemLog") ? BST_CHECKED : BST_UNCHECKED);
  408. Button_SetCheck(GetDlgItem(hDlg, IDC_STARTUP_CDMP_LOG), checkState);
  409. }
  410. else
  411. {
  412. Button_SetCheck(GetDlgItem(hDlg, IDC_STARTUP_CDMP_LOG), BST_CHECKED);
  413. EnableWindow(GetDlgItem(hDlg, IDC_STARTUP_CDMP_LOG),FALSE);
  414. }
  415. //
  416. // Load the dump options combo box.
  417. //
  418. dwDebugInfoType = GetDebugInfoType();
  419. TCHAR szBuf[MAX_PATH]; // The largest string loaded here is 24 chars.
  420. szBuf[0] = _T('\0');
  421. ComboHwnd = GetDlgItem(hDlg, IDC_STARTUP_CDMP_OPTIONS);
  422. LoadString(HINST_THISDLL,
  423. IDS_NO_DUMP,
  424. szBuf,
  425. sizeof(szBuf) / sizeof(TCHAR));
  426. ComboBox_AddString(ComboHwnd, szBuf);
  427. szBuf[0] = _T('\0');
  428. LoadString(HINST_THISDLL,
  429. IDS_COMPLETE_DUMP,
  430. szBuf,
  431. sizeof(szBuf) / sizeof(TCHAR));
  432. ComboBox_AddString(ComboHwnd, szBuf);
  433. szBuf[0] = _T('\0');
  434. LoadString(HINST_THISDLL,
  435. IDS_KERNEL_DUMP,
  436. szBuf,
  437. sizeof(szBuf) / sizeof(TCHAR));
  438. ComboBox_AddString(ComboHwnd, szBuf);
  439. if (!m_bDownlevelTarget)
  440. {
  441. szBuf[0] = _T('\0');
  442. LoadString(HINST_THISDLL,
  443. IDS_SMALL_DUMP,
  444. szBuf,
  445. sizeof(szBuf) / sizeof(TCHAR));
  446. ComboBox_AddString(ComboHwnd, szBuf);
  447. }
  448. ComboBox_SetCurSel(ComboHwnd, dwDebugInfoType);
  449. checkState = (m_recovery.GetBool("SendAdminAlert") ? BST_CHECKED : BST_UNCHECKED);
  450. Button_SetCheck(GetDlgItem(hDlg, IDC_STARTUP_CDMP_SEND), checkState);
  451. bstr_t debugPath;
  452. if (dwDebugInfoType == SMALL_DUMP_OPTION)
  453. {
  454. debugPath = m_recovery.GetString("MiniDumpDirectory");
  455. }
  456. else
  457. {
  458. debugPath = m_recovery.GetString("DebugFilePath");
  459. }
  460. Edit_SetText(GetDlgItem(hDlg, IDC_STARTUP_CDMP_FILENAME), debugPath);
  461. checkState = (m_recovery.GetBool("OverwriteExistingDebugFile") ? BST_CHECKED : BST_UNCHECKED);
  462. Button_SetCheck(GetDlgItem(hDlg, IDC_STARTUP_CDMP_OVERWRITE), checkState);
  463. checkState = (m_recovery.GetBool("AutoReboot") ? BST_CHECKED : BST_UNCHECKED);
  464. Button_SetCheck(GetDlgItem(hDlg, IDC_STARTUP_CDMP_AUTOREBOOT), checkState);
  465. //
  466. // Special case disable the overwrite and logfile controls if no debug
  467. // info option specified.
  468. //
  469. EnableWindow(GetDlgItem(hDlg, IDC_STARTUP_CDMP_FILENAME),
  470. dwDebugInfoType != NO_DUMP_OPTION);
  471. EnableWindow(GetDlgItem(hDlg, IDC_STARTUP_CDMP_OVERWRITE),
  472. dwDebugInfoType != NO_DUMP_OPTION);
  473. //
  474. // Test to determine if the user is an admin.
  475. //
  476. RemoteRegWriteable(szCrashKey, m_writable);
  477. if (!m_writable)
  478. {
  479. // Non-admin - disable controls.
  480. //
  481. EnableWindow(GetDlgItem(hDlg, IDC_STARTUP_CDMP_LOG ), FALSE);
  482. EnableWindow(GetDlgItem(hDlg, IDC_STARTUP_CDMP_SEND ), FALSE);
  483. EnableWindow(GetDlgItem(hDlg, IDC_STARTUP_CDMP_FILENAME), FALSE);
  484. EnableWindow(GetDlgItem(hDlg, IDC_STARTUP_CDMP_OVERWRITE), FALSE);
  485. EnableWindow(GetDlgItem(hDlg, IDC_STARTUP_CDMP_OPTIONS), FALSE);
  486. EnableWindow(GetDlgItem(hDlg, IDC_STARTUP_CDMP_AUTOREBOOT), FALSE);
  487. }
  488. BOOL hasPriv = true, hasMethExecute = false;
  489. if(m_serviceThread && m_serviceThread->LocalConnection())
  490. {
  491. hasPriv = HasPriv(SE_SHUTDOWN_NAME);
  492. }
  493. hasMethExecute = HasPerm(WBEM_METHOD_EXECUTE);
  494. //
  495. // Enable the edit button for local-only.
  496. // Disable boot options label and edit button on i64.
  497. //
  498. #if defined(_IA64_)
  499. EnableWindow(GetDlgItem (m_hDlg, IDC_EDIT_BOOT_INI), FALSE);
  500. EnableWindow(GetDlgItem (m_hDlg, IDC_EDIT_BOOT_INI_LABEL), FALSE);
  501. #else
  502. EnableWindow(GetDlgItem (m_hDlg, IDC_EDIT_BOOT_INI_LABEL),
  503. m_writable ?
  504. (m_serviceThread && m_serviceThread->LocalConnection()) :
  505. FALSE);
  506. EnableWindow(GetDlgItem(hDlg, IDC_EDIT_BOOT_INI),
  507. m_writable ?
  508. (m_serviceThread && m_serviceThread->LocalConnection()) :
  509. FALSE);
  510. #endif // IA64
  511. EnableWindow(GetDlgItem(hDlg, IDC_REBOOT),
  512. m_writable ? (hasPriv && hasMethExecute) : FALSE);
  513. }
  514. //----------------------------------------------------------------------------
  515. DWORD StartupPage::GetDebugInfoType(void)
  516. {
  517. // NB: Whistler on, the win32 provider supports new DebugInfoType
  518. // (none,complete,kernel,small) and MiniDumpDirectory properties.
  519. // Logic is needed to compensate for downlevel machines.
  520. //
  521. // *Important note* The small dump option cannot be supported
  522. // on Win2K since the provider doesn't.
  523. //
  524. DWORD dwDebugInfoType = 0;
  525. if (FAILED(m_recovery.Get("DebugInfoType", (long&)dwDebugInfoType)))
  526. {
  527. // Downlevel or error case.
  528. //
  529. if (!m_bDownlevelTarget)
  530. {
  531. // Bail. We've previously established this isn't downlevel
  532. // but now fail to read the property.
  533. //
  534. return NO_DUMP_OPTION;
  535. }
  536. m_bDownlevelTarget = TRUE;
  537. bool bWriteDebugInfo = FALSE;
  538. if (FAILED(m_recovery.Get("WriteDebugInfo", bWriteDebugInfo)))
  539. {
  540. // Now we're clueless; default to (none).
  541. //
  542. bWriteDebugInfo = FALSE;
  543. dwDebugInfoType = NO_DUMP_OPTION;
  544. }
  545. if (bWriteDebugInfo)
  546. {
  547. bool bKernelDumpOnly;
  548. if (FAILED(m_recovery.Get("KernelDumpOnly", bKernelDumpOnly)))
  549. {
  550. // If we fail to get KernelDumpOnly we must assume complete,
  551. // since they've elected to write debugging info.
  552. //
  553. bKernelDumpOnly = FALSE;
  554. }
  555. if (bKernelDumpOnly)
  556. {
  557. dwDebugInfoType = KERNEL_DUMP_OPTION;
  558. }
  559. else
  560. {
  561. dwDebugInfoType = COMPLETE_DUMP_OPTION;
  562. }
  563. }
  564. }
  565. else
  566. {
  567. m_bDownlevelTarget = FALSE;
  568. }
  569. return dwDebugInfoType;
  570. }
  571. //----------------------------------------------------------------------------
  572. HRESULT StartupPage::PutDebugInfoType(DWORD dwDebugInfoType)
  573. {
  574. HRESULT hr;
  575. if (m_bDownlevelTarget)
  576. {
  577. switch (dwDebugInfoType) // Intentionally verbose - compiler will
  578. // optimize.
  579. {
  580. case NO_DUMP_OPTION:
  581. hr = m_recovery.Put("WriteDebugInfo", (bool)FALSE);
  582. break;
  583. case COMPLETE_DUMP_OPTION:
  584. hr = m_recovery.Put("WriteDebugInfo", (bool)TRUE);
  585. if (SUCCEEDED(hr))
  586. {
  587. hr = m_recovery.Put("KernelDumpOnly", (bool)FALSE);
  588. }
  589. break;
  590. case KERNEL_DUMP_OPTION:
  591. hr = m_recovery.Put("WriteDebugInfo", (bool)TRUE);
  592. if (SUCCEEDED(hr))
  593. {
  594. hr = m_recovery.Put("KernelDumpOnly", (bool)TRUE);
  595. }
  596. break;
  597. case SMALL_DUMP_OPTION:
  598. ATLASSERT(!"Downlevel small dump option!");
  599. hr = E_FAIL;
  600. break;
  601. default:
  602. ATLASSERT(!"Downlevel unknown dump option!");
  603. hr = E_FAIL;
  604. }
  605. }
  606. else
  607. {
  608. hr = m_recovery.Put("DebugInfoType", (long)dwDebugInfoType);
  609. }
  610. return hr;
  611. }
  612. //----------------------------------------------------------------------------
  613. #define MIN_SWAPSIZE 2 // Min swap file size.
  614. int StartupPage::CoreDumpHandleOk(HWND hDlg)
  615. {
  616. DWORD requiredFileSize = 0;
  617. int iRet = RET_NO_CHANGE;
  618. // Validate core dump filename
  619. if(!CoreDumpValidFile(hDlg))
  620. {
  621. SetFocus(GetDlgItem(hDlg, IDC_STARTUP_CDMP_FILENAME));
  622. SetWindowLongPtr(hDlg, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE);
  623. iRet = RET_ERROR;
  624. return(iRet);
  625. }
  626. // If we are to write the dump file, it must be >= sizeof
  627. // phyical memory.
  628. // writing debug info?
  629. HWND ComboHwnd = GetDlgItem(m_hDlg, IDC_STARTUP_CDMP_OPTIONS);
  630. if (ComboBox_GetCurSel(ComboHwnd) != NO_DUMP_OPTION)
  631. {
  632. // go figure my pagefile requirements.
  633. requiredFileSize = ((DWORD)m_memory.GetLong("TotalPhysicalMemory") / 1024) + 1;
  634. }
  635. else if(IsDlgButtonChecked(hDlg, IDC_STARTUP_CDMP_LOG) ||
  636. IsDlgButtonChecked(hDlg, IDC_STARTUP_CDMP_SEND))
  637. {
  638. // I'll need this much to write a reminder to myself to send an
  639. // alert or write to event log once I come back up.
  640. requiredFileSize = MIN_SWAPSIZE;
  641. }
  642. // size of swapfile on the boot partition.
  643. TCHAR bootDrv[4] = {0};
  644. DWORD bootPartitionPageFileSize = GetPageFileSize(bootDrv);
  645. // is it too small?
  646. if(bootPartitionPageFileSize < requiredFileSize)
  647. {
  648. DWORD Ret;
  649. TCHAR szTemp[30] = {0};
  650. // Warn that the dump file may be truncated.
  651. Ret = MsgBoxParam(hDlg, SYSTEM + 29, IDS_TITLE,
  652. MB_ICONEXCLAMATION | MB_YESNO,
  653. bootDrv, _itow(requiredFileSize, szTemp, 10));
  654. if(Ret == IDNO)
  655. {
  656. return RET_ERROR;
  657. }
  658. }
  659. return(iRet);
  660. }
  661. //----------------------------------------------------------------------------
  662. BOOL StartupPage::CoreDumpValidFile(HWND hDlg)
  663. {
  664. TCHAR szInputPath[MAX_PATH] = {0};
  665. TCHAR * pszPath = NULL;
  666. HWND ComboHwnd;
  667. ComboHwnd = GetDlgItem(m_hDlg, IDC_STARTUP_CDMP_OPTIONS);
  668. if (ComboBox_GetCurSel(ComboHwnd) != NO_DUMP_OPTION)
  669. {
  670. /*
  671. * get the filename
  672. */
  673. if(GetDlgItemText(hDlg, IDC_STARTUP_CDMP_FILENAME, szInputPath,
  674. ARRAYSIZE(szInputPath)) == 0)
  675. {
  676. //ERR: enter a filename for the dumpfile.
  677. MsgBoxParam(hDlg, SYSTEM+30, IDS_DISPLAY_NAME, MB_ICONSTOP | MB_OK);
  678. return FALSE;
  679. }
  680. //
  681. // For local paths only, confirm/validate the path. Remote validation
  682. // can be done later - too complicated, if not possible in the
  683. // Whistler timeframe.
  684. //
  685. if (m_serviceThread != NULL && m_serviceThread->LocalConnection())
  686. {
  687. /*
  688. * Expand any environment vars, and then check to make sure it
  689. * is a fully quallified path
  690. */
  691. // if it has a '%' in it, then try to expand it
  692. if (_tcschr(szInputPath, _T('%')) != NULL)
  693. {
  694. TCHAR szExpandedPath[MAX_PATH] = {0};
  695. DWORD cExpanded;
  696. cExpanded = ExpandEnvironmentStrings(szInputPath,
  697. szExpandedPath,
  698. sizeof(szExpandedPath) / sizeof(TCHAR));
  699. if (cExpanded == 0 || _tcschr(szExpandedPath, _T('%')) != NULL)
  700. {
  701. //
  702. // Environment variable name(s) undefined or an error
  703. // occurred during replacement.
  704. //
  705. MsgBoxParam(hDlg, SYSTEM+40, IDS_DISPLAY_NAME,
  706. MB_ICONSTOP | MB_OK );
  707. return FALSE;
  708. }
  709. else if (cExpanded > (sizeof(szExpandedPath) / sizeof(TCHAR)))
  710. {
  711. TCHAR buf[10];
  712. MsgBoxParam(hDlg, SYSTEM+33, IDS_DISPLAY_NAME,
  713. MB_ICONSTOP | MB_OK, _ltow((DWORD)MAX_PATH,
  714. buf,
  715. 10));
  716. return FALSE;
  717. }
  718. else
  719. {
  720. pszPath = szExpandedPath;
  721. }
  722. }
  723. else
  724. {
  725. pszPath = szInputPath;
  726. }
  727. // check to see that it already was cannonicalized
  728. TCHAR drv[_MAX_DRIVE] = {0};
  729. TCHAR path[_MAX_PATH] = {0};
  730. TCHAR fname[_MAX_FNAME] = {0};
  731. // build the instance path.
  732. _wsplitpath(pszPath, drv, path, fname, NULL);
  733. if((_tcslen(drv) == 0) || (_tcslen(path) == 0) ||
  734. (_tcslen(fname) == 0) )
  735. {
  736. // ERR: must be a full path.
  737. MsgBoxParam(hDlg, SYSTEM+34, IDS_DISPLAY_NAME,
  738. MB_ICONSTOP | MB_OK );
  739. return FALSE;
  740. }
  741. /*
  742. * check the drive (don't allow remote)
  743. */
  744. if(!LocalDrive(pszPath))
  745. {
  746. // ERR: Local drives only
  747. MsgBoxParam(hDlg, SYSTEM+31, IDS_DISPLAY_NAME,
  748. MB_ICONSTOP | MB_OK );
  749. return FALSE;
  750. }
  751. /*
  752. * if path is non-existent, tell user and let him decide what to
  753. * do
  754. */
  755. if(!DirExists(pszPath))
  756. {
  757. if(MsgBoxParam(hDlg, SYSTEM+32, IDS_DISPLAY_NAME,
  758. MB_ICONQUESTION | MB_YESNO ) == IDNO)
  759. {
  760. return FALSE;
  761. }
  762. }
  763. }
  764. }
  765. return TRUE;
  766. }
  767. //----------------------------------------------------------------------------
  768. DWORD StartupPage::GetPageFileSize(LPTSTR bootDrv)
  769. {
  770. IWbemClassObject *pInst = NULL;
  771. CWbemClassObject OS;
  772. bstr_t path;
  773. DWORD cMegBootPF = 0;
  774. TCHAR szBootPath[_MAX_PATH] = {0};
  775. szBootPath[0] = 0;
  776. if(m_OS)
  777. {
  778. // WATCH: what's the value if GetWindowsDirectory fails?
  779. path = m_OS.GetString("WindowsDirectory");
  780. if(path.length())
  781. {
  782. // build the instance path.
  783. _tcscpy(szBootPath, _T("Win32_PageFileSetting=\""));
  784. _tcsncat(szBootPath, path, 3);
  785. _tcscat(szBootPath, _T("\\pagefile.sys\""));
  786. // while we're here....
  787. _tcsncpy(bootDrv, path, 3);
  788. m_page = m_WbemServices.GetObject(szBootPath);
  789. if(m_page)
  790. {
  791. // NOTE: We'll need this later to change the swapfile size.
  792. /* long dwTotalPhys = m_page.GetLong("Size");
  793. cMegBootPF = (dwTotalPhys / ONE_MEG) + 1;*/
  794. long dwMinPageFileSize = m_page.GetLong("InitialSize");
  795. cMegBootPF = dwMinPageFileSize;
  796. }
  797. }
  798. }
  799. return cMegBootPF;
  800. }
  801. //-------------------------------------------------------------
  802. BOOL StartupPage::ExpandRemoteEnvPath(LPTSTR szPath, LPTSTR expPath, UINT size)
  803. {
  804. //TODO: really expand the vars.
  805. _tcscpy(szPath, expPath);
  806. return TRUE;
  807. }
  808. //-------------------------------------------------------------
  809. BOOL StartupPage::LocalDrive(LPCTSTR szPath)
  810. {
  811. CWbemClassObject drive;
  812. TCHAR ltr[_MAX_PATH] = {0};
  813. long type = 0;
  814. BOOL retval = FALSE;
  815. __int64 free = 0;
  816. // build the instance path.
  817. _tcscpy(ltr, _T("win32_LogicalDisk=\""));
  818. _tcsncat(ltr, szPath, 2);
  819. _tcscat(ltr, _T("\""));
  820. // save the drive letter for msgs.
  821. _tcsncpy(m_DriveLtr, szPath, 2);
  822. drive = m_WbemServices.GetObject(ltr);
  823. if(drive)
  824. {
  825. type = drive.GetLong("DriveType");
  826. retval = ((type == DRIVE_REMOVABLE) ||
  827. (type == DRIVE_FIXED));
  828. // WARNING: this is only here cuz the LocalDrive check happens
  829. // to come before the freespace check and I didn't want to do
  830. // another GetObject() over a potentially slow network.
  831. free = drive.GetI64("FreeSpace");
  832. m_freeSpace = (DWORD)(free / ONE_MEG);
  833. }
  834. return retval;
  835. }
  836. //-------------------------------------------------------------
  837. BOOL StartupPage::DirExists(LPCTSTR szPath)
  838. {
  839. BOOL exists = TRUE;
  840. CWbemClassObject drive;
  841. TCHAR objPath[_MAX_PATH] = {0}, drv[_MAX_DRIVE] = {0}, path[_MAX_PATH] = {0};
  842. // build the instance path.
  843. _wsplitpath(szPath, drv, path, NULL, NULL);
  844. path[_tcslen(path) - 1] = _T('\0');
  845. _tcscpy(objPath, _T("Win32_Directory=\""));
  846. _tcscat(objPath, drv);
  847. // double the whacks cuz wmi has bad syntax.
  848. TCHAR cooked[_MAX_PATH] = {0};
  849. TCHAR input[_MAX_PATH] = {0};
  850. int len = _tcslen(path);
  851. _tcscpy(input, path);
  852. for(int x = 0; x < len; x++)
  853. {
  854. _tcsncat(cooked, &input[x], 1);
  855. // if its a whack...
  856. if(input[x] == _T('\\'))
  857. {
  858. // have another pleeb.
  859. _tcscat(cooked, _T("\\"));
  860. }
  861. } //endfor
  862. _tcscat(objPath, cooked);
  863. _tcscat(objPath, _T("\""));
  864. drive = m_WbemServices.GetObject(objPath);
  865. exists = (drive.IsNull() ? FALSE : TRUE);
  866. return exists;
  867. }
  868. //-------------------------------------------------------------
  869. BOOL StartupPage::IsAlerterSvcStarted(HWND hDlg)
  870. {
  871. CWbemClassObject service;
  872. bool started = false;
  873. service = m_WbemServices.GetObject(_T("win32_Service=\"Alerter\""));
  874. if(service)
  875. {
  876. started = service.GetBool("started");
  877. if(!started)
  878. {
  879. // get the method signature. dummy wont actually be used.
  880. CWbemClassObject paramCls, inSig, dummy, outSig;
  881. // need to class def to get the method signature.
  882. paramCls = m_WbemServices.GetObject("win32_Service");
  883. if(paramCls)
  884. {
  885. HRESULT hr = paramCls.GetMethod(L"ChangeStartMode", inSig, outSig);
  886. // if got a good signature....
  887. if((bool)inSig)
  888. {
  889. bstr_t path = service.GetString(_T("__PATH"));
  890. inSig.Put(L"StartMode", (const _bstr_t&) L"Automatic");
  891. // make sure the service starts on bootup.
  892. hr = m_WbemServices.ExecMethod(path, L"ChangeStartMode",
  893. inSig, outSig);
  894. // did it work?
  895. if(SUCCEEDED(hr) && (bool)outSig)
  896. {
  897. // NOTE: this guy return STATUS codes.
  898. DWORD autoStart = outSig.GetLong(L"ReturnValue");
  899. if(autoStart == 0)
  900. {
  901. // now actually start the service.
  902. outSig = (IWbemClassObject *)0;
  903. // now call the method.
  904. hr = m_WbemServices.ExecMethod(path, L"StartService",
  905. dummy, outSig);
  906. // did the caller want the ReturnValue.
  907. if(SUCCEEDED(hr) && (bool)outSig)
  908. {
  909. // NOTE: this guy return STATUS codes.
  910. DWORD rv = outSig.GetLong(L"ReturnValue");
  911. started = ((rv == 0) ? true : false);
  912. }
  913. } //endif autoStart
  914. } //endif SUCCEEDED() execmMethod
  915. } //endif (bool)inSig
  916. } //endif paramCls
  917. } //endif !started
  918. if(!started)
  919. {
  920. MsgBoxParam(hDlg, SYSTEM+35, IDS_DISPLAY_NAME, MB_ICONEXCLAMATION );
  921. }
  922. }
  923. return started;
  924. }
  925. //-------------------------------------------------------------
  926. bool StartupPage::Save(void)
  927. {
  928. HRESULT hr;
  929. HWND ComboHwnd;
  930. // if its writeable-- do the work.
  931. if(m_writable)
  932. {
  933. bool computerDirty = false, recoveryDirty = false;
  934. variant_t array;
  935. SAFEARRAY *startupArray = NULL;
  936. VARTYPE varType = VT_ARRAY;
  937. ComboHwnd = GetDlgItem(m_hDlg, IDC_STARTUP_SYS_OS);
  938. // see if the selection changed (watch out for 'lBound' values)
  939. long oldIdx = m_computer.GetLong("SystemStartupSetting");
  940. long newIdx = ComboBox_GetCurSel(ComboHwnd) + m_lBound;
  941. if(oldIdx != newIdx)
  942. {
  943. hr = m_computer.Put("SystemStartupSetting", variant_t((BYTE)newIdx));
  944. computerDirty = true;
  945. }
  946. // see if the delay changed.
  947. WCHAR oldBuf[30], newBuf[30];
  948. short delay = (short)m_computer.GetLong("SystemStartupDelay");
  949. _ltow(delay, oldBuf, 10);
  950. Edit_GetText(GetDlgItem(m_hDlg, IDC_STARTUP_SYS_SECONDS), newBuf, 30);
  951. if(wcscmp(oldBuf, newBuf) != 0)
  952. {
  953. short newVal = (short)_wtol(newBuf);
  954. hr = m_computer.Put("SystemStartupDelay", variant_t(newVal));
  955. computerDirty = true;
  956. }
  957. // evaluate all the recovery controls.
  958. WPARAM oldCheckState = (m_recovery.GetBool("WriteToSystemLog") ? BST_CHECKED : BST_UNCHECKED);
  959. WPARAM newCheckState = Button_GetCheck(GetDlgItem(m_hDlg, IDC_STARTUP_CDMP_LOG));
  960. if(oldCheckState != newCheckState)
  961. {
  962. m_recovery.Put("WriteToSystemLog", (newCheckState == BST_CHECKED? true : false));
  963. recoveryDirty = true;
  964. }
  965. oldCheckState = (m_recovery.GetBool("SendAdminAlert") ? BST_CHECKED : BST_UNCHECKED);
  966. newCheckState = Button_GetCheck(GetDlgItem(m_hDlg, IDC_STARTUP_CDMP_SEND));
  967. // did the state change?
  968. if(oldCheckState != newCheckState)
  969. {
  970. m_recovery.Put("SendAdminAlert", (newCheckState == BST_CHECKED? true : false));
  971. recoveryDirty = true;
  972. // turning ON
  973. if(newCheckState == TRUE)
  974. {
  975. // NOTE: had to move this fragment up to avoid being trapped under the wcsicmp() condition.
  976. // If the Alert button is checked, make sure the alerter service is started.
  977. IsAlerterSvcStarted(m_hDlg);
  978. }
  979. }
  980. ComboHwnd = GetDlgItem(m_hDlg, IDC_STARTUP_CDMP_OPTIONS);
  981. DWORD dwOldDebugInfoType = GetDebugInfoType();
  982. DWORD dwNewDebugInfoType = ComboBox_GetCurSel(ComboHwnd);
  983. if (dwOldDebugInfoType != dwNewDebugInfoType)
  984. {
  985. // I detest this code. You add a member with a return code yet
  986. // nothing here checks them. At least keep the recover dirty
  987. // flag from being set and don't set the modify bit on the
  988. // filename edit control if the put fails.
  989. //
  990. hr = PutDebugInfoType(dwNewDebugInfoType);
  991. if (SUCCEEDED(hr))
  992. {
  993. recoveryDirty = true;
  994. Edit_SetModify(GetDlgItem(m_hDlg, IDC_STARTUP_CDMP_FILENAME),
  995. TRUE);
  996. }
  997. }
  998. //
  999. // Only bother with these if other than "none" debug options is
  1000. // specified.
  1001. //
  1002. if (dwNewDebugInfoType != NO_DUMP_OPTION)
  1003. {
  1004. oldCheckState = (m_recovery.GetBool("OverwriteExistingDebugFile") ? BST_CHECKED : BST_UNCHECKED);
  1005. newCheckState = Button_GetCheck(GetDlgItem(m_hDlg, IDC_STARTUP_CDMP_OVERWRITE));
  1006. if(oldCheckState != newCheckState)
  1007. {
  1008. m_recovery.Put("OverwriteExistingDebugFile", (newCheckState == BST_CHECKED? true : false));
  1009. recoveryDirty = true;
  1010. }
  1011. bstr_t oldDebugPath = m_recovery.GetString(
  1012. (dwOldDebugInfoType == SMALL_DUMP_OPTION) ?
  1013. "MiniDumpDirectory" : "DebugFilePath");
  1014. TCHAR newDebugPath[MAX_PATH];
  1015. Edit_GetText(GetDlgItem(m_hDlg, IDC_STARTUP_CDMP_FILENAME),
  1016. newDebugPath, sizeof(newDebugPath) / sizeof(TCHAR));
  1017. if(_tcsicmp(oldDebugPath,newDebugPath) != 0)
  1018. {
  1019. if(RET_ERROR != CoreDumpHandleOk(m_hDlg))
  1020. {
  1021. m_recovery.Put(
  1022. (dwNewDebugInfoType == SMALL_DUMP_OPTION) ?
  1023. "MiniDumpDirectory" : "DebugFilePath",
  1024. (bstr_t)newDebugPath);
  1025. recoveryDirty = true;
  1026. }
  1027. else
  1028. {
  1029. long wl = GetWindowLongPtr(m_hDlg, DWLP_MSGRESULT);
  1030. if(wl == PSNRET_INVALID_NOCHANGEPAGE)
  1031. {
  1032. return false;
  1033. }
  1034. }
  1035. }
  1036. } //endif 'WriteDebugInfo'
  1037. oldCheckState = (m_recovery.GetBool("AutoReboot") ? BST_CHECKED : BST_UNCHECKED);
  1038. newCheckState = Button_GetCheck(GetDlgItem(m_hDlg, IDC_STARTUP_CDMP_AUTOREBOOT));
  1039. if(oldCheckState != newCheckState)
  1040. {
  1041. m_recovery.Put("AutoReboot", (newCheckState == BST_CHECKED? true : false));
  1042. recoveryDirty = true;
  1043. }
  1044. m_WbemServices.SetPriv();
  1045. // who needs to be written?
  1046. if(computerDirty)
  1047. {
  1048. hr = m_WbemServices.PutInstance(m_computer);
  1049. }
  1050. if(recoveryDirty)
  1051. {
  1052. //
  1053. // Apparently recovery options don't require reboot in Whistler...
  1054. //
  1055. /*
  1056. g_fRebootRequired = TRUE;
  1057. MsgBoxParam(m_hDlg, SYSTEM + 39, IDS_TITLE,
  1058. MB_OK | MB_ICONINFORMATION);
  1059. */
  1060. hr = m_WbemServices.PutInstance(m_recovery);
  1061. }
  1062. m_WbemServices.ClearPriv();
  1063. } //endif m_writable
  1064. return true; // close the dialog.
  1065. }