windows-nt/Source/XPSP1/NT/admin/activec/nodemgr/propsht.cpp
2020-09-26 16:20:57 +08:00

1977 lines
61 KiB
C++

//+-------------------------------------------------------------------------
//
// 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;
}
}