|
|
//=--------------------------------------------------------------------------=
// proxy.cpp
//=--------------------------------------------------------------------------=
// Copyright (c) 1999, Microsoft Corp.
// All Rights Reserved
// Information Contained Herein Is Proprietary and Confidential.
//=--------------------------------------------------------------------------=
//
// MMC Interfcace Proxy and Stub Functions
//
//=--------------------------------------------------------------------------=
// This file contains proxy and stub functions for MMC methods that are not
// remotable using a MIDL generated proxy and stub. Non-remotable methods have
// parameters that are ambiguous i.e. can be casted to different data types.
// For example, IComponentData::Notify() is passed an event and two additional
// LPARAM arguments that are interpreted according to the event. Sometimes an
// LPARAM contains a simple value such as a long or two BOOLs and sometimes it
// contains an IDataObject *. MIDL doesn't know the difference between
// MMCN_SELECT and MMCN_PRINT so we need to write some code to help out.
//
// The version of MMC.IDL in the designer directory has added a [local]
// attribute to all non-remotable methods. In addition, an extra method has
// been added to the same interface that is a remotable version of the method.
// The remotable version has more parameters and represents the union of all
// possible interpretations of the ambiguous parameter. For example,
// IExtendControlbar::ControlbarNotify() is defined as:
//
// [helpstring("User actions"), local]
// HRESULT ControlbarNotify([in] MMC_NOTIFY_TYPE event,
// [in] LPARAM arg, [in] LPARAM param);
//
// This method can receive MMCN_SELECT, MMCN_BTN_CLICK, and MMCN_MENU_BTNCLICK.
// The union of all possible parameter types is used in the following method
// added to that interface:
//
//
// HRESULT RemControlbarNotify([in] MMC_NOTIFY_TYPE event,
// [in] LPARAM lparam,
// [in] IDataObject *piDataObject,
// [in] MENUBUTTONDATA *MenuButtonData);
//
// Note, that normal, in-proc, non-remoted versions of IExtendControlbar do not
// have this extra method in their vtable because no one is going to call it.
// It is only used in the MILD generated proxy object.
//
// In order to tell MIDL which method remotes ControlbarNotify() an attribute
// control file (ACF) is used. The entry in the ACF for IExtendControlbar is:
//
// interface IExtendControlbar
// {
// [call_as (ControlbarNotify)]
// RemControlbarNotify();
//
// }
//
// This says that RemControlbarNotify should be called when remoting
// ControlbarNotify(). MIDL generates the proxy/stub code as usual but it only
// generates the prototypes for ControlbarNotify proxy and stub. We have to
// write these routines.
//
// When a remote client has an IExtendControlbarNotify pointer it actually
// points into the proxy vtable. MIDL sets the ControlbarNotify entry pointing to
// our IExtendControlbar_ControlbarNotify_Proxy() function below. That function
// interprets the parameters and then calls the MIDL generated
// IExtendControlbar_RemControlbarNotify_Proxy() that packs up the parameters
// and sends them off to the server. If a parameters is not applicable, (e.g.
// MMCN_SELECT does not receive a pointer to a MENUBUTTONDATA stuct), then a
// pointer to an empty struct or zeroes are sent.
//
// When the packet reaches the server side the MIDL generated
// IExtendControlbar_RemControlbarNotify_Stub() unpacks them and then calls our
// IExtendControlbar_ControlbarNotify_Stub() passing it the parameters and the
// IExtendControlbar pointer into the server. This function interprets the
// parameters and then calls ControlbarNotify in the server.
//
//=--------------------------------------------------------------------------=
#include "mmc.h"
extern HRESULT GetClipboardFormat ( WCHAR *pwszFormatName, CLIPFORMAT *pcfFormat );
extern HRESULT CreateMultiSelDataObject ( IDataObject **ppiDataObjects, long cDataObjects, IDataObject **ppiMultiSelDataObject );
static HRESULT MenuButtonClickProxy ( IExtendControlbar __RPC_FAR *This, IDataObject *piDataObject, MENUBUTTONDATA *pMenuButtonData );
static HRESULT IsMultiSelect(IDataObject *piDataObject, BOOL *pfMultiSelect) { HRESULT hr = S_OK; DWORD *pdwMultiSelect = NULL; BOOL fGotData = FALSE; FORMATETC FmtEtc; STGMEDIUM StgMed;
ZeroMemory(&FmtEtc, sizeof(FmtEtc)); ZeroMemory(&StgMed, sizeof(StgMed));
*pfMultiSelect = FALSE;
if (NULL == piDataObject) { goto Cleanup; }
if (IS_SPECIAL_DATAOBJECT(piDataObject)) { goto Cleanup; }
hr = GetClipboardFormat(CCF_MMC_MULTISELECT_DATAOBJECT, &FmtEtc.cfFormat); if (FAILED(hr)) { goto Cleanup; } FmtEtc.dwAspect = DVASPECT_CONTENT; FmtEtc.lindex = -1L; FmtEtc.tymed = TYMED_HGLOBAL; StgMed.tymed = TYMED_HGLOBAL;
hr = piDataObject->lpVtbl->GetData(piDataObject, &FmtEtc, &StgMed); if (SUCCEEDED(hr)) { fGotData = TRUE; } else { hr = S_OK; }
// Ignore any failures and assume that it is not multi-select. Snap-ins
// should return DV_E_FORMATETC or DV_E_CLIPFORMAT but in practice that
// is not the case. For example, the IIS snap-in returns E_NOTIMPL.
// It would be impossible to cover the range of reasonable return codes so
// we treat any error as format not supported.
if (fGotData) { pdwMultiSelect = (DWORD *)GlobalLock(StgMed.hGlobal);
if ((DWORD)1 == *pdwMultiSelect) { *pfMultiSelect = TRUE; } }
Cleanup: if (NULL != pdwMultiSelect) { (void)GlobalUnlock(StgMed.hGlobal); }
if (fGotData) { ReleaseStgMedium(&StgMed); } return hr; }
static HRESULT InterpretMultiSelect ( IDataObject *piDataObject, long *pcDataObjects, IDataObject ***pppiDataObjects ) { HRESULT hr = S_OK; SMMCDataObjects *pMMCDataObjects = NULL; BOOL fGotData = FALSE; size_t cbObjectTypes = 0; long i = 0; FORMATETC FmtEtc; STGMEDIUM StgMed;
ZeroMemory(&FmtEtc, sizeof(FmtEtc)); ZeroMemory(&StgMed, sizeof(StgMed));
*pcDataObjects = 0; *pppiDataObjects = NULL;
// Get the SMMCDataObjects structure from MMC
hr = GetClipboardFormat(CCF_MULTI_SELECT_SNAPINS, &FmtEtc.cfFormat); if (FAILED(hr)) { goto Cleanup; } FmtEtc.dwAspect = DVASPECT_CONTENT; FmtEtc.lindex = -1L; FmtEtc.tymed = TYMED_HGLOBAL; StgMed.tymed = TYMED_HGLOBAL;
hr = piDataObject->lpVtbl->GetData(piDataObject, &FmtEtc, &StgMed); if (FAILED(hr)) { goto Cleanup; }
fGotData = TRUE;
pMMCDataObjects = (SMMCDataObjects *)GlobalLock(StgMed.hGlobal); if (NULL == pMMCDataObjects) { hr = HRESULT_FROM_WIN32(GetLastError()); goto Cleanup; }
// Allocate an array of IDataObject and copy the IDataObjects to it
*pcDataObjects = pMMCDataObjects->count; *pppiDataObjects = (IDataObject **)GlobalAlloc(GPTR, pMMCDataObjects->count * sizeof(IDataObject *));
if (NULL == *pppiDataObjects) { hr = E_OUTOFMEMORY; goto Cleanup; }
for (i = 0; i < *pcDataObjects; i++) { (*pppiDataObjects)[i] = pMMCDataObjects->lpDataObject[i]; }
Cleanup: if (NULL != pMMCDataObjects) { (void)GlobalUnlock(StgMed.hGlobal); }
if (fGotData) { ReleaseStgMedium(&StgMed); }
return hr; }
void CheckForSpecialDataObjects ( IDataObject **ppiDataObject, BOOL *pfSpecialDataObject, long *plSpecialDataObject ) { long lSpecialDataObject = (long)(*ppiDataObject);
if (IS_SPECIAL_DATAOBJECT(lSpecialDataObject)) { *plSpecialDataObject = lSpecialDataObject; *ppiDataObject = NULL; *pfSpecialDataObject = TRUE; } else { *pfSpecialDataObject = FALSE; } }
static HRESULT SetRemote(IUnknown *This) { HRESULT hr = S_OK; IMMCRemote *piMMCRemote = NULL; DWORD cbFileName = 0; char szModuleFileName[MAX_PATH] = "";
// Call IMMCRemote methods: ObjectIsRemote and SetMMCExePath so that the
// snap-in will know it is remote and so that it will have MMC.EXE's full
// path in order to build taskpad display strings.
hr = This->lpVtbl->QueryInterface(This, &IID_IMMCRemote, (void **)&piMMCRemote); if (FAILED(hr)) { // If the object doesn't support IMMCRemote that is not an error.
// The designer runtime will get this QI on both its main object and
// its IComponent object but only the main object needs to support the
// interface.
hr = S_OK; goto Cleanup; }
hr = piMMCRemote->lpVtbl->ObjectIsRemote(piMMCRemote); if (FAILED(hr)) { goto Cleanup; }
cbFileName = GetModuleFileName(NULL, // get executable that loaded us (MMC)
szModuleFileName, sizeof(szModuleFileName));
if (0 == cbFileName) { hr = HRESULT_FROM_WIN32(GetLastError()); goto Cleanup; }
hr = piMMCRemote->lpVtbl->SetMMCExePath(piMMCRemote, szModuleFileName); if (FAILED(hr)) { goto Cleanup; }
hr = piMMCRemote->lpVtbl->SetMMCCommandLine(piMMCRemote, GetCommandLine()); if (FAILED(hr)) { goto Cleanup; }
Cleanup: if (NULL != piMMCRemote) { piMMCRemote->lpVtbl->Release(piMMCRemote); }
return hr; }
HRESULT STDMETHODCALLTYPE IExtendControlbar_SetControlbar_Proxy ( IExtendControlbar __RPC_FAR *This, LPCONTROLBAR pControlbar ) { HRESULT hr = S_OK;
// Make sure the snap-in knows we are remoted. We do this here because
// this is the first opportunity for the proxy to inform a toolbar
// extension that it is remote.
hr = SetRemote((IUnknown *)This); if (FAILED(hr)) { goto Cleanup; }
hr = IExtendControlbar_RemSetControlbar_Proxy(This, pControlbar);
Cleanup: return hr; }
HRESULT STDMETHODCALLTYPE IExtendControlbar_SetControlbar_Stub ( IExtendControlbar __RPC_FAR *This, LPCONTROLBAR pControlbar ) { return This->lpVtbl->SetControlbar(This, pControlbar); }
HRESULT STDMETHODCALLTYPE IExtendControlbar_ControlbarNotify_Proxy ( IExtendControlbar __RPC_FAR *This, MMC_NOTIFY_TYPE event, LPARAM arg, LPARAM param ) { HRESULT hr = S_OK; BOOL fIsMultiSelect = FALSE; long cDataObjects = 1L; IDataObject *piDataObject = NULL; // Not AddRef()ed
IDataObject **ppiDataObjects = NULL; BOOL fSpecialDataObject = FALSE; long lSpecialDataObject = 0;
// If this is not a menu button click then we can use the generated remoting
// code with the arg and param unions
if (MMCN_MENU_BTNCLICK == event) { hr = MenuButtonClickProxy(This, (IDataObject *)arg, (MENUBUTTONDATA *)param); goto Cleanup; } // Get any IDataObject associated with the event
switch (event) { case MMCN_SELECT: piDataObject = (IDataObject *)param; break;
case MMCN_BTN_CLICK: piDataObject = (IDataObject *)arg; break;
default: piDataObject = NULL; break; }
// Check for special data objects such DOBJ_CUSTOMWEB etc.
CheckForSpecialDataObjects(&piDataObject, &fSpecialDataObject, &lSpecialDataObject);
// If this is a mutliple selection then we need to extract the data
// objects in the HGLOBAL
if (!fSpecialDataObject) { hr = IsMultiSelect(piDataObject, &fIsMultiSelect); if (FAILED(hr)) { goto Cleanup; } }
if (fIsMultiSelect) { hr = InterpretMultiSelect(piDataObject, &cDataObjects, &ppiDataObjects); if (FAILED(hr)) { goto Cleanup; } } else { ppiDataObjects = &piDataObject; }
hr = IExtendControlbar_RemControlbarNotify_Proxy(This, cDataObjects, ppiDataObjects, fSpecialDataObject, lSpecialDataObject, event, arg, param); Cleanup: if ( fIsMultiSelect && (NULL != ppiDataObjects) ) { (void)GlobalFree(ppiDataObjects); } return hr; }
HRESULT STDMETHODCALLTYPE IExtendControlbar_ControlbarNotify_Stub ( IExtendControlbar __RPC_FAR *This, long cDataObjects, IDataObject **ppiDataObjects, BOOL fSpecialDataObject, long lSpecialDataObject, MMC_NOTIFY_TYPE event, LPARAM arg, LPARAM param ) { HRESULT hr = S_OK; IDataObject *piDataObject = NULL; // Not AddRef()ed
IDataObject *piMultiSelDataObject = NULL;
// If there is more than one data object then we need to pack them into a
// a separate data object that appears as a multi-select data object.
if (cDataObjects > 1L) { hr = CreateMultiSelDataObject(ppiDataObjects, cDataObjects, &piMultiSelDataObject); if (FAILED(hr)) { goto Cleanup; } piDataObject = piMultiSelDataObject; } else if (fSpecialDataObject) { piDataObject = (IDataObject *)lSpecialDataObject; } else { piDataObject = ppiDataObjects[0]; }
// Put the IDataObject into the corresponding parameter for the event
switch (event) { case MMCN_SELECT: param = (LPARAM)piDataObject; break;
case MMCN_BTN_CLICK: arg = (LPARAM)piDataObject; break;
default: break; }
// Call into the snap-in with all parameters appearing as they would
// when in-proc.
hr = This->lpVtbl->ControlbarNotify(This, event, arg, param);
Cleanup: if (NULL != piMultiSelDataObject) { piMultiSelDataObject->lpVtbl->Release(piMultiSelDataObject); } return hr; }
static HRESULT MenuButtonClickProxy ( IExtendControlbar __RPC_FAR *This, IDataObject *piDataObject, MENUBUTTONDATA *pMenuButtonData ) { HRESULT hr = S_OK; POPUP_MENUDEF *pPopupMenuDef = NULL; HMENU hMenu = NULL; UINT uiSelectedItemID = 0; IExtendControlbarRemote *piECRemote = NULL; long i = 0; BOOL fIsMultiSelect = FALSE; long cDataObjects = 1L; IDataObject **ppiDataObjects = NULL;
// The generated remoting cannot easily handle what we need to do so we get
// IExtendControlbarRemote on the snap-in. This interface has methods that
// allow us to ask the snap-in for its popup menu items, display the menu
// here on the MMC side, and then tell the snap-in which item was selected.
hr = This->lpVtbl->QueryInterface(This, &IID_IExtendControlbarRemote, (void **)&piECRemote); if (FAILED(hr)) { goto Cleanup; }
// Tell the snap-in about the menu button click and get back its list of
// popup menu items.
hr = piECRemote->lpVtbl->MenuButtonClick(piECRemote, piDataObject, pMenuButtonData->idCommand, &pPopupMenuDef);
if ( FAILED(hr) || (NULL == pPopupMenuDef) ) { goto Cleanup; }
// Create an empty Win32 menu
hMenu = CreatePopupMenu(); if (NULL == hMenu) { hr = HRESULT_FROM_WIN32(GetLastError()); goto Cleanup; }
// Iterate through each of the items and add them to the menu
for (i = 0; i < pPopupMenuDef->cMenuItems; i++) { if (!AppendMenu(hMenu, pPopupMenuDef->MenuItems[i].uiFlags, pPopupMenuDef->MenuItems[i].uiItemID, pPopupMenuDef->MenuItems[i].pszItemText)) { hr = HRESULT_FROM_WIN32(GetLastError()); goto Cleanup; } }
// If the owner HWND is NULL then this is an extension and it does not have
// access to IConsole2 on MMC to get the main frame HWND. In this case just
// use the active window on this thread.
if (NULL == pPopupMenuDef->hwndMenuOwner) { pPopupMenuDef->hwndMenuOwner = GetActiveWindow(); }
// Display the popup menu and wait for a selection.
uiSelectedItemID = (UINT)TrackPopupMenu( hMenu, // menu to display
TPM_LEFTALIGN | // align left side of menu with x
TPM_TOPALIGN | // align top of menu with y
TPM_NONOTIFY | // don't send any messages during selection
TPM_RETURNCMD | // make the ret val the selected item
TPM_LEFTBUTTON, // allow selection with left button only
pMenuButtonData->x, // left side coordinate
pMenuButtonData->y, // top coordinate
0, // reserved,
pPopupMenuDef->hwndMenuOwner, // owner window, this comes from snap-in
// as it can call IConsole2->GetMainWindow
NULL); // not used
// A zero return could indicate either an error or that the user hit
// Escape or clicked off of the menu to cancel the operation. GetLastError()
// determines whether there was an error. Either way we're done but set the
// hr first.
if (0 == uiSelectedItemID) { hr = HRESULT_FROM_WIN32(GetLastError()); goto Cleanup; }
// If i is non-zero then it contains the ID of the selected item.
// Tell the snap-in what was selected and pass it the extra IUnknown it
// included in its menu definition (this is snap-in defined and it allows
// the snap-in to include some more identifying information to handle the
// event).
if (0 != uiSelectedItemID) { hr = piECRemote->lpVtbl->PopupMenuClick( piECRemote, piDataObject, uiSelectedItemID, pPopupMenuDef->punkSnapInDefined); }
Cleanup:
if (NULL != piECRemote) { piECRemote->lpVtbl->Release(piECRemote); } if (NULL != hMenu) { (void)DestroyMenu(hMenu); }
if (NULL != pPopupMenuDef) { for (i = 0; i < pPopupMenuDef->cMenuItems; i++) { if (NULL != pPopupMenuDef->MenuItems[i].pszItemText) { CoTaskMemFree(pPopupMenuDef->MenuItems[i].pszItemText); } } if (NULL != pPopupMenuDef->punkSnapInDefined) { pPopupMenuDef->punkSnapInDefined->lpVtbl->Release(pPopupMenuDef->punkSnapInDefined); } CoTaskMemFree(pPopupMenuDef); }
return hr; }
HRESULT STDMETHODCALLTYPE IExtendControlbarRemote_MenuButtonClick_Proxy ( IExtendControlbarRemote __RPC_FAR *This, IDataObject __RPC_FAR *piDataObject, int idCommand, POPUP_MENUDEF __RPC_FAR *__RPC_FAR *ppPopupMenuDef ) { HRESULT hr = S_OK; BOOL fIsMultiSelect = FALSE; long cDataObjects = 1L; IDataObject **ppiDataObjects = NULL; BOOL fSpecialDataObject = FALSE; long lSpecialDataObject = 0;
// Check for special data objects such DOBJ_CUSTOMWEB etc.
CheckForSpecialDataObjects(&piDataObject, &fSpecialDataObject, &lSpecialDataObject);
// If this is a mutliple selection then we need to extract the data
// objects in the HGLOBAL
if (!fSpecialDataObject) { hr = IsMultiSelect(piDataObject, &fIsMultiSelect); if (FAILED(hr)) { goto Cleanup; } }
if (fIsMultiSelect) { hr = InterpretMultiSelect(piDataObject, &cDataObjects, &ppiDataObjects); if (FAILED(hr)) { goto Cleanup; } } else { ppiDataObjects = &piDataObject; }
hr = IExtendControlbarRemote_RemMenuButtonClick_Proxy(This, cDataObjects, ppiDataObjects, fSpecialDataObject, lSpecialDataObject, idCommand, ppPopupMenuDef); Cleanup: if ( fIsMultiSelect && (NULL != ppiDataObjects) ) { (void)GlobalFree(ppiDataObjects); } return hr; }
HRESULT STDMETHODCALLTYPE IExtendControlbarRemote_MenuButtonClick_Stub ( IExtendControlbarRemote __RPC_FAR *This, long cDataObjects, IDataObject __RPC_FAR *__RPC_FAR ppiDataObjects[ ], BOOL fSpecialDataObject, long lSpecialDataObject, int idCommand, POPUP_MENUDEF __RPC_FAR *__RPC_FAR *ppPopupMenuDef ) { HRESULT hr = S_OK; IDataObject *piDataObject = NULL; // Not AddRef()ed
IDataObject *piMultiSelDataObject = NULL;
// If there is more than one data object then we need to pack them into a
// a separate data object that appears as a multi-select data object.
if (cDataObjects > 1L) { hr = CreateMultiSelDataObject(ppiDataObjects, cDataObjects, &piMultiSelDataObject); if (FAILED(hr)) { goto Cleanup; } piDataObject = piMultiSelDataObject; } else if (fSpecialDataObject) { piDataObject = (IDataObject *)lSpecialDataObject; } else { piDataObject = ppiDataObjects[0]; }
// Call the snap-in
hr = This->lpVtbl->MenuButtonClick(This, piDataObject, idCommand, ppPopupMenuDef);
Cleanup: if (NULL != piMultiSelDataObject) { piMultiSelDataObject->lpVtbl->Release(piMultiSelDataObject); } return hr; }
HRESULT STDMETHODCALLTYPE IExtendControlbarRemote_PopupMenuClick_Proxy ( IExtendControlbarRemote __RPC_FAR *This, IDataObject __RPC_FAR *piDataObject, UINT uIDItem, IUnknown __RPC_FAR *punkParam ) { HRESULT hr = S_OK; BOOL fIsMultiSelect = FALSE; long cDataObjects = 1L; IDataObject **ppiDataObjects = NULL; BOOL fSpecialDataObject = FALSE; long lSpecialDataObject = 0;
// Check for special data objects such DOBJ_CUSTOMWEB etc.
CheckForSpecialDataObjects(&piDataObject, &fSpecialDataObject, &lSpecialDataObject);
// If this is a mutliple selection then we need to extract the data
// objects in the HGLOBAL
if (!fSpecialDataObject) { hr = IsMultiSelect(piDataObject, &fIsMultiSelect); if (FAILED(hr)) { goto Cleanup; } }
if (fIsMultiSelect) { hr = InterpretMultiSelect(piDataObject, &cDataObjects, &ppiDataObjects); if (FAILED(hr)) { goto Cleanup; } } else { ppiDataObjects = &piDataObject; }
hr = IExtendControlbarRemote_RemPopupMenuClick_Proxy(This, cDataObjects, ppiDataObjects, fSpecialDataObject, lSpecialDataObject, uIDItem, punkParam); Cleanup: if ( fIsMultiSelect && (NULL != ppiDataObjects) ) { (void)GlobalFree(ppiDataObjects); } return hr; }
HRESULT STDMETHODCALLTYPE IExtendControlbarRemote_PopupMenuClick_Stub ( IExtendControlbarRemote __RPC_FAR *This, long cDataObjects, IDataObject __RPC_FAR *__RPC_FAR ppiDataObjects[ ], BOOL fSpecialDataObject, long lSpecialDataObject, UINT uIDItem, IUnknown __RPC_FAR *punkParam ) { HRESULT hr = S_OK; IDataObject *piDataObject = NULL; // Not AddRef()ed
IDataObject *piMultiSelDataObject = NULL;
// If there is more than one data object then we need to pack them into a
// a separate data object that appears as a multi-select data object.
if (cDataObjects > 1L) { hr = CreateMultiSelDataObject(ppiDataObjects, cDataObjects, &piMultiSelDataObject); if (FAILED(hr)) { goto Cleanup; } piDataObject = piMultiSelDataObject; } else if (fSpecialDataObject) { piDataObject = (IDataObject *)lSpecialDataObject; } else { piDataObject = ppiDataObjects[0]; }
// Call the snap-in
hr = This->lpVtbl->PopupMenuClick(This, piDataObject, uIDItem, punkParam);
Cleanup: if (NULL != piMultiSelDataObject) { piMultiSelDataObject->lpVtbl->Release(piMultiSelDataObject); } return hr; }
HRESULT STDMETHODCALLTYPE IComponentData_Initialize_Proxy ( IComponentData *This, LPUNKNOWN pUnknown ) { HRESULT hr = S_OK;
// Tell the object it is remote and give the path to mmc.exe
hr = SetRemote((IUnknown *)This); if (FAILED(hr)) { goto Cleanup; }
// Now pass on the Initiaize call normally. Using this order allows a snap-in
// to know it is remote prior to its IComponentData::Initialize in case it
// needs that information up front.
hr = IComponentData_RemInitialize_Proxy(This, pUnknown); if (FAILED(hr)) { goto Cleanup; }
Cleanup:
return hr; }
HRESULT STDMETHODCALLTYPE IComponentData_Initialize_Stub ( IComponentData *This, LPUNKNOWN pUnknown ) { return This->lpVtbl->Initialize(This, pUnknown); }
HRESULT STDMETHODCALLTYPE IComponentData_CreateComponent_Proxy ( IComponentData *This, LPCOMPONENT *ppComponent ) { HRESULT hr = S_OK;
// Tell the object it is remote and give the path to mmc.exe
hr = SetRemote((IUnknown *)This); if (FAILED(hr)) { goto Cleanup; }
// Now pass on the CreateComponent call normally. Using this order allows a
// snap-in to know it is remote prior to its
// IComponentData::CreateComponent in case it needs that information up
// front.
// We do this in IComponentData::Initialize and
// IComponentData::CreateComponent. Most cases will use Initialize but in
// MMC 1.1 a taskpad extension does not receive IComponentData::Initialize.
// MMC only calls IComponentData::CreateComponent. As a taskpad extension
// may need to resolve a res:// URL to use the mmc.exe path we need to do
// it here as well.
hr = IComponentData_RemCreateComponent_Proxy(This, ppComponent); if (FAILED(hr)) { goto Cleanup; }
Cleanup:
return hr; }
HRESULT STDMETHODCALLTYPE IComponentData_CreateComponent_Stub ( IComponentData *This, LPCOMPONENT *ppComponent ) { return This->lpVtbl->CreateComponent(This, ppComponent); }
HRESULT STDMETHODCALLTYPE IComponentData_Notify_Proxy ( IComponentData __RPC_FAR *This, LPDATAOBJECT piDataObject, MMC_NOTIFY_TYPE event, LPARAM arg, LPARAM param ) { BOOL fSpecialDataObject = FALSE; long lSpecialDataObject = 0;
ICDNotifyParam ParamUnion; ZeroMemory(&ParamUnion, sizeof(ParamUnion));
// Check for special data objects such DOBJ_CUSTOMWEB etc.
CheckForSpecialDataObjects(&piDataObject, &fSpecialDataObject, &lSpecialDataObject);
ParamUnion.value = param; return IComponentData_RemNotify_Proxy(This, piDataObject, fSpecialDataObject, lSpecialDataObject, event, arg, &ParamUnion); }
HRESULT STDMETHODCALLTYPE IComponentData_Notify_Stub ( IComponentData __RPC_FAR *This, LPDATAOBJECT piDataObject, BOOL fSpecialDataObject, long lSpecialDataObject, MMC_NOTIFY_TYPE event, LPARAM arg, ICDNotifyParam *pParamUnion ) { if (fSpecialDataObject) { piDataObject = (IDataObject *)lSpecialDataObject; } return This->lpVtbl->Notify(This, piDataObject, event, arg, pParamUnion->value); }
HRESULT STDMETHODCALLTYPE IComponentData_CompareObjects_Proxy ( IComponentData __RPC_FAR *This, IDataObject *piDataObjectA, IDataObject *piDataObjectB ) { HRESULT hr = S_OK;
BOOL fIsMultiSelectA = FALSE; long cDataObjectsA = 1L; IDataObject **ppiDataObjectsA = NULL; BOOL fSpecialDataObjectA = FALSE; long lSpecialDataObjectA = 0;
BOOL fIsMultiSelectB = FALSE; long cDataObjectsB = 1L; IDataObject **ppiDataObjectsB = NULL; BOOL fSpecialDataObjectB = FALSE; long lSpecialDataObjectB = 0;
// Check for special data objects such DOBJ_CUSTOMWEB etc.
CheckForSpecialDataObjects(&piDataObjectA, &fSpecialDataObjectA, &lSpecialDataObjectA);
CheckForSpecialDataObjects(&piDataObjectB, &fSpecialDataObjectB, &lSpecialDataObjectB);
// If this is a mutliple selection then we need to extract the data
// objects in the HGLOBAL
if (!fSpecialDataObjectA) { hr = IsMultiSelect(piDataObjectA, &fIsMultiSelectA); if (FAILED(hr)) { goto Cleanup; } }
if (!fSpecialDataObjectB) { hr = IsMultiSelect(piDataObjectB, &fIsMultiSelectB); if (FAILED(hr)) { goto Cleanup; } }
if (fIsMultiSelectA) { hr = InterpretMultiSelect(piDataObjectA, &cDataObjectsA, &ppiDataObjectsA); if (FAILED(hr)) { goto Cleanup; } } else { ppiDataObjectsA = &piDataObjectA; }
if (fIsMultiSelectB) { hr = InterpretMultiSelect(piDataObjectB, &cDataObjectsB, &ppiDataObjectsB); if (FAILED(hr)) { goto Cleanup; } } else { ppiDataObjectsB = &piDataObjectB; }
hr = IComponentData_RemCompareObjects_Proxy(This, cDataObjectsA, ppiDataObjectsA, fSpecialDataObjectA, lSpecialDataObjectA,
cDataObjectsB, ppiDataObjectsB, fSpecialDataObjectB, lSpecialDataObjectB); Cleanup: if ( fIsMultiSelectA && (NULL != ppiDataObjectsA) ) { (void)GlobalFree(ppiDataObjectsA); } if ( fIsMultiSelectB && (NULL != ppiDataObjectsB) ) { (void)GlobalFree(ppiDataObjectsB); } return hr; }
HRESULT STDMETHODCALLTYPE IComponentData_CompareObjects_Stub ( IComponentData __RPC_FAR *This,
long cDataObjectsA, IDataObject __RPC_FAR *__RPC_FAR ppiDataObjectsA[ ], BOOL fSpecialDataObjectA, long lSpecialDataObjectA,
long cDataObjectsB, IDataObject __RPC_FAR *__RPC_FAR ppiDataObjectsB[ ], BOOL fSpecialDataObjectB, long lSpecialDataObjectB ) { HRESULT hr = S_OK; IDataObject *piDataObjectA = NULL; // Not AddRef()ed
IDataObject *piMultiSelDataObjectA = NULL; IDataObject *piDataObjectB = NULL; // Not AddRef()ed
IDataObject *piMultiSelDataObjectB = NULL;
// If there is more than one data object then we need to pack them into a
// a separate data object that appears as a multi-select data object.
if (cDataObjectsA > 1L) { hr = CreateMultiSelDataObject(ppiDataObjectsA, cDataObjectsA, &piMultiSelDataObjectA); if (FAILED(hr)) { goto Cleanup; } piDataObjectA = piMultiSelDataObjectA; } else if (fSpecialDataObjectA) { piDataObjectA = (IDataObject *)lSpecialDataObjectA; } else { piDataObjectA = ppiDataObjectsA[0]; }
if (cDataObjectsB > 1L) { hr = CreateMultiSelDataObject(ppiDataObjectsB, cDataObjectsB, &piMultiSelDataObjectB); if (FAILED(hr)) { goto Cleanup; } piDataObjectB = piMultiSelDataObjectB; } else if (fSpecialDataObjectB) { piDataObjectB = (IDataObject *)lSpecialDataObjectB; } else { piDataObjectB = ppiDataObjectsB[0]; }
// Call the snap-in
hr = This->lpVtbl->CompareObjects(This, piDataObjectA, piDataObjectB);
Cleanup: if (NULL != piMultiSelDataObjectA) { piMultiSelDataObjectA->lpVtbl->Release(piMultiSelDataObjectA); } if (NULL != piMultiSelDataObjectB) { piMultiSelDataObjectB->lpVtbl->Release(piMultiSelDataObjectB); } return hr; }
HRESULT STDMETHODCALLTYPE IComponent_Notify_Proxy ( IComponent __RPC_FAR *This, LPDATAOBJECT piDataObject, MMC_NOTIFY_TYPE event, LPARAM arg, LPARAM param ) { ICNotifyArg ArgUnion; ICNotifyParam ParamUnion; ICOutParam *pOutParam = NULL; HRESULT hr = S_OK; BOOL fIsMultiSelect = FALSE; long cDataObjects = 1L; IDataObject **ppiDataObjects = NULL; BOOL fSpecialDataObject = FALSE; long lSpecialDataObject = 0;
ZeroMemory(&ArgUnion, sizeof(ArgUnion)); ZeroMemory(&ParamUnion, sizeof(ParamUnion));
// Switch any potential multiselect data objects with arg/param so that
// piDataObject always contains the potential multiselect.
switch (event) { case MMCN_QUERY_PASTE: ArgUnion.pidoQueryPasteTarget = piDataObject; piDataObject = (IDataObject *)arg; ParamUnion.value = param; break;
case MMCN_PASTE: ArgUnion.pidoPasteTarget = piDataObject; piDataObject = (IDataObject *)arg; // Pass through param as an LPARAM rather than the IDataObject **
// it really is. This is just to let the stub know whether it is
// a copy or a move. If it is a move the CUTORMOVE IDataObject will
// be in the ICOutParam returned from the stub.
ParamUnion.value = param; break;
case MMCN_RESTORE_VIEW: ArgUnion.value = arg; // Don't pass param because it is a BOOL * that will not be
// marshaled. The BOOL will be received in the ICOutParam returned
// from the stub.
break; default: ArgUnion.value = arg; ParamUnion.value = param; }
// Check for special data objects such DOBJ_CUSTOMWEB etc.
CheckForSpecialDataObjects(&piDataObject, &fSpecialDataObject, &lSpecialDataObject);
// If this is a mutliple selection then we need to extract the data
// objects in the HGLOBAL
if (!fSpecialDataObject) { hr = IsMultiSelect(piDataObject, &fIsMultiSelect); if (FAILED(hr)) { goto Cleanup; } }
if (fIsMultiSelect) { hr = InterpretMultiSelect(piDataObject, &cDataObjects, &ppiDataObjects); if (FAILED(hr)) { goto Cleanup; } } else { ppiDataObjects = &piDataObject; }
hr = IComponent_RemNotify_Proxy(This, cDataObjects, ppiDataObjects, fSpecialDataObject, lSpecialDataObject, event, &ArgUnion, &ParamUnion, &pOutParam); Cleanup: if (NULL != pOutParam) { if (MMCN_PASTE == event) { *((IDataObject **)param) = pOutParam->pidoCutOrMove; } else if (MMCN_RESTORE_VIEW == event) { *((BOOL *)param) = pOutParam->fRestoreHandled; } CoTaskMemFree(pOutParam); }
if ( fIsMultiSelect && (NULL != ppiDataObjects) ) { (void)GlobalFree(ppiDataObjects); }
return hr; }
HRESULT STDMETHODCALLTYPE IComponent_Notify_Stub ( IComponent __RPC_FAR *This, long cDataObjects, IDataObject **ppiDataObjects, BOOL fSpecialDataObject, long lSpecialDataObject, MMC_NOTIFY_TYPE event, ICNotifyArg *pArgUnion, ICNotifyParam *pParamUnion, ICOutParam **ppOutParam ) { HRESULT hr = S_OK; IDataObject *piDataObject = NULL; // Not AddRef()ed
IDataObject *piMultiSelDataObject = NULL; LPARAM Arg = 0; LPARAM Param = 0;
*ppOutParam = (ICOutParam *)CoTaskMemAlloc(sizeof(ICOutParam)); if (NULL == *ppOutParam) { hr = E_OUTOFMEMORY; goto Cleanup; }
ZeroMemory(*ppOutParam, sizeof(ICOutParam));
// If there is more than one data object then we need to pack them into a
// a separate data object that appears as a multi-select data object.
if (cDataObjects > 1L) { hr = CreateMultiSelDataObject(ppiDataObjects, cDataObjects, &piMultiSelDataObject); if (FAILED(hr)) { goto Cleanup; } piDataObject = piMultiSelDataObject; } else if (fSpecialDataObject) { piDataObject = (IDataObject *)lSpecialDataObject; } else { piDataObject = ppiDataObjects[0]; }
// If the event required swaping Arg and IDataObject then swap it back
// before calling into the object. For MMCN_QUERY_PASTE and
// MMCN_RESTORE_VIEW we need to make Param contain the out pointer.
switch (event) { case MMCN_PASTE: Arg = (LPARAM)piDataObject; piDataObject = pArgUnion->pidoPasteTarget; if (0 == pParamUnion->value) { // This is a copy, pass zero in Param so snap-in will know that
Param = 0; } else { // This is a move. Pass the address of the IDataObject in
// the ICOutParam that we will return to the proxy.
Param = (LPARAM)&((*ppOutParam)->pidoCutOrMove); } break;
case MMCN_QUERY_PASTE: Arg = (LPARAM)piDataObject; piDataObject = pArgUnion->pidoQueryPasteTarget; Param = pParamUnion->value; break;
case MMCN_RESTORE_VIEW: Arg = pArgUnion->value; Param = (LPARAM)&((*ppOutParam)->fRestoreHandled); break;
default: Arg = pArgUnion->value; Param = pParamUnion->value; break; }
hr = This->lpVtbl->Notify(This, piDataObject, event, Arg, Param);
Cleanup: if (FAILED(hr)) { if (NULL != *ppOutParam) { CoTaskMemFree(*ppOutParam); *ppOutParam = NULL; } } if (NULL != piMultiSelDataObject) { piMultiSelDataObject->lpVtbl->Release(piMultiSelDataObject); } return hr; }
HRESULT STDMETHODCALLTYPE IComponent_CompareObjects_Proxy ( IComponent __RPC_FAR *This, IDataObject *piDataObjectA, IDataObject *piDataObjectB ) { HRESULT hr = S_OK;
BOOL fIsMultiSelectA = FALSE; long cDataObjectsA = 1L; IDataObject **ppiDataObjectsA = NULL; BOOL fSpecialDataObjectA = FALSE; long lSpecialDataObjectA = 0;
BOOL fIsMultiSelectB = FALSE; long cDataObjectsB = 1L; IDataObject **ppiDataObjectsB = NULL; BOOL fSpecialDataObjectB = FALSE; long lSpecialDataObjectB = 0;
// Check for special data objects such DOBJ_CUSTOMWEB etc.
CheckForSpecialDataObjects(&piDataObjectA, &fSpecialDataObjectA, &lSpecialDataObjectA);
CheckForSpecialDataObjects(&piDataObjectB, &fSpecialDataObjectB, &lSpecialDataObjectB);
// If this is a mutliple selection then we need to extract the data
// objects in the HGLOBAL
if (!fSpecialDataObjectA) { hr = IsMultiSelect(piDataObjectA, &fIsMultiSelectA); if (FAILED(hr)) { goto Cleanup; } }
if (!fSpecialDataObjectB) { hr = IsMultiSelect(piDataObjectB, &fIsMultiSelectB); if (FAILED(hr)) { goto Cleanup; } }
if (fIsMultiSelectA) { hr = InterpretMultiSelect(piDataObjectA, &cDataObjectsA, &ppiDataObjectsA); if (FAILED(hr)) { goto Cleanup; } } else { ppiDataObjectsA = &piDataObjectA; }
if (fIsMultiSelectB) { hr = InterpretMultiSelect(piDataObjectB, &cDataObjectsB, &ppiDataObjectsB); if (FAILED(hr)) { goto Cleanup; } } else { ppiDataObjectsB = &piDataObjectB; }
hr = IComponent_RemCompareObjects_Proxy(This, cDataObjectsA, ppiDataObjectsA, fSpecialDataObjectA, lSpecialDataObjectA,
cDataObjectsB, ppiDataObjectsB, fSpecialDataObjectB, lSpecialDataObjectB); Cleanup: if ( fIsMultiSelectA && (NULL != ppiDataObjectsA) ) { (void)GlobalFree(ppiDataObjectsA); } if ( fIsMultiSelectB && (NULL != ppiDataObjectsB) ) { (void)GlobalFree(ppiDataObjectsB); } return hr; }
HRESULT STDMETHODCALLTYPE IComponent_CompareObjects_Stub ( IComponent __RPC_FAR *This,
long cDataObjectsA, IDataObject __RPC_FAR *__RPC_FAR ppiDataObjectsA[ ], BOOL fSpecialDataObjectA, long lSpecialDataObjectA,
long cDataObjectsB, IDataObject __RPC_FAR *__RPC_FAR ppiDataObjectsB[ ], BOOL fSpecialDataObjectB, long lSpecialDataObjectB ) { HRESULT hr = S_OK; IDataObject *piDataObjectA = NULL; // Not AddRef()ed
IDataObject *piMultiSelDataObjectA = NULL; IDataObject *piDataObjectB = NULL; // Not AddRef()ed
IDataObject *piMultiSelDataObjectB = NULL;
// If there is more than one data object then we need to pack them into a
// a separate data object that appears as a multi-select data object.
if (cDataObjectsA > 1L) { hr = CreateMultiSelDataObject(ppiDataObjectsA, cDataObjectsA, &piMultiSelDataObjectA); if (FAILED(hr)) { goto Cleanup; } piDataObjectA = piMultiSelDataObjectA; } else if (fSpecialDataObjectA) { piDataObjectA = (IDataObject *)lSpecialDataObjectA; } else { piDataObjectA = ppiDataObjectsA[0]; }
if (cDataObjectsB > 1L) { hr = CreateMultiSelDataObject(ppiDataObjectsB, cDataObjectsB, &piMultiSelDataObjectB); if (FAILED(hr)) { goto Cleanup; } piDataObjectB = piMultiSelDataObjectB; } else if (fSpecialDataObjectB) { piDataObjectB = (IDataObject *)lSpecialDataObjectB; } else { piDataObjectB = ppiDataObjectsB[0]; }
// Call the snap-in
hr = This->lpVtbl->CompareObjects(This, piDataObjectA, piDataObjectB);
Cleanup: if (NULL != piMultiSelDataObjectA) { piMultiSelDataObjectA->lpVtbl->Release(piMultiSelDataObjectA); } if (NULL != piMultiSelDataObjectB) { piMultiSelDataObjectB->lpVtbl->Release(piMultiSelDataObjectB); } return hr; }
//=--------------------------------------------------------------------------=
//
// SCOPEDATAITEM Marshaling
//
//
//=--------------------------------------------------------------------------=
// Caveat: When returning a string in SCOPEDATAITEM MMC does not use a
// callee allocate/caller free strategy. When in-proc, the owner of the memory
// must insure that it stays alive for as long as the caller is expected to
// use it (e.g. scope item display name must remain valid for the life of the
// scope item). When out-of-proc, that returned string will be allocated by
// the proxy using CoTaskMemAlloc() and it will never be freed so there will
// be some leaks.
//=--------------------------------------------------------------------------=
static void SCOPEDATAITEM_TO_WIRE ( SCOPEDATAITEM *psdi, WIRE_SCOPEDATAITEM *pwsdi ) { pwsdi->mask = psdi->mask; pwsdi->nImage = psdi->nImage; pwsdi->nOpenImage = psdi->nOpenImage; pwsdi->nState = psdi->nState; pwsdi->cChildren = psdi->cChildren; pwsdi->lParam = psdi->lParam; pwsdi->relativeID = psdi->relativeID; pwsdi->ID = psdi->ID;
if ( SDI_STR != (psdi->mask & SDI_STR) ) { pwsdi->pwszDisplayName = NULL; pwsdi->fUsingCallbackForString = FALSE; } else if (MMC_CALLBACK == psdi->displayname) { pwsdi->pwszDisplayName = NULL; pwsdi->fUsingCallbackForString = TRUE; } else if (NULL == psdi->displayname) { pwsdi->pwszDisplayName = NULL; pwsdi->fUsingCallbackForString = FALSE; } else { // A string is being passed. Need to CoTaskMemAlloc() it so that
// the MIDL generated stub can free it after transmission
int cbString = (lstrlenW(psdi->displayname) + 1) * sizeof(psdi->displayname[0]);
pwsdi->pwszDisplayName = (LPOLESTR)CoTaskMemAlloc(cbString); if (NULL == pwsdi->pwszDisplayName) { RpcRaiseException( E_OUTOFMEMORY ); } else { memcpy(pwsdi->pwszDisplayName, psdi->displayname, cbString); } pwsdi->fUsingCallbackForString = FALSE; } }
static void WIRE_TO_SCOPEDATAITEM ( WIRE_SCOPEDATAITEM *pwsdi, SCOPEDATAITEM *psdi ) { psdi->mask = pwsdi->mask; psdi->nImage = pwsdi->nImage; psdi->nOpenImage = pwsdi->nOpenImage; psdi->nState = pwsdi->nState; psdi->cChildren = pwsdi->cChildren; psdi->lParam = pwsdi->lParam; psdi->relativeID = pwsdi->relativeID; psdi->ID = pwsdi->ID;
if ( SDI_STR != (psdi->mask & SDI_STR) ) { psdi->displayname = NULL; } else if (pwsdi->fUsingCallbackForString) { psdi->displayname = MMC_CALLBACK; } else { psdi->displayname = pwsdi->pwszDisplayName; } }
HRESULT STDMETHODCALLTYPE IComponentData_GetDisplayInfo_Proxy ( IComponentData __RPC_FAR *This, SCOPEDATAITEM __RPC_FAR *pScopeDataItem ) { WIRE_SCOPEDATAITEM wsdi; HRESULT hr;
// Make sure the string pointer is NULL so that it is not marshaled as it
// is never passed from MMC to the snap-in. (MMC might not have initialized
// the pointer).
pScopeDataItem->displayname = NULL;
SCOPEDATAITEM_TO_WIRE(pScopeDataItem, &wsdi);
hr = IComponentData_RemGetDisplayInfo_Proxy(This, &wsdi); WIRE_TO_SCOPEDATAITEM(&wsdi, pScopeDataItem);
return hr; }
HRESULT STDMETHODCALLTYPE IComponentData_GetDisplayInfo_Stub ( IComponentData __RPC_FAR *This, WIRE_SCOPEDATAITEM __RPC_FAR *pwsdi ) { SCOPEDATAITEM sdi; HRESULT hr;
WIRE_TO_SCOPEDATAITEM(pwsdi, &sdi); hr = This->lpVtbl->GetDisplayInfo(This, &sdi); SCOPEDATAITEM_TO_WIRE(&sdi, pwsdi);
return hr; }
HRESULT STDMETHODCALLTYPE IConsoleNameSpace_InsertItem_Proxy ( IConsoleNameSpace __RPC_FAR *This, LPSCOPEDATAITEM pItem ) { WIRE_SCOPEDATAITEM wsdi; HRESULT hr; HSCOPEITEM ItemID;
SCOPEDATAITEM_TO_WIRE(pItem, &wsdi); hr = IConsoleNameSpace_RemInsertItem_Proxy(This, &wsdi, &ItemID);
// The only returned field is the item ID so copy it from the wire
// structure to the client structure
pItem->ID = ItemID;
return hr; }
HRESULT STDMETHODCALLTYPE IConsoleNameSpace_InsertItem_Stub ( IConsoleNameSpace __RPC_FAR *This, WIRE_SCOPEDATAITEM __RPC_FAR *pwsdi, HSCOPEITEM __RPC_FAR *pItemID ) { SCOPEDATAITEM sdi; HRESULT hr;
WIRE_TO_SCOPEDATAITEM(pwsdi, &sdi); hr = This->lpVtbl->InsertItem(This, &sdi);
// The only returned field is the itemID.
*pItemID = sdi.ID; return hr; }
HRESULT STDMETHODCALLTYPE IConsoleNameSpace_SetItem_Proxy ( IConsoleNameSpace __RPC_FAR *This, LPSCOPEDATAITEM pItem ) { WIRE_SCOPEDATAITEM wsdi;
SCOPEDATAITEM_TO_WIRE(pItem, &wsdi); return IConsoleNameSpace_RemSetItem_Proxy(This, &wsdi); }
HRESULT STDMETHODCALLTYPE IConsoleNameSpace_SetItem_Stub ( IConsoleNameSpace __RPC_FAR *This, WIRE_SCOPEDATAITEM __RPC_FAR *pwsdi ) { SCOPEDATAITEM sdi;
WIRE_TO_SCOPEDATAITEM(pwsdi, &sdi); return This->lpVtbl->SetItem(This, &sdi); }
HRESULT STDMETHODCALLTYPE IConsoleNameSpace_GetItem_Proxy ( IConsoleNameSpace __RPC_FAR *This, LPSCOPEDATAITEM pItem ) { WIRE_SCOPEDATAITEM wsdi; HRESULT hr;
// Make sure the string pointer is NULL so that it is not marshaled as it
// is never passed from the snap-in to MMC. (It might not have be
// initialized).
pItem->displayname = NULL;
SCOPEDATAITEM_TO_WIRE(pItem, &wsdi); hr = IConsoleNameSpace_RemGetItem_Proxy(This, &wsdi); WIRE_TO_SCOPEDATAITEM(&wsdi, pItem); return hr; }
HRESULT STDMETHODCALLTYPE IConsoleNameSpace_GetItem_Stub ( IConsoleNameSpace __RPC_FAR *This, WIRE_SCOPEDATAITEM __RPC_FAR *pwsdi ) { SCOPEDATAITEM sdi; HRESULT hr;
WIRE_TO_SCOPEDATAITEM(pwsdi, &sdi); hr = This->lpVtbl->GetItem(This, &sdi); SCOPEDATAITEM_TO_WIRE(&sdi, pwsdi);
return hr; }
//=--------------------------------------------------------------------------=
//
// RESULTDATAITEM Marshaling
//
//
//=--------------------------------------------------------------------------=
// Caveat: When returning a string in RESULTDATAITEM MMC does not use a
// callee allocate/caller free strategy. When in-proc, the owner of the memory
// must insure that it stays alive for as long as the caller is expected to
// use it (e.g. list item column data must remain valid for the life of the
// list item). When out-of-proc, that returned string will be allocated by
// the proxy using SysAllocString() and it will never be freed so there will
// be some leaks.
//=--------------------------------------------------------------------------=
static void RESULTDATAITEM_TO_WIRE ( RESULTDATAITEM *prdi, WIRE_RESULTDATAITEM *pwrdi ) { pwrdi->mask = prdi->mask; pwrdi->bScopeItem = prdi->bScopeItem; pwrdi->itemID = prdi->itemID; pwrdi->nIndex = prdi->nIndex; pwrdi->nCol = prdi->nCol; pwrdi->nImage = prdi->nImage; pwrdi->nState = prdi->nState; pwrdi->lParam = prdi->lParam; pwrdi->iIndent = prdi->iIndent;
if ( RDI_STR != (prdi->mask & RDI_STR) ) { pwrdi->str = NULL; pwrdi->fUsingCallbackForString = FALSE; } else if (MMC_CALLBACK == prdi->str) { pwrdi->str = NULL; pwrdi->fUsingCallbackForString = TRUE; } else if (NULL == prdi->str) { pwrdi->str = NULL; pwrdi->fUsingCallbackForString = FALSE; } else { // A string is being passed. Need to CoTaskMemAlloc() it so that
// the MIDL generated stub can free it after transmission
int cbString = (lstrlenW(prdi->str) + 1) * sizeof(prdi->str[0]);
pwrdi->str = (LPOLESTR)CoTaskMemAlloc(cbString); if (NULL == pwrdi->str) { RpcRaiseException( E_OUTOFMEMORY ); } else { memcpy(pwrdi->str, prdi->str, cbString); } pwrdi->fUsingCallbackForString = FALSE; } }
static void WIRE_TO_RESULTDATAITEM ( WIRE_RESULTDATAITEM *pwrdi, RESULTDATAITEM *prdi ) { prdi->mask = pwrdi->mask; prdi->bScopeItem = pwrdi->bScopeItem; prdi->itemID = pwrdi->itemID; prdi->nIndex = pwrdi->nIndex; prdi->nCol = pwrdi->nCol; prdi->nImage = pwrdi->nImage; prdi->nState = pwrdi->nState; prdi->lParam = pwrdi->lParam; prdi->iIndent = pwrdi->iIndent;
if ( RDI_STR != (prdi->mask & RDI_STR) ) { prdi->str = NULL; } else if (pwrdi->fUsingCallbackForString) { prdi->str = MMC_CALLBACK; } else { prdi->str = pwrdi->str; } }
HRESULT STDMETHODCALLTYPE IComponent_GetDisplayInfo_Proxy ( IComponent __RPC_FAR *This, RESULTDATAITEM __RPC_FAR *pResultDataItem ) { WIRE_RESULTDATAITEM wrdi; HRESULT hr;
// Make sure the string pointer is NULL so that it is not marshaled as it
// is never passed from MMC to the snap-in. (MMC might not have initialized
// the pointer).
pResultDataItem->str = NULL;
RESULTDATAITEM_TO_WIRE(pResultDataItem, &wrdi);
hr = IComponent_RemGetDisplayInfo_Proxy(This, &wrdi); WIRE_TO_RESULTDATAITEM(&wrdi, pResultDataItem);
return hr; }
HRESULT STDMETHODCALLTYPE IComponent_GetDisplayInfo_Stub ( IComponent __RPC_FAR *This, WIRE_RESULTDATAITEM __RPC_FAR *pwrdi ) { RESULTDATAITEM rdi; HRESULT hr;
WIRE_TO_RESULTDATAITEM(pwrdi, &rdi); hr = This->lpVtbl->GetDisplayInfo(This, &rdi); RESULTDATAITEM_TO_WIRE(&rdi, pwrdi);
return hr; }
HRESULT STDMETHODCALLTYPE IResultData_InsertItem_Proxy ( IResultData __RPC_FAR *This, LPRESULTDATAITEM pItem ) { WIRE_RESULTDATAITEM wrdi; HRESULT hr; HRESULTITEM ItemID;
RESULTDATAITEM_TO_WIRE(pItem, &wrdi); hr = IResultData_RemInsertItem_Proxy(This, &wrdi, &ItemID);
// The only returned field is the itemID so copy it from the wire
// structure to the client structure
pItem->itemID = ItemID;
return hr; }
HRESULT STDMETHODCALLTYPE IResultData_InsertItem_Stub ( IResultData __RPC_FAR *This, WIRE_RESULTDATAITEM __RPC_FAR *pwrdi, HRESULTITEM __RPC_FAR *pItemID ) { RESULTDATAITEM rdi; HRESULT hr;
WIRE_TO_RESULTDATAITEM(pwrdi, &rdi); hr = This->lpVtbl->InsertItem(This, &rdi);
// The only returned field is the itemID.
*pItemID = rdi.itemID; return hr; }
HRESULT STDMETHODCALLTYPE IResultData_SetItem_Proxy ( IResultData __RPC_FAR *This, LPRESULTDATAITEM pItem ) { WIRE_RESULTDATAITEM wrdi;
RESULTDATAITEM_TO_WIRE(pItem, &wrdi); return IResultData_RemSetItem_Proxy(This, &wrdi); }
HRESULT STDMETHODCALLTYPE IResultData_SetItem_Stub ( IResultData __RPC_FAR *This, WIRE_RESULTDATAITEM __RPC_FAR *pwrdi ) { RESULTDATAITEM rdi;
WIRE_TO_RESULTDATAITEM(pwrdi, &rdi); return This->lpVtbl->SetItem(This, &rdi); }
HRESULT STDMETHODCALLTYPE IResultData_GetItem_Proxy ( IResultData __RPC_FAR *This, LPRESULTDATAITEM pItem ) { WIRE_RESULTDATAITEM wrdi; HRESULT hr;
// Make sure the string pointer is NULL so that it is not marshaled as it
// is never passed from the snap-in to MMC. (It might not have be
// initialized).
pItem->str = NULL;
RESULTDATAITEM_TO_WIRE(pItem, &wrdi); hr = IResultData_RemGetItem_Proxy(This, &wrdi); WIRE_TO_RESULTDATAITEM(&wrdi, pItem); return hr; }
HRESULT STDMETHODCALLTYPE IResultData_GetItem_Stub ( IResultData __RPC_FAR *This, WIRE_RESULTDATAITEM __RPC_FAR *pwrdi ) { RESULTDATAITEM rdi; HRESULT hr;
WIRE_TO_RESULTDATAITEM(pwrdi, &rdi); hr = This->lpVtbl->GetItem(This, &rdi); RESULTDATAITEM_TO_WIRE(&rdi, pwrdi);
return hr; }
HRESULT STDMETHODCALLTYPE IResultData_GetNextItem_Proxy ( IResultData __RPC_FAR *This, LPRESULTDATAITEM pItem ) { WIRE_RESULTDATAITEM wrdi; HRESULT hr;
// Make sure the string pointer is NULL so that it is not marshaled as it
// is never passed from the snap-in to MMC. (It might not have be
// initialized).
pItem->str = NULL;
RESULTDATAITEM_TO_WIRE(pItem, &wrdi); hr = IResultData_RemGetNextItem_Proxy(This, &wrdi); WIRE_TO_RESULTDATAITEM(&wrdi, pItem); return hr; }
HRESULT STDMETHODCALLTYPE IResultData_GetNextItem_Stub ( IResultData __RPC_FAR *This, WIRE_RESULTDATAITEM __RPC_FAR *pwrdi ) { RESULTDATAITEM rdi; HRESULT hr;
WIRE_TO_RESULTDATAITEM(pwrdi, &rdi); hr = This->lpVtbl->GetNextItem(This, &rdi); RESULTDATAITEM_TO_WIRE(&rdi, pwrdi); return hr; }
//=--------------------------------------------------------------------------=
//
// HICON Marshaling
//
//=--------------------------------------------------------------------------=
// In wtypes.idl HICON is defined with the wire_marshal attribute with its
// 'on-the-wire' type as a pointer to a RemotableHandle. RemotableHandle is
// defined in wtypes.idl as
//
// typedef union _RemotableHandle switch( long fContext ) u
// {
// case WDT_INPROC_CALL: long hInproc;
// case WDT_REMOTE_CALL: long hRemote;
// } RemotableHandle;
//
// A wire_marshal type must supply routines to size, marhsal, unmarshal, and
// free marshaling data. Those routines are in ole32.dll but someone forgot to
// export them. (ole32 also has routines to marshal bitmaps, hwnds, etc. that
// are all exported). The code has been plagiarized here from ole32. The source
// is in \\savik\cairo\src\ole32\oleprx32\proxy\transmit.cxx with some macros in
// transmit.h in that same directory.
//
//=--------------------------------------------------------------------------=
//
// The following defines and macros are from transmit.h. Note that
// USER_CALL_CTXT_MASK is from rpcndr.h and WDT_REMOTE_CALL is from wtypes.idl.
//
#define ALIGN( pStuff, cAlign ) \
pStuff = (unsigned char *)((ULONG_PTR)((pStuff) + (cAlign)) & ~ (cAlign))
#define LENGTH_ALIGN( Length, cAlign ) \
Length = (((Length) + (cAlign)) & ~ (cAlign))
#define PULONG_LV_CAST *(unsigned long __RPC_FAR * __RPC_FAR *)&
#define DIFFERENT_MACHINE_CALL( Flags) \
(USER_CALL_CTXT_MASK(Flags) == MSHCTX_DIFFERENTMACHINE)
#define WDT_HANDLE_MARKER WDT_INPROC_CALL
//=--------------------------------------------------------------------------=
// HICON_UserSize
//=--------------------------------------------------------------------------=
//
// Parameters:
// unsigned long __RPC_FAR *pFlags [in] data format & context (see below)
// unsigned long StartingSize [in] current buffer size
// HICON __RPC_FAR *hIcon [in] HICON to be marshaled
//
//
// Flags Layout
// ============
//
//----------------------------------------------------------------------
// Bits Flag Value
//----------------------------------------------------------------------
// 31-24 Floating-point representation 0 = IEEE
// 1 = VAX
// 2 = Cray
// 3 = IBM
//----------------------------------------------------------------------
// 23-20 Integer and floating-point byte
// order 0 = Big-endian
// 1 = Little-endian
//----------------------------------------------------------------------
// 19-16 Character representation 0 = ASCII
// 1 = EBCDIC
//----------------------------------------------------------------------
// 15-0 Marshaling context flag 0 = MSHCTX_LOCAL
// 1 = MSHCTX_NOSHAREDMEM
// 2 = MSHCTX_DIFFERENTMSCHINE
// 3 = MSHCTX_INPROC
//----------------------------------------------------------------------
//
// Output:
// New buffer size after adding amount needed for HICON marshaled data
//
// Notes:
//
// Called from MIDL generated proxy to determine buffer size needed for
// marhaled data.
//
unsigned long __RPC_USER HICON_UserSize ( unsigned long __RPC_FAR *pFlags, unsigned long StartingSize, HICON __RPC_FAR *hIcon ) { if (NULL == hIcon) { return StartingSize; }
// If marshaling context is to a different machine then we don't support
// that.
if ( DIFFERENT_MACHINE_CALL(*pFlags) ) { RpcRaiseException( RPC_S_INVALID_TAG ); }
// Make sure that our data will fall at a long boundary
LENGTH_ALIGN( StartingSize, 3 );
//Add the length
return StartingSize + 8; }
//=--------------------------------------------------------------------------=
// HICON_UserMarhsal
//=--------------------------------------------------------------------------=
//
// Parameters:
// unsigned long __RPC_FAR *pFlags [in] data format & context (see above)
// unsigned char __RPC_FAR *pBuffer [in] current buffer size
// HICON __RPC_FAR *hIcon [in] HICON to be marshaled
//
//
// Output:
// Pointer to buffer location following HICON's marshaling data
//
// Notes:
//
// Called from MIDL generated proxy to marshal an HICON
//
unsigned char __RPC_FAR * __RPC_USER HICON_UserMarshal ( unsigned long __RPC_FAR *pFlags, unsigned char __RPC_FAR *pBuffer, HICON __RPC_FAR *hIcon ) { if (NULL == hIcon) { return pBuffer; }
if ( DIFFERENT_MACHINE_CALL(*pFlags) ) { RpcRaiseException( RPC_S_INVALID_TAG ); }
// Make sure that our data will fall at a long boundary
ALIGN( pBuffer, 3 );
*( PULONG_LV_CAST pBuffer)++ = WDT_HANDLE_MARKER; *( PULONG_LV_CAST pBuffer)++ = *((long *)hIcon);
return pBuffer; }
//=--------------------------------------------------------------------------=
// HICON_UserUnmarhsal
//=--------------------------------------------------------------------------=
//
// Parameters:
// unsigned long __RPC_FAR *pFlags [in] data format & context (see above)
// unsigned char __RPC_FAR *pBuffer [in] current buffer size
// HICON __RPC_FAR *hIcon [in] HICON to be marshaled
//
//
// Output:
// Pointer to buffer location following HICON's marshaling data
//
// Notes:
//
// Called from MIDL generated stub to unmarshal an HICON
//
unsigned char __RPC_FAR *__RPC_USER HICON_UserUnmarshal ( unsigned long __RPC_FAR *pFlags, unsigned char __RPC_FAR *pBuffer, HICON __RPC_FAR *hIcon ) { unsigned long HandleMarker;
ALIGN( pBuffer, 3 );
HandleMarker = *( PULONG_LV_CAST pBuffer)++;
if ( HandleMarker == WDT_HANDLE_MARKER ) *((long *)hIcon) = *( PULONG_LV_CAST pBuffer)++; else RpcRaiseException( RPC_S_INVALID_TAG );
return pBuffer; }
//=--------------------------------------------------------------------------=
// HICON_UserUnmarhsal
//=--------------------------------------------------------------------------=
//
// Parameters:
// unsigned long __RPC_FAR *pFlags [in] data format & context (see above)
// HICON __RPC_FAR *hIcon [in] HICON that was unmarshaled
//
//
// Output:
// None
//
// Notes:
//
// Called from MIDL generated stub to free any associated marshaling data
// allocated during unmarshaling for embedded pointers. Not used for HICON.
//
void __RPC_USER HICON_UserFree ( unsigned long __RPC_FAR *pFlags, HICON __RPC_FAR *hIcon ) { }
//=--------------------------------------------------------------------------=
//
// IImageList Marshaling
//
//
//=--------------------------------------------------------------------------=
// These methods needed call_as because the HICON and HBITMAP parameters are
// specified as long pointers in the original IDL.
//=--------------------------------------------------------------------------=
HRESULT STDMETHODCALLTYPE IImageList_ImageListSetIcon_Proxy ( IImageList __RPC_FAR *This, LONG_PTR __RPC_FAR *pIcon, long nLoc ) { return IImageList_RemImageListSetIcon_Proxy(This, (HICON)pIcon, nLoc); }
HRESULT STDMETHODCALLTYPE IImageList_ImageListSetIcon_Stub ( IImageList __RPC_FAR *This, HICON hIcon, long nLoc ) { return This->lpVtbl->ImageListSetIcon(This, (LONG_PTR __RPC_FAR*)hIcon, nLoc); }
HRESULT STDMETHODCALLTYPE IImageList_ImageListSetStrip_Proxy ( IImageList __RPC_FAR *This, LONG_PTR __RPC_FAR *pBMapSm, LONG_PTR __RPC_FAR *pBMapLg, long nStartLoc, COLORREF cMask ) { return IImageList_RemImageListSetStrip_Proxy(This, (HBITMAP)pBMapSm, (HBITMAP)pBMapLg, nStartLoc, cMask); }
HRESULT STDMETHODCALLTYPE IImageList_ImageListSetStrip_Stub ( IImageList __RPC_FAR *This, HBITMAP hbmSmall, HBITMAP hbmLarge, long nStartLoc, COLORREF cMask ) { return This->lpVtbl->ImageListSetStrip(This, (LONG_PTR __RPC_FAR*)hbmSmall, (LONG_PTR __RPC_FAR*)hbmLarge, nStartLoc, cMask); }
HRESULT STDMETHODCALLTYPE IExtendPropertySheet_CreatePropertyPages_Proxy ( IExtendPropertySheet __RPC_FAR *This, LPPROPERTYSHEETCALLBACK lpProvider, LONG_PTR handle, LPDATAOBJECT lpIDataObject ) { HRESULT hr = S_OK; WIRE_PROPERTYPAGES *pPages = NULL; WIRE_PROPERTYPAGE *pPage = NULL; ULONG i = 0; ULONG j = 0; IExtendPropertySheetRemote *piExtendPropertySheetRemote = NULL; IRemotePropertySheetManager *piRemotePropertySheetManager = NULL;
// Make sure the snap-in knows we are remoted. We do this here because
// this is the first opportunity for the proxy to inform a property page
// extension that it is remote and pass it data such as the MMC.exe path
// and the MMC command line.
hr = SetRemote((IUnknown *)This); if (FAILED(hr)) { goto Cleanup; }
// Call the IExtendPropertySheetRemote method which will return a filled in
// WIRE_PROPERTYPAGES from the remoted snap-in.
hr = This->lpVtbl->QueryInterface(This, &IID_IExtendPropertySheetRemote, (void **)&piExtendPropertySheetRemote); if (FAILED(hr)) { goto Cleanup; }
hr = piExtendPropertySheetRemote->lpVtbl->CreatePropertyPageDefs( piExtendPropertySheetRemote, lpIDataObject, &pPages); if (FAILED(hr)) { goto Cleanup; }
// If there are pages (snap-in might not have added any) then
// CoCreateInstance the remote property sheet manager using the clsid
// returned in WIRE_PROPERTYPAGES. This object will be created in-proc here
// on the MMC side.
if (0 == pPages->cPages) { goto Cleanup; }
hr = CoCreateInstance(&pPages->clsidRemotePropertySheetManager, NULL, // no aggregation,
CLSCTX_INPROC_SERVER, &IID_IRemotePropertySheetManager, (void **)&piRemotePropertySheetManager); if (FAILED(hr)) { goto Cleanup; }
// Pass the remote property sheet manager the WIRE_PROPERTYPAGES and let it
// actually create the property pages and add them to the sheet here on the
// MMC side.
hr = piRemotePropertySheetManager->lpVtbl->CreateRemotePages( piRemotePropertySheetManager, lpProvider, handle, lpIDataObject, pPages); Cleanup: if (NULL != piRemotePropertySheetManager) { piRemotePropertySheetManager->lpVtbl->Release(piRemotePropertySheetManager); }
if (NULL != piExtendPropertySheetRemote) { piExtendPropertySheetRemote->lpVtbl->Release(piExtendPropertySheetRemote); }
// Free the WIRE_PROPERTYPAGES and all of its contents.
if (NULL != pPages) { // Release the object and free the title for each individual page
for (i = 0, pPage = &pPages->aPages[0]; i < pPages->cPages; i++, pPage++) { if (NULL != pPage->apunkObjects) { for (j = 0; j < pPage->cObjects; j++) { if (NULL != pPage->apunkObjects[j]) { pPage->apunkObjects[j]->lpVtbl->Release(pPage->apunkObjects[j]); } } CoTaskMemFree(pPage->apunkObjects); } if (NULL != pPage->pwszTitle) { CoTaskMemFree(pPage->pwszTitle); } }
// Free the ProgID prefix
if (NULL != pPages->pwszProgIDStart) { CoTaskMemFree(pPages->pwszProgIDStart); }
// Free all of the snap-in's property page info
if (NULL != pPages->pPageInfos) { for (i = 0; i < pPages->pPageInfos->cPages; i++) { if (NULL != pPages->pPageInfos->aPageInfo[i].pwszTitle) { CoTaskMemFree(pPages->pPageInfos->aPageInfo[i].pwszTitle); } if (NULL != pPages->pPageInfos->aPageInfo[i].pwszProgID) { CoTaskMemFree(pPages->pPageInfos->aPageInfo[i].pwszProgID); } } CoTaskMemFree(pPages->pPageInfos); }
// Free all of the objects associated with the sheet
if (NULL != pPages->apunkObjects) { for (i = 0; i < pPages->cObjects; i++) { if (NULL != pPages->apunkObjects[i]) { pPages->apunkObjects[i]->lpVtbl->Release(pPages->apunkObjects[i]); } } CoTaskMemFree(pPages->apunkObjects); }
// Release the extra object and the WIRE_PROPERTYPAGES struct itself
if (NULL != pPages->punkExtra) { pPages->punkExtra->lpVtbl->Release(pPages->punkExtra); } CoTaskMemFree(pPages); }
return hr; }
//=--------------------------------------------------------------------------=
// IExtendPropertySheet_CreatePropertyPages_Stub
//=--------------------------------------------------------------------------=
//
// Parameters:
// IExtendPropertySheet __RPC_FAR *This [in] this pointer
//
// Output:
//
// Notes:
//
// This stub is never called because
// IExtendPropertySheet_CreatePropertyPages_Proxy() (see above) reroutes the
// call to IExtendPropertySheetRemote::CreatePropertyPageDefs().
//
HRESULT STDMETHODCALLTYPE IExtendPropertySheet_CreatePropertyPages_Stub ( IExtendPropertySheet __RPC_FAR *This ) { return S_OK; }
HRESULT STDMETHODCALLTYPE IExtendPropertySheet_QueryPagesFor_Proxy ( IExtendPropertySheet __RPC_FAR *This, IDataObject *piDataObject ) { HRESULT hr = S_OK; BOOL fIsMultiSelect = FALSE; long cDataObjects = 1L; IDataObject **ppiDataObjects = NULL; BOOL fSpecialDataObject = FALSE; long lSpecialDataObject = 0;
// Check for special data objects such DOBJ_CUSTOMWEB etc.
CheckForSpecialDataObjects(&piDataObject, &fSpecialDataObject, &lSpecialDataObject);
// If this is a mutliple selection then we need to extract the data
// objects in the HGLOBAL
if (!fSpecialDataObject) { hr = IsMultiSelect(piDataObject, &fIsMultiSelect); if (FAILED(hr)) { goto Cleanup; } }
if (fIsMultiSelect) { hr = InterpretMultiSelect(piDataObject, &cDataObjects, &ppiDataObjects); if (FAILED(hr)) { goto Cleanup; } } else { ppiDataObjects = &piDataObject; }
hr = IExtendPropertySheet_RemQueryPagesFor_Proxy(This, cDataObjects, ppiDataObjects, fSpecialDataObject, lSpecialDataObject); Cleanup: if ( fIsMultiSelect && (NULL != ppiDataObjects) ) { (void)GlobalFree(ppiDataObjects); } return hr; }
HRESULT STDMETHODCALLTYPE IExtendPropertySheet_QueryPagesFor_Stub ( IExtendPropertySheet __RPC_FAR *This, long cDataObjects, IDataObject __RPC_FAR *__RPC_FAR ppiDataObjects[ ], BOOL fSpecialDataObject, long lSpecialDataObject ) { HRESULT hr = S_OK; IDataObject *piDataObject = NULL; // Not AddRef()ed
IDataObject *piMultiSelDataObject = NULL;
// If there is more than one data object then we need to pack them into a
// a separate data object that appears as a multi-select data object.
if (cDataObjects > 1L) { hr = CreateMultiSelDataObject(ppiDataObjects, cDataObjects, &piMultiSelDataObject); if (FAILED(hr)) { goto Cleanup; } piDataObject = piMultiSelDataObject; } else if (fSpecialDataObject) { piDataObject = (IDataObject *)lSpecialDataObject; } else { piDataObject = ppiDataObjects[0]; }
// Call the snap-in
hr = This->lpVtbl->QueryPagesFor(This, piDataObject);
Cleanup: if (NULL != piMultiSelDataObject) { piMultiSelDataObject->lpVtbl->Release(piMultiSelDataObject); } return hr; }
HRESULT STDMETHODCALLTYPE IExtendPropertySheet2_GetWatermarks_Proxy ( IExtendPropertySheet2 __RPC_FAR *This, IDataObject *piDataObject, HBITMAP *lphWatermark, HBITMAP *lphHeader, HPALETTE *lphPalette, BOOL *bStretch ) { HRESULT hr = S_OK; BOOL fIsMultiSelect = FALSE; long cDataObjects = 1L; IDataObject **ppiDataObjects = NULL; BOOL fSpecialDataObject = FALSE; long lSpecialDataObject = 0;
// Check for special data objects such DOBJ_CUSTOMWEB etc.
CheckForSpecialDataObjects(&piDataObject, &fSpecialDataObject, &lSpecialDataObject);
// If this is a mutliple selection then we need to extract the data
// objects in the HGLOBAL
if (!fSpecialDataObject) { hr = IsMultiSelect(piDataObject, &fIsMultiSelect); if (FAILED(hr)) { goto Cleanup; } }
if (fIsMultiSelect) { hr = InterpretMultiSelect(piDataObject, &cDataObjects, &ppiDataObjects); if (FAILED(hr)) { goto Cleanup; } } else { ppiDataObjects = &piDataObject; }
hr = IExtendPropertySheet2_RemGetWatermarks_Proxy(This, cDataObjects, ppiDataObjects, fSpecialDataObject, lSpecialDataObject, lphWatermark, lphHeader, lphPalette, bStretch); Cleanup: if ( fIsMultiSelect && (NULL != ppiDataObjects) ) { (void)GlobalFree(ppiDataObjects); } return hr; }
HRESULT STDMETHODCALLTYPE IExtendPropertySheet2_GetWatermarks_Stub ( IExtendPropertySheet2 __RPC_FAR *This, long cDataObjects, IDataObject __RPC_FAR *__RPC_FAR ppiDataObjects[ ], BOOL fSpecialDataObject, long lSpecialDataObject, HBITMAP *lphWatermark, HBITMAP *lphHeader, HPALETTE *lphPalette, BOOL *bStretch ) { HRESULT hr = S_OK; IDataObject *piDataObject = NULL; // Not AddRef()ed
IDataObject *piMultiSelDataObject = NULL;
// If there is more than one data object then we need to pack them into a
// a separate data object that appears as a multi-select data object.
if (cDataObjects > 1L) { hr = CreateMultiSelDataObject(ppiDataObjects, cDataObjects, &piMultiSelDataObject); if (FAILED(hr)) { goto Cleanup; } piDataObject = piMultiSelDataObject; } else if (fSpecialDataObject) { piDataObject = (IDataObject *)lSpecialDataObject; } else { piDataObject = ppiDataObjects[0]; }
// Call the snap-in
hr = This->lpVtbl->GetWatermarks(This, piDataObject, lphWatermark, lphHeader, lphPalette, bStretch);
Cleanup: if (NULL != piMultiSelDataObject) { piMultiSelDataObject->lpVtbl->Release(piMultiSelDataObject); } return hr; }
HRESULT STDMETHODCALLTYPE IExtendPropertySheetRemote_CreatePropertyPageDefs_Proxy ( IExtendPropertySheetRemote __RPC_FAR *This, IDataObject *piDataObject, WIRE_PROPERTYPAGES **ppPages ) { HRESULT hr = S_OK; BOOL fIsMultiSelect = FALSE; long cDataObjects = 1L; IDataObject **ppiDataObjects = NULL; BOOL fSpecialDataObject = FALSE; long lSpecialDataObject = 0;
// Check for special data objects such DOBJ_CUSTOMWEB etc.
CheckForSpecialDataObjects(&piDataObject, &fSpecialDataObject, &lSpecialDataObject);
// If this is a mutliple selection then we need to extract the data
// objects in the HGLOBAL
if (!fSpecialDataObject) { hr = IsMultiSelect(piDataObject, &fIsMultiSelect); if (FAILED(hr)) { goto Cleanup; } }
if (fIsMultiSelect) { hr = InterpretMultiSelect(piDataObject, &cDataObjects, &ppiDataObjects); if (FAILED(hr)) { goto Cleanup; } } else { ppiDataObjects = &piDataObject; }
hr = IExtendPropertySheetRemote_RemCreatePropertyPageDefs_Proxy( This, cDataObjects, ppiDataObjects, fSpecialDataObject, lSpecialDataObject, ppPages); Cleanup: if ( fIsMultiSelect && (NULL != ppiDataObjects) ) { (void)GlobalFree(ppiDataObjects); } return hr; }
HRESULT STDMETHODCALLTYPE IExtendPropertySheetRemote_CreatePropertyPageDefs_Stub ( IExtendPropertySheetRemote __RPC_FAR *This, long cDataObjects, IDataObject __RPC_FAR *__RPC_FAR ppiDataObjects[ ], BOOL fSpecialDataObject, long lSpecialDataObject, WIRE_PROPERTYPAGES **ppPages ) { HRESULT hr = S_OK; IDataObject *piDataObject = NULL; // Not AddRef()ed
IDataObject *piMultiSelDataObject = NULL;
// If there is more than one data object then we need to pack them into a
// a separate data object that appears as a multi-select data object.
if (cDataObjects > 1L) { hr = CreateMultiSelDataObject(ppiDataObjects, cDataObjects, &piMultiSelDataObject); if (FAILED(hr)) { goto Cleanup; } piDataObject = piMultiSelDataObject; } else if (fSpecialDataObject) { piDataObject = (IDataObject *)lSpecialDataObject; } else { piDataObject = ppiDataObjects[0]; }
// Call the snap-in
hr = This->lpVtbl->CreatePropertyPageDefs(This, piDataObject, ppPages);
Cleanup: if (NULL != piMultiSelDataObject) { piMultiSelDataObject->lpVtbl->Release(piMultiSelDataObject); } return hr; }
HRESULT STDMETHODCALLTYPE IPropertySheetProvider_CreatePropertySheet_Proxy ( IPropertySheetProvider __RPC_FAR *This, LPCWSTR title, boolean type, MMC_COOKIE cookie, IDataObject *piDataObject, DWORD dwOptions ) { HRESULT hr = S_OK; BOOL fIsMultiSelect = FALSE; long cDataObjects = 1L; IDataObject **ppiDataObjects = NULL; BOOL fSpecialDataObject = FALSE; long lSpecialDataObject = 0;
// Check for special data objects such DOBJ_CUSTOMWEB etc.
CheckForSpecialDataObjects(&piDataObject, &fSpecialDataObject, &lSpecialDataObject);
// If this is a mutliple selection then we need to extract the data
// objects in the HGLOBAL
if (!fSpecialDataObject) { hr = IsMultiSelect(piDataObject, &fIsMultiSelect); if (FAILED(hr)) { goto Cleanup; } }
if (fIsMultiSelect) { hr = InterpretMultiSelect(piDataObject, &cDataObjects, &ppiDataObjects); if (FAILED(hr)) { goto Cleanup; } } else { ppiDataObjects = &piDataObject; }
hr = IPropertySheetProvider_RemCreatePropertySheet_Proxy(This, title, type, cookie, cDataObjects, ppiDataObjects, fSpecialDataObject, lSpecialDataObject, dwOptions); Cleanup: if ( fIsMultiSelect && (NULL != ppiDataObjects) ) { (void)GlobalFree(ppiDataObjects); } return hr; }
HRESULT STDMETHODCALLTYPE IPropertySheetProvider_CreatePropertySheet_Stub ( IPropertySheetProvider __RPC_FAR *This, LPCWSTR title, boolean type, MMC_COOKIE cookie, long cDataObjects, IDataObject __RPC_FAR *__RPC_FAR ppiDataObjects[ ], BOOL fSpecialDataObject, long lSpecialDataObject, DWORD dwOptions ) { HRESULT hr = S_OK; IDataObject *piDataObject = NULL; // Not AddRef()ed
IDataObject *piMultiSelDataObject = NULL;
// If there is more than one data object then we need to pack them into a
// a separate data object that appears as a multi-select data object.
if (cDataObjects > 1L) { hr = CreateMultiSelDataObject(ppiDataObjects, cDataObjects, &piMultiSelDataObject); if (FAILED(hr)) { goto Cleanup; } piDataObject = piMultiSelDataObject; } else if (fSpecialDataObject) { piDataObject = (IDataObject *)lSpecialDataObject; } else { piDataObject = ppiDataObjects[0]; }
// Call the snap-in
hr = This->lpVtbl->CreatePropertySheet(This, title, type, cookie, piDataObject, dwOptions);
Cleanup: if (NULL != piMultiSelDataObject) { piMultiSelDataObject->lpVtbl->Release(piMultiSelDataObject); } return hr; }
HRESULT STDMETHODCALLTYPE IPropertySheetProvider_FindPropertySheet_Proxy ( IPropertySheetProvider __RPC_FAR *This, MMC_COOKIE cookie, IComponent *piComponent, IDataObject *piDataObject ) { HRESULT hr = S_OK; BOOL fIsMultiSelect = FALSE; long cDataObjects = 1L; IDataObject **ppiDataObjects = NULL; BOOL fSpecialDataObject = FALSE; long lSpecialDataObject = 0;
// Check for special data objects such DOBJ_CUSTOMWEB etc.
CheckForSpecialDataObjects(&piDataObject, &fSpecialDataObject, &lSpecialDataObject);
// If this is a mutliple selection then we need to extract the data
// objects in the HGLOBAL
if (!fSpecialDataObject) { hr = IsMultiSelect(piDataObject, &fIsMultiSelect); if (FAILED(hr)) { goto Cleanup; } }
if (fIsMultiSelect) { hr = InterpretMultiSelect(piDataObject, &cDataObjects, &ppiDataObjects); if (FAILED(hr)) { goto Cleanup; } } else { ppiDataObjects = &piDataObject; }
hr = IPropertySheetProvider_RemFindPropertySheet_Proxy(This, cookie, piComponent, cDataObjects, ppiDataObjects, fSpecialDataObject, lSpecialDataObject); Cleanup: if ( fIsMultiSelect && (NULL != ppiDataObjects) ) { (void)GlobalFree(ppiDataObjects); } return hr; }
HRESULT STDMETHODCALLTYPE IPropertySheetProvider_FindPropertySheet_Stub ( IPropertySheetProvider __RPC_FAR *This, MMC_COOKIE cookie, IComponent *piComponent, long cDataObjects, IDataObject __RPC_FAR *__RPC_FAR ppiDataObjects[ ], BOOL fSpecialDataObject, long lSpecialDataObject ) { HRESULT hr = S_OK; IDataObject *piDataObject = NULL; // Not AddRef()ed
IDataObject *piMultiSelDataObject = NULL;
// If there is more than one data object then we need to pack them into a
// a separate data object that appears as a multi-select data object.
if (cDataObjects > 1L) { hr = CreateMultiSelDataObject(ppiDataObjects, cDataObjects, &piMultiSelDataObject); if (FAILED(hr)) { goto Cleanup; } piDataObject = piMultiSelDataObject; } else if (fSpecialDataObject) { piDataObject = (IDataObject *)lSpecialDataObject; } else { piDataObject = ppiDataObjects[0]; }
// Call the snap-in
hr = This->lpVtbl->FindPropertySheet(This, cookie, piComponent, piDataObject);
Cleanup: if (NULL != piMultiSelDataObject) { piMultiSelDataObject->lpVtbl->Release(piMultiSelDataObject); } return hr; }
HRESULT STDMETHODCALLTYPE IExtendContextMenu_AddMenuItems_Proxy ( IExtendContextMenu __RPC_FAR *This, LPDATAOBJECT piDataObject, LPCONTEXTMENUCALLBACK piCallback, long __RPC_FAR *pInsertionAllowed ) { HRESULT hr = S_OK; BOOL fIsMultiSelect = FALSE; long cDataObjects = 1L; IDataObject **ppiDataObjects = NULL; BOOL fSpecialDataObject = FALSE; long lSpecialDataObject = 0;
// Make sure the snap-in knows we are remoted. We do this here because
// this is the first opportunity for the proxy to inform a context menu
// extension that it is remote.
hr = SetRemote((IUnknown *)This); if (FAILED(hr)) { goto Cleanup; }
// Check for special data objects such DOBJ_CUSTOMWEB etc.
CheckForSpecialDataObjects(&piDataObject, &fSpecialDataObject, &lSpecialDataObject);
// If this is a mutliple selection then we need to extract the data
// objects in the HGLOBAL
if (!fSpecialDataObject) { hr = IsMultiSelect(piDataObject, &fIsMultiSelect); if (FAILED(hr)) { goto Cleanup; } }
if (fIsMultiSelect) { hr = InterpretMultiSelect(piDataObject, &cDataObjects, &ppiDataObjects); if (FAILED(hr)) { goto Cleanup; } } else { ppiDataObjects = &piDataObject; }
hr = IExtendContextMenu_RemAddMenuItems_Proxy(This, cDataObjects, ppiDataObjects, fSpecialDataObject, lSpecialDataObject, piCallback, pInsertionAllowed); Cleanup: if ( fIsMultiSelect && (NULL != ppiDataObjects) ) { (void)GlobalFree(ppiDataObjects); } return hr; }
HRESULT STDMETHODCALLTYPE IExtendContextMenu_AddMenuItems_Stub ( IExtendContextMenu __RPC_FAR *This, long cDataObjects, IDataObject __RPC_FAR *__RPC_FAR ppiDataObjects[ ], BOOL fSpecialDataObject, long lSpecialDataObject, LPCONTEXTMENUCALLBACK piCallback, long __RPC_FAR *pInsertionAllowed ) { HRESULT hr = S_OK; IDataObject *piDataObject = NULL; // Not AddRef()ed
IDataObject *piMultiSelDataObject = NULL;
// If there is more than one data object then we need to pack them into a
// a separate data object that appears as a multi-select data object.
if (cDataObjects > 1L) { hr = CreateMultiSelDataObject(ppiDataObjects, cDataObjects, &piMultiSelDataObject); if (FAILED(hr)) { goto Cleanup; } piDataObject = piMultiSelDataObject; } else if (fSpecialDataObject) { piDataObject = (IDataObject *)lSpecialDataObject; } else { piDataObject = ppiDataObjects[0]; }
// Call the snap-in
hr = This->lpVtbl->AddMenuItems(This, piDataObject, piCallback, pInsertionAllowed);
Cleanup: if (NULL != piMultiSelDataObject) { piMultiSelDataObject->lpVtbl->Release(piMultiSelDataObject); } return hr; }
HRESULT STDMETHODCALLTYPE IExtendContextMenu_Command_Proxy ( IExtendContextMenu __RPC_FAR *This, long lCommandID, LPDATAOBJECT piDataObject ) { HRESULT hr = S_OK; BOOL fIsMultiSelect = FALSE; long cDataObjects = 1L; IDataObject **ppiDataObjects = NULL; BOOL fSpecialDataObject = FALSE; long lSpecialDataObject = 0;
// Check for special data objects such DOBJ_CUSTOMWEB etc.
CheckForSpecialDataObjects(&piDataObject, &fSpecialDataObject, &lSpecialDataObject);
// If this is a mutliple selection then we need to extract the data
// objects in the HGLOBAL
if (!fSpecialDataObject) { hr = IsMultiSelect(piDataObject, &fIsMultiSelect); if (FAILED(hr)) { goto Cleanup; } }
if (fIsMultiSelect) { hr = InterpretMultiSelect(piDataObject, &cDataObjects, &ppiDataObjects); if (FAILED(hr)) { goto Cleanup; } } else { ppiDataObjects = &piDataObject; }
hr = IExtendContextMenu_RemCommand_Proxy(This, cDataObjects, ppiDataObjects, fSpecialDataObject, lSpecialDataObject, lCommandID); Cleanup: if ( fIsMultiSelect && (NULL != ppiDataObjects) ) { (void)GlobalFree(ppiDataObjects); } return hr; }
HRESULT STDMETHODCALLTYPE IExtendContextMenu_Command_Stub ( IExtendContextMenu __RPC_FAR *This, long cDataObjects, IDataObject __RPC_FAR *__RPC_FAR ppiDataObjects[ ], BOOL fSpecialDataObject, long lSpecialDataObject, long lCommandID ) { HRESULT hr = S_OK; IDataObject *piDataObject = NULL; // Not AddRef()ed
IDataObject *piMultiSelDataObject = NULL;
// If there is more than one data object then we need to pack them into a
// a separate data object that appears as a multi-select data object.
if (cDataObjects > 1L) { hr = CreateMultiSelDataObject(ppiDataObjects, cDataObjects, &piMultiSelDataObject); if (FAILED(hr)) { goto Cleanup; } piDataObject = piMultiSelDataObject; } else if (fSpecialDataObject) { piDataObject = (IDataObject *)lSpecialDataObject; } else { piDataObject = ppiDataObjects[0]; }
// Call the snap-in
hr = This->lpVtbl->Command(This, lCommandID, piDataObject);
Cleanup: if (NULL != piMultiSelDataObject) { piMultiSelDataObject->lpVtbl->Release(piMultiSelDataObject); } return hr; }
HRESULT STDMETHODCALLTYPE IColumnData_GetColumnConfigData_Proxy ( IColumnData __RPC_FAR *This, SColumnSetID __RPC_FAR *pColID, MMC_COLUMN_SET_DATA __RPC_FAR *__RPC_FAR *ppColSetData ) { HRESULT hr = S_OK; size_t cbColData = 0; MMC_COLUMN_SET_DATA *pColSetData = NULL;
// Call the proxy and get the returned data from MMC
hr = IColumnData_RemGetColumnConfigData_Proxy(This, pColID, ppColSetData); if (FAILED(hr)) { goto Cleanup; }
// If no data was returned then there is nothing to do
if (NULL == *ppColSetData) { goto Cleanup; }
if (NULL == (*ppColSetData)->pColData) { goto Cleanup; }
// At this point the MIDL-generated proxy has returned an MMC_COLUMN_SET_DATA
// in which the embedded pointer pColData points to a separate block of
// memory which must be freed independently. The snap-in thinks that
// pColData points into the same block of memory so it will only call
// CoTaskMemFree for the MMC_COLUMN_SET_DATA. We need to allocate a new single
// block in the format the snap-in is expecting and free the memory returned
// from the proxy.
cbColData = sizeof(MMC_COLUMN_DATA) * (*ppColSetData)->nNumCols;
pColSetData = (MMC_COLUMN_SET_DATA *)CoTaskMemAlloc( sizeof(MMC_COLUMN_SET_DATA) + cbColData); if (NULL == pColSetData) { CoTaskMemFree((*ppColSetData)->pColData); CoTaskMemFree(*ppColSetData); *ppColSetData = NULL; hr = E_OUTOFMEMORY; } else { // Copy the column set data
memcpy(pColSetData, (*ppColSetData), sizeof(MMC_COLUMN_SET_DATA));
// Set the embedded pointer to point immediately following the
// MMC_COLUMN_SET_DATA
pColSetData->pColData = (MMC_COLUMN_DATA *)(pColSetData + 1);
// Copy the column data
memcpy(pColSetData->pColData, (*ppColSetData)->pColData, cbColData);
// Free the data returned from the proxy
CoTaskMemFree((*ppColSetData)->pColData); CoTaskMemFree(*ppColSetData);
// Return the new single block pointer to the snap-in
*ppColSetData = pColSetData; }
Cleanup: return hr; }
HRESULT STDMETHODCALLTYPE IColumnData_GetColumnConfigData_Stub ( IColumnData __RPC_FAR *This, SColumnSetID __RPC_FAR *pColID, MMC_COLUMN_SET_DATA __RPC_FAR *__RPC_FAR *ppColSetData ) { HRESULT hr = S_OK; size_t cbColData = 0; MMC_COLUMN_DATA *pColData = NULL;
// Call into MMC and get the returned data
hr = This->lpVtbl->GetColumnConfigData(This, pColID, ppColSetData); if (FAILED(hr)) { goto Cleanup; }
// If no data was returned then there is nothing to do
if (NULL == *ppColSetData) { goto Cleanup; }
if (NULL == (*ppColSetData)->pColData) { goto Cleanup; }
// At this point MMC has returned a pointer to an MMC_COLUMN_SET_DATA
// that contains an embedded pointer into the same block of memory (pColData).
// MMC expects that the caller will make a single call to CoTaskMemFree().
// The MIDL-generated stub thinks the embedded pointer needs to be freed
// separately so we need to reconstruct the output to use two separate
// blocks.
// Allocate a new MMC_COLUMN_DATA array
cbColData = sizeof(MMC_COLUMN_DATA) * (*ppColSetData)->nNumCols;
pColData = (MMC_COLUMN_DATA *)CoTaskMemAlloc(cbColData); if (NULL == pColData) { CoTaskMemFree(*ppColSetData); *ppColSetData = NULL; hr = E_OUTOFMEMORY; } else { // Copy the column data
memcpy(pColData, (*ppColSetData)->pColData, cbColData);
// Overwrite the existing embedded pointer. There will be no memory leak
// because that pointer pointed into the same block as the
// MMC_COLUMN_SET_DATA and the stub will free the MMC_COLUMN_SET_DATA pointer.
// Now both pointers can be freed independently as the stub expects.
(*ppColSetData)->pColData = pColData; }
Cleanup: return hr; }
HRESULT STDMETHODCALLTYPE IColumnData_GetColumnSortData_Proxy ( IColumnData __RPC_FAR *This, SColumnSetID __RPC_FAR *pColID, MMC_SORT_SET_DATA __RPC_FAR *__RPC_FAR *ppColSortData ) { HRESULT hr = S_OK; size_t cbSortData = 0; MMC_SORT_SET_DATA *pColSortData = NULL;
// Call the proxy and get the returned data from MMC
hr = IColumnData_RemGetColumnSortData_Proxy(This, pColID, ppColSortData); if (FAILED(hr)) { goto Cleanup; }
// If no data was returned then there is nothing to do
if (NULL == *ppColSortData) { goto Cleanup; }
if (NULL == (*ppColSortData)->pSortData) { goto Cleanup; }
// At this point the MIDL-generated proxy has returned an MMC_SORT_SET_DATA
// in which the embedded pointer pSortData points to a separate block of
// memory which must be freed independently. The snap-in thinks that
// pSortData points into the same block of memory so it will only call
// CoTaskMemFree for the MMC_SORT_SET_DATA. We need to allocate a new single
// block in the format the snap-in is expecting and free the memory returned
// from the proxy.
cbSortData = sizeof(MMC_SORT_DATA) * (*ppColSortData)->nNumItems;
pColSortData = (MMC_SORT_SET_DATA *)CoTaskMemAlloc( sizeof(MMC_SORT_SET_DATA) + cbSortData);
if (NULL == pColSortData) { CoTaskMemFree((*ppColSortData)->pSortData); CoTaskMemFree(*ppColSortData); *ppColSortData = NULL; hr = E_OUTOFMEMORY; } else { // Copy the sort set data
memcpy(pColSortData, (*ppColSortData), sizeof(MMC_SORT_SET_DATA));
// Set the embedded pointer to point immediately following the
// MMC_SORT_SET_DATA
pColSortData->pSortData = (MMC_SORT_DATA *)(pColSortData + 1);
// Copy the sort data
memcpy(pColSortData->pSortData, (*ppColSortData)->pSortData, cbSortData);
// Free the data returned from the proxy
CoTaskMemFree((*ppColSortData)->pSortData); CoTaskMemFree(*ppColSortData);
// Return the new single block pointer to the snap-in
*ppColSortData = pColSortData; }
Cleanup: return hr; }
HRESULT STDMETHODCALLTYPE IColumnData_GetColumnSortData_Stub ( IColumnData __RPC_FAR *This, SColumnSetID __RPC_FAR *pColID, MMC_SORT_SET_DATA __RPC_FAR *__RPC_FAR *ppColSortData ) { HRESULT hr = S_OK; size_t cbSortData = 0; MMC_SORT_DATA *pSortData = NULL;
// Call into MMC and get the returned data
hr = This->lpVtbl->GetColumnSortData(This, pColID, ppColSortData); if (FAILED(hr)) { goto Cleanup; }
// If no data was returned then there is nothing to do
if (NULL == *ppColSortData) { goto Cleanup; }
if (NULL == (*ppColSortData)->pSortData) { goto Cleanup; }
// At this point MMC has returned a pointer to an MMC_SORT_SET_DATA
// that contains an embedded pointer into the same block of memory (pSortData).
// MMC expects that the caller will make a single call to CoTaskMemFree().
// The MIDL-generated stub thinks the embedded pointer needs to be freed
// separately so we need to reconstruct the output to use two separate
// blocks.
// Allocate a new MMC_SORT_DATA array
cbSortData = sizeof(MMC_SORT_DATA) * (*ppColSortData)->nNumItems;
pSortData = (MMC_SORT_DATA *)CoTaskMemAlloc(cbSortData); if (NULL == pSortData) { CoTaskMemFree(*ppColSortData); *ppColSortData = NULL; hr = E_OUTOFMEMORY; } else { // Copy the column data
memcpy(pSortData, (*ppColSortData)->pSortData, cbSortData);
// Overwrite the existing embedded pointer. There will be no memory leak
// because that pointer pointed into the same block as the
// MMC_SORT_SET_DATA and the stub will free the MMC_SORT_SET_DATA pointer.
// Now both pointers can be freed independently as the stub expects.
(*ppColSortData)->pSortData = pSortData; }
Cleanup: return hr; }
|