1977 lines
61 KiB
C++
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;
|
|
}
|
|
}
|