Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1037 lines
33 KiB

#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);
}