|
|
//+-------------------------------------------------------------------------
//
// 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 <process.h>
#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 <multimon.h>
#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<LPPROPERTYNOTIFYINFO>( ::GlobalAlloc(GPTR, sizeof(PROPERTYNOTIFYINFO)));
if (pNotifyT == NULL) return E_OUTOFMEMORY;
*pNotifyT = *pNotify;
::PostMessage (pNotifyT->hwnd, MMC_MSG_PROP_SHEET_NOTIFY, reinterpret_cast<WPARAM>(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);
memset(&m_pstHeader, 0, sizeof(m_pstHeader)); memset(&m_pages, 0, 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)) { memset(&wndClass, 0, 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<HANDLE>( _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<HPROPSHEETPAGE>(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<UINT> 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<void**>(&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<void**>(&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(TOOLINFO)); 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; i<wndTabCtrl.GetItemCount(); i++) { // get rect area of each tab
wndTabCtrl.GetItemRect(i, &rc); POINT p[2]; p[0].x = rc.left; p[0].y = rc.top; p[1].x = rc.right; p[1].y = rc.bottom;
// Map the co-ordinates relative to property sheet.
MapWindowPoints(wndTabCtrl.m_hWnd, m_hDlg, p, 2); ti.rect.left = p[0].x; ti.rect.top = p[0].y; ti.rect.right = p[1].x; ti.rect.bottom = p[1].y ;
m_PropToolTips.AddTool(&ti); }
m_PropToolTips.Activate(TRUE);
} while (FALSE);
}
// Add third party extension
if (m_bAddExtension) { //AddExtensionPages();
m_bAddExtension = FALSE; }
return 0; }
LRESULT CPropertySheet::OnNcDestroy(CWPRETSTRUCT* pMsg) { if (m_hDlg != pMsg->hwnd) 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, 14); 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<HWND>(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<CPropertySheet*>(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. */ 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
if (dwErrorSave != NO_ERROR || !ExitWindowsEx(EWX_REBOOT, 0)) { 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<LONG_PTR>(new DataWindowData)); _Module.Lock(); // Lock the dll so that it does not get unloaded
// when property sheet is up (507338)[XPSP1: 59916]
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<CWPRETSTRUCT*>(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<LPPROPERTYNOTIFYINFO>( ::GlobalAlloc(GPTR, sizeof(PROPERTYNOTIFYINFO)));
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<CNodeInitObject*>(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<LPPROPERTYSHEETCALLBACK>(this), reinterpret_cast<LONG_PTR>(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<HPROPSHEETPAGE>(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<LPPROPERTYSHEETCALLBACK>(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): [XPSP1 Bug 599913]:
// 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<CMultiSelection*>(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<CLSID, CLSID&> 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<LPPROPERTYSHEETCALLBACK>(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(TOKEN_PRIVILEGES), &OldTokenPrivileges, &ReturnLength )) { CloseHandle(TokenHandle); return GetLastError(); } else { if (OldPrivilegeAttribute != NULL) { *OldPrivilegeAttribute = OldTokenPrivileges.Privileges[0].Attributes; } CloseHandle(TokenHandle); return NO_ERROR; } }
|