|
|
#include "pch.h"
#include "wab.h"
#pragma hdrstop
/*-----------------------------------------------------------------------------
/ Misc data /----------------------------------------------------------------------------*/
//
// CDsPropertyPages is used to display the property pages, context menus etc
//
class CDsPropertyPages : public IWABExtInit, IShellExtInit, IContextMenu, IShellPropSheetExt, IObjectWithSite { private: LONG _cRef; IUnknown* _punkSite; IDataObject* _pDataObject; HDSA _hdsaMenuItems;
SHORT AddMenuItem(HMENU hMenu, LPWSTR pMenuReference, UINT index, UINT uIDFirst, UINT uIDLast, UINT uFlags);
public: CDsPropertyPages(); ~CDsPropertyPages();
// IUnknown members
STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release(); STDMETHODIMP QueryInterface(REFIID, LPVOID FAR*);
// IShellExtInit
STDMETHODIMP Initialize(LPCITEMIDLIST pIDFolder, LPDATAOBJECT pDataObj, HKEY hKeyID);
// IWABExtInit
STDMETHODIMP Initialize(LPWABEXTDISPLAY pWED);
// IShellPropSheetExt
STDMETHODIMP AddPages(LPFNADDPROPSHEETPAGE pAddPageProc, LPARAM lParam); STDMETHODIMP ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE pReplacePageFunc, LPARAM lParam);
// IContextMenu
STDMETHODIMP QueryContextMenu(HMENU hMenu, UINT uIndex, UINT uIDFirst, UINT uIDLast, UINT uFlags); STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO pCMI); STDMETHODIMP GetCommandString(UINT_PTR uID, UINT uFlags, UINT FAR* reserved, LPSTR pName, UINT ccMax);
// IObjectWithSite
STDMETHODIMP SetSite(IUnknown* punk); STDMETHODIMP GetSite(REFIID riid, void **ppv); };
//
// To handle the conversion from a IWABExtInit to an IShellExtInit we must
// provide an IDataObject implementation that supports this. This doesn't need
// to be too public, therefore lets define it here.
//
class CWABDataObject : public IDataObject { private: LONG _cRef; LPWSTR _pPath; IADs* _pDsObject;
public: CWABDataObject(LPWSTR pDN); ~CWABDataObject();
// IUnknown
STDMETHOD(QueryInterface)(REFIID riid, LPVOID* ppvObject); STDMETHOD_(ULONG, AddRef)(); STDMETHOD_(ULONG, Release)();
// IDataObject
STDMETHODIMP GetData(FORMATETC *pformatetcIn, STGMEDIUM *pmedium); STDMETHODIMP GetDataHere(FORMATETC *pformatetc, STGMEDIUM *pmedium) { return E_NOTIMPL; } STDMETHODIMP QueryGetData(FORMATETC *pformatetc) { return E_NOTIMPL; } STDMETHODIMP GetCanonicalFormatEtc(FORMATETC *pformatectIn, FORMATETC *pformatetcOut) { return E_NOTIMPL; } STDMETHODIMP SetData(FORMATETC *pformatetc, STGMEDIUM *pmedium, BOOL fRelease) { return E_NOTIMPL; } STDMETHODIMP EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ppenumFormatEtc) { return E_NOTIMPL; } STDMETHODIMP DAdvise(FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection) { return E_NOTIMPL; } STDMETHODIMP DUnadvise(DWORD dwConnection) { return E_NOTIMPL; } STDMETHODIMP EnumDAdvise(IEnumSTATDATA **ppenumAdvise) { return E_NOTIMPL; } };
//
// clipboard formats exposed
//
CLIPFORMAT g_cfDsObjectNames = 0; CLIPFORMAT g_cfDsDispSpecOptions = 0;
//
// Having extracted the menu item handler list from the cache we then
// convert it DSA made of the following items. For
//
typedef struct { INT cAdded; // number of verbs added
IContextMenu* pContextMenu; // IContextMenu handler interface / = NULL
LPTSTR pCaption; // Display text for the command, used for the help text
LPTSTR pCommand; // Command line passed to shell execute
} DSMENUITEM, * LPDSMENUITEM;
/*----------------------------------------------------------------------------
/ Helper functions /----------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------
/ _FreeMenuItem / ------------- / Tidy up a DSMENUITEM structure, releasing all memory, interfaces etc. / / In: / pItem -> item to be released / / Out: / VOID /----------------------------------------------------------------------------*/
VOID _FreeMenuItem(LPDSMENUITEM pItem) { TraceEnter(TRACE_UI, "_FreeMenuItem");
// ensure we free the site object, or we will leak memory
if (pItem->pContextMenu) { IObjectWithSite *pows; if (SUCCEEDED(pItem->pContextMenu->QueryInterface(IID_PPV_ARG(IObjectWithSite, &pows)))) { pows->SetSite(NULL); pows->Release(); } }
DoRelease(pItem->pContextMenu); LocalFreeString(&pItem->pCaption); LocalFreeString(&pItem->pCommand);
TraceLeave(); }
//
// Helper for DSA destruction
//
INT _FreeMenuItemCB(LPVOID pVoid, LPVOID pData) { LPDSMENUITEM pItem = (LPDSMENUITEM)pVoid; TraceAssert(pItem);
TraceEnter(TRACE_UI, "_FreeMenuItemCB");
_FreeMenuItem(pItem);
TraceLeaveValue(TRUE); }
/*----------------------------------------------------------------------------
/ CDsPropertyPages implementation /----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
/ IUnknown /----------------------------------------------------------------------------*/
CDsPropertyPages::CDsPropertyPages() : _cRef(1), _punkSite(NULL), _pDataObject(NULL), _hdsaMenuItems(NULL) { DllAddRef(); }
CDsPropertyPages::~CDsPropertyPages() { DoRelease(_punkSite); DoRelease(_pDataObject);
if (_hdsaMenuItems) DSA_DestroyCallback(_hdsaMenuItems, _FreeMenuItemCB, NULL);
DllRelease(); }
// IUnknown
ULONG CDsPropertyPages::AddRef() { return InterlockedIncrement(&_cRef); }
ULONG CDsPropertyPages::Release() { TraceAssert( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; }
HRESULT CDsPropertyPages::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CDsPropertyPages, IShellExtInit), // IID_IShellExtInit
QITABENT(CDsPropertyPages, IShellPropSheetExt), // IID_IShellPropSheetExt
QITABENT(CDsPropertyPages, IContextMenu), // IID_IContextMenu
QITABENT(CDsPropertyPages, IWABExtInit), // IID_IWABExtInit
QITABENT(CDsPropertyPages, IObjectWithSite), // IID_IObjectWithSite
{0, 0 }, }; return QISearch(this, qit, riid, ppv); }
//
// handle create instance
//
STDAPI CDsPropertyPages_CreateInstance(IUnknown* punkOuter, IUnknown** ppunk, LPCOBJECTINFO poi) { CDsPropertyPages *pdpp = new CDsPropertyPages(); if (!pdpp) return E_OUTOFMEMORY;
HRESULT hres = pdpp->QueryInterface(IID_IUnknown, (void **)ppunk); pdpp->Release(); return hres; }
/*-----------------------------------------------------------------------------
/ CDsPropertyPages::AddMenuItem / ----------------------------- / This object maintains a DSA containing the currently active menu item list, / this adds a menu item to that list and also merges with the specified / hMenu. We are given a string which reperesnets the menu to add, this / can either be a GUID, or "display text,command" which we then parse / and make a suitable entry for. / / The DSA reflects the items that we add and contains the IContextMenu / handler iface pointers for the things we drag in. / / In: / hMenu = menu to merge into / pMenuReference -> string defining item to add / index = index to insert the item at / uIDFirst, uIDLast, uFlags = IContextMenu::QueryContextMenu parameters / / Out: / SHORT = the number of items merged /----------------------------------------------------------------------------*/ SHORT CDsPropertyPages::AddMenuItem(HMENU hMenu, LPWSTR pMenuReference, UINT index, UINT uIDFirst, UINT uIDLast, UINT uFlags) { HRESULT hres; GUID guid; WCHAR szCaption[MAX_PATH]; WCHAR szCommand[MAX_PATH]; DSMENUITEM item; IShellExtInit* pShellExtInit = NULL; IObjectWithSite *pows = NULL;
TraceEnter(TRACE_UI, "CDsPropertyPages::AddMenuItem");
// initialize the item structure we are going to keep, then try and crack the
// item information we have been given
item.cAdded = 0; item.pContextMenu = NULL; item.pCaption = NULL; item.pCommand = NULL;
if (!hMenu) ExitGracefully(hres, E_INVALIDARG, "Bad arguments to _AddMenuItem");
if (GetGUIDFromString(pMenuReference, &guid)) { // its a GUID, therefore lets pull in the Win32 extension that provides it, and allow it
// to add in its verbs. We then hang onto the IContextMenu interface so that we can
// pass further requests to it (InvokeCommand, GetCommandString).
hres = CoCreateInstance(guid, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IContextMenu, &item.pContextMenu)); FailGracefully(hres, "Failed to get IContextMenu from the GUID");
if (_punkSite && SUCCEEDED(item.pContextMenu->QueryInterface(IID_PPV_ARG(IObjectWithSite, &pows)))) { hres = pows->SetSite(_punkSite); FailGracefully(hres, "Failed to ::SetSite on the extension object"); }
if (SUCCEEDED(item.pContextMenu->QueryInterface(IID_PPV_ARG(IShellExtInit, &pShellExtInit)))) { hres = pShellExtInit->Initialize(NULL, _pDataObject, NULL); FailGracefully(hres, "Failed when calling IShellExtInit::Initialize"); }
hres = item.pContextMenu->QueryContextMenu(hMenu, index, uIDFirst, uIDLast, uFlags); FailGracefully(hres, "Failed when calling QueryContextMenu");
item.cAdded = ShortFromResult(hres); } else { // its not a GUID therefore lets pull apart the string we have, it should
// consist of the display text for the menu item, and then a command to pass
// to ShellExecute.
Trace(TEXT("Parsing: %s"), pMenuReference);
if (SUCCEEDED(GetStringElementW(pMenuReference, 0, szCaption, ARRAYSIZE(szCaption))) && SUCCEEDED(GetStringElementW(pMenuReference, 1, szCommand, ARRAYSIZE(szCommand)))) { hres = LocalAllocStringW(&item.pCaption, szCaption); FailGracefully(hres, "Failed to add 'prompt' to structure");
hres = LocalAllocStringW(&item.pCommand, szCommand); FailGracefully(hres, "Failed to add 'command' to structure");
Trace(TEXT("uID: %08x, Caption: %s, Command: %s"), uIDFirst, item.pCaption, item.pCommand);
if (!InsertMenu(hMenu, index, MF_BYPOSITION|MF_STRING, uIDFirst, item.pCaption)) ExitGracefully(hres, E_FAIL, "Failed to add the menu item to hMenu");
item.cAdded = 1; } } hres = S_OK; // success
exit_gracefully: if (SUCCEEDED(hres)) { if (-1 == DSA_AppendItem(_hdsaMenuItems, &item)) ExitGracefully(hres, E_FAIL, "Failed to add the item to the DSA"); } else { _FreeMenuItem(&item); // make sure we tidy up
}
DoRelease(pows); DoRelease(pShellExtInit);
TraceLeaveValue((SHORT)item.cAdded); }
/*----------------------------------------------------------------------------
/ IShellExtInit /----------------------------------------------------------------------------*/
STDMETHODIMP CDsPropertyPages::Initialize(LPCITEMIDLIST pIDFolder, LPDATAOBJECT pDataObj, HKEY hKeyID) { HRESULT hres;
TraceEnter(TRACE_UI, "CDsPropertyPages::Initialize (IShellExtInit)");
// Release the previous data object and then pick up the new one that
// we are going to be using.
DoRelease(_pDataObject);
if (!pDataObj) ExitGracefully(hres, E_INVALIDARG, "Failed because we don't have a data object");
pDataObj->AddRef(); _pDataObject = pDataObj;
// Check that we have the clipboard format correctly registered so that we
// can collect a DSOBJECTNAMES structure
if (!g_cfDsObjectNames) { g_cfDsObjectNames = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DSOBJECTNAMES); g_cfDsDispSpecOptions = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DSDISPLAYSPECOPTIONS);
if (!g_cfDsObjectNames || !g_cfDsDispSpecOptions) { ExitGracefully(hres, E_FAIL, "No clipboard form registered"); } }
hres = S_OK; // success
exit_gracefully:
TraceLeaveResult(hres); }
/*----------------------------------------------------------------------------
/ IWABExtInit /----------------------------------------------------------------------------*/
#define WAB_PREFIX L"ldap:///"
#define CCH_WAB_PREFIX ARRAYSIZE(WAB_PREFIX)-1
STDMETHODIMP CDsPropertyPages::Initialize(LPWABEXTDISPLAY pWED) { HRESULT hres; WCHAR szDecodedURL[INTERNET_MAX_URL_LENGTH]; LPWSTR pszDecodedURL = szDecodedURL; INT cchDecodedURL; DWORD dwLen = ARRAYSIZE(szDecodedURL); IDataObject* pDataObject = NULL; LPWSTR pszPath = NULL; LPWSTR pURL = (LPWSTR)pWED->lpsz; INT i;
TraceEnter(TRACE_UI, "CDsPropertyPages::Initialize (IWABExtInit)");
if (!(pWED->ulFlags & WAB_DISPLAY_ISNTDS)) ExitGracefully(hres, E_FAIL, "The URL is not from NTDS, therefore ignoring");
if (!pURL) ExitGracefully(hres, E_FAIL, "URL pointer is NULL");
Trace(TEXT("LDAP URL is: %s"), pURL);
//
// we must now convert from a RFC LDAP URL to something that ADSI can handle, because
// although they both have the LDAP scheme they don't really mean the same thing.
//
// WAB will pass us an encoded URL, this we need to decode, strip the scheme name and
// then remove the tripple slash,
//
// eg: "LDAP:///dn%20dn" becomes, "LDAP://dn dn"
//
hres = UrlUnescapeW(pURL, szDecodedURL, &dwLen, 0); FailGracefully(hres, "Failed to convert URL to decoded format");
Trace(TEXT("Decoded URL is: %s"), szDecodedURL);
pszDecodedURL += CCH_WAB_PREFIX; // skip the LDAP:///
//
// now tail the URL removing all trailing slashes from it
//
for (cchDecodedURL = lstrlenW(pszDecodedURL); (cchDecodedURL > 0) && (pszDecodedURL[cchDecodedURL] == L'/'); cchDecodedURL--) { pszDecodedURL[cchDecodedURL] = L'\0'; } if (!cchDecodedURL) ExitGracefully(hres, E_UNEXPECTED, "URL is now NULL");
//
// so we have a DN, so lets allocate a IDataObject using it so that we
// can pass this into the real initialize method for shell extensions.
//
Trace(TEXT("DN from the LDAP URL we were given: %s"), pszDecodedURL);
pDataObject = new CWABDataObject(pszDecodedURL); TraceAssert(pDataObject);
if (!pDataObject) ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate the data object");
hres = Initialize(NULL, pDataObject, NULL); FailGracefully(hres, "Failed to initialize with the IDataObject");
// hres = S_OK; // success
exit_gracefully:
DoRelease(pDataObject);
TraceLeaveResult(hres); }
/*----------------------------------------------------------------------------
/ IShellPropSheetExt /----------------------------------------------------------------------------*/
HRESULT TabCollector_Collect(IUnknown *punkSite, IDataObject* pDataObject, LPFNADDPROPSHEETPAGE pAddPageProc, LPARAM lParam);
STDMETHODIMP CDsPropertyPages::AddPages(LPFNADDPROPSHEETPAGE pAddPageProc, LPARAM lParam) { HRESULT hres; TraceEnter(TRACE_UI, "CDsPropertyPages::AddPages");
hres = TabCollector_Collect(_punkSite, _pDataObject, pAddPageProc, lParam); FailGracefully(hres, "Failed when calling the collector");
//hres = S_OK; // success
exit_gracefully:
TraceLeaveResult(hres); }
/*---------------------------------------------------------------------------*/
STDMETHODIMP CDsPropertyPages::ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE lpfnReplaceWith, LPARAM lParam) { TraceEnter(TRACE_UI, "CDsPropertyPages::ReplacePage"); TraceLeaveResult(E_NOTIMPL); }
/*----------------------------------------------------------------------------
/ IContextMenu /----------------------------------------------------------------------------*/
STDMETHODIMP CDsPropertyPages::QueryContextMenu(HMENU hMenu, UINT index, UINT uIDFirst, UINT uIDLast, UINT uFlags) { HRESULT hres; STGMEDIUM medium = { TYMED_NULL }; FORMATETC fmte = {g_cfDsObjectNames, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; LPDSOBJECTNAMES pDsObjectNames = NULL; LPWSTR pPath; LPWSTR pObjectClass; CLASSCACHEGETINFO ccgi = { 0 }; LPCLASSCACHEENTRY pCacheEntry = NULL; INT i; INT cAdded = 0; TraceEnter(TRACE_UI, "CDsPropertyPages::QueryContextMenu");
if (!hMenu || !_pDataObject) ExitGracefully(hres, E_FAIL, "Either no IDataObject or no hMenu");
// Get the bits of information we need from the data object, we are not
// interested in a attributePrefix, therefore we skip that bit
// and then look up the menu list in the cache.
hres = _pDataObject->GetData(&fmte, &medium); FailGracefully(hres, "Failed to GetData using CF_DSOBJECTNAMES");
pDsObjectNames = (LPDSOBJECTNAMES)GlobalLock(medium.hGlobal);
if (pDsObjectNames->cItems < 1) ExitGracefully(hres, E_FAIL, "Not enough objects in DSOBJECTNAMES structure");
pPath = (LPWSTR)ByteOffset(pDsObjectNames, pDsObjectNames->aObjects[0].offsetName); pObjectClass = (LPWSTR)ByteOffset(pDsObjectNames, pDsObjectNames->aObjects[0].offsetClass);
// fill the CLASSCACHEGETINFO record so we can cache the information from the
// display specifiers.
ccgi.dwFlags = CLASSCACHE_CONTEXTMENUS; ccgi.pPath = pPath; ccgi.pObjectClass = pObjectClass; ccgi.pDataObject = _pDataObject;
hres = GetServerAndCredentails(&ccgi); FailGracefully(hres, "Failed to get the server name");
hres = GetAttributePrefix(&ccgi.pAttributePrefix, _pDataObject); FailGracefully(hres, "Failed to get attributePrefix");
Trace(TEXT("Class: %s; Attribute Prefix: %s; Server: %s"), pObjectClass, ccgi.pAttributePrefix, ccgi.pServer ? ccgi.pServer:TEXT("<none>"));
hres = ClassCache_GetClassInfo(&ccgi, &pCacheEntry); FailGracefully(hres, "Failed to get page list (via the cache)");
// did we get a menu list? If so lets pull it a part and generate a DSA
// which lists the menu items we are going to be displaying.
if ((pCacheEntry->dwCached & CLASSCACHE_CONTEXTMENUS) && pCacheEntry->hdsaMenuHandlers) { if (_hdsaMenuItems) DSA_DestroyCallback(_hdsaMenuItems, _FreeMenuItemCB, NULL);
_hdsaMenuItems = DSA_Create(SIZEOF(DSMENUITEM), 4);
if (!_hdsaMenuItems) ExitGracefully(hres, E_OUTOFMEMORY, "Failed to construct DSA for menu items");
for (i = DSA_GetItemCount(pCacheEntry->hdsaMenuHandlers) ; --i >= 0 ;) { LPDSMENUHANDLER pHandlerItem = (LPDSMENUHANDLER)DSA_GetItemPtr(pCacheEntry->hdsaMenuHandlers, i); TraceAssert(pHandlerItem);
cAdded += AddMenuItem(hMenu, pHandlerItem->pMenuReference, index, uIDFirst+cAdded, uIDLast, uFlags); } }
hres = S_OK; // success
exit_gracefully:
LocalFreeStringW(&ccgi.pAttributePrefix);
SecureLocalFreeStringW(&ccgi.pUserName); SecureLocalFreeStringW(&ccgi.pPassword); SecureLocalFreeStringW(&ccgi.pServer);
ClassCache_ReleaseClassInfo(&pCacheEntry);
if (pDsObjectNames) GlobalUnlock(medium.hGlobal); ReleaseStgMedium(&medium);
TraceLeaveResult(ResultFromShort(cAdded)); }
/*---------------------------------------------------------------------------*/
STDMETHODIMP CDsPropertyPages::InvokeCommand(LPCMINVOKECOMMANDINFO pCMI) { HRESULT hres; BOOL bReleaseMedium = FALSE; STGMEDIUM medium = { TYMED_NULL }; FORMATETC fmte = {g_cfDsObjectNames, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; LPTSTR pArguments = NULL; LPDSOBJECTNAMES pDsObjectNames; LPTSTR pPath; LPWSTR pObjectClass; DWORD object; INT i, id; TraceEnter(TRACE_UI, "CDsPropertyPages::InvokeCommand");
// Walk the DSA until we find an item in it that contains the range of
// items we are looking for, this will either involve invoking the
// command (via IContextMenu::InvokeCommand) or calling ShellExecute
// for the objects in the selection.
if (HIWORD(pCMI->lpVerb)) ExitGracefully(hres, E_FAIL, "Bad lpVerb value for this handler");
if (!_hdsaMenuItems) ExitGracefully(hres, E_INVALIDARG, "No menu item DSA");
for (id = LOWORD(pCMI->lpVerb), i = 0 ; i < DSA_GetItemCount(_hdsaMenuItems) ; i++) { LPDSMENUITEM pItem = (LPDSMENUITEM)DSA_GetItemPtr(_hdsaMenuItems, i); TraceAssert(pItem);
Trace(TEXT("id %08x, cAdded %d"), id, pItem->cAdded); if (id < pItem->cAdded) { if (pItem->pContextMenu) { CMINVOKECOMMANDINFO cmi = *pCMI; cmi.lpVerb = (LPCSTR)IntToPtr(id);
Trace(TEXT("Calling IContextMenu iface with ID %d"), id);
hres = pItem->pContextMenu->InvokeCommand(&cmi); FailGracefully(hres, "Failed when calling context menu handler (InvokeCommand)"); } else { // the command is not serviced via an IContextMenu handler, therefore lets for
// each object in the IDataObject call the command passing the arguments of
// the ADsPath and the class.
hres = _pDataObject->GetData(&fmte, &medium); FailGracefully(hres, "Failed to GetData using CF_DSOBJECTNAMES");
pDsObjectNames = (LPDSOBJECTNAMES)GlobalLock(medium.hGlobal); bReleaseMedium = TRUE;
if (pDsObjectNames->cItems < 1) ExitGracefully(hres, E_FAIL, "Not enough objects in DSOBJECTNAMES structure");
Trace(TEXT("Calling ShellExecute for ID %d (%s)"), id, pItem->pCommand);
for (object = 0 ; object < pDsObjectNames->cItems ; object++) { pPath = (LPWSTR)ByteOffset(pDsObjectNames, pDsObjectNames->aObjects[object].offsetName); pObjectClass = (LPWSTR)ByteOffset(pDsObjectNames, pDsObjectNames->aObjects[object].offsetClass);
int cchArguments = lstrlen(pPath)+lstrlenW(pObjectClass)+5; // nb: +5 for space and quotes
hres = LocalAllocStringLen(&pArguments, cchArguments); FailGracefully(hres, "Failed to allocate buffer for arguments");
// does the object path have a space? if so then lets wrap it in quotes
if (StrChr(pPath, TEXT(' '))) { StrCpyN(pArguments, TEXT("\""), cchArguments); StrCatBuff(pArguments, pPath, cchArguments); StrCatBuff(pArguments, TEXT("\""), cchArguments); } else { StrCpyN(pArguments, pPath, cchArguments); }
StrCatBuff(pArguments, TEXT(" "), cchArguments); StrCatBuff(pArguments, pObjectClass, cchArguments);
Trace(TEXT("Executing: %s"), pItem->pCommand); Trace(TEXT("Arguments: %s"), pArguments);
// calls ShellExecute with a command from the Display Specifier string.
ShellExecute(NULL, NULL, pItem->pCommand, pArguments, NULL, SW_SHOWNORMAL); LocalFreeString(&pArguments); }
GlobalUnlock(medium.hGlobal); ReleaseStgMedium(&medium); bReleaseMedium = FALSE; }
break; }
id -= pItem->cAdded; }
hres = (i < DSA_GetItemCount(_hdsaMenuItems)) ? S_OK:E_FAIL;
exit_gracefully:
if (bReleaseMedium) { GlobalUnlock(medium.hGlobal); ReleaseStgMedium(&medium); }
LocalFreeString(&pArguments);
TraceLeaveResult(hres); }
/*---------------------------------------------------------------------------*/
STDMETHODIMP CDsPropertyPages::GetCommandString(UINT_PTR uID, UINT uFlags, UINT FAR* reserved, LPSTR pName, UINT ccNameMax) { HRESULT hres; INT i; INT id = (INT)uID;
TraceEnter(TRACE_UI, "CDsPropertyPages::GetCommandString");
// Walk down the list of the menu items looking for one that matches the
// item we are trying get the command string from. If it is an IContextMenu
// handler then we must call down to that.
if (!_hdsaMenuItems) ExitGracefully(hres, E_INVALIDARG, "No menu item DSA");
for (i = 0 ; i < DSA_GetItemCount(_hdsaMenuItems) ; i++) { LPDSMENUITEM pItem = (LPDSMENUITEM)DSA_GetItemPtr(_hdsaMenuItems, i); TraceAssert(pItem);
Trace(TEXT("id %08x, cAdded %d"), id, pItem->cAdded); if (id < pItem->cAdded) { if (pItem->pContextMenu) { hres = pItem->pContextMenu->GetCommandString(id, uFlags, reserved, pName, ccNameMax); FailGracefully(hres, "Failed when calling context menu handler (GetCommandString)"); } else { if (uFlags != GCS_HELPTEXT) ExitGracefully(hres, E_FAIL, "We only respond to GCS_HELPTEXT");
Trace(TEXT("GCS_HELPTEXT returns for non-IContextMenu item: %s"), pItem->pCaption); StrCpyN((LPTSTR)pName, pItem->pCaption, ccNameMax); }
break; }
id -= pItem->cAdded; }
hres = (i < DSA_GetItemCount(_hdsaMenuItems)) ? S_OK:E_FAIL;
exit_gracefully:
TraceLeaveResult(hres); }
/*----------------------------------------------------------------------------
/ IObjectWithSite /----------------------------------------------------------------------------*/
STDMETHODIMP CDsPropertyPages::SetSite(IUnknown* punk) { HRESULT hres = S_OK;
TraceEnter(TRACE_UI, "CDsPropertyPages::SetSite");
DoRelease(_punkSite);
if (punk) { TraceMsg("QIing for IUnknown from the site object");
hres = punk->QueryInterface(IID_IUnknown, (void **)&_punkSite); FailGracefully(hres, "Failed to get IUnknown from the site object"); }
exit_gracefully:
TraceLeaveResult(hres); }
/*---------------------------------------------------------------------------*/
STDMETHODIMP CDsPropertyPages::GetSite(REFIID riid, void **ppv) { HRESULT hres; TraceEnter(TRACE_UI, "CDsPropertyPages::GetSite");
if (!_punkSite) ExitGracefully(hres, E_NOINTERFACE, "No site to QI from");
hres = _punkSite->QueryInterface(riid, ppv); FailGracefully(hres, "QI failed on the site unknown object");
exit_gracefully:
TraceLeaveResult(hres); }
/*-----------------------------------------------------------------------------
/ CWABDataObject /----------------------------------------------------------------------------*/
CWABDataObject::CWABDataObject(LPWSTR pDN) : _cRef(1) { TraceEnter(TRACE_WAB, "CWABDataObject::CWABDataObject");
int cchPath = lstrlenW(pDN)+7; // +7 for LDAP://
if (SUCCEEDED(LocalAllocStringLenW(&_pPath, cchPath))) { StrCpyW(_pPath, L"LDAP://"); StrCatW(_pPath, pDN); Trace(TEXT("DN converted to an ADSI path: %s"), _pPath); }
DllAddRef();
TraceLeave(); }
CWABDataObject::~CWABDataObject() { TraceEnter(TRACE_WAB, "CWABDataObject::~CWABDataObject");
LocalFreeStringW(&_pPath); DoRelease(_pDsObject);
DllRelease();
TraceLeave(); }
// IUnknown
ULONG CWABDataObject::AddRef() { return InterlockedIncrement(&_cRef); }
ULONG CWABDataObject::Release() { TraceAssert( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; }
HRESULT CWABDataObject::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CWABDataObject, IDataObject), // IID_IDataObject
{0, 0 }, }; return QISearch(this, qit, riid, ppv); }
// IDataObject methods
STDMETHODIMP CWABDataObject::GetData(FORMATETC* pFmt, STGMEDIUM* pMedium) { HRESULT hres; BOOL bReleaseMedium = FALSE; BSTR bstrObjectClass = NULL; DWORD cbStruct = SIZEOF(DSOBJECTNAMES); DWORD offset = SIZEOF(DSOBJECTNAMES); LPDSOBJECTNAMES pDsObjectNames = NULL; CLASSCACHEGETINFO ccgi = { 0 }; CLASSCACHEENTRY *pcce = NULL;
TraceEnter(TRACE_WAB, "CWABDataObject::GetData");
if (!g_cfDsObjectNames) ExitGracefully(hres, E_FAIL, "g_cfDsObjectNames == NULL, therefore GetData cannot work");
if (!_pPath) ExitGracefully(hres, E_FAIL, "No _pPath set in data object");
if (pFmt->cfFormat == g_cfDsObjectNames) { // do we have the ADsObject that represents this path yet? If not then
// lets grab it, but only do that once otherwise we will continually hit
// the wire.
if (!_pDsObject) { Trace(TEXT("Caching IADs for %s"), _pPath); hres = AdminToolsOpenObject(_pPath, NULL, NULL, ADS_SECURE_AUTHENTICATION, IID_PPV_ARG(IADs, &_pDsObject)); FailGracefully(hres, "Failed to get IADs for ADsPath we have"); }
// lets allocate a storage medium, put in the only object we have
// and then return that to the caller.
hres = _pDsObject->get_Class(&bstrObjectClass); FailGracefully(hres, "Failed to get the class of the object");
// we have the information we need so lets allocate the storage medium and
// return the DSOBJECTNAMES structure to the caller.
cbStruct += StringByteSizeW(_pPath); cbStruct += StringByteSizeW(bstrObjectClass);
hres = AllocStorageMedium(pFmt, pMedium, cbStruct, (LPVOID*)&pDsObjectNames); FailGracefully(hres, "Failed to allocate storage medium");
bReleaseMedium = TRUE;
pDsObjectNames->clsidNamespace = CLSID_MicrosoftDS; pDsObjectNames->cItems = 1;
pDsObjectNames->aObjects[0].dwFlags = 0;
// check to see if the object is a container, if it is then set the attributes
// accordingly.
ccgi.dwFlags = CLASSCACHE_CONTAINER|CLASSCACHE_TREATASLEAF; ccgi.pPath = _pPath; ccgi.pObjectClass = bstrObjectClass;
hres = ClassCache_GetClassInfo(&ccgi, &pcce); if (SUCCEEDED(hres)) { if (_IsClassContainer(pcce, FALSE)) { TraceMsg("Flagging the object as a container"); pDsObjectNames->aObjects[0].dwFlags |= DSOBJECT_ISCONTAINER; } ClassCache_ReleaseClassInfo(&pcce); }
pDsObjectNames->aObjects[0].dwProviderFlags = 0;
pDsObjectNames->aObjects[0].offsetName = offset; StringByteCopyW(pDsObjectNames, offset, _pPath); offset += StringByteSizeW(_pPath);
pDsObjectNames->aObjects[0].offsetClass = offset; StringByteCopyW(pDsObjectNames, offset, bstrObjectClass); offset += StringByteSizeW(bstrObjectClass); } else if (pFmt->cfFormat == g_cfDsDispSpecOptions) { PDSDISPLAYSPECOPTIONS pOptions; DWORD cbSize = SIZEOF(DSDISPLAYSPECOPTIONS)+StringByteSizeW(DS_PROP_SHELL_PREFIX);
// return the display spec options so we can indicate that WAB is involved
// in the menus.
hres = AllocStorageMedium(pFmt, pMedium, cbSize, (LPVOID*)&pOptions); FailGracefully(hres, "Failed to allocate the storage medium");
bReleaseMedium = TRUE;
pOptions->dwSize = cbSize; pOptions->dwFlags = DSDSOF_INVOKEDFROMWAB; // invoked from WAB however
pOptions->offsetAttribPrefix = SIZEOF(DSDISPLAYSPECOPTIONS); StringByteCopyW(pOptions, pOptions->offsetAttribPrefix, DS_PROP_SHELL_PREFIX); } else { ExitGracefully(hres, DV_E_FORMATETC, "Bad format passed to GetData"); }
hres = S_OK; // success
exit_gracefully:
if (FAILED(hres) && bReleaseMedium) ReleaseStgMedium(pMedium);
SysFreeString(bstrObjectClass);
TraceLeaveResult(hres); }
|