Source code of Windows XP (NT5)
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.

541 lines
17 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1996 - 1999
  6. //
  7. // File: pagebase.cpp
  8. //
  9. // This file contains the implementation of the CSecurityPage base class.
  10. //
  11. //--------------------------------------------------------------------------
  12. #include "aclpriv.h"
  13. CSecurityPage::CSecurityPage( LPSECURITYINFO psi, SI_PAGE_TYPE siType )
  14. : m_siPageType(siType), m_psi(psi), m_psi2(NULL),m_pei(NULL), m_pObjectPicker(NULL),
  15. m_psoti(NULL),
  16. m_flLastOPOptions(DWORD(-1))
  17. {
  18. ZeroMemory(&m_siObjectInfo, sizeof(m_siObjectInfo));
  19. // Initialize COM incase our client hasn't
  20. m_hrComInit = CoInitialize(NULL);
  21. if (m_psi != NULL)
  22. {
  23. m_psi->AddRef();
  24. // It's normal for this to fail
  25. m_psi->QueryInterface(IID_ISecurityInformation2, (LPVOID*)&m_psi2);
  26. m_psi->QueryInterface(IID_IEffectivePermission, (LPVOID*)&m_pei);
  27. m_psi->QueryInterface(IID_ISecurityObjectTypeInfo, (LPVOID*)&m_psoti);
  28. }
  29. }
  30. CSecurityPage::~CSecurityPage( void )
  31. {
  32. DoRelease(m_psi);
  33. DoRelease(m_psi2);
  34. DoRelease(m_pObjectPicker);
  35. DoRelease(m_pei);
  36. DoRelease(m_psoti);
  37. if (SUCCEEDED(m_hrComInit))
  38. CoUninitialize();
  39. }
  40. HPROPSHEETPAGE
  41. CSecurityPage::CreatePropSheetPage(LPCTSTR pszDlgTemplate, LPCTSTR pszDlgTitle)
  42. {
  43. PROPSHEETPAGE psp;
  44. psp.dwSize = sizeof(psp);
  45. psp.dwFlags = PSP_USECALLBACK ;
  46. psp.hInstance = ::hModule;
  47. psp.pszTemplate = pszDlgTemplate;
  48. psp.pszTitle = pszDlgTitle;
  49. psp.pfnDlgProc = CSecurityPage::_DlgProc;
  50. psp.lParam = (LPARAM)this;
  51. psp.pfnCallback = CSecurityPage::_PSPageCallback;
  52. if (pszDlgTitle != NULL)
  53. psp.dwFlags |= PSP_USETITLE;
  54. return CreatePropertySheetPage(&psp);
  55. }
  56. HRESULT
  57. CSecurityPage::GetObjectPicker(IDsObjectPicker **ppObjectPicker)
  58. {
  59. #if(_WIN32_WINNT >= 0x0500)
  60. HRESULT hr = S_OK;
  61. if (!m_pObjectPicker)
  62. {
  63. if (!m_psi)
  64. return E_UNEXPECTED;
  65. // See if the object supports IDsObjectPicker
  66. hr = m_psi->QueryInterface(IID_IDsObjectPicker, (LPVOID*)&m_pObjectPicker);
  67. // If the object doesn't support IDsObjectPicker, create one.
  68. if (FAILED(hr))
  69. {
  70. hr = CoCreateInstance(CLSID_DsObjectPicker,
  71. NULL,
  72. CLSCTX_INPROC_SERVER,
  73. IID_IDsObjectPicker,
  74. (LPVOID*)&m_pObjectPicker);
  75. }
  76. }
  77. if (ppObjectPicker)
  78. {
  79. *ppObjectPicker = m_pObjectPicker;
  80. // Return a reference (caller must Release)
  81. if (m_pObjectPicker)
  82. m_pObjectPicker->AddRef();
  83. }
  84. return hr;
  85. #else
  86. *ppObjectPicker = NULL;
  87. return E_NOTIMPL;
  88. #endif
  89. }
  90. #if(_WIN32_WINNT >= 0x0500)
  91. //
  92. // Stuff used for initializing the Object Picker below
  93. //
  94. #define DSOP_FILTER_COMMON1 ( DSOP_FILTER_INCLUDE_ADVANCED_VIEW \
  95. | DSOP_FILTER_USERS \
  96. | DSOP_FILTER_UNIVERSAL_GROUPS_SE \
  97. | DSOP_FILTER_GLOBAL_GROUPS_SE \
  98. | DSOP_FILTER_COMPUTERS \
  99. )
  100. #define DSOP_FILTER_COMMON2 ( DSOP_FILTER_COMMON1 \
  101. | DSOP_FILTER_WELL_KNOWN_PRINCIPALS \
  102. | DSOP_FILTER_DOMAIN_LOCAL_GROUPS_SE\
  103. )
  104. #define DSOP_FILTER_COMMON3 ( DSOP_FILTER_COMMON2 \
  105. | DSOP_FILTER_BUILTIN_GROUPS \
  106. )
  107. #define DSOP_FILTER_DL_COMMON1 ( DSOP_DOWNLEVEL_FILTER_USERS \
  108. | DSOP_DOWNLEVEL_FILTER_GLOBAL_GROUPS \
  109. )
  110. #define DSOP_FILTER_DL_COMMON2 ( DSOP_FILTER_DL_COMMON1 \
  111. | DSOP_DOWNLEVEL_FILTER_ALL_WELLKNOWN_SIDS \
  112. )
  113. #define DSOP_FILTER_DL_COMMON3 ( DSOP_FILTER_DL_COMMON2 \
  114. | DSOP_DOWNLEVEL_FILTER_LOCAL_GROUPS \
  115. )
  116. // Same as DSOP_DOWNLEVEL_FILTER_ALL_WELLKNOWN_SIDS, except no CREATOR flags.
  117. // Note that we need to keep this in sync with any object picker changes.
  118. #define DSOP_FILTER_DL_WELLKNOWN ( DSOP_DOWNLEVEL_FILTER_WORLD \
  119. | DSOP_DOWNLEVEL_FILTER_AUTHENTICATED_USER \
  120. | DSOP_DOWNLEVEL_FILTER_ANONYMOUS \
  121. | DSOP_DOWNLEVEL_FILTER_BATCH \
  122. | DSOP_DOWNLEVEL_FILTER_DIALUP \
  123. | DSOP_DOWNLEVEL_FILTER_INTERACTIVE \
  124. | DSOP_DOWNLEVEL_FILTER_NETWORK \
  125. | DSOP_DOWNLEVEL_FILTER_SERVICE \
  126. | DSOP_DOWNLEVEL_FILTER_SYSTEM \
  127. | DSOP_DOWNLEVEL_FILTER_TERMINAL_SERVER \
  128. )
  129. #if 0
  130. { // DSOP_SCOPE_INIT_INFO
  131. cbSize,
  132. flType,
  133. flScope,
  134. { // DSOP_FILTER_FLAGS
  135. { // DSOP_UPLEVEL_FILTER_FLAGS
  136. flBothModes,
  137. flMixedModeOnly,
  138. flNativeModeOnly
  139. },
  140. flDownlevel
  141. },
  142. pwzDcName,
  143. pwzADsPath,
  144. hr // OUT
  145. }
  146. #endif
  147. #define DECLARE_SCOPE(t,f,b,m,n,d) \
  148. { sizeof(DSOP_SCOPE_INIT_INFO), (t), (f|DSOP_SCOPE_FLAG_DEFAULT_FILTER_GROUPS|DSOP_SCOPE_FLAG_DEFAULT_FILTER_USERS), { { (b), (m), (n) }, (d) }, NULL, NULL, S_OK }
  149. // The domain to which the target computer is joined.
  150. // Make 2 scopes, one for uplevel domains, the other for downlevel.
  151. #define JOINED_DOMAIN_SCOPE(f) \
  152. DECLARE_SCOPE(DSOP_SCOPE_TYPE_UPLEVEL_JOINED_DOMAIN,(f),0,(DSOP_FILTER_COMMON2 & ~(DSOP_FILTER_UNIVERSAL_GROUPS_SE|DSOP_FILTER_DOMAIN_LOCAL_GROUPS_SE)),DSOP_FILTER_COMMON2,0), \
  153. DECLARE_SCOPE(DSOP_SCOPE_TYPE_DOWNLEVEL_JOINED_DOMAIN,(f),0,0,0,DSOP_FILTER_DL_COMMON2)
  154. // The domain for which the target computer is a Domain Controller.
  155. // Make 2 scopes, one for uplevel domains, the other for downlevel.
  156. #define JOINED_DOMAIN_SCOPE_DC(f) \
  157. DECLARE_SCOPE(DSOP_SCOPE_TYPE_UPLEVEL_JOINED_DOMAIN,(f),0,(DSOP_FILTER_COMMON3 & ~DSOP_FILTER_UNIVERSAL_GROUPS_SE),DSOP_FILTER_COMMON3,0), \
  158. DECLARE_SCOPE(DSOP_SCOPE_TYPE_DOWNLEVEL_JOINED_DOMAIN,(f),0,0,0,DSOP_FILTER_DL_COMMON3)
  159. // Target computer scope. Computer scopes are always treated as
  160. // downlevel (i.e., they use the WinNT provider).
  161. #define TARGET_COMPUTER_SCOPE(f)\
  162. DECLARE_SCOPE(DSOP_SCOPE_TYPE_TARGET_COMPUTER,(f),0,0,0,DSOP_FILTER_DL_COMMON3)
  163. // The Global Catalog
  164. #define GLOBAL_CATALOG_SCOPE(f) \
  165. DECLARE_SCOPE(DSOP_SCOPE_TYPE_GLOBAL_CATALOG,(f),DSOP_FILTER_COMMON1|DSOP_FILTER_WELL_KNOWN_PRINCIPALS,0,0,0)
  166. // The domains in the same forest (enterprise) as the domain to which
  167. // the target machine is joined. Note these can only be DS-aware
  168. #define ENTERPRISE_SCOPE(f) \
  169. DECLARE_SCOPE(DSOP_SCOPE_TYPE_ENTERPRISE_DOMAIN,(f),DSOP_FILTER_COMMON1,0,0,0)
  170. // Domains external to the enterprise but trusted directly by the
  171. // domain to which the target machine is joined.
  172. #define EXTERNAL_SCOPE(f) \
  173. DECLARE_SCOPE(DSOP_SCOPE_TYPE_EXTERNAL_UPLEVEL_DOMAIN|DSOP_SCOPE_TYPE_EXTERNAL_DOWNLEVEL_DOMAIN,\
  174. (f),DSOP_FILTER_COMMON1,0,0,DSOP_DOWNLEVEL_FILTER_USERS|DSOP_DOWNLEVEL_FILTER_GLOBAL_GROUPS)
  175. // Workgroup scope. Only valid if the target computer is not joined
  176. // to a domain.
  177. #define WORKGROUP_SCOPE(f) \
  178. DECLARE_SCOPE(DSOP_SCOPE_TYPE_WORKGROUP,(f),0,0,0, DSOP_FILTER_DL_COMMON1|DSOP_DOWNLEVEL_FILTER_LOCAL_GROUPS )
  179. //
  180. // Array of Default Scopes
  181. //
  182. static const DSOP_SCOPE_INIT_INFO g_aDefaultScopes[] =
  183. {
  184. JOINED_DOMAIN_SCOPE(DSOP_SCOPE_FLAG_STARTING_SCOPE),
  185. TARGET_COMPUTER_SCOPE(0),
  186. GLOBAL_CATALOG_SCOPE(0),
  187. ENTERPRISE_SCOPE(0),
  188. EXTERNAL_SCOPE(0),
  189. };
  190. //
  191. // Same as above, but without the Target Computer
  192. // Used when the target is a Domain Controller
  193. //
  194. static const DSOP_SCOPE_INIT_INFO g_aDCScopes[] =
  195. {
  196. JOINED_DOMAIN_SCOPE_DC(DSOP_SCOPE_FLAG_STARTING_SCOPE),
  197. GLOBAL_CATALOG_SCOPE(0),
  198. ENTERPRISE_SCOPE(0),
  199. EXTERNAL_SCOPE(0),
  200. };
  201. //
  202. // Array of scopes for standalone machines
  203. //
  204. static const DSOP_SCOPE_INIT_INFO g_aStandAloneScopes[] =
  205. {
  206. //
  207. //On Standalone machine Both User And Groups are selected by default
  208. //
  209. TARGET_COMPUTER_SCOPE(DSOP_SCOPE_FLAG_STARTING_SCOPE|DSOP_SCOPE_FLAG_DEFAULT_FILTER_USERS),
  210. };
  211. //
  212. // Attributes that we want the Object Picker to retrieve
  213. //
  214. static const LPCTSTR g_aszOPAttributes[] =
  215. {
  216. TEXT("ObjectSid"),
  217. };
  218. HRESULT
  219. CSecurityPage::InitObjectPicker(BOOL bMultiSelect)
  220. {
  221. HRESULT hr = S_OK;
  222. DSOP_INIT_INFO InitInfo;
  223. PCDSOP_SCOPE_INIT_INFO pScopes;
  224. ULONG cScopes;
  225. USES_CONVERSION;
  226. TraceEnter(TRACE_MISC, "InitObjectPicker");
  227. hr = GetObjectPicker();
  228. if (FAILED(hr))
  229. TraceLeaveResult(hr);
  230. TraceAssert(m_pObjectPicker != NULL);
  231. InitInfo.cbSize = sizeof(InitInfo);
  232. // We do the DC check at WM_INITDIALOG
  233. InitInfo.flOptions = DSOP_FLAG_SKIP_TARGET_COMPUTER_DC_CHECK;
  234. if (bMultiSelect)
  235. InitInfo.flOptions |= DSOP_FLAG_MULTISELECT;
  236. // flOptions is the only thing that changes from call to call,
  237. // so optimize this by only reinitializing if flOptions changes.
  238. if (m_flLastOPOptions == InitInfo.flOptions)
  239. TraceLeaveResult(S_OK); // Already initialized
  240. m_flLastOPOptions = (DWORD)-1;
  241. pScopes = g_aDefaultScopes;
  242. cScopes = ARRAYSIZE(g_aDefaultScopes);
  243. if (m_bStandalone)
  244. {
  245. cScopes = ARRAYSIZE(g_aStandAloneScopes);
  246. pScopes = g_aStandAloneScopes;
  247. }
  248. else if (m_siObjectInfo.dwFlags & SI_SERVER_IS_DC)
  249. {
  250. cScopes = ARRAYSIZE(g_aDCScopes);
  251. pScopes = g_aDCScopes;
  252. }
  253. //
  254. // The pwzTargetComputer member allows the object picker to be
  255. // retargetted to a different computer. It will behave as if it
  256. // were being run ON THAT COMPUTER.
  257. //
  258. InitInfo.pwzTargetComputer = T2CW(m_siObjectInfo.pszServerName);
  259. InitInfo.cDsScopeInfos = cScopes;
  260. InitInfo.aDsScopeInfos = (PDSOP_SCOPE_INIT_INFO)LocalAlloc(LPTR, sizeof(*pScopes)*cScopes);
  261. if (!InitInfo.aDsScopeInfos)
  262. TraceLeaveResult(E_OUTOFMEMORY);
  263. CopyMemory(InitInfo.aDsScopeInfos, pScopes, sizeof(*pScopes)*cScopes);
  264. InitInfo.cAttributesToFetch = ARRAYSIZE(g_aszOPAttributes);
  265. InitInfo.apwzAttributeNames = (LPCTSTR*)g_aszOPAttributes;
  266. if ((m_siObjectInfo.dwFlags & SI_SERVER_IS_DC) || !(m_siObjectInfo.dwFlags & SI_CONTAINER))
  267. {
  268. for (ULONG i = 0; i < cScopes; i++)
  269. {
  270. // Set the DC name if appropriate
  271. if ((m_siObjectInfo.dwFlags & SI_SERVER_IS_DC) &&
  272. (InitInfo.aDsScopeInfos[i].flType & DSOP_SCOPE_TYPE_UPLEVEL_JOINED_DOMAIN))
  273. {
  274. InitInfo.aDsScopeInfos[i].pwzDcName = InitInfo.pwzTargetComputer;
  275. }
  276. // Turn off CREATOR_OWNER & CREATOR_GROUP for non-containers
  277. if (!(m_siObjectInfo.dwFlags & SI_CONTAINER) &&
  278. (InitInfo.aDsScopeInfos[i].FilterFlags.flDownlevel & DSOP_DOWNLEVEL_FILTER_ALL_WELLKNOWN_SIDS))
  279. {
  280. InitInfo.aDsScopeInfos[i].FilterFlags.flDownlevel &= ~DSOP_DOWNLEVEL_FILTER_ALL_WELLKNOWN_SIDS;
  281. InitInfo.aDsScopeInfos[i].FilterFlags.flDownlevel |= DSOP_FILTER_DL_WELLKNOWN;
  282. }
  283. }
  284. }
  285. hr = m_pObjectPicker->Initialize(&InitInfo);
  286. if (SUCCEEDED(hr))
  287. {
  288. // Remember the Options for next time
  289. m_flLastOPOptions = InitInfo.flOptions;
  290. }
  291. LocalFree(InitInfo.aDsScopeInfos);
  292. TraceLeaveResult(hr);
  293. }
  294. #endif // (_WIN32_WINNT >= 0x0500)
  295. HRESULT
  296. CSecurityPage::GetUserGroup(HWND hDlg, BOOL bMultiSelect, PUSER_LIST *ppUserList)
  297. {
  298. #if(_WIN32_WINNT < 0x0500)
  299. DWORD dwFlags = 0;
  300. if (bMultiSelect)
  301. dwFlags |= GU_MULTI_SELECT;
  302. if (m_siObjectInfo.dwFlags & SI_CONTAINER)
  303. dwFlags |= GU_CONTAINER;
  304. if (m_siObjectInfo.dwFlags & SI_SERVER_IS_DC)
  305. dwFlags |= GU_DC_SERVER;
  306. if (m_siPageType == SI_PAGE_AUDIT)
  307. dwFlags |= GU_AUDIT_HLP;
  308. return ::GetUserGroup(hDlg,
  309. dwFlags,
  310. m_siObjectInfo.pszServerName,
  311. m_bStandalone,
  312. ppUserList);
  313. #else // (_WIN32_WINNT >= 0x0500)
  314. HRESULT hr;
  315. LPDATAOBJECT pdoSelection = NULL;
  316. STGMEDIUM medium = {0};
  317. FORMATETC fe = { (CLIPFORMAT)g_cfDsSelectionList, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
  318. PDS_SELECTION_LIST pDsSelList = NULL;
  319. HCURSOR hcur = NULL;
  320. PSIDCACHE pSidCache = NULL;
  321. UINT idErrorMsg = IDS_GET_USER_FAILED;
  322. TraceEnter(TRACE_MISC, "GetUserGroup");
  323. TraceAssert(ppUserList != NULL);
  324. *ppUserList = NULL;
  325. //
  326. // Create and initialize the Object Picker object
  327. //
  328. hr = InitObjectPicker(bMultiSelect);
  329. FailGracefully(hr, "Unable to initialize Object Picker object");
  330. //
  331. // Create the global sid cache object, if necessary
  332. //
  333. pSidCache = GetSidCache();
  334. if (pSidCache == NULL)
  335. ExitGracefully(hr, E_OUTOFMEMORY, "Unable to create SID cache");
  336. //
  337. // Bring up the object picker dialog
  338. //
  339. hr = m_pObjectPicker->InvokeDialog(hDlg, &pdoSelection);
  340. FailGracefully(hr, "IDsObjectPicker->Invoke failed");
  341. if (S_FALSE == hr)
  342. ExitGracefully(hr, S_FALSE, "IDsObjectPicker->Invoke cancelled by user");
  343. hr = pdoSelection->GetData(&fe, &medium);
  344. FailGracefully(hr, "Unable to get CFSTR_DSOP_DS_SELECTION_LIST from DataObject");
  345. pDsSelList = (PDS_SELECTION_LIST)GlobalLock(medium.hGlobal);
  346. if (!pDsSelList)
  347. ExitGracefully(hr, E_FAIL, "Unable to lock stgmedium.hGlobal");
  348. TraceAssert(pDsSelList->cItems > 0);
  349. Trace((TEXT("%d items selected"), pDsSelList->cItems));
  350. hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));
  351. //
  352. // Lookup the names/sids and cache them
  353. //
  354. if (!pSidCache->LookupNames(pDsSelList,
  355. m_siObjectInfo.pszServerName,
  356. ppUserList,
  357. m_bStandalone))
  358. {
  359. hr = E_FAIL;
  360. idErrorMsg = IDS_SID_LOOKUP_FAILED;
  361. }
  362. SetCursor(hcur);
  363. exit_gracefully:
  364. if (pSidCache)
  365. pSidCache->Release();
  366. if (FAILED(hr))
  367. {
  368. SysMsgPopup(hDlg,
  369. MAKEINTRESOURCE(idErrorMsg),
  370. MAKEINTRESOURCE(IDS_SECURITY),
  371. MB_OK | MB_ICONERROR,
  372. ::hModule,
  373. hr);
  374. }
  375. if (pDsSelList)
  376. GlobalUnlock(medium.hGlobal);
  377. ReleaseStgMedium(&medium);
  378. DoRelease(pdoSelection);
  379. TraceLeaveResult(hr);
  380. #endif // (_WIN32_WINNT >= 0x0500)
  381. }
  382. UINT
  383. CSecurityPage::PSPageCallback(HWND hwnd,
  384. UINT uMsg,
  385. LPPROPSHEETPAGE /*ppsp*/)
  386. {
  387. m_hrLastPSPCallbackResult = E_FAIL;
  388. if (m_psi != NULL)
  389. {
  390. m_hrLastPSPCallbackResult = m_psi->PropertySheetPageCallback(hwnd, uMsg, m_siPageType);
  391. if (m_hrLastPSPCallbackResult == E_NOTIMPL)
  392. m_hrLastPSPCallbackResult = S_OK;
  393. }
  394. return SUCCEEDED(m_hrLastPSPCallbackResult);
  395. }
  396. INT_PTR
  397. CALLBACK
  398. CSecurityPage::_DlgProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  399. {
  400. LPSECURITYPAGE pThis = (LPSECURITYPAGE)GetWindowLongPtr(hDlg, DWLP_USER);
  401. // The following messages arrive before WM_INITDIALOG
  402. // which means pThis is NULL for them. We don't need these
  403. // messages so let DefDlgProc handle them.
  404. //
  405. // WM_SETFONT
  406. // WM_NOTIFYFORMAT
  407. // WM_NOTIFY (LVN_HEADERCREATED)
  408. if (uMsg == WM_INITDIALOG)
  409. {
  410. pThis = (LPSECURITYPAGE)(((LPPROPSHEETPAGE)lParam)->lParam);
  411. SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR)pThis);
  412. if (pThis)
  413. {
  414. if (!pThis->PSPageCallback(hDlg, PSPCB_SI_INITDIALOG, NULL))
  415. pThis->m_bAbortPage = TRUE;
  416. if (pThis->m_psi)
  417. {
  418. BOOL bIsDC = FALSE;
  419. pThis->m_psi->GetObjectInformation(&pThis->m_siObjectInfo);
  420. pThis->m_bStandalone = IsStandalone(pThis->m_siObjectInfo.pszServerName, &bIsDC);
  421. if (bIsDC)
  422. pThis->m_siObjectInfo.dwFlags |= SI_SERVER_IS_DC;
  423. }
  424. }
  425. }
  426. if (pThis != NULL)
  427. return pThis->DlgProc(hDlg, uMsg, wParam, lParam);
  428. return FALSE;
  429. }
  430. UINT
  431. CALLBACK
  432. CSecurityPage::_PSPageCallback(HWND hWnd, UINT uMsg, LPPROPSHEETPAGE ppsp)
  433. {
  434. LPSECURITYPAGE pThis = (LPSECURITYPAGE)ppsp->lParam;
  435. if (pThis)
  436. {
  437. UINT nResult = pThis->PSPageCallback(hWnd, uMsg, ppsp);
  438. switch (uMsg)
  439. {
  440. case PSPCB_CREATE:
  441. if (!nResult)
  442. pThis->m_bAbortPage = TRUE;
  443. break;
  444. case PSPCB_RELEASE:
  445. delete pThis;
  446. break;
  447. }
  448. }
  449. //
  450. // Always return non-zero or else our tab will disappear and whichever
  451. // property page becomes active won't repaint properly. Instead, use
  452. // the m_bAbortPage flag during WM_INITDIALOG to disable the page if
  453. // the callback failed.
  454. //
  455. return 1;
  456. }