//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1999 - 1999 // // File: propsht.cpp // //-------------------------------------------------------------------------- #include "stdafx.h" #include "menuitem.h" #include "amcmsgid.h" #include "regutil.h" #include "multisel.h" #include "ndmgrp.h" #include #include "cicsthkl.h" #include "util.h" /* * multimon.h is included by stdafx.h, without defining COMPILE_MULTIMON_STUBS * first. We need to include it again here after defining COMPILE_MULTIMON_STUBS * so we'll get the stub functions. */ #if (_WIN32_WINNT < 0x0500) #define COMPILE_MULTIMON_STUBS #include #endif // static variables. CThreadToSheetMap CPropertySheetProvider::TID_LIST; UINT __stdcall PropertySheetThreadProc(LPVOID dwParam); HRESULT PropertySheetProc(AMC::CPropertySheet* pSheet); DWORD SetPrivilegeAttribute(LPCTSTR PrivilegeName, DWORD NewPrivilegeAttribute, DWORD *OldPrivilegeAttribute); STDMETHODIMP CPropertySheetProvider::Notify(LPPROPERTYNOTIFYINFO pNotify, LPARAM lParam) { TRACE_METHOD(CPropertySheetProvider, Update); if (pNotify == 0) return E_INVALIDARG; if (!IsWindow (pNotify->hwnd)) return (E_FAIL); // Cast it to the internal type and post the message to the window LPPROPERTYNOTIFYINFO pNotifyT = reinterpret_cast( ::GlobalAlloc(GPTR, sizeof(PROPERTYNOTIFYINFO))); if (pNotifyT == NULL) return E_OUTOFMEMORY; *pNotifyT = *pNotify; ::PostMessage (pNotifyT->hwnd, MMC_MSG_PROP_SHEET_NOTIFY, reinterpret_cast(pNotifyT), lParam); return S_OK; } ///////////////////////////////////////////////////////////////////////////// // CPropertySheet DEBUG_DECLARE_INSTANCE_COUNTER(CPropertySheet); namespace AMC { CPropertySheet::CPropertySheet() : m_dwThreadID (GetCurrentThreadId ()) { CommonConstruct(); DEBUG_INCREMENT_INSTANCE_COUNTER(CPropertySheet); } CPropertySheet::~CPropertySheet() { DEBUG_DECREMENT_INSTANCE_COUNTER(CPropertySheet); } void CPropertySheet::CommonConstruct() { TRACE_METHOD(CPropertySheet, CommonConstruct); ZeroMemory(&m_pstHeader, sizeof(m_pstHeader)); ZeroMemory(&m_pages, sizeof(m_pages)); m_hDlg = NULL; m_msgHook = NULL; m_hDataWindow = NULL; m_cookie = 0; m_lpMasterNode = NULL; m_pStream = NULL; m_bModalProp = FALSE; m_pThreadLocalDataObject = NULL; m_bAddExtension = FALSE; m_pMTNode = NULL; } BOOL CPropertySheet::Create(LPCTSTR lpszCaption, bool fPropSheet, MMC_COOKIE cookie, LPDATAOBJECT pDataObject, LONG_PTR lpMasterNode, DWORD dwOptions) { TRACE_METHOD(CPropertySheet, Create); // Save the data object and the master tree node pointer m_spDataObject = pDataObject; m_lpMasterNode = pDataObject ? 0 : cookie; DWORD dwStyle = PSH_DEFAULT; // is it a property sheet? if (fPropSheet) { if (!(dwOptions & MMC_PSO_NO_PROPTITLE)) dwStyle |= PSH_PROPTITLE; if (dwOptions & MMC_PSO_NOAPPLYNOW) dwStyle |= PSH_NOAPPLYNOW; } // nope, wizard else { dwStyle |= PSH_PROPTITLE; if (dwOptions & MMC_PSO_NEWWIZARDTYPE) dwStyle |= PSH_WIZARD97; else dwStyle |= PSH_WIZARD; } ASSERT(lpszCaption != NULL); m_cookie = cookie; m_pstHeader.dwSize = sizeof(m_pstHeader); m_pstHeader.dwFlags = dwStyle & ~PSH_HASHELP; // array contains handles m_pstHeader.hInstance = _Module.GetModuleInstance(); // Assume no bitmaps or palette m_pstHeader.hbmWatermark = NULL; m_pstHeader.hbmHeader = NULL; m_pstHeader.hplWatermark = NULL; // deep copy the title m_title = lpszCaption; m_pstHeader.pszCaption = m_title; m_pstHeader.nPages = 0; m_pstHeader.phpage = m_pages; return TRUE; } BOOL CPropertySheet::CreateDataWindow(HWND hParent) { TRACE_METHOD(CPropertySheet, CreateDataWindow); HINSTANCE hInstance = _Module.GetModuleInstance(); WNDCLASS wndClass; // See if the class is registered and register a new one if not USES_CONVERSION; if (!GetClassInfo(hInstance, OLE2T(DATAWINDOW_CLASS_NAME), &wndClass)) { ZeroMemory(&wndClass, sizeof(wndClass)); wndClass.lpfnWndProc = DataWndProc; // This holds the cookie and the HWND for the sheet wndClass.cbWndExtra = WINDOW_DATA_SIZE; wndClass.hInstance = hInstance; wndClass.lpszClassName = OLE2T(DATAWINDOW_CLASS_NAME); if (!RegisterClass(&wndClass)) return FALSE; } m_hDataWindow = CreateWindowEx (WS_EX_APPWINDOW, OLE2T(DATAWINDOW_CLASS_NAME), NULL, WS_DLGFRAME | WS_BORDER | WS_DISABLED, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, NULL, NULL, hInstance, NULL); return (m_hDataWindow != 0); } HRESULT CPropertySheet::DoSheet(HWND hParent, int nPage) { TRACE_METHOD(CPropertySheet, DoSheet); // A NULL hParent is allowed for property sheets // but not for wizards if (hParent != NULL) { if (!IsWindow(hParent)) return E_FAIL; } else { if (IsWizard()) return E_INVALIDARG; } if (nPage < 0 || m_dwTid != 0) { ASSERT(FALSE); // Object is already running! return E_FAIL; } m_pstHeader.nStartPage = nPage; m_pstHeader.hwndParent = hParent; HRESULT hr = S_OK; if (IsWizard()) { if (m_pstHeader.nPages > 0) { // Don't create a thread, it's a wizard hr = PropertySheetProc (this); ASSERT(SUCCEEDED(hr)); } else { hr = E_UNEXPECTED; } } else // modal or modeless prop sheet with data window { do { // Create data window for a property sheet if (CreateDataWindow(hParent) == FALSE) { hr = E_FAIL; break; } // Setup data in the hidden window DataWindowData* pData = GetDataWindowData (m_hDataWindow); pData->cookie = m_cookie; pData->lpMasterNode = m_lpMasterNode; pData->spDataObject = m_spDataObject; pData->spComponent = m_spComponent; pData->spComponentData = m_spComponentData; pData->hDlg = NULL; if (m_bModalProp == TRUE) { // Don't create a thread, it's a modal property sheet hr = PropertySheetProc (this); ASSERT(SUCCEEDED(hr)); } else { // If non-null data object, marshal interface to stream if (m_spDataObject != NULL) { hr = CoMarshalInterThreadInterfaceInStream(IID_IDataObject, m_spDataObject, &m_pStream); /* * Bug 318357: once it's marshalled, we're done with * the data object on this thread, release it */ m_spDataObject = NULL; if (hr != S_OK) { TRACE(_T("DoSheet(): Marshalling Failed (%0x08x)\n"), hr); break; } ASSERT(m_pStream != NULL); for (int i = 0; i < m_Extenders.size(); i++) { IStream* pstm; hr = CoMarshalInterThreadInterfaceInStream ( IID_IUnknown, m_Extenders[i], &pstm); if (FAILED (hr)) { TRACE(_T("DoSheet(): Marshalling Failed (%0x08x)\n"), hr); break; } m_ExtendersMarshallStreams.push_back (pstm); } BREAK_ON_FAIL (hr); /* * Clear out the extenders vector to keep the ref * counting correct. It'll be repopulated when * the interfaces are unmarshalled later. */ ASSERT (m_Extenders.size() == m_ExtendersMarshallStreams.size()); m_Extenders.clear(); } m_pstHeader.hwndParent = m_hDataWindow; HANDLE hThread = reinterpret_cast( _beginthreadex (NULL, 0, PropertySheetThreadProc, this, 0, &m_dwTid)); CloseHandle (hThread); } } while(0); } return hr; } void CPropertySheet::GetWatermarks (IExtendPropertySheet2* pExtend2) { ASSERT (IsWizard97()); /* * make sure our resource management objects are empty * * Bug 187702: Note that we Detach here rather than calling * DeleteObject. Yes, it leaks, but it's required for app compat. */ if (!m_bmpWatermark.IsNull()) m_bmpWatermark.Detach(); if (!m_bmpHeader.IsNull()) m_bmpHeader.Detach(); if (!m_Palette.IsNull()) m_Palette.Detach(); BOOL bStretch = FALSE; HRESULT hr = pExtend2->GetWatermarks (m_spDataObject, &m_bmpWatermark.m_hBitmap, &m_bmpHeader.m_hBitmap, &m_Palette.m_hPalette, &bStretch); /* * If we failed to get watermark info, revert to an old-style * wizard for MMC 1.1 compatibility. */ if (FAILED (hr)) { ForceOldStyleWizard(); return; } if (!m_bmpWatermark.IsNull()) { m_pstHeader.dwFlags |= (PSH_USEHBMWATERMARK | PSH_WATERMARK); m_pstHeader.hbmWatermark = m_bmpWatermark; } if (!m_bmpHeader.IsNull()) { m_pstHeader.dwFlags |= (PSH_USEHBMHEADER | PSH_HEADER); m_pstHeader.hbmHeader = m_bmpHeader; } if (!m_Palette.IsNull()) { m_pstHeader.dwFlags |= PSH_USEHPLWATERMARK; m_pstHeader.hplWatermark = m_Palette; } if (bStretch) m_pstHeader.dwFlags |= PSH_STRETCHWATERMARK; } BOOL CPropertySheet::AddExtensionPages() { TRACE_METHOD(CPropertySheet, AddExtensionPages); #ifdef EXTENSIONS_CANNOT_ADD_PAGES_IF_PRIMARY_DOESNT if (m_pstHeader.nPages == 0) { ASSERT(m_pstHeader.nPages != 0); return FALSE; } #endif POSITION pos; int nCount = m_pstHeader.nPages; pos = m_PageList.GetHeadPosition(); if (pos != NULL) { while(pos && nCount < MAXPROPPAGES) { m_pages[nCount++] = reinterpret_cast(m_PageList.GetNext(pos)); } ASSERT(nCount < MAXPROPPAGES); m_pstHeader.nPages = nCount; // Empty the list for the extensions m_PageList.RemoveAll(); } return TRUE; } void CPropertySheet::AddNoPropsPage () { m_pages[m_pstHeader.nPages++] = m_NoPropsPage.Create(); } LRESULT CPropertySheet::OnCreate(CWPRETSTRUCT* pMsg) { if (m_hDlg != 0) return 0; // Assign the hwnd in the object // Get the class name of the window to make sure it's the propsheet TCHAR name[256]; if (GetClassName(pMsg->hwnd, name, sizeof(name)/sizeof(TCHAR))) { ASSERT(m_hDlg == 0); if (_tcsncmp(name, _T("#32770"), 6) == 0) { m_hDlg = pMsg->hwnd; } } return 0; } static RECT s_rectLastPropertySheetPos; static bool s_bLastPropertySheetPosValid = false; void SetLastPropertySheetPosition(HWND hWndPropertySheet) { ::GetWindowRect(hWndPropertySheet, &s_rectLastPropertySheetPos); } /*+-------------------------------------------------------------------------* * * SetPropertySheetPosition * * PURPOSE: The algorithm for positioning a property sheet. (See bug 8584) * 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 * displayed at the top-left. * 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. * 3) When a new property sheet is brought up, mmc starts by using the rectangle stored in (2) above. * 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. * 5) Step 4 is repeated until a positon is located that does not collide with any other property sheets from the same thread. * 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. * * PARAMETERS: * HWND hWndPropertySheet : * * RETURNS: * void * *+-------------------------------------------------------------------------*/ void SetPropertySheetPosition(HWND hWndPropertySheet) { // Find the height and width of the property sheet for later use RECT rectCurrentPos; ::GetWindowRect(hWndPropertySheet, &rectCurrentPos); //get the current position int width = rectCurrentPos.right - rectCurrentPos.left; int height = rectCurrentPos.bottom - rectCurrentPos.top; // Initialize the position if (!s_bLastPropertySheetPosValid) { s_rectLastPropertySheetPos.top = 0; s_rectLastPropertySheetPos.left = 0; s_rectLastPropertySheetPos.bottom = 0; s_rectLastPropertySheetPos.right = 0; CScopeTree * pScopeTree = CScopeTree::GetScopeTree(); if(pScopeTree) // if pScopeTree == NULL, can still execute gracefully by using zero rect. { HWND hWndMain = pScopeTree->GetMainWindow(); RECT rectTemp; GetWindowRect(hWndMain, &rectTemp); // center the property sheet on the center of the main window s_rectLastPropertySheetPos.top = (rectTemp.top + rectTemp.bottom)/2 - (height/2); s_rectLastPropertySheetPos.left = (rectTemp.left + rectTemp.right )/2 - (width/2); s_rectLastPropertySheetPos.right = s_rectLastPropertySheetPos.left + width; // these last two are not strictly needed s_rectLastPropertySheetPos.bottom = s_rectLastPropertySheetPos.top + height; // but are here for consistency. } s_bLastPropertySheetPosValid = true; } RECT rectNewPos = s_rectLastPropertySheetPos; // try this initially int offset = GetSystemMetrics(SM_CYDLGFRAME) + GetSystemMetrics(SM_CYCAPTION); // how much to stagger the windows by bool bPosOK = true; HWND hWnd = NULL; typedef std::set t_set; t_set s; // collect all the window positions into a vector while (1) { // make sure there isn't a property sheet already at this location hWnd = ::FindWindowEx(NULL, hWnd, MAKEINTATOM(32770), NULL); // No windows found, use the position if (hWnd == NULL) break; // Check if the window belongs to the current process DWORD dwPid; ::GetWindowThreadProcessId(hWnd, &dwPid); if (dwPid != ::GetCurrentProcessId()) continue; if(hWnd == hWndPropertySheet) // don't check against the same window. continue; RECT rectPos; ::GetWindowRect(hWnd, &rectPos); // look only for possible collisions starting from the point and to the right and below it. if(rectPos.top >= rectNewPos.top) { UINT offsetTemp = (rectPos.top - rectNewPos.top) / offset; if(rectPos.left != (offsetTemp * offset + rectNewPos.left) ) continue; if(rectPos.top != (offsetTemp * offset + rectNewPos.top) ) continue; s.insert(offsetTemp); } } // at this point s contains all the offsets that can collide. for(UINT i = 0; /*empty*/ ; i++) { if(s.find(i) == s.end()) // located the end break; } rectNewPos.left += i*offset; rectNewPos.top += i*offset; rectNewPos.bottom = rectNewPos.top + height; rectNewPos.right = rectNewPos.left + width; /* * Bug 211145: make sure the new position is within the work area */ HMONITOR hmon = MonitorFromPoint (WTL::CPoint (rectNewPos.left, rectNewPos.top), MONITOR_DEFAULTTONEAREST); MONITORINFO mi = { sizeof (mi) }; WTL::CRect rectWorkArea; /* * if we could get the info for the monitor containing the window origin, * use it's workarea as the bounding rectangle; otherwise get the workarea * for the default monitor; if that failed as well, default to 640x480 */ if (GetMonitorInfo (hmon, &mi)) rectWorkArea = mi.rcWork; else if (!SystemParametersInfo (SPI_GETWORKAREA, 0, &rectWorkArea, false)) rectWorkArea.SetRect (0, 0, 639, 479); if (rectNewPos.left < rectWorkArea.left) { rectNewPos.left = rectWorkArea.left; rectNewPos.right = rectNewPos.left + width; } if (rectNewPos.top < rectWorkArea.top) { rectNewPos.top = rectWorkArea.top; rectNewPos.bottom = rectNewPos.top + height; } // is the window completely visible? POINT ptTopLeft = {rectNewPos.left, rectNewPos.top}; POINT ptBottomRight = {rectNewPos.right, rectNewPos.bottom}; if( (MonitorFromPoint(ptTopLeft, MONITOR_DEFAULTTONULL) == NULL) || (MonitorFromPoint(ptBottomRight, MONITOR_DEFAULTTONULL) == NULL)) { // the property sheet is not completely visible. Move it to the top-left. rectNewPos.left = rectWorkArea.left; rectNewPos.top = rectWorkArea.top; rectNewPos.bottom = rectNewPos.top + height; rectNewPos.right = rectNewPos.left + width; } MoveWindow(hWndPropertySheet, rectNewPos.left, rectNewPos.top, width, height, true /*bRepaint*/); // save the position s_rectLastPropertySheetPos = rectNewPos; } LRESULT CPropertySheet::OnInitDialog(CWPRETSTRUCT* pMsg) { if (m_hDlg != pMsg->hwnd) return 1; if (!IsWizard()) { SetPropertySheetPosition(m_hDlg); ASSERT (IsWindow (m_hDataWindow)); // Add data dialog hanndle to hidden window if (IsWindow (m_hDataWindow)) { DataWindowData* pData = GetDataWindowData (m_hDataWindow); pData->hDlg = m_hDlg; // Create the marshalled data object pointer from stream if (m_pStream != NULL) { // Unmarshall the Data object HRESULT hr = ::CoGetInterfaceAndReleaseStream(m_pStream, IID_IDataObject, reinterpret_cast(&m_pThreadLocalDataObject)); ASSERT(hr == S_OK); TRACE(_T("WM_INITDIALOG: Unmarshalled returned %X\n"), hr); for (int i = 0; i < m_ExtendersMarshallStreams.size(); i++) { IUnknown* pUnk = NULL; hr = CoGetInterfaceAndReleaseStream ( m_ExtendersMarshallStreams[i], IID_IUnknown, reinterpret_cast(&pUnk)); ASSERT (hr == S_OK); ASSERT (pUnk != NULL); TRACE(_T("WM_INITDIALOG: Unmarshalled returned %X\n"), hr); /* * m_Extenders is a collection of smart pointers, which * will AddRef. We don't need to AddRef an interface * that's returned to us, so Release here to keep the * bookkeeping straight. */ m_Extenders.push_back (pUnk); if (pUnk) pUnk->Release(); } ASSERT (m_Extenders.size() == m_ExtendersMarshallStreams.size()); m_ExtendersMarshallStreams.clear(); } } /* * Bug 215593: If we're running at low resolution we don't want * more than two rows of tabs. If we find that is the case, use * a single scrolling row of tabs instead of multiple rows. */ if (GetSystemMetrics (SM_CXSCREEN) < 800) { WTL::CTabCtrl wndTabCtrl = PropSheet_GetTabControl (m_hDlg); ASSERT (wndTabCtrl.m_hWnd != NULL); /* * if we have more than two rows, remove the multiline style */ if (wndTabCtrl.GetRowCount() > 2) wndTabCtrl.ModifyStyle (TCS_MULTILINE, 0); } // Create tooltip control for the property sheet. do { if (IsWizard()) break; HWND hWnd = m_PropToolTips.Create(m_hDlg); ASSERT(hWnd); if (NULL == hWnd) break; TOOLINFO ti; RECT rc; GetWindowRect(m_hDlg, &rc); // Set the tooltip for property sheet title. // Set the control for a rectangle from (0, - (titlewidth)) // to (right-end,0) ti.cbSize = sizeof(TOOLINFO); ti.uFlags = TTF_SUBCLASS; ti.hwnd = m_hDlg; // This is the id used for the tool tip control for property sheet // title. So when we get TTN_NEEDTEXT we can identify if the text // is for title or a tab. ti.uId = PROPSHEET_TITLE_TOOLTIP_ID; ti.rect.left = 0; ti.rect.right = rc.right - rc.left; ti.rect.top = -GetSystemMetrics(SM_CXSIZE); ti.rect.bottom = 0; ti.hinst = _Module.GetModuleInstance(); ti.lpszText = LPSTR_TEXTCALLBACK ; m_PropToolTips.AddTool(&ti); m_PropToolTips.Activate(TRUE); // Now add tooltips for the tab control WTL::CTabCtrl wndTabCtrl = PropSheet_GetTabControl (m_hDlg); ASSERT (wndTabCtrl.m_hWnd != NULL); if (NULL == wndTabCtrl.m_hWnd) break; ::ZeroMemory(&ti, sizeof(ti)); ti.cbSize = sizeof(TOOLINFO); ti.uFlags = TTF_SUBCLASS; ti.hwnd = wndTabCtrl.m_hWnd; ti.uId = (LONG)::GetDlgCtrlID((HWND)wndTabCtrl.m_hWnd); ti.hinst = _Module.GetModuleInstance(); ti.lpszText = LPSTR_TEXTCALLBACK; //define the rect area (for each tab) and the tool tip associated withit for (int i=0; ihwnd) return 1; SetLastPropertySheetPosition(m_hDlg); ASSERT(m_msgHook != NULL); UnhookWindowsHookEx(m_msgHook); // Clean up the key and the object CPropertySheetProvider::TID_LIST.Remove(GetCurrentThreadId()); if (m_pThreadLocalDataObject != NULL) m_pThreadLocalDataObject->Release(); // Only Property Sheets have Data windows if (!IsWizard()) { // Close the data window ASSERT(IsWindow(m_hDataWindow)); SendMessage(m_hDataWindow, WM_CLOSE, 0, 0); } delete this; return 0; } LRESULT CPropertySheet::OnWMNotify(CWPRETSTRUCT* pMsg) { LPNMHDR pHdr = (LPNMHDR)pMsg->lParam; if (NULL == pHdr) return 0; switch(pHdr->code) { case TTN_NEEDTEXT: { /* * we only want to do our thing if the Ctrl key is * pressed, so bail if it's not */ if (!(GetKeyState(VK_CONTROL) < 0)) break; // Make sure our property sheet tooltip sent this message. if (pHdr->hwndFrom != ((CWindow)m_PropToolTips).m_hWnd) break; LPTOOLTIPTEXT lpttt = (LPTOOLTIPTEXT)pMsg->lParam; lpttt->lpszText = NULL; // This is the id used for the tool tip control for property sheet // title. So check if the text is for title or a tab. if (pHdr->idFrom == PROPSHEET_TITLE_TOOLTIP_ID) lpttt->lpszText = (LPTSTR)m_PropToolTips.GetFullPath(); else { // A tab is selected, find out which tab. HWND hTabCtrl = PropSheet_GetTabControl(m_hDlg); if (NULL == hTabCtrl) break; POINT pt; GetCursorPos(&pt); ScreenToClient(hTabCtrl, &pt); TCHITTESTINFO tch; tch.flags = TCHT_ONITEM; tch.pt = pt; int n = TabCtrl_HitTest(hTabCtrl, &tch); if ((-1 == n) || (m_PropToolTips.GetNumPages() <= n) ) break; lpttt->lpszText = (LPTSTR)m_PropToolTips.GetSnapinPage(n); } } break; default: break; } return 0; } void CPropertySheet::ForceOldStyleWizard () { /* * We shouldn't be forcing old-style wizard behavior on a * property sheet that's not already a wizard. */ ASSERT (IsWizard()); m_pstHeader.dwFlags |= PSH_WIZARD; m_pstHeader.dwFlags &= ~PSH_WIZARD97; /* * The sheet should still be a wizard, but not a Wiz97 wizard. */ ASSERT ( IsWizard()); ASSERT (!IsWizard97()); } } DEBUG_DECLARE_INSTANCE_COUNTER(CPropertySheetProvider); CPropertySheetProvider::CPropertySheetProvider() { TRACE_METHOD(CPropertySheetProvider, CPropertySheetProvider); m_pSheet = NULL; DEBUG_INCREMENT_INSTANCE_COUNTER(CPropertySheetProvider); } CPropertySheetProvider::~CPropertySheetProvider() { TRACE_METHOD(CPropertySheetProvider, ~CPropertySheetProvider); m_pSheet = NULL; DEBUG_DECREMENT_INSTANCE_COUNTER(CPropertySheetProvider); } /////////////////////////////////////////////////////////////////////////////// // IPropertySheetProvider // BOOL CALLBACK MyEnumThreadWindProc (HWND current, LPARAM lParam) { // this enumerates non-child-windows created by a given thread if (!IsWindow (current)) return TRUE; // this shouldn't happen, but does!!! if (!IsWindowVisible (current)) // if they've explicitly hidden a window, return TRUE; // don't set focus to it. // we'll return hwnd in here HWND * hwnd = (HWND *)lParam; // don't bother returning property sheet dialog window handle if (*hwnd == current) return TRUE; // also, don't return OleMainThreadWndClass window TCHAR szCaption[14]; GetWindowText (current, szCaption, countof(szCaption)); if (!lstrcmp (szCaption, _T("OLEChannelWnd"))) return TRUE; // anything else will do *hwnd = current; return FALSE; } STDMETHODIMP CPropertySheetProvider::FindPropertySheet(MMC_COOKIE cookie, LPCOMPONENT lpComponent, LPDATAOBJECT lpDataObject) { return FindPropertySheetEx(cookie, lpComponent, NULL, lpDataObject); } STDMETHODIMP CPropertySheetProvider::FindPropertySheetEx(MMC_COOKIE cookie, LPCOMPONENT lpComponent, LPCOMPONENTDATA lpComponentData, LPDATAOBJECT lpDataObject) { TRACE_METHOD(CPropertySheetProvider, FindPropertySheet); using AMC::CPropertySheet; if ((cookie == NULL) && ( (lpComponent == NULL && lpComponentData == NULL) || lpDataObject == NULL)) { ASSERT(FALSE); return E_POINTER; } HRESULT hr = S_FALSE; HWND hWnd = NULL; while (1) { USES_CONVERSION; hWnd = FindWindowEx(NULL, hWnd, OLE2T(DATAWINDOW_CLASS_NAME), NULL); // No windows found if (hWnd == NULL) { hr = S_FALSE; break; } // Check if the window belongs to the current process DWORD dwPid; ::GetWindowThreadProcessId(hWnd, &dwPid); if (dwPid != ::GetCurrentProcessId()) continue; // Get the extra bytes and compare the data objects ASSERT(GetClassLong(hWnd, GCL_CBWNDEXTRA) == WINDOW_DATA_SIZE); ASSERT(IsWindow(hWnd)); // The original Data object can be NULL if there isn't an IComponent. // this occurs with built-in nodes(i.e. nodes owned by the console) DataWindowData* pData = GetDataWindowData (hWnd); // Ask the snapin of the the two data objects are the same // Does this one match? if (lpComponent != NULL) { ASSERT(pData->spDataObject != NULL); hr = lpComponent->CompareObjects(lpDataObject, pData->spDataObject); } else { // Although the NULL cookie is the static folder, the cookie stored in the data // window is the pointer to the master tree node. This is why it is not null. ASSERT(cookie != NULL); // Compare the cookies if it's a scope item if (pData->cookie == cookie) hr = S_OK; } // bring the property sheet to the foreground // note: hDlg can be null if the secondary thread has not finished creating // the property sheet if (hr == S_OK) { if (pData->hDlg != NULL) { // // Found previous instance, restore the // window plus its popups // SetActiveWindow (pData->hDlg); SetForegroundWindow (pData->hDlg); // grab first one that isn't property sheet dialog HWND hwnd = pData->hDlg; EnumThreadWindows(::GetWindowThreadProcessId(pData->hDlg, NULL), MyEnumThreadWindProc, (LPARAM)&hwnd); if (hwnd) { SetActiveWindow (hwnd); SetForegroundWindow (hwnd); } } break; } } return hr; } STDMETHODIMP CPropertySheetProvider::CreatePropertySheet( LPCWSTR title, unsigned char bType, MMC_COOKIE cookie, LPDATAOBJECT pDataObject, DWORD dwOptions) { return CreatePropertySheetEx(title, bType, cookie, pDataObject, NULL, dwOptions); } STDMETHODIMP CPropertySheetProvider::CreatePropertySheetEx(LPCWSTR title, unsigned char bType, MMC_COOKIE cookie, LPDATAOBJECT pDataObject, LONG_PTR lpMasterTreeNode, DWORD dwOptions) { TRACE_METHOD(CPropertySheetProvider, CreatePropertySheet); using AMC::CPropertySheet; if (!title) return E_POINTER; // You called CreatePropertySheet more than once. // Either release the object or call ::Show(-1, 0) // to free the resources if (m_pSheet != NULL) { ASSERT(FALSE); return E_UNEXPECTED; } // Create the actual sheet and the list for page management m_pSheet = new CPropertySheet(); // Add it to the list of sheets and add it to the list USES_CONVERSION; m_pSheet->Create(OLE2CT(title), bType, cookie, pDataObject, lpMasterTreeNode, dwOptions); return S_OK; } STDMETHODIMP CPropertySheetProvider::Show(LONG_PTR window, int page) { TRACE_METHOD(CPropertySheetProvider, Show); return ShowEx(reinterpret_cast(window), page, FALSE); } STDMETHODIMP CPropertySheetProvider::ShowEx(HWND hwnd, int page, BOOL bModalPage) { TRACE_METHOD(CPropertySheetProvider, ShowEx); HRESULT hr = E_UNEXPECTED; if (page < 0) { hr = E_INVALIDARG; goto exit; } if (m_pSheet == NULL) { // didn't call Create() ASSERT(FALSE); goto exit; } m_pSheet->m_bModalProp = bModalPage; hr = m_pSheet->DoSheet(hwnd, page); // Note: lifetime management of m_pSheet is not trivial here: // 1. upon successfull execution the object deletes itself post WM_NCDESTROY; // 2. In case the sheet executes on the main thread, and the error is encountered, // the object is deleted in this function (below) // 3. In case sheet is executed on the non-main thread, thread function will // take ownership of object: // 3.1. In case of successfull execution - same as #1. // 3.2. In case error occurres before spawning the thread - same as #2 // 3.3. In case error occurres in the thread, thread function deletes the object. // // Re-design of this should be considered in post-whistler releases. if (SUCCEEDED(hr)) { // gets delete after sheet is destroyed m_pSheet = NULL; return hr; } // The m_pSheet needs to be deleted if hr is != S_OK exit: delete m_pSheet; m_pSheet = NULL; return hr; } /////////////////////////////////////////////////////////////////////////////// // IPropertySheetCallback // STDMETHODIMP CPropertySheetProvider::AddPage(HPROPSHEETPAGE lpPage) { TRACE_METHOD(CPropertySheetProvider, AddPage); if (!lpPage) { ASSERT(FALSE); return E_POINTER; } ASSERT(m_pSheet != NULL); if (m_pSheet->m_PageList.GetCount() >= MAXPROPPAGES) return S_FALSE; m_pSheet->m_PageList.AddTail(lpPage); // Add the snapin name for this page in // the array for tooltips m_pSheet->m_PropToolTips.AddSnapinPage(); return S_OK; } STDMETHODIMP CPropertySheetProvider::RemovePage(HPROPSHEETPAGE lpPage) { TRACE_METHOD(CPropertySheetProvider, RemovePage); if (!lpPage) { ASSERT(FALSE); return E_POINTER; } ASSERT(m_pSheet != NULL); if (m_pSheet->m_PageList.IsEmpty()) { TRACE(_T("Page list is empty")); return S_OK; } POSITION pos = m_pSheet->m_PageList.Find(lpPage); if (pos == NULL) return S_FALSE; m_pSheet->m_PageList.RemoveAt(pos); return S_OK; } UINT __stdcall PropertySheetThreadProc(LPVOID dwParam) { TRACE_FUNCTION(PropertySheetThreadProc); HRESULT hr = S_OK; using AMC::CPropertySheet; CPropertySheet* pSheet = reinterpret_cast(dwParam); ASSERT(pSheet != NULL); if ( pSheet == NULL ) return E_INVALIDARG; /* * Bug 372188: Allow this thread to inherit the input locale (aka * keyboard layout) of the originating thread. */ /* Bug 608076 */ HKL hklThread = GetKeyboardLayout(pSheet->GetOriginatingThreadID()); BOOL fUseCicSubstitehKL = FALSE; if (SUCCEEDED(CoInitialize(0))) { // // On CUAS/AIMM12 environment, GetKeyboardLayout() could return // non-IME hKL but Cicero Keyboard TIP is running, we need to get // the substitute hKL of the current language. // HKL hkl = CicSubstGetDefaultKeyboardLayout((LANGID)(DWORD)HandleToLong(hklThread)); CoUninitialize(); if (hkl && (hkl != hklThread)) { fUseCicSubstitehKL = TRUE; ActivateKeyboardLayout(hkl, 0); } } if (!fUseCicSubstitehKL) ActivateKeyboardLayout (hklThread, 0); // do the property sheet hr = PropertySheetProc( pSheet ); if ( FAILED(hr) ) { // the error occured - thread needs to clenup delete pSheet; return hr; } return hr; } //+------------------------------------------------------------------- // // Member: MmcIsolationAwarePropertySheet // // Synopsis: Gets the isolation aware PropertySheet on fusion // aware systems. // // Description: Bug: // A non-themed snapin calls calls COMCTL32 v5 ! CreatePropertySheetPageW // mmcndmgr calls comctl32v6 ! PropertySheetW, via IsolationAwarePropertySheetW // v5 propertysheetpages have no context IsolationAwarePropertySheetW pushs // mmcndmgr's context, which gives comctl v6 so, pages with "no" context // (not even the null context) get the activation context of the container. // This is wrong, they should get NULL. // // Cause: (see windows bug # 342553) // Before this change, the PropertySheetW wrapper in shfusion1 activated null actually. // But activating not NULL is what many scenarios expect (hosted code, but not hosted // property sheet/pages), and a number of people hit this, so comctl team changed // IsolationAwarePropertySheetW. // // Fix: // There is no win-win here. As a hoster of third party property pages, mmcmdmgr should // push null around PropertySheetW. It'd call IsolationAwareLoadLibrary to get the HMODULE // to comctl v6, GetProcess, IsolationAwareActivateActCtx to get a delayloaded ActivateActCtx... // Basically, hosters (with manifest) of fusion unaware plugins I think cannot call IsolationAwarePropertySheetW // // Arguments: // [lpph] - See PropertySheet Windows API for details // //-------------------------------------------------------------------- typedef int ( WINAPI * PFN_PROPERTY_SHEET)( LPCPROPSHEETHEADER lppph); int MmcIsolationAwarePropertySheet( LPCPROPSHEETHEADER lpph) { static PFN_PROPERTY_SHEET s_pfn; ULONG_PTR ulCookie; int i = -1; if (s_pfn == NULL) { HMODULE hmod = LoadLibrary( TEXT("Comctl32.dll") ); // actually IsolationAwareLoadLibrary, via the macros in winbase.inl if (hmod == NULL) return i; #ifdef UNICODE s_pfn = (PFN_PROPERTY_SHEET) GetProcAddress(hmod, "PropertySheetW"); #else //UNICODE s_pfn = (PFN_PROPERTY_SHEET) GetProcAddress(hmod, "PropertySheetA"); #endif //!UNICODE if (s_pfn == NULL) return i; } if (!MmcDownlevelActivateActCtx(NULL, &ulCookie)) return i; __try { i = s_pfn(lpph); } __finally { MmcDownlevelDeactivateActCtx(0, ulCookie); } return i; } /***************************************************************************\ * * METHOD: PropertySheetProc * * PURPOSE: Property sheet procedure used both from the main thread, as * well from other threads * * PARAMETERS: * CPropertySheet* pSheet [in] pointer to the sheet * * RETURNS: * HRESULT - result code (NOTE: cannot use SC, since it isn't thread-safe) * NOTE: if error is returned , caller needs to delete the sheet, * else the sheet will be deleted when the window is closed * \***************************************************************************/ HRESULT PropertySheetProc(AMC::CPropertySheet* pSheet) { // parameter check if ( pSheet == NULL ) return E_INVALIDARG; using AMC::CPropertySheet; HWND hwnd = NULL; int nReturn = -1; BOOL bIsWizard = (pSheet->IsWizard() || pSheet->m_bModalProp == TRUE); DWORD tid = GetCurrentThreadId(); pSheet->m_dwTid = tid; // if there aren't any pages, add the No Props page if (pSheet->m_pstHeader.nPages == 0) pSheet->AddNoPropsPage(); if (pSheet->m_pstHeader.nPages == 0) { TRACE(_T("PropertySheetProc(): No pages for the property sheet\n")); return E_FAIL; } // Hook the WndProc to get the message pSheet->m_msgHook = SetWindowsHookEx(WH_CALLWNDPROCRET, MessageProc, GetModuleHandle(NULL), tid); if (pSheet->m_msgHook == NULL) { TRACE(_T("PropertySheetProc(): Unable to create hook\n"), GetLastError()); return E_FAIL; } else { if (!bIsWizard) { HRESULT hr = ::CoInitialize(NULL); if ( FAILED(hr) ) return hr; } CPropertySheetProvider::TID_LIST.Add(tid, pSheet); nReturn = MmcIsolationAwarePropertySheet(&pSheet->m_pstHeader); if (!bIsWizard) ::CoUninitialize(); } // Reboot the system if the propsheet wants it. if (nReturn == ID_PSREBOOTSYSTEM || nReturn == ID_PSRESTARTWINDOWS) { DWORD OldState, Status; DWORD dwErrorSave; SetLastError(0); // Be really safe about last error value! // detect if we are running on Win95 and skip security DWORD dwVer = GetVersion(); if (!((dwVer & 0x80000000) && LOBYTE(LOWORD(dwVer)) == 4)) { SetPrivilegeAttribute(SE_SHUTDOWN_NAME, SE_PRIVILEGE_ENABLED, &OldState); } dwErrorSave = GetLastError(); // ERROR_NOT_ALL_ASSIGNED sometimes // the SHTDN_REASON_MINOR_MMC flag was added in .NET Server and is ignored on all previous versions. if (dwErrorSave != NO_ERROR || !ExitWindowsEx(EWX_REBOOT, SHTDN_REASON_MAJOR_OTHER | SHTDN_REASON_MINOR_MMC)) { CStr strText; strText.LoadString(GetStringModule(), IDS_NO_PERMISSION_SHUTDOWN); MessageBox(NULL, strText, NULL, MB_ICONSTOP); } } // return the value from the Win32 PropertySheet call return (nReturn == IDOK) ? S_OK : S_FALSE; } /////////////////////////////////////////////////////////////////////////////// // Hidden Data Window // LRESULT CALLBACK DataWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam) { switch (nMsg) { case WM_CREATE: // this structure is initialized by the creator of the data window SetWindowLongPtr (hWnd, WINDOW_DATA_PTR_SLOT, reinterpret_cast(new DataWindowData)); _Module.Lock(); // Lock the dll so that it does not get unloaded when // property sheet is up (507338). break; case WM_DESTROY: delete GetDataWindowData (hWnd); _Module.Unlock(); // See above Lock for comments. break; } return DefWindowProc(hWnd, nMsg, wParam, lParam); } /////////////////////////////////////////////////////////////////////////////// // Callback procedures // LRESULT CALLBACK MessageProc(int nCode, WPARAM wParam, LPARAM lParam) { using AMC::CPropertySheet; CPropertySheet* pSheet = NULL; BOOL b = CPropertySheetProvider::TID_LIST.Find(GetCurrentThreadId(), pSheet); if (!b) { ASSERT(FALSE); return 0; } // WM_NCDESTROY will delete pSheet, so make a copy of the hook ASSERT (pSheet != NULL); ASSERT (pSheet->m_msgHook != NULL); HHOOK hHook = pSheet->m_msgHook; if (nCode == HC_ACTION) { CWPRETSTRUCT* pMsg = reinterpret_cast(lParam); switch (pMsg->message) { case WM_CREATE: pSheet->OnCreate(pMsg); break; case WM_INITDIALOG: pSheet->OnInitDialog(pMsg); break; case WM_NCDESTROY: pSheet->OnNcDestroy(pMsg); break; case WM_NOTIFY: pSheet->OnWMNotify(pMsg); break; default: break; } } return CallNextHookEx(hHook, nCode, wParam, lParam); } STDMETHODIMP CPropertySheetProvider::AddPrimaryPages(LPUNKNOWN lpUnknown, BOOL bCreateHandle, HWND hNotifyWindow, BOOL bScopePane) { // The primary pages are added first before the sheet is created // Use the internal list to collect the pages, then empty it for the // extensions // NULL IComponent means the owner of the provider has added pages // without implementing IExtendPropertySheet LPPROPERTYNOTIFYINFO pNotify = NULL; HRESULT hr = S_OK; if (lpUnknown != NULL) { ASSERT(m_pSheet != NULL); if(bScopePane) { IComponentDataPtr spComponentData = lpUnknown; m_pSheet->SetComponentData(spComponentData); } else { IComponentPtr spComponent = lpUnknown; m_pSheet->SetComponent(spComponent); } // Bug 149211: Allow callers to pass a NULL IDataObject* to CreatePropertySheet // ASSERT(m_pSheet->m_spDataObject != NULL); IExtendPropertySheetPtr spExtend = lpUnknown; IExtendPropertySheet2Ptr spExtend2 = lpUnknown; // determine which pointer to use IExtendPropertySheet* pExtend; if (spExtend2 != NULL) pExtend = spExtend2; else pExtend = spExtend; if (pExtend == NULL) return E_NOINTERFACE; /* * Bug 282932: make sure this property sheet extension * stays alive for the life of the property sheet */ m_pSheet->m_Extenders.push_back (pExtend); hr = pExtend->QueryPagesFor(m_pSheet->m_spDataObject); if (hr != S_OK) return hr; // Create the notify object if (bCreateHandle == TRUE) { pNotify = reinterpret_cast( ::GlobalAlloc(GPTR, sizeof(PROPERTYNOTIFYINFO))); if (!pNotify) return E_OUTOFMEMORY; pNotify->pComponentData = NULL; pNotify->pComponent = NULL; pNotify->fScopePane = bScopePane; /* * Bug 190060: Ignore the window passed in. We always want to * notify the main frame window because that's the only window * that knows how to process MMC_MSG_PROP_SHEET_NOTIFY. */ // pNotify->hwnd = hNotifyWindow; pNotify->hwnd = CScopeTree::GetScopeTree()->GetMainWindow(); // The component data and component are not ref counted. // This is OK because the snap-in has to exist. // Because the snapin and it's in another thread // and I would have to marshall the pointers. if (bScopePane == TRUE) { IComponentDataPtr spCompData = lpUnknown; pNotify->pComponentData = spCompData; } else { IComponentPtr spComp = lpUnknown; pNotify->pComponent = spComp; } } /* * if it's a new-style wizard, get the watermark info */ if (m_pSheet->IsWizard97()) { /* * we get the watermark info with IExtendPropertySheet2 */ if (spExtend2 != NULL) { /* * this may force an old-style wizard */ m_pSheet->GetWatermarks (spExtend2); } /* * If the snap-in doesn't support IExtendPropertySheet2, * we'll give him an old-style wizard. This is * broken, but it maintains compatibility with 1.1 * snap-ins (e.g. SMS) that counted on not getting a Wizard97- * style wizard, even though they asked for one with * MMC_PSO_NEWWIZARDTYPE. */ else m_pSheet->ForceOldStyleWizard(); } if (! m_pSheet->IsWizard()) { // If m_pSheet->m_pMTNode is null then we get the mtnode // from CNodeInitObject. But this is root node of snapin // So add ellipses to full path. BOOL bAddEllipses = FALSE; if (NULL == m_pSheet->m_pMTNode) { // Looks like the snapin used property sheet provider. So get the // root master node of the snapin. CNodeInitObject* pNodeInitObj = dynamic_cast(this); m_pSheet->m_pMTNode = pNodeInitObj ? pNodeInitObj->GetMTNode() : NULL; // We need to add ellipses bAddEllipses = TRUE; } if (m_pSheet->m_pMTNode) { LPOLESTR lpszPath = NULL; CScopeTree::GetScopeTree()->GetPathString(NULL, CMTNode::ToHandle(m_pSheet->m_pMTNode), &lpszPath); USES_CONVERSION; m_pSheet->m_PropToolTips.SetFullPath(OLE2T(lpszPath), bAddEllipses); ::CoTaskMemFree((LPVOID)lpszPath); } // Now let us get the primary snapin name. LPDATAOBJECT lpDataObject = (m_pSheet->m_spDataObject) ? m_pSheet->m_spDataObject : m_pSheet->m_pThreadLocalDataObject; // Get the snapin name that is going to add pages. // This is stored in temp member of CPropertySheetToolTips // so that IPropertySheetCallback::AddPage knows which snapin // is adding pages. CLSID clsidSnapin; SC sc = ExtractSnapInCLSID(lpDataObject, &clsidSnapin); if (sc) { sc.TraceAndClear(); } else { tstring strName; if ( GetSnapinNameFromCLSID(clsidSnapin, strName)) m_pSheet->m_PropToolTips.SetThisSnapin(strName.data()); } } hr = pExtend->CreatePropertyPages( dynamic_cast(this), reinterpret_cast(pNotify), // deleted in Nodemgr m_pSheet->m_spDataObject); } /* * Bug 28193: If we're called with a NULL IUnknown, we also want to * force old-style wizards. */ else if (m_pSheet->IsWizard97()) m_pSheet->ForceOldStyleWizard(); // Build the property sheet structure from the list of pages if (hr == S_OK) { POSITION pos; int nCount = 0; pos = m_pSheet->m_PageList.GetHeadPosition(); { while(pos) { m_pSheet->m_pages[nCount] = reinterpret_cast(m_pSheet->m_PageList.GetNext(pos)); nCount++; } ASSERT(nCount < MAXPROPPAGES); m_pSheet->m_pstHeader.nPages = nCount; // must be page 0 for wizards if (m_pSheet->IsWizard()) m_pSheet->m_pstHeader.nStartPage = 0; // Empty the list for the extensions m_pSheet->m_PageList.RemoveAll(); return S_OK; // All done } } // Reached here because of error or the snap-in decided not to add any pages if (FAILED(hr) && pNotify != NULL) ::GlobalFree(pNotify); return hr; } STDMETHODIMP CPropertySheetProvider::AddExtensionPages() { DECLARE_SC(sc, TEXT("CPropertySheetProvider::AddExtensionPages")); if (m_pSheet == NULL) return E_UNEXPECTED; // Note: extension are not added until the WM_INITDIALOG of the sheet // This insures that the primaries pages are created the original size // and will make the extension pages conform if (m_pSheet->m_PageList.GetCount() != 0) return E_UNEXPECTED; // Make sure I have one of the two data objects(main or marshalled) ASSERT ((m_pSheet->m_spDataObject == NULL) != (m_pSheet->m_pThreadLocalDataObject == NULL)); if ((m_pSheet->m_spDataObject == NULL) == (m_pSheet->m_pThreadLocalDataObject == NULL)) return E_UNEXPECTED; LPDATAOBJECT lpDataObject = (m_pSheet->m_spDataObject) ? m_pSheet->m_spDataObject : m_pSheet->m_pThreadLocalDataObject; CExtensionsIterator it; sc = it.ScInitialize(lpDataObject, g_szPropertySheet); if (sc) { return S_FALSE; } IExtendPropertySheetPtr spPropertyExtension; LPPROPERTYSHEETCALLBACK pCallBack = dynamic_cast(this); ASSERT(pCallBack != NULL); // CoCreate each snap-in and have it add a sheet for ( ;!it.IsEnd(); it.Advance()) { sc = spPropertyExtension.CreateInstance(it.GetCLSID(), NULL, MMC_CLSCTX_INPROC); if (!sc.IsError()) { // Get the snapin name that is going to add pages. // This is stored in temp member of CPropertySheetToolTips // so that IPropertySheetCallback::AddPage knows which snapin // is adding pages. WTL::CString strName; // Fix for bug #469922(9/20/2001): DynamicExtensions broken in MMC20 // Snapin structures are only avail on static extensions - // get the name from reg for DynExtensions if (!it.IsDynamic()) { if (!it.GetSnapIn()->ScGetSnapInName(strName).IsError()) m_pSheet->m_PropToolTips.SetThisSnapin(strName); } else { if(!ScGetSnapinNameFromRegistry(it.GetCLSID(),strName).IsError()) m_pSheet->m_PropToolTips.SetThisSnapin(strName); } spPropertyExtension->CreatePropertyPages(pCallBack, NULL, lpDataObject); /* * Bug 282932: make sure this property sheet extension * stays alive for the life of the property sheet */ m_pSheet->m_Extenders.push_back (spPropertyExtension); } else { #if 0 //#ifdef DBG USES_CONVERSION; wchar_t buf[64]; StringFromGUID2 (spSnapIn->GetSnapInCLSID(), buf, countof(buf)); TRACE(_T("CLSID %s does not implement IID_IExtendPropertySheet\n"), W2T(buf)); #endif } } m_pSheet->AddExtensionPages(); m_pSheet->m_bAddExtension = TRUE; return S_OK; } STDMETHODIMP CPropertySheetProvider::AddMultiSelectionExtensionPages(LONG_PTR lMultiSelection) { if (m_pSheet == NULL) return E_UNEXPECTED; if (lMultiSelection == 0) return E_INVALIDARG; CMultiSelection* pMS = reinterpret_cast(lMultiSelection); ASSERT(pMS != NULL); // Note: extension are not added until the WM_INITDIALOG of the sheet // This insures that the primaries pages are created the original size // and will make the extension pages conform if (m_pSheet->m_PageList.GetCount() != 0) return E_UNEXPECTED; // Make sure I have one of the two data objects(main or marshalled) ASSERT ((m_pSheet->m_spDataObject == NULL) != (m_pSheet->m_pThreadLocalDataObject == NULL)); if ((m_pSheet->m_spDataObject == NULL) == (m_pSheet->m_pThreadLocalDataObject == NULL)) return E_UNEXPECTED; do // not a loop { CList snapinClsidList; HRESULT hr = pMS->GetExtensionSnapins(g_szPropertySheet, snapinClsidList); BREAK_ON_FAIL(hr); POSITION pos = snapinClsidList.GetHeadPosition(); if (pos == NULL) break; IDataObjectPtr spDataObject; hr = pMS->GetMultiSelDataObject(&spDataObject); ASSERT(SUCCEEDED(hr)); BREAK_ON_FAIL(hr); BOOL fProblem = FALSE; IExtendPropertySheetPtr spPropertyExtension; LPPROPERTYSHEETCALLBACK pCallBack = dynamic_cast(this); ASSERT(pCallBack != NULL); while (pos) { CLSID clsid = snapinClsidList.GetNext(pos); // CoCreate each snap-in and have it add a sheet // hr = spPropertyExtension.CreateInstance(clsid, NULL, MMC_CLSCTX_INPROC); CHECK_HRESULT(hr); if (FAILED(hr)) { #ifdef DBG wchar_t buf[64]; buf[0] = NULL; StringFromCLSID(clsid, (LPOLESTR*)&buf); TRACE(_T("CLSID %s does not implement IID_IExtendPropertySheet\n"), &buf); #endif fProblem = TRUE; // Continue even on error. continue; } spPropertyExtension->CreatePropertyPages(pCallBack, NULL, spDataObject); } if (fProblem == TRUE) hr = S_FALSE; } while (0); m_pSheet->AddExtensionPages(); m_pSheet->m_bAddExtension = TRUE; return S_OK; } //+------------------------------------------------------------------- // // Member: SetPropertySheetData // // Synopsis: Data pertaining to property sheet // // Arguments: [nPropertySheetType] - EPropertySheetType enum (scope item, result item...) // [hMTNode] - The master node that owns the property sheet for scope item // or that owns list view item of property sheet. // //-------------------------------------------------------------------- STDMETHODIMP CPropertySheetProvider::SetPropertySheetData(INT nPropSheetType, HMTNODE hMTNode) { m_pSheet->m_PropToolTips.SetPropSheetType((EPropertySheetType)nPropSheetType); if (hMTNode) { m_pSheet->m_pMTNode = CMTNode::FromHandle(hMTNode); } return S_OK; } // Copied from security.c in shell\shelldll /*++ Routine Description: This routine sets the security attributes for a given privilege. Arguments: PrivilegeName - Name of the privilege we are manipulating. NewPrivilegeAttribute - The new attribute value to use. OldPrivilegeAttribute - Pointer to receive the old privilege value. OPTIONAL Return value: NO_ERROR or WIN32 error. --*/ DWORD SetPrivilegeAttribute(LPCTSTR PrivilegeName, DWORD NewPrivilegeAttribute, DWORD *OldPrivilegeAttribute) { LUID PrivilegeValue; BOOL Result; TOKEN_PRIVILEGES TokenPrivileges, OldTokenPrivileges; DWORD ReturnLength; HANDLE TokenHandle; // // First, find out the LUID Value of the privilege // if(!LookupPrivilegeValue(NULL, PrivilegeName, &PrivilegeValue)) { return GetLastError(); } // // Get the token handle // if (!OpenProcessToken ( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &TokenHandle )) { return GetLastError(); } // // Set up the privilege set we will need // TokenPrivileges.PrivilegeCount = 1; TokenPrivileges.Privileges[0].Luid = PrivilegeValue; TokenPrivileges.Privileges[0].Attributes = NewPrivilegeAttribute; ReturnLength = sizeof(TOKEN_PRIVILEGES); if (!AdjustTokenPrivileges ( TokenHandle, FALSE, &TokenPrivileges, sizeof(OldTokenPrivileges), &OldTokenPrivileges, &ReturnLength )) { CloseHandle(TokenHandle); return GetLastError(); } else { if (OldPrivilegeAttribute != NULL) { *OldPrivilegeAttribute = OldTokenPrivileges.Privileges[0].Attributes; } CloseHandle(TokenHandle); return NO_ERROR; } }