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.

1976 lines
61 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1999 - 1999
  6. //
  7. // File: propsht.cpp
  8. //
  9. //--------------------------------------------------------------------------
  10. #include "stdafx.h"
  11. #include "menuitem.h"
  12. #include "amcmsgid.h"
  13. #include "regutil.h"
  14. #include "multisel.h"
  15. #include "ndmgrp.h"
  16. #include <process.h>
  17. #include "cicsthkl.h"
  18. #include "util.h"
  19. /*
  20. * multimon.h is included by stdafx.h, without defining COMPILE_MULTIMON_STUBS
  21. * first. We need to include it again here after defining COMPILE_MULTIMON_STUBS
  22. * so we'll get the stub functions.
  23. */
  24. #if (_WIN32_WINNT < 0x0500)
  25. #define COMPILE_MULTIMON_STUBS
  26. #include <multimon.h>
  27. #endif
  28. // static variables.
  29. CThreadToSheetMap CPropertySheetProvider::TID_LIST;
  30. UINT __stdcall PropertySheetThreadProc(LPVOID dwParam);
  31. HRESULT PropertySheetProc(AMC::CPropertySheet* pSheet);
  32. DWORD SetPrivilegeAttribute(LPCTSTR PrivilegeName, DWORD NewPrivilegeAttribute, DWORD *OldPrivilegeAttribute);
  33. STDMETHODIMP CPropertySheetProvider::Notify(LPPROPERTYNOTIFYINFO pNotify, LPARAM lParam)
  34. {
  35. TRACE_METHOD(CPropertySheetProvider, Update);
  36. if (pNotify == 0)
  37. return E_INVALIDARG;
  38. if (!IsWindow (pNotify->hwnd))
  39. return (E_FAIL);
  40. // Cast it to the internal type and post the message to the window
  41. LPPROPERTYNOTIFYINFO pNotifyT =
  42. reinterpret_cast<LPPROPERTYNOTIFYINFO>(
  43. ::GlobalAlloc(GPTR, sizeof(PROPERTYNOTIFYINFO)));
  44. if (pNotifyT == NULL)
  45. return E_OUTOFMEMORY;
  46. *pNotifyT = *pNotify;
  47. ::PostMessage (pNotifyT->hwnd, MMC_MSG_PROP_SHEET_NOTIFY,
  48. reinterpret_cast<WPARAM>(pNotifyT), lParam);
  49. return S_OK;
  50. }
  51. /////////////////////////////////////////////////////////////////////////////
  52. // CPropertySheet
  53. DEBUG_DECLARE_INSTANCE_COUNTER(CPropertySheet);
  54. namespace AMC
  55. {
  56. CPropertySheet::CPropertySheet()
  57. : m_dwThreadID (GetCurrentThreadId ())
  58. {
  59. CommonConstruct();
  60. DEBUG_INCREMENT_INSTANCE_COUNTER(CPropertySheet);
  61. }
  62. CPropertySheet::~CPropertySheet()
  63. {
  64. DEBUG_DECREMENT_INSTANCE_COUNTER(CPropertySheet);
  65. }
  66. void CPropertySheet::CommonConstruct()
  67. {
  68. TRACE_METHOD(CPropertySheet, CommonConstruct);
  69. memset(&m_pstHeader, 0, sizeof(m_pstHeader));
  70. memset(&m_pages, 0, sizeof(m_pages));
  71. m_hDlg = NULL;
  72. m_msgHook = NULL;
  73. m_hDataWindow = NULL;
  74. m_cookie = 0;
  75. m_lpMasterNode = NULL;
  76. m_pStream = NULL;
  77. m_bModalProp = FALSE;
  78. m_pThreadLocalDataObject = NULL;
  79. m_bAddExtension = FALSE;
  80. m_pMTNode = NULL;
  81. }
  82. BOOL CPropertySheet::Create(LPCTSTR lpszCaption, bool fPropSheet,
  83. MMC_COOKIE cookie, LPDATAOBJECT pDataObject, LONG_PTR lpMasterNode, DWORD dwOptions)
  84. {
  85. TRACE_METHOD(CPropertySheet, Create);
  86. // Save the data object and the master tree node pointer
  87. m_spDataObject = pDataObject;
  88. m_lpMasterNode = pDataObject ? 0 : cookie;
  89. DWORD dwStyle = PSH_DEFAULT;
  90. // is it a property sheet?
  91. if (fPropSheet)
  92. {
  93. if (!(dwOptions & MMC_PSO_NO_PROPTITLE))
  94. dwStyle |= PSH_PROPTITLE;
  95. if (dwOptions & MMC_PSO_NOAPPLYNOW)
  96. dwStyle |= PSH_NOAPPLYNOW;
  97. }
  98. // nope, wizard
  99. else
  100. {
  101. dwStyle |= PSH_PROPTITLE;
  102. if (dwOptions & MMC_PSO_NEWWIZARDTYPE)
  103. dwStyle |= PSH_WIZARD97;
  104. else
  105. dwStyle |= PSH_WIZARD;
  106. }
  107. ASSERT(lpszCaption != NULL);
  108. m_cookie = cookie;
  109. m_pstHeader.dwSize = sizeof(m_pstHeader);
  110. m_pstHeader.dwFlags = dwStyle & ~PSH_HASHELP; // array contains handles
  111. m_pstHeader.hInstance = _Module.GetModuleInstance();
  112. // Assume no bitmaps or palette
  113. m_pstHeader.hbmWatermark = NULL;
  114. m_pstHeader.hbmHeader = NULL;
  115. m_pstHeader.hplWatermark = NULL;
  116. // deep copy the title
  117. m_title = lpszCaption;
  118. m_pstHeader.pszCaption = m_title;
  119. m_pstHeader.nPages = 0;
  120. m_pstHeader.phpage = m_pages;
  121. return TRUE;
  122. }
  123. BOOL CPropertySheet::CreateDataWindow(HWND hParent)
  124. {
  125. TRACE_METHOD(CPropertySheet, CreateDataWindow);
  126. HINSTANCE hInstance = _Module.GetModuleInstance();
  127. WNDCLASS wndClass;
  128. // See if the class is registered and register a new one if not
  129. USES_CONVERSION;
  130. if (!GetClassInfo(hInstance, OLE2T(DATAWINDOW_CLASS_NAME), &wndClass))
  131. {
  132. memset(&wndClass, 0, sizeof(WNDCLASS));
  133. wndClass.lpfnWndProc = DataWndProc;
  134. // This holds the cookie and the HWND for the sheet
  135. wndClass.cbWndExtra = WINDOW_DATA_SIZE;
  136. wndClass.hInstance = hInstance;
  137. wndClass.lpszClassName = OLE2T(DATAWINDOW_CLASS_NAME);
  138. if (!RegisterClass(&wndClass))
  139. return FALSE;
  140. }
  141. m_hDataWindow = CreateWindowEx (WS_EX_APPWINDOW, OLE2T(DATAWINDOW_CLASS_NAME),
  142. NULL, WS_DLGFRAME | WS_BORDER | WS_DISABLED,
  143. CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, NULL, NULL,
  144. hInstance, NULL);
  145. return (m_hDataWindow != 0);
  146. }
  147. HRESULT CPropertySheet::DoSheet(HWND hParent, int nPage)
  148. {
  149. TRACE_METHOD(CPropertySheet, DoSheet);
  150. // A NULL hParent is allowed for property sheets
  151. // but not for wizards
  152. if (hParent != NULL)
  153. {
  154. if (!IsWindow(hParent))
  155. return E_FAIL;
  156. }
  157. else
  158. {
  159. if (IsWizard())
  160. return E_INVALIDARG;
  161. }
  162. if (nPage < 0 || m_dwTid != 0)
  163. {
  164. ASSERT(FALSE); // Object is already running!
  165. return E_FAIL;
  166. }
  167. m_pstHeader.nStartPage = nPage;
  168. m_pstHeader.hwndParent = hParent;
  169. HRESULT hr = S_OK;
  170. if (IsWizard())
  171. {
  172. if (m_pstHeader.nPages > 0)
  173. {
  174. // Don't create a thread, it's a wizard
  175. hr = PropertySheetProc (this);
  176. ASSERT(SUCCEEDED(hr));
  177. }
  178. else
  179. {
  180. hr = E_UNEXPECTED;
  181. }
  182. }
  183. else // modal or modeless prop sheet with data window
  184. {
  185. do
  186. {
  187. // Create data window for a property sheet
  188. if (CreateDataWindow(hParent) == FALSE)
  189. {
  190. hr = E_FAIL;
  191. break;
  192. }
  193. // Setup data in the hidden window
  194. DataWindowData* pData = GetDataWindowData (m_hDataWindow);
  195. pData->cookie = m_cookie;
  196. pData->lpMasterNode = m_lpMasterNode;
  197. pData->spDataObject = m_spDataObject;
  198. pData->spComponent = m_spComponent;
  199. pData->spComponentData = m_spComponentData;
  200. pData->hDlg = NULL;
  201. if (m_bModalProp == TRUE)
  202. {
  203. // Don't create a thread, it's a modal property sheet
  204. hr = PropertySheetProc (this);
  205. ASSERT(SUCCEEDED(hr));
  206. }
  207. else
  208. {
  209. // If non-null data object, marshal interface to stream
  210. if (m_spDataObject != NULL)
  211. {
  212. hr = CoMarshalInterThreadInterfaceInStream(IID_IDataObject,
  213. m_spDataObject, &m_pStream);
  214. /*
  215. * Bug 318357: once it's marshalled, we're done with
  216. * the data object on this thread, release it
  217. */
  218. m_spDataObject = NULL;
  219. if (hr != S_OK)
  220. {
  221. TRACE(_T("DoSheet(): Marshalling Failed (%0x08x)\n"), hr);
  222. break;
  223. }
  224. ASSERT(m_pStream != NULL);
  225. for (int i = 0; i < m_Extenders.size(); i++)
  226. {
  227. IStream* pstm;
  228. hr = CoMarshalInterThreadInterfaceInStream (
  229. IID_IUnknown,
  230. m_Extenders[i],
  231. &pstm);
  232. if (FAILED (hr))
  233. {
  234. TRACE(_T("DoSheet(): Marshalling Failed (%0x08x)\n"), hr);
  235. break;
  236. }
  237. m_ExtendersMarshallStreams.push_back (pstm);
  238. }
  239. BREAK_ON_FAIL (hr);
  240. /*
  241. * Clear out the extenders vector to keep the ref
  242. * counting correct. It'll be repopulated when
  243. * the interfaces are unmarshalled later.
  244. */
  245. ASSERT (m_Extenders.size() == m_ExtendersMarshallStreams.size());
  246. m_Extenders.clear();
  247. }
  248. m_pstHeader.hwndParent = m_hDataWindow;
  249. HANDLE hThread = reinterpret_cast<HANDLE>(
  250. _beginthreadex (NULL, 0, PropertySheetThreadProc,
  251. this, 0, &m_dwTid));
  252. CloseHandle (hThread);
  253. }
  254. } while(0);
  255. }
  256. return hr;
  257. }
  258. void CPropertySheet::GetWatermarks (IExtendPropertySheet2* pExtend2)
  259. {
  260. ASSERT (IsWizard97());
  261. /*
  262. * make sure our resource management objects are empty
  263. *
  264. * Bug 187702: Note that we Detach here rather than calling
  265. * DeleteObject. Yes, it leaks, but it's required for app compat.
  266. */
  267. if (!m_bmpWatermark.IsNull())
  268. m_bmpWatermark.Detach();
  269. if (!m_bmpHeader.IsNull())
  270. m_bmpHeader.Detach();
  271. if (!m_Palette.IsNull())
  272. m_Palette.Detach();
  273. BOOL bStretch = FALSE;
  274. HRESULT hr = pExtend2->GetWatermarks (m_spDataObject,
  275. &m_bmpWatermark.m_hBitmap,
  276. &m_bmpHeader.m_hBitmap,
  277. &m_Palette.m_hPalette,
  278. &bStretch);
  279. /*
  280. * If we failed to get watermark info, revert to an old-style
  281. * wizard for MMC 1.1 compatibility.
  282. */
  283. if (FAILED (hr))
  284. {
  285. ForceOldStyleWizard();
  286. return;
  287. }
  288. if (!m_bmpWatermark.IsNull())
  289. {
  290. m_pstHeader.dwFlags |= (PSH_USEHBMWATERMARK | PSH_WATERMARK);
  291. m_pstHeader.hbmWatermark = m_bmpWatermark;
  292. }
  293. if (!m_bmpHeader.IsNull())
  294. {
  295. m_pstHeader.dwFlags |= (PSH_USEHBMHEADER | PSH_HEADER);
  296. m_pstHeader.hbmHeader = m_bmpHeader;
  297. }
  298. if (!m_Palette.IsNull())
  299. {
  300. m_pstHeader.dwFlags |= PSH_USEHPLWATERMARK;
  301. m_pstHeader.hplWatermark = m_Palette;
  302. }
  303. if (bStretch)
  304. m_pstHeader.dwFlags |= PSH_STRETCHWATERMARK;
  305. }
  306. BOOL CPropertySheet::AddExtensionPages()
  307. {
  308. TRACE_METHOD(CPropertySheet, AddExtensionPages);
  309. #ifdef EXTENSIONS_CANNOT_ADD_PAGES_IF_PRIMARY_DOESNT
  310. if (m_pstHeader.nPages == 0)
  311. {
  312. ASSERT(m_pstHeader.nPages != 0);
  313. return FALSE;
  314. }
  315. #endif
  316. POSITION pos;
  317. int nCount = m_pstHeader.nPages;
  318. pos = m_PageList.GetHeadPosition();
  319. if (pos != NULL)
  320. {
  321. while(pos && nCount < MAXPROPPAGES)
  322. {
  323. m_pages[nCount++] =
  324. reinterpret_cast<HPROPSHEETPAGE>(m_PageList.GetNext(pos));
  325. }
  326. ASSERT(nCount < MAXPROPPAGES);
  327. m_pstHeader.nPages = nCount;
  328. // Empty the list for the extensions
  329. m_PageList.RemoveAll();
  330. }
  331. return TRUE;
  332. }
  333. void CPropertySheet::AddNoPropsPage ()
  334. {
  335. m_pages[m_pstHeader.nPages++] = m_NoPropsPage.Create();
  336. }
  337. LRESULT CPropertySheet::OnCreate(CWPRETSTRUCT* pMsg)
  338. {
  339. if (m_hDlg != 0)
  340. return 0;
  341. // Assign the hwnd in the object
  342. // Get the class name of the window to make sure it's the propsheet
  343. TCHAR name[256];
  344. if (GetClassName(pMsg->hwnd, name, sizeof(name)/sizeof(TCHAR)))
  345. {
  346. ASSERT(m_hDlg == 0);
  347. if (_tcsncmp(name, _T("#32770"), 6) == 0)
  348. {
  349. m_hDlg = pMsg->hwnd;
  350. }
  351. }
  352. return 0;
  353. }
  354. static RECT s_rectLastPropertySheetPos;
  355. static bool s_bLastPropertySheetPosValid = false;
  356. void SetLastPropertySheetPosition(HWND hWndPropertySheet)
  357. {
  358. ::GetWindowRect(hWndPropertySheet, &s_rectLastPropertySheetPos);
  359. }
  360. /*+-------------------------------------------------------------------------*
  361. *
  362. * SetPropertySheetPosition
  363. *
  364. * PURPOSE: The algorithm for positioning a property sheet. (See bug 8584)
  365. * 1) The first property sheet in an mmc process is always brought up centered on the MMC application window. If it falls off the screen, it is
  366. * displayed at the top-left.
  367. * 2) MMC stores the initial position of the last property sheet that was brought up, or the final position of the last property sheet that was destroyed.
  368. * 3) When a new property sheet is brought up, mmc starts by using the rectangle stored in (2) above.
  369. * 4) If there is already a property sheet from the same MMC instance in this position, MMC staggers the position down and to the right.
  370. * 5) Step 4 is repeated until a positon is located that does not collide with any other property sheets from the same thread.
  371. * 6) If the property sheet in this new postion does not completely lie on the screen, it is displayed at the top-left of the desktop.
  372. *
  373. * PARAMETERS:
  374. * HWND hWndPropertySheet :
  375. *
  376. * RETURNS:
  377. * void
  378. *
  379. *+-------------------------------------------------------------------------*/
  380. void SetPropertySheetPosition(HWND hWndPropertySheet)
  381. {
  382. // Find the height and width of the property sheet for later use
  383. RECT rectCurrentPos;
  384. ::GetWindowRect(hWndPropertySheet, &rectCurrentPos); //get the current position
  385. int width = rectCurrentPos.right - rectCurrentPos.left;
  386. int height = rectCurrentPos.bottom - rectCurrentPos.top;
  387. // Initialize the position
  388. if (!s_bLastPropertySheetPosValid)
  389. {
  390. s_rectLastPropertySheetPos.top = 0;
  391. s_rectLastPropertySheetPos.left = 0;
  392. s_rectLastPropertySheetPos.bottom = 0;
  393. s_rectLastPropertySheetPos.right = 0;
  394. CScopeTree * pScopeTree = CScopeTree::GetScopeTree();
  395. if(pScopeTree) // if pScopeTree == NULL, can still execute gracefully by using zero rect.
  396. {
  397. HWND hWndMain = pScopeTree->GetMainWindow();
  398. RECT rectTemp;
  399. GetWindowRect(hWndMain, &rectTemp);
  400. // center the property sheet on the center of the main window
  401. s_rectLastPropertySheetPos.top = (rectTemp.top + rectTemp.bottom)/2 - (height/2);
  402. s_rectLastPropertySheetPos.left = (rectTemp.left + rectTemp.right )/2 - (width/2);
  403. s_rectLastPropertySheetPos.right = s_rectLastPropertySheetPos.left + width; // these last two are not strictly needed
  404. s_rectLastPropertySheetPos.bottom = s_rectLastPropertySheetPos.top + height; // but are here for consistency.
  405. }
  406. s_bLastPropertySheetPosValid = true;
  407. }
  408. RECT rectNewPos = s_rectLastPropertySheetPos; // try this initially
  409. int offset = GetSystemMetrics(SM_CYDLGFRAME) + GetSystemMetrics(SM_CYCAPTION); // how much to stagger the windows by
  410. bool bPosOK = true;
  411. HWND hWnd = NULL;
  412. typedef std::set<UINT> t_set;
  413. t_set s;
  414. // collect all the window positions into a vector
  415. while (1)
  416. {
  417. // make sure there isn't a property sheet already at this location
  418. hWnd = ::FindWindowEx(NULL, hWnd, MAKEINTATOM(32770), NULL);
  419. // No windows found, use the position
  420. if (hWnd == NULL)
  421. break;
  422. // Check if the window belongs to the current process
  423. DWORD dwPid;
  424. ::GetWindowThreadProcessId(hWnd, &dwPid);
  425. if (dwPid != ::GetCurrentProcessId())
  426. continue;
  427. if(hWnd == hWndPropertySheet) // don't check against the same window.
  428. continue;
  429. RECT rectPos;
  430. ::GetWindowRect(hWnd, &rectPos);
  431. // look only for possible collisions starting from the point and to the right and below it.
  432. if(rectPos.top >= rectNewPos.top)
  433. {
  434. UINT offsetTemp = (rectPos.top - rectNewPos.top) / offset;
  435. if(rectPos.left != (offsetTemp * offset + rectNewPos.left) )
  436. continue;
  437. if(rectPos.top != (offsetTemp * offset + rectNewPos.top) )
  438. continue;
  439. s.insert(offsetTemp);
  440. }
  441. }
  442. // at this point s contains all the offsets that can collide.
  443. for(UINT i = 0; /*empty*/ ; i++)
  444. {
  445. if(s.find(i) == s.end()) // located the end
  446. break;
  447. }
  448. rectNewPos.left += i*offset;
  449. rectNewPos.top += i*offset;
  450. rectNewPos.bottom = rectNewPos.top + height;
  451. rectNewPos.right = rectNewPos.left + width;
  452. /*
  453. * Bug 211145: make sure the new position is within the work area
  454. */
  455. HMONITOR hmon = MonitorFromPoint (WTL::CPoint (rectNewPos.left,
  456. rectNewPos.top),
  457. MONITOR_DEFAULTTONEAREST);
  458. MONITORINFO mi = { sizeof (mi) };
  459. WTL::CRect rectWorkArea;
  460. /*
  461. * if we could get the info for the monitor containing the window origin,
  462. * use it's workarea as the bounding rectangle; otherwise get the workarea
  463. * for the default monitor; if that failed as well, default to 640x480
  464. */
  465. if (GetMonitorInfo (hmon, &mi))
  466. rectWorkArea = mi.rcWork;
  467. else if (!SystemParametersInfo (SPI_GETWORKAREA, 0, &rectWorkArea, false))
  468. rectWorkArea.SetRect (0, 0, 639, 479);
  469. if (rectNewPos.left < rectWorkArea.left)
  470. {
  471. rectNewPos.left = rectWorkArea.left;
  472. rectNewPos.right = rectNewPos.left + width;
  473. }
  474. if (rectNewPos.top < rectWorkArea.top)
  475. {
  476. rectNewPos.top = rectWorkArea.top;
  477. rectNewPos.bottom = rectNewPos.top + height;
  478. }
  479. // is the window completely visible?
  480. POINT ptTopLeft = {rectNewPos.left, rectNewPos.top};
  481. POINT ptBottomRight = {rectNewPos.right, rectNewPos.bottom};
  482. if( (MonitorFromPoint(ptTopLeft, MONITOR_DEFAULTTONULL) == NULL) ||
  483. (MonitorFromPoint(ptBottomRight, MONITOR_DEFAULTTONULL) == NULL))
  484. {
  485. // the property sheet is not completely visible. Move it to the top-left.
  486. rectNewPos.left = rectWorkArea.left;
  487. rectNewPos.top = rectWorkArea.top;
  488. rectNewPos.bottom = rectNewPos.top + height;
  489. rectNewPos.right = rectNewPos.left + width;
  490. }
  491. MoveWindow(hWndPropertySheet, rectNewPos.left, rectNewPos.top, width, height, true /*bRepaint*/);
  492. // save the position
  493. s_rectLastPropertySheetPos = rectNewPos;
  494. }
  495. LRESULT CPropertySheet::OnInitDialog(CWPRETSTRUCT* pMsg)
  496. {
  497. if (m_hDlg != pMsg->hwnd)
  498. return 1;
  499. if (!IsWizard())
  500. {
  501. SetPropertySheetPosition(m_hDlg);
  502. ASSERT (IsWindow (m_hDataWindow));
  503. // Add data dialog hanndle to hidden window
  504. if (IsWindow (m_hDataWindow))
  505. {
  506. DataWindowData* pData = GetDataWindowData (m_hDataWindow);
  507. pData->hDlg = m_hDlg;
  508. // Create the marshalled data object pointer from stream
  509. if (m_pStream != NULL)
  510. {
  511. // Unmarshall the Data object
  512. HRESULT hr = ::CoGetInterfaceAndReleaseStream(m_pStream, IID_IDataObject,
  513. reinterpret_cast<void**>(&m_pThreadLocalDataObject));
  514. ASSERT(hr == S_OK);
  515. TRACE(_T("WM_INITDIALOG: Unmarshalled returned %X\n"), hr);
  516. for (int i = 0; i < m_ExtendersMarshallStreams.size(); i++)
  517. {
  518. IUnknown* pUnk = NULL;
  519. hr = CoGetInterfaceAndReleaseStream (
  520. m_ExtendersMarshallStreams[i],
  521. IID_IUnknown,
  522. reinterpret_cast<void**>(&pUnk));
  523. ASSERT (hr == S_OK);
  524. ASSERT (pUnk != NULL);
  525. TRACE(_T("WM_INITDIALOG: Unmarshalled returned %X\n"), hr);
  526. /*
  527. * m_Extenders is a collection of smart pointers, which
  528. * will AddRef. We don't need to AddRef an interface
  529. * that's returned to us, so Release here to keep the
  530. * bookkeeping straight.
  531. */
  532. m_Extenders.push_back (pUnk);
  533. if (pUnk)
  534. pUnk->Release();
  535. }
  536. ASSERT (m_Extenders.size() == m_ExtendersMarshallStreams.size());
  537. m_ExtendersMarshallStreams.clear();
  538. }
  539. }
  540. /*
  541. * Bug 215593: If we're running at low resolution we don't want
  542. * more than two rows of tabs. If we find that is the case, use
  543. * a single scrolling row of tabs instead of multiple rows.
  544. */
  545. if (GetSystemMetrics (SM_CXSCREEN) < 800)
  546. {
  547. WTL::CTabCtrl wndTabCtrl = PropSheet_GetTabControl (m_hDlg);
  548. ASSERT (wndTabCtrl.m_hWnd != NULL);
  549. /*
  550. * if we have more than two rows, remove the multiline style
  551. */
  552. if (wndTabCtrl.GetRowCount() > 2)
  553. wndTabCtrl.ModifyStyle (TCS_MULTILINE, 0);
  554. }
  555. // Create tooltip control for the property sheet.
  556. do
  557. {
  558. if (IsWizard())
  559. break;
  560. HWND hWnd = m_PropToolTips.Create(m_hDlg);
  561. ASSERT(hWnd);
  562. if (NULL == hWnd)
  563. break;
  564. TOOLINFO ti;
  565. RECT rc;
  566. GetWindowRect(m_hDlg, &rc);
  567. // Set the tooltip for property sheet title.
  568. // Set the control for a rectangle from (0, - (titlewidth))
  569. // to (right-end,0)
  570. ti.cbSize = sizeof(TOOLINFO);
  571. ti.uFlags = TTF_SUBCLASS;
  572. ti.hwnd = m_hDlg;
  573. // This is the id used for the tool tip control for property sheet
  574. // title. So when we get TTN_NEEDTEXT we can identify if the text
  575. // is for title or a tab.
  576. ti.uId = PROPSHEET_TITLE_TOOLTIP_ID;
  577. ti.rect.left = 0;
  578. ti.rect.right = rc.right - rc.left;
  579. ti.rect.top = -GetSystemMetrics(SM_CXSIZE);
  580. ti.rect.bottom = 0;
  581. ti.hinst = _Module.GetModuleInstance();
  582. ti.lpszText = LPSTR_TEXTCALLBACK ;
  583. m_PropToolTips.AddTool(&ti);
  584. m_PropToolTips.Activate(TRUE);
  585. // Now add tooltips for the tab control
  586. WTL::CTabCtrl wndTabCtrl = PropSheet_GetTabControl (m_hDlg);
  587. ASSERT (wndTabCtrl.m_hWnd != NULL);
  588. if (NULL == wndTabCtrl.m_hWnd)
  589. break;
  590. ::ZeroMemory(&ti, sizeof(TOOLINFO));
  591. ti.cbSize = sizeof(TOOLINFO);
  592. ti.uFlags = TTF_SUBCLASS;
  593. ti.hwnd = wndTabCtrl.m_hWnd;
  594. ti.uId = (LONG)::GetDlgCtrlID((HWND)wndTabCtrl.m_hWnd);
  595. ti.hinst = _Module.GetModuleInstance();
  596. ti.lpszText = LPSTR_TEXTCALLBACK;
  597. //define the rect area (for each tab) and the tool tip associated withit
  598. for (int i=0; i<wndTabCtrl.GetItemCount(); i++)
  599. {
  600. // get rect area of each tab
  601. wndTabCtrl.GetItemRect(i, &rc);
  602. POINT p[2];
  603. p[0].x = rc.left;
  604. p[0].y = rc.top;
  605. p[1].x = rc.right;
  606. p[1].y = rc.bottom;
  607. // Map the co-ordinates relative to property sheet.
  608. MapWindowPoints(wndTabCtrl.m_hWnd, m_hDlg, p, 2);
  609. ti.rect.left = p[0].x;
  610. ti.rect.top = p[0].y;
  611. ti.rect.right = p[1].x;
  612. ti.rect.bottom = p[1].y ;
  613. m_PropToolTips.AddTool(&ti);
  614. }
  615. m_PropToolTips.Activate(TRUE);
  616. } while (FALSE);
  617. }
  618. // Add third party extension
  619. if (m_bAddExtension)
  620. {
  621. //AddExtensionPages();
  622. m_bAddExtension = FALSE;
  623. }
  624. return 0;
  625. }
  626. LRESULT CPropertySheet::OnNcDestroy(CWPRETSTRUCT* pMsg)
  627. {
  628. if (m_hDlg != pMsg->hwnd)
  629. return 1;
  630. SetLastPropertySheetPosition(m_hDlg);
  631. ASSERT(m_msgHook != NULL);
  632. UnhookWindowsHookEx(m_msgHook);
  633. // Clean up the key and the object
  634. CPropertySheetProvider::TID_LIST.Remove(GetCurrentThreadId());
  635. if (m_pThreadLocalDataObject != NULL)
  636. m_pThreadLocalDataObject->Release();
  637. // Only Property Sheets have Data windows
  638. if (!IsWizard())
  639. {
  640. // Close the data window
  641. ASSERT(IsWindow(m_hDataWindow));
  642. SendMessage(m_hDataWindow, WM_CLOSE, 0, 0);
  643. }
  644. delete this;
  645. return 0;
  646. }
  647. LRESULT CPropertySheet::OnWMNotify(CWPRETSTRUCT* pMsg)
  648. {
  649. LPNMHDR pHdr = (LPNMHDR)pMsg->lParam;
  650. if (NULL == pHdr)
  651. return 0;
  652. switch(pHdr->code)
  653. {
  654. case TTN_NEEDTEXT:
  655. {
  656. /*
  657. * we only want to do our thing if the Ctrl key is
  658. * pressed, so bail if it's not
  659. */
  660. if (!(GetKeyState(VK_CONTROL) < 0))
  661. break;
  662. // Make sure our property sheet tooltip sent this message.
  663. if (pHdr->hwndFrom != ((CWindow)m_PropToolTips).m_hWnd)
  664. break;
  665. LPTOOLTIPTEXT lpttt = (LPTOOLTIPTEXT)pMsg->lParam;
  666. lpttt->lpszText = NULL;
  667. // This is the id used for the tool tip control for property sheet
  668. // title. So check if the text is for title or a tab.
  669. if (pHdr->idFrom == PROPSHEET_TITLE_TOOLTIP_ID)
  670. lpttt->lpszText = (LPTSTR)m_PropToolTips.GetFullPath();
  671. else
  672. {
  673. // A tab is selected, find out which tab.
  674. HWND hTabCtrl = PropSheet_GetTabControl(m_hDlg);
  675. if (NULL == hTabCtrl)
  676. break;
  677. POINT pt;
  678. GetCursorPos(&pt);
  679. ScreenToClient(hTabCtrl, &pt);
  680. TCHITTESTINFO tch;
  681. tch.flags = TCHT_ONITEM;
  682. tch.pt = pt;
  683. int n = TabCtrl_HitTest(hTabCtrl, &tch);
  684. if ((-1 == n) || (m_PropToolTips.GetNumPages() <= n) )
  685. break;
  686. lpttt->lpszText = (LPTSTR)m_PropToolTips.GetSnapinPage(n);
  687. }
  688. }
  689. break;
  690. default:
  691. break;
  692. }
  693. return 0;
  694. }
  695. void CPropertySheet::ForceOldStyleWizard ()
  696. {
  697. /*
  698. * We shouldn't be forcing old-style wizard behavior on a
  699. * property sheet that's not already a wizard.
  700. */
  701. ASSERT (IsWizard());
  702. m_pstHeader.dwFlags |= PSH_WIZARD;
  703. m_pstHeader.dwFlags &= ~PSH_WIZARD97;
  704. /*
  705. * The sheet should still be a wizard, but not a Wiz97 wizard.
  706. */
  707. ASSERT ( IsWizard());
  708. ASSERT (!IsWizard97());
  709. }
  710. }
  711. DEBUG_DECLARE_INSTANCE_COUNTER(CPropertySheetProvider);
  712. CPropertySheetProvider::CPropertySheetProvider()
  713. {
  714. TRACE_METHOD(CPropertySheetProvider, CPropertySheetProvider);
  715. m_pSheet = NULL;
  716. DEBUG_INCREMENT_INSTANCE_COUNTER(CPropertySheetProvider);
  717. }
  718. CPropertySheetProvider::~CPropertySheetProvider()
  719. {
  720. TRACE_METHOD(CPropertySheetProvider, ~CPropertySheetProvider);
  721. m_pSheet = NULL;
  722. DEBUG_DECREMENT_INSTANCE_COUNTER(CPropertySheetProvider);
  723. }
  724. ///////////////////////////////////////////////////////////////////////////////
  725. // IPropertySheetProvider
  726. //
  727. BOOL CALLBACK MyEnumThreadWindProc (HWND current, LPARAM lParam)
  728. { // this enumerates non-child-windows created by a given thread
  729. if (!IsWindow (current))
  730. return TRUE; // this shouldn't happen, but does!!!
  731. if (!IsWindowVisible (current)) // if they've explicitly hidden a window,
  732. return TRUE; // don't set focus to it.
  733. // we'll return hwnd in here
  734. HWND * hwnd = (HWND *)lParam;
  735. // don't bother returning property sheet dialog window handle
  736. if (*hwnd == current)
  737. return TRUE;
  738. // also, don't return OleMainThreadWndClass window
  739. TCHAR szCaption[14];
  740. GetWindowText (current, szCaption, 14);
  741. if (!lstrcmp (szCaption, _T("OLEChannelWnd")))
  742. return TRUE;
  743. // anything else will do
  744. *hwnd = current;
  745. return FALSE;
  746. }
  747. STDMETHODIMP CPropertySheetProvider::FindPropertySheet(MMC_COOKIE cookie,
  748. LPCOMPONENT lpComponent,
  749. LPDATAOBJECT lpDataObject)
  750. {
  751. return FindPropertySheetEx(cookie, lpComponent, NULL, lpDataObject);
  752. }
  753. STDMETHODIMP
  754. CPropertySheetProvider::FindPropertySheetEx(MMC_COOKIE cookie, LPCOMPONENT lpComponent,
  755. LPCOMPONENTDATA lpComponentData, LPDATAOBJECT lpDataObject)
  756. {
  757. TRACE_METHOD(CPropertySheetProvider, FindPropertySheet);
  758. using AMC::CPropertySheet;
  759. if ((cookie == NULL) && ( (lpComponent == NULL && lpComponentData == NULL) || lpDataObject == NULL))
  760. {
  761. ASSERT(FALSE);
  762. return E_POINTER;
  763. }
  764. HRESULT hr = S_FALSE;
  765. HWND hWnd = NULL;
  766. while (1)
  767. {
  768. USES_CONVERSION;
  769. hWnd = FindWindowEx(NULL, hWnd, OLE2T(DATAWINDOW_CLASS_NAME), NULL);
  770. // No windows found
  771. if (hWnd == NULL)
  772. {
  773. hr = S_FALSE;
  774. break;
  775. }
  776. // Check if the window belongs to the current process
  777. DWORD dwPid;
  778. ::GetWindowThreadProcessId(hWnd, &dwPid);
  779. if (dwPid != ::GetCurrentProcessId())
  780. continue;
  781. // Get the extra bytes and compare the data objects
  782. ASSERT(GetClassLong(hWnd, GCL_CBWNDEXTRA) == WINDOW_DATA_SIZE);
  783. ASSERT(IsWindow(hWnd));
  784. // The original Data object can be NULL if there isn't an IComponent.
  785. // this occurs with built-in nodes(i.e. nodes owned by the console)
  786. DataWindowData* pData = GetDataWindowData (hWnd);
  787. // Ask the snapin of the the two data objects are the same
  788. // Does this one match?
  789. if (lpComponent != NULL)
  790. {
  791. ASSERT(pData->spDataObject != NULL);
  792. hr = lpComponent->CompareObjects(lpDataObject, pData->spDataObject);
  793. }
  794. else
  795. {
  796. // Although the NULL cookie is the static folder, the cookie stored in the data
  797. // window is the pointer to the master tree node. This is why it is not null.
  798. ASSERT(cookie != NULL);
  799. // Compare the cookies if it's a scope item
  800. if (pData->cookie == cookie)
  801. hr = S_OK;
  802. }
  803. // bring the property sheet to the foreground
  804. // note: hDlg can be null if the secondary thread has not finished creating
  805. // the property sheet
  806. if (hr == S_OK)
  807. {
  808. if (pData->hDlg != NULL)
  809. {
  810. //
  811. // Found previous instance, restore the
  812. // window plus its popups
  813. //
  814. SetActiveWindow (pData->hDlg);
  815. SetForegroundWindow (pData->hDlg);
  816. // grab first one that isn't property sheet dialog
  817. HWND hwnd = pData->hDlg;
  818. EnumThreadWindows(::GetWindowThreadProcessId(pData->hDlg, NULL),
  819. MyEnumThreadWindProc, (LPARAM)&hwnd);
  820. if (hwnd)
  821. {
  822. SetActiveWindow (hwnd);
  823. SetForegroundWindow (hwnd);
  824. }
  825. }
  826. break;
  827. }
  828. }
  829. return hr;
  830. }
  831. STDMETHODIMP
  832. CPropertySheetProvider::CreatePropertySheet(
  833. LPCWSTR title,
  834. unsigned char bType,
  835. MMC_COOKIE cookie,
  836. LPDATAOBJECT pDataObject,
  837. DWORD dwOptions)
  838. {
  839. return CreatePropertySheetEx(title, bType, cookie, pDataObject, NULL, dwOptions);
  840. }
  841. STDMETHODIMP CPropertySheetProvider::CreatePropertySheetEx(LPCWSTR title, unsigned char bType, MMC_COOKIE cookie,
  842. LPDATAOBJECT pDataObject, LONG_PTR lpMasterTreeNode, DWORD dwOptions)
  843. {
  844. TRACE_METHOD(CPropertySheetProvider, CreatePropertySheet);
  845. using AMC::CPropertySheet;
  846. if (!title)
  847. return E_POINTER;
  848. // You called CreatePropertySheet more than once.
  849. // Either release the object or call ::Show(-1, 0)
  850. // to free the resources
  851. if (m_pSheet != NULL)
  852. {
  853. ASSERT(FALSE);
  854. return E_UNEXPECTED;
  855. }
  856. // Create the actual sheet and the list for page management
  857. m_pSheet = new CPropertySheet();
  858. // Add it to the list of sheets and add it to the list
  859. USES_CONVERSION;
  860. m_pSheet->Create(OLE2CT(title), bType, cookie, pDataObject, lpMasterTreeNode, dwOptions);
  861. return S_OK;
  862. }
  863. STDMETHODIMP CPropertySheetProvider::Show(LONG_PTR window, int page)
  864. {
  865. TRACE_METHOD(CPropertySheetProvider, Show);
  866. return ShowEx(reinterpret_cast<HWND>(window), page, FALSE);
  867. }
  868. STDMETHODIMP CPropertySheetProvider::ShowEx(HWND hwnd, int page, BOOL bModalPage)
  869. {
  870. TRACE_METHOD(CPropertySheetProvider, ShowEx);
  871. HRESULT hr = E_UNEXPECTED;
  872. if (page < 0)
  873. {
  874. hr = E_INVALIDARG;
  875. goto exit;
  876. }
  877. if (m_pSheet == NULL)
  878. {
  879. // didn't call Create()
  880. ASSERT(FALSE);
  881. goto exit;
  882. }
  883. m_pSheet->m_bModalProp = bModalPage;
  884. hr = m_pSheet->DoSheet(hwnd, page);
  885. // Note: lifetime management of m_pSheet is not trivial here:
  886. // 1. upon successfull execution the object deletes itself post WM_NCDESTROY;
  887. // 2. In case the sheet executes on the main thread, and the error is encountered,
  888. // the object is deleted in this function (below)
  889. // 3. In case sheet is executed on the non-main thread, thread function will
  890. // take ownership of object:
  891. // 3.1. In case of successfull execution - same as #1.
  892. // 3.2. In case error occurres before spawning the thread - same as #2
  893. // 3.3. In case error occurres in the thread, thread function deletes the object.
  894. //
  895. // Re-design of this should be considered in post-whistler releases.
  896. if (SUCCEEDED(hr))
  897. {
  898. // gets delete after sheet is destroyed
  899. m_pSheet = NULL;
  900. return hr;
  901. }
  902. // The m_pSheet needs to be deleted if hr is != S_OK
  903. exit:
  904. delete m_pSheet;
  905. m_pSheet = NULL;
  906. return hr;
  907. }
  908. ///////////////////////////////////////////////////////////////////////////////
  909. // IPropertySheetCallback
  910. //
  911. STDMETHODIMP CPropertySheetProvider::AddPage(HPROPSHEETPAGE lpPage)
  912. {
  913. TRACE_METHOD(CPropertySheetProvider, AddPage);
  914. if (!lpPage)
  915. {
  916. ASSERT(FALSE);
  917. return E_POINTER;
  918. }
  919. ASSERT(m_pSheet != NULL);
  920. if (m_pSheet->m_PageList.GetCount() >= MAXPROPPAGES)
  921. return S_FALSE;
  922. m_pSheet->m_PageList.AddTail(lpPage);
  923. // Add the snapin name for this page in
  924. // the array for tooltips
  925. m_pSheet->m_PropToolTips.AddSnapinPage();
  926. return S_OK;
  927. }
  928. STDMETHODIMP CPropertySheetProvider::RemovePage(HPROPSHEETPAGE lpPage)
  929. {
  930. TRACE_METHOD(CPropertySheetProvider, RemovePage);
  931. if (!lpPage)
  932. {
  933. ASSERT(FALSE);
  934. return E_POINTER;
  935. }
  936. ASSERT(m_pSheet != NULL);
  937. if (m_pSheet->m_PageList.IsEmpty())
  938. {
  939. TRACE(_T("Page list is empty"));
  940. return S_OK;
  941. }
  942. POSITION pos = m_pSheet->m_PageList.Find(lpPage);
  943. if (pos == NULL)
  944. return S_FALSE;
  945. m_pSheet->m_PageList.RemoveAt(pos);
  946. return S_OK;
  947. }
  948. UINT __stdcall PropertySheetThreadProc(LPVOID dwParam)
  949. {
  950. TRACE_FUNCTION(PropertySheetThreadProc);
  951. HRESULT hr = S_OK;
  952. using AMC::CPropertySheet;
  953. CPropertySheet* pSheet = reinterpret_cast<CPropertySheet*>(dwParam);
  954. ASSERT(pSheet != NULL);
  955. if ( pSheet == NULL )
  956. return E_INVALIDARG;
  957. /*
  958. * Bug 372188: Allow this thread to inherit the input locale (aka
  959. * keyboard layout) of the originating thread.
  960. */
  961. HKL hklThread = GetKeyboardLayout(pSheet->GetOriginatingThreadID());
  962. BOOL fUseCicSubstitehKL = FALSE;
  963. if (SUCCEEDED(CoInitialize(0)))
  964. {
  965. //
  966. // On CUAS/AIMM12 environment, GetKeyboardLayout() could return
  967. // non-IME hKL but Cicero Keyboard TIP is running, we need to get
  968. // the substitute hKL of the current language.
  969. //
  970. HKL hkl = CicSubstGetDefaultKeyboardLayout((LANGID)(DWORD)HandleToLong(hklThread));
  971. CoUninitialize();
  972. if (hkl && (hkl != hklThread))
  973. {
  974. fUseCicSubstitehKL = TRUE;
  975. ActivateKeyboardLayout(hkl, 0);
  976. }
  977. }
  978. if (!fUseCicSubstitehKL)
  979. ActivateKeyboardLayout (hklThread, 0);
  980. // do the property sheet
  981. hr = PropertySheetProc( pSheet );
  982. if ( FAILED(hr) )
  983. {
  984. // the error occured - thread needs to clenup
  985. delete pSheet;
  986. return hr;
  987. }
  988. return hr;
  989. }
  990. //+-------------------------------------------------------------------
  991. //
  992. // Member: MmcIsolationAwarePropertySheet
  993. //
  994. // Synopsis: Gets the isolation aware PropertySheet on fusion
  995. // aware systems.
  996. //
  997. // Description: Bug:
  998. // A non-themed snapin calls calls COMCTL32 v5 ! CreatePropertySheetPageW
  999. // mmcndmgr calls comctl32v6 ! PropertySheetW, via IsolationAwarePropertySheetW
  1000. // v5 propertysheetpages have no context IsolationAwarePropertySheetW pushs
  1001. // mmcndmgr's context, which gives comctl v6 so, pages with "no" context
  1002. // (not even the null context) get the activation context of the container.
  1003. // This is wrong, they should get NULL.
  1004. //
  1005. // Cause: (see windows bug # 342553)
  1006. // Before this change, the PropertySheetW wrapper in shfusion1 activated null actually.
  1007. // But activating not NULL is what many scenarios expect (hosted code, but not hosted
  1008. // property sheet/pages), and a number of people hit this, so comctl team changed
  1009. // IsolationAwarePropertySheetW.
  1010. //
  1011. // Fix:
  1012. // There is no win-win here. As a hoster of third party property pages, mmcmdmgr should
  1013. // push null around PropertySheetW. It'd call IsolationAwareLoadLibrary to get the HMODULE
  1014. // to comctl v6, GetProcess, IsolationAwareActivateActCtx to get a delayloaded ActivateActCtx...
  1015. // Basically, hosters (with manifest) of fusion unaware plugins I think cannot call IsolationAwarePropertySheetW
  1016. //
  1017. // Arguments:
  1018. // [lpph] - See PropertySheet Windows API for details
  1019. //
  1020. //--------------------------------------------------------------------
  1021. typedef int ( WINAPI * PFN_PROPERTY_SHEET)( LPCPROPSHEETHEADER lppph);
  1022. int MmcIsolationAwarePropertySheet( LPCPROPSHEETHEADER lpph)
  1023. {
  1024. static PFN_PROPERTY_SHEET s_pfn;
  1025. ULONG_PTR ulCookie;
  1026. int i = -1;
  1027. if (s_pfn == NULL)
  1028. {
  1029. HMODULE hmod = LoadLibrary( TEXT("Comctl32.dll") ); // actually IsolationAwareLoadLibrary, via the macros in winbase.inl
  1030. if (hmod == NULL)
  1031. return i;
  1032. #ifdef UNICODE
  1033. s_pfn = (PFN_PROPERTY_SHEET) GetProcAddress(hmod, "PropertySheetW");
  1034. #else //UNICODE
  1035. s_pfn = (PFN_PROPERTY_SHEET) GetProcAddress(hmod, "PropertySheetA");
  1036. #endif //!UNICODE
  1037. if (s_pfn == NULL)
  1038. return i;
  1039. }
  1040. if (!MmcDownlevelActivateActCtx(NULL, &ulCookie))
  1041. return i;
  1042. __try
  1043. {
  1044. i = s_pfn(lpph);
  1045. }
  1046. __finally
  1047. {
  1048. MmcDownlevelDeactivateActCtx(0, ulCookie);
  1049. }
  1050. return i;
  1051. }
  1052. /***************************************************************************\
  1053. *
  1054. * METHOD: PropertySheetProc
  1055. *
  1056. * PURPOSE: Property sheet procedure used both from the main thread, as
  1057. * well from other threads
  1058. *
  1059. * PARAMETERS:
  1060. * CPropertySheet* pSheet [in] pointer to the sheet
  1061. *
  1062. * RETURNS:
  1063. * HRESULT - result code (NOTE: cannot use SC, since it isn't thread-safe)
  1064. * NOTE: if error is returned , caller needs to delete the sheet,
  1065. * else the sheet will be deleted when the window is closed
  1066. *
  1067. \***************************************************************************/
  1068. HRESULT PropertySheetProc(AMC::CPropertySheet* pSheet)
  1069. {
  1070. // parameter check
  1071. if ( pSheet == NULL )
  1072. return E_INVALIDARG;
  1073. using AMC::CPropertySheet;
  1074. HWND hwnd = NULL;
  1075. int nReturn = -1;
  1076. BOOL bIsWizard = (pSheet->IsWizard() || pSheet->m_bModalProp == TRUE);
  1077. DWORD tid = GetCurrentThreadId();
  1078. pSheet->m_dwTid = tid;
  1079. // if there aren't any pages, add the No Props page
  1080. if (pSheet->m_pstHeader.nPages == 0)
  1081. pSheet->AddNoPropsPage();
  1082. if (pSheet->m_pstHeader.nPages == 0)
  1083. {
  1084. TRACE(_T("PropertySheetProc(): No pages for the property sheet\n"));
  1085. return E_FAIL;
  1086. }
  1087. // Hook the WndProc to get the message
  1088. pSheet->m_msgHook = SetWindowsHookEx(WH_CALLWNDPROCRET, MessageProc,
  1089. GetModuleHandle(NULL), tid);
  1090. if (pSheet->m_msgHook == NULL)
  1091. {
  1092. TRACE(_T("PropertySheetProc(): Unable to create hook\n"), GetLastError());
  1093. return E_FAIL;
  1094. }
  1095. else
  1096. {
  1097. if (!bIsWizard)
  1098. {
  1099. HRESULT hr = ::CoInitialize(NULL);
  1100. if ( FAILED(hr) )
  1101. return hr;
  1102. }
  1103. CPropertySheetProvider::TID_LIST.Add(tid, pSheet);
  1104. nReturn = MmcIsolationAwarePropertySheet(&pSheet->m_pstHeader);
  1105. if (!bIsWizard)
  1106. ::CoUninitialize();
  1107. }
  1108. // Reboot the system if the propsheet wants it.
  1109. if (nReturn == ID_PSREBOOTSYSTEM || nReturn == ID_PSRESTARTWINDOWS)
  1110. {
  1111. DWORD OldState, Status;
  1112. DWORD dwErrorSave;
  1113. SetLastError(0); // Be really safe about last error value!
  1114. // detect if we are running on Win95 and skip security
  1115. DWORD dwVer = GetVersion();
  1116. if (!((dwVer & 0x80000000) && LOBYTE(LOWORD(dwVer)) == 4))
  1117. {
  1118. SetPrivilegeAttribute(SE_SHUTDOWN_NAME,
  1119. SE_PRIVILEGE_ENABLED,
  1120. &OldState);
  1121. }
  1122. dwErrorSave = GetLastError(); // ERROR_NOT_ALL_ASSIGNED sometimes
  1123. if (dwErrorSave != NO_ERROR || !ExitWindowsEx(EWX_REBOOT, 0))
  1124. {
  1125. CStr strText;
  1126. strText.LoadString(GetStringModule(), IDS_NO_PERMISSION_SHUTDOWN);
  1127. MessageBox(NULL, strText, NULL, MB_ICONSTOP);
  1128. }
  1129. }
  1130. // return the value from the Win32 PropertySheet call
  1131. return (nReturn == IDOK) ? S_OK : S_FALSE;
  1132. }
  1133. ///////////////////////////////////////////////////////////////////////////////
  1134. // Hidden Data Window
  1135. //
  1136. LRESULT CALLBACK DataWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
  1137. {
  1138. switch (nMsg)
  1139. {
  1140. case WM_CREATE:
  1141. // this structure is initialized by the creator of the data window
  1142. SetWindowLongPtr (hWnd, WINDOW_DATA_PTR_SLOT,
  1143. reinterpret_cast<LONG_PTR>(new DataWindowData));
  1144. _Module.Lock(); // Lock the dll so that it does not get unloaded
  1145. // when property sheet is up (507338)[XPSP1: 59916]
  1146. break;
  1147. case WM_DESTROY:
  1148. delete GetDataWindowData (hWnd);
  1149. _Module.Unlock(); // See above Lock for comments.
  1150. break;
  1151. }
  1152. return DefWindowProc(hWnd, nMsg, wParam, lParam);
  1153. }
  1154. ///////////////////////////////////////////////////////////////////////////////
  1155. // Callback procedures
  1156. //
  1157. LRESULT CALLBACK MessageProc(int nCode, WPARAM wParam, LPARAM lParam)
  1158. {
  1159. using AMC::CPropertySheet;
  1160. CPropertySheet* pSheet = NULL;
  1161. BOOL b = CPropertySheetProvider::TID_LIST.Find(GetCurrentThreadId(), pSheet);
  1162. if (!b)
  1163. {
  1164. ASSERT(FALSE);
  1165. return 0;
  1166. }
  1167. // WM_NCDESTROY will delete pSheet, so make a copy of the hook
  1168. ASSERT (pSheet != NULL);
  1169. ASSERT (pSheet->m_msgHook != NULL);
  1170. HHOOK hHook = pSheet->m_msgHook;
  1171. if (nCode == HC_ACTION)
  1172. {
  1173. CWPRETSTRUCT* pMsg = reinterpret_cast<CWPRETSTRUCT*>(lParam);
  1174. switch (pMsg->message)
  1175. {
  1176. case WM_CREATE:
  1177. pSheet->OnCreate(pMsg);
  1178. break;
  1179. case WM_INITDIALOG:
  1180. pSheet->OnInitDialog(pMsg);
  1181. break;
  1182. case WM_NCDESTROY:
  1183. pSheet->OnNcDestroy(pMsg);
  1184. break;
  1185. case WM_NOTIFY:
  1186. pSheet->OnWMNotify(pMsg);
  1187. break;
  1188. default:
  1189. break;
  1190. }
  1191. }
  1192. return CallNextHookEx(hHook, nCode, wParam, lParam);
  1193. }
  1194. STDMETHODIMP CPropertySheetProvider::AddPrimaryPages(LPUNKNOWN lpUnknown,
  1195. BOOL bCreateHandle, HWND hNotifyWindow, BOOL bScopePane)
  1196. {
  1197. // The primary pages are added first before the sheet is created
  1198. // Use the internal list to collect the pages, then empty it for the
  1199. // extensions
  1200. // NULL IComponent means the owner of the provider has added pages
  1201. // without implementing IExtendPropertySheet
  1202. LPPROPERTYNOTIFYINFO pNotify = NULL;
  1203. HRESULT hr = S_OK;
  1204. if (lpUnknown != NULL)
  1205. {
  1206. ASSERT(m_pSheet != NULL);
  1207. if(bScopePane)
  1208. {
  1209. IComponentDataPtr spComponentData = lpUnknown;
  1210. m_pSheet->SetComponentData(spComponentData);
  1211. }
  1212. else
  1213. {
  1214. IComponentPtr spComponent = lpUnknown;
  1215. m_pSheet->SetComponent(spComponent);
  1216. }
  1217. // Bug 149211: Allow callers to pass a NULL IDataObject* to CreatePropertySheet
  1218. // ASSERT(m_pSheet->m_spDataObject != NULL);
  1219. IExtendPropertySheetPtr spExtend = lpUnknown;
  1220. IExtendPropertySheet2Ptr spExtend2 = lpUnknown;
  1221. // determine which pointer to use
  1222. IExtendPropertySheet* pExtend;
  1223. if (spExtend2 != NULL)
  1224. pExtend = spExtend2;
  1225. else
  1226. pExtend = spExtend;
  1227. if (pExtend == NULL)
  1228. return E_NOINTERFACE;
  1229. /*
  1230. * Bug 282932: make sure this property sheet extension
  1231. * stays alive for the life of the property sheet
  1232. */
  1233. m_pSheet->m_Extenders.push_back (pExtend);
  1234. hr = pExtend->QueryPagesFor(m_pSheet->m_spDataObject);
  1235. if (hr != S_OK)
  1236. return hr;
  1237. // Create the notify object
  1238. if (bCreateHandle == TRUE)
  1239. {
  1240. pNotify = reinterpret_cast<LPPROPERTYNOTIFYINFO>(
  1241. ::GlobalAlloc(GPTR, sizeof(PROPERTYNOTIFYINFO)));
  1242. pNotify->pComponentData = NULL;
  1243. pNotify->pComponent = NULL;
  1244. pNotify->fScopePane = bScopePane;
  1245. /*
  1246. * Bug 190060: Ignore the window passed in. We always want to
  1247. * notify the main frame window because that's the only window
  1248. * that knows how to process MMC_MSG_PROP_SHEET_NOTIFY.
  1249. */
  1250. // pNotify->hwnd = hNotifyWindow;
  1251. pNotify->hwnd = CScopeTree::GetScopeTree()->GetMainWindow();
  1252. // The component data and component are not ref counted.
  1253. // This is OK because the snap-in has to exist.
  1254. // Because the snapin and it's in another thread
  1255. // and I would have to marshall the pointers.
  1256. if (bScopePane == TRUE)
  1257. {
  1258. IComponentDataPtr spCompData = lpUnknown;
  1259. pNotify->pComponentData = spCompData;
  1260. }
  1261. else
  1262. {
  1263. IComponentPtr spComp = lpUnknown;
  1264. pNotify->pComponent = spComp;
  1265. }
  1266. }
  1267. /*
  1268. * if it's a new-style wizard, get the watermark info
  1269. */
  1270. if (m_pSheet->IsWizard97())
  1271. {
  1272. /*
  1273. * we get the watermark info with IExtendPropertySheet2
  1274. */
  1275. if (spExtend2 != NULL)
  1276. {
  1277. /*
  1278. * this may force an old-style wizard
  1279. */
  1280. m_pSheet->GetWatermarks (spExtend2);
  1281. }
  1282. /*
  1283. * If the snap-in doesn't support IExtendPropertySheet2,
  1284. * we'll give him an old-style wizard. This is
  1285. * broken, but it maintains compatibility with 1.1
  1286. * snap-ins (e.g. SMS) that counted on not getting a Wizard97-
  1287. * style wizard, even though they asked for one with
  1288. * MMC_PSO_NEWWIZARDTYPE.
  1289. */
  1290. else
  1291. m_pSheet->ForceOldStyleWizard();
  1292. }
  1293. if (! m_pSheet->IsWizard())
  1294. {
  1295. // If m_pSheet->m_pMTNode is null then we get the mtnode
  1296. // from CNodeInitObject. But this is root node of snapin
  1297. // So add ellipses to full path.
  1298. BOOL bAddEllipses = FALSE;
  1299. if (NULL == m_pSheet->m_pMTNode)
  1300. {
  1301. // Looks like the snapin used property sheet provider. So get the
  1302. // root master node of the snapin.
  1303. CNodeInitObject* pNodeInitObj = dynamic_cast<CNodeInitObject*>(this);
  1304. m_pSheet->m_pMTNode = pNodeInitObj ? pNodeInitObj->GetMTNode() : NULL;
  1305. // We need to add ellipses
  1306. bAddEllipses = TRUE;
  1307. }
  1308. if (m_pSheet->m_pMTNode)
  1309. {
  1310. LPOLESTR lpszPath = NULL;
  1311. CScopeTree::GetScopeTree()->GetPathString(NULL,
  1312. CMTNode::ToHandle(m_pSheet->m_pMTNode),
  1313. &lpszPath);
  1314. USES_CONVERSION;
  1315. m_pSheet->m_PropToolTips.SetFullPath(OLE2T(lpszPath), bAddEllipses);
  1316. ::CoTaskMemFree((LPVOID)lpszPath);
  1317. }
  1318. // Now let us get the primary snapin name.
  1319. LPDATAOBJECT lpDataObject = (m_pSheet->m_spDataObject) ?
  1320. m_pSheet->m_spDataObject :
  1321. m_pSheet->m_pThreadLocalDataObject;
  1322. // Get the snapin name that is going to add pages.
  1323. // This is stored in temp member of CPropertySheetToolTips
  1324. // so that IPropertySheetCallback::AddPage knows which snapin
  1325. // is adding pages.
  1326. CLSID clsidSnapin;
  1327. SC sc = ExtractSnapInCLSID(lpDataObject, &clsidSnapin);
  1328. if (sc)
  1329. {
  1330. sc.TraceAndClear();
  1331. }
  1332. else
  1333. {
  1334. tstring strName;
  1335. if ( GetSnapinNameFromCLSID(clsidSnapin, strName))
  1336. m_pSheet->m_PropToolTips.SetThisSnapin(strName.data());
  1337. }
  1338. }
  1339. hr = pExtend->CreatePropertyPages(
  1340. dynamic_cast<LPPROPERTYSHEETCALLBACK>(this),
  1341. reinterpret_cast<LONG_PTR>(pNotify), // deleted in Nodemgr
  1342. m_pSheet->m_spDataObject);
  1343. }
  1344. /*
  1345. * Bug 28193: If we're called with a NULL IUnknown, we also want to
  1346. * force old-style wizards.
  1347. */
  1348. else if (m_pSheet->IsWizard97())
  1349. m_pSheet->ForceOldStyleWizard();
  1350. // Build the property sheet structure from the list of pages
  1351. if (hr == S_OK)
  1352. {
  1353. POSITION pos;
  1354. int nCount = 0;
  1355. pos = m_pSheet->m_PageList.GetHeadPosition();
  1356. {
  1357. while(pos)
  1358. {
  1359. m_pSheet->m_pages[nCount] =
  1360. reinterpret_cast<HPROPSHEETPAGE>(m_pSheet->m_PageList.GetNext(pos));
  1361. nCount++;
  1362. }
  1363. ASSERT(nCount < MAXPROPPAGES);
  1364. m_pSheet->m_pstHeader.nPages = nCount;
  1365. // must be page 0 for wizards
  1366. if (m_pSheet->IsWizard())
  1367. m_pSheet->m_pstHeader.nStartPage = 0;
  1368. // Empty the list for the extensions
  1369. m_pSheet->m_PageList.RemoveAll();
  1370. return S_OK; // All done
  1371. }
  1372. }
  1373. // Reached here because of error or the snap-in decided not to add any pages
  1374. if (FAILED(hr) && pNotify != NULL)
  1375. ::GlobalFree(pNotify);
  1376. return hr;
  1377. }
  1378. STDMETHODIMP CPropertySheetProvider::AddExtensionPages()
  1379. {
  1380. DECLARE_SC(sc, TEXT("CPropertySheetProvider::AddExtensionPages"));
  1381. if (m_pSheet == NULL)
  1382. return E_UNEXPECTED;
  1383. // Note: extension are not added until the WM_INITDIALOG of the sheet
  1384. // This insures that the primaries pages are created the original size
  1385. // and will make the extension pages conform
  1386. if (m_pSheet->m_PageList.GetCount() != 0)
  1387. return E_UNEXPECTED;
  1388. // Make sure I have one of the two data objects(main or marshalled)
  1389. ASSERT ((m_pSheet->m_spDataObject == NULL) != (m_pSheet->m_pThreadLocalDataObject == NULL));
  1390. if ((m_pSheet->m_spDataObject == NULL) == (m_pSheet->m_pThreadLocalDataObject == NULL))
  1391. return E_UNEXPECTED;
  1392. LPDATAOBJECT lpDataObject = (m_pSheet->m_spDataObject) ?
  1393. m_pSheet->m_spDataObject :
  1394. m_pSheet->m_pThreadLocalDataObject;
  1395. CExtensionsIterator it;
  1396. sc = it.ScInitialize(lpDataObject, g_szPropertySheet);
  1397. if (sc)
  1398. {
  1399. return S_FALSE;
  1400. }
  1401. IExtendPropertySheetPtr spPropertyExtension;
  1402. LPPROPERTYSHEETCALLBACK pCallBack = dynamic_cast<LPPROPERTYSHEETCALLBACK>(this);
  1403. ASSERT(pCallBack != NULL);
  1404. // CoCreate each snap-in and have it add a sheet
  1405. for ( ;!it.IsEnd(); it.Advance())
  1406. {
  1407. sc = spPropertyExtension.CreateInstance(it.GetCLSID(), NULL, MMC_CLSCTX_INPROC);
  1408. if (!sc.IsError())
  1409. {
  1410. // Get the snapin name that is going to add pages.
  1411. // This is stored in temp member of CPropertySheetToolTips
  1412. // so that IPropertySheetCallback::AddPage knows which snapin
  1413. // is adding pages.
  1414. WTL::CString strName;
  1415. // Fix for bug #469922(9/20/2001): [XPSP1 Bug 599913]:
  1416. // DynamicExtensions broken in MMC20
  1417. // Snapin structures are only avail on static extensions -
  1418. // get the name from reg for DynExtensions
  1419. if (!it.IsDynamic())
  1420. {
  1421. if (!it.GetSnapIn()->ScGetSnapInName(strName).IsError())
  1422. m_pSheet->m_PropToolTips.SetThisSnapin(strName);
  1423. }
  1424. else
  1425. {
  1426. if(!ScGetSnapinNameFromRegistry(it.GetCLSID(),strName).IsError())
  1427. m_pSheet->m_PropToolTips.SetThisSnapin(strName);
  1428. }
  1429. spPropertyExtension->CreatePropertyPages(pCallBack, NULL, lpDataObject);
  1430. /*
  1431. * Bug 282932: make sure this property sheet extension
  1432. * stays alive for the life of the property sheet
  1433. */
  1434. m_pSheet->m_Extenders.push_back (spPropertyExtension);
  1435. }
  1436. else
  1437. {
  1438. #if 0 //#ifdef DBG
  1439. USES_CONVERSION;
  1440. wchar_t buf[64];
  1441. StringFromGUID2 (spSnapIn->GetSnapInCLSID(), buf, countof(buf));
  1442. TRACE(_T("CLSID %s does not implement IID_IExtendPropertySheet\n"), W2T(buf));
  1443. #endif
  1444. }
  1445. }
  1446. m_pSheet->AddExtensionPages();
  1447. m_pSheet->m_bAddExtension = TRUE;
  1448. return S_OK;
  1449. }
  1450. STDMETHODIMP
  1451. CPropertySheetProvider::AddMultiSelectionExtensionPages(LONG_PTR lMultiSelection)
  1452. {
  1453. if (m_pSheet == NULL)
  1454. return E_UNEXPECTED;
  1455. if (lMultiSelection == 0)
  1456. return E_INVALIDARG;
  1457. CMultiSelection* pMS = reinterpret_cast<CMultiSelection*>(lMultiSelection);
  1458. ASSERT(pMS != NULL);
  1459. // Note: extension are not added until the WM_INITDIALOG of the sheet
  1460. // This insures that the primaries pages are created the original size
  1461. // and will make the extension pages conform
  1462. if (m_pSheet->m_PageList.GetCount() != 0)
  1463. return E_UNEXPECTED;
  1464. // Make sure I have one of the two data objects(main or marshalled)
  1465. ASSERT ((m_pSheet->m_spDataObject == NULL) != (m_pSheet->m_pThreadLocalDataObject == NULL));
  1466. if ((m_pSheet->m_spDataObject == NULL) == (m_pSheet->m_pThreadLocalDataObject == NULL))
  1467. return E_UNEXPECTED;
  1468. do // not a loop
  1469. {
  1470. CList<CLSID, CLSID&> snapinClsidList;
  1471. HRESULT hr = pMS->GetExtensionSnapins(g_szPropertySheet, snapinClsidList);
  1472. BREAK_ON_FAIL(hr);
  1473. POSITION pos = snapinClsidList.GetHeadPosition();
  1474. if (pos == NULL)
  1475. break;
  1476. IDataObjectPtr spDataObject;
  1477. hr = pMS->GetMultiSelDataObject(&spDataObject);
  1478. ASSERT(SUCCEEDED(hr));
  1479. BREAK_ON_FAIL(hr);
  1480. BOOL fProblem = FALSE;
  1481. IExtendPropertySheetPtr spPropertyExtension;
  1482. LPPROPERTYSHEETCALLBACK pCallBack = dynamic_cast<LPPROPERTYSHEETCALLBACK>(this);
  1483. ASSERT(pCallBack != NULL);
  1484. while (pos)
  1485. {
  1486. CLSID clsid = snapinClsidList.GetNext(pos);
  1487. // CoCreate each snap-in and have it add a sheet
  1488. //
  1489. hr = spPropertyExtension.CreateInstance(clsid, NULL,
  1490. MMC_CLSCTX_INPROC);
  1491. CHECK_HRESULT(hr);
  1492. if (FAILED(hr))
  1493. {
  1494. #ifdef DBG
  1495. wchar_t buf[64];
  1496. buf[0] = NULL;
  1497. StringFromCLSID(clsid, (LPOLESTR*)&buf);
  1498. TRACE(_T("CLSID %s does not implement IID_IExtendPropertySheet\n"), &buf);
  1499. #endif
  1500. fProblem = TRUE; // Continue even on error.
  1501. continue;
  1502. }
  1503. spPropertyExtension->CreatePropertyPages(pCallBack, NULL, spDataObject);
  1504. }
  1505. if (fProblem == TRUE)
  1506. hr = S_FALSE;
  1507. } while (0);
  1508. m_pSheet->AddExtensionPages();
  1509. m_pSheet->m_bAddExtension = TRUE;
  1510. return S_OK;
  1511. }
  1512. //+-------------------------------------------------------------------
  1513. //
  1514. // Member: SetPropertySheetData
  1515. //
  1516. // Synopsis: Data pertaining to property sheet
  1517. //
  1518. // Arguments: [nPropertySheetType] - EPropertySheetType enum (scope item, result item...)
  1519. // [hMTNode] - The master node that owns the property sheet for scope item
  1520. // or that owns list view item of property sheet.
  1521. //
  1522. //--------------------------------------------------------------------
  1523. STDMETHODIMP CPropertySheetProvider::SetPropertySheetData(INT nPropSheetType, HMTNODE hMTNode)
  1524. {
  1525. m_pSheet->m_PropToolTips.SetPropSheetType((EPropertySheetType)nPropSheetType);
  1526. if (hMTNode)
  1527. {
  1528. m_pSheet->m_pMTNode = CMTNode::FromHandle(hMTNode);
  1529. }
  1530. return S_OK;
  1531. }
  1532. // Copied from security.c in shell\shelldll
  1533. /*++
  1534. Routine Description:
  1535. This routine sets the security attributes for a given privilege.
  1536. Arguments:
  1537. PrivilegeName - Name of the privilege we are manipulating.
  1538. NewPrivilegeAttribute - The new attribute value to use.
  1539. OldPrivilegeAttribute - Pointer to receive the old privilege value. OPTIONAL
  1540. Return value:
  1541. NO_ERROR or WIN32 error.
  1542. --*/
  1543. DWORD SetPrivilegeAttribute(LPCTSTR PrivilegeName, DWORD NewPrivilegeAttribute, DWORD *OldPrivilegeAttribute)
  1544. {
  1545. LUID PrivilegeValue;
  1546. BOOL Result;
  1547. TOKEN_PRIVILEGES TokenPrivileges, OldTokenPrivileges;
  1548. DWORD ReturnLength;
  1549. HANDLE TokenHandle;
  1550. //
  1551. // First, find out the LUID Value of the privilege
  1552. //
  1553. if(!LookupPrivilegeValue(NULL, PrivilegeName, &PrivilegeValue)) {
  1554. return GetLastError();
  1555. }
  1556. //
  1557. // Get the token handle
  1558. //
  1559. if (!OpenProcessToken (
  1560. GetCurrentProcess(),
  1561. TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
  1562. &TokenHandle
  1563. )) {
  1564. return GetLastError();
  1565. }
  1566. //
  1567. // Set up the privilege set we will need
  1568. //
  1569. TokenPrivileges.PrivilegeCount = 1;
  1570. TokenPrivileges.Privileges[0].Luid = PrivilegeValue;
  1571. TokenPrivileges.Privileges[0].Attributes = NewPrivilegeAttribute;
  1572. ReturnLength = sizeof(TOKEN_PRIVILEGES);
  1573. if (!AdjustTokenPrivileges (
  1574. TokenHandle,
  1575. FALSE,
  1576. &TokenPrivileges,
  1577. sizeof(TOKEN_PRIVILEGES),
  1578. &OldTokenPrivileges,
  1579. &ReturnLength
  1580. )) {
  1581. CloseHandle(TokenHandle);
  1582. return GetLastError();
  1583. }
  1584. else {
  1585. if (OldPrivilegeAttribute != NULL) {
  1586. *OldPrivilegeAttribute = OldTokenPrivileges.Privileges[0].Attributes;
  1587. }
  1588. CloseHandle(TokenHandle);
  1589. return NO_ERROR;
  1590. }
  1591. }