core.cpp
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 34k
Category:

Windows Kernel

Development Platform:

Visual C++

  1. #include "pch.h"
  2. #include "wab.h"
  3. #pragma hdrstop
  4. /*-----------------------------------------------------------------------------
  5. / Misc data
  6. /----------------------------------------------------------------------------*/
  7. //
  8. //  CDsPropertyPages is used to display the property pages, context menus etc
  9. //
  10. class CDsPropertyPages : public IWABExtInit, IShellExtInit, IContextMenu, IShellPropSheetExt, IObjectWithSite, CUnknown
  11. {
  12.     private:
  13.         IUnknown* _punkSite;
  14.         IDataObject* _pDataObject;
  15.         HDSA         _hdsaMenuItems;               
  16.         SHORT AddMenuItem(HMENU hMenu, LPWSTR pMenuReference, UINT index, UINT uIDFirst, UINT uIDLast, UINT uFlags);
  17.     public:
  18.         CDsPropertyPages();
  19.         ~CDsPropertyPages();
  20.         // IUnknown members
  21.         STDMETHODIMP_(ULONG) AddRef();
  22.         STDMETHODIMP_(ULONG) Release();
  23.         STDMETHODIMP QueryInterface( REFIID, LPVOID FAR* );
  24.         // IShellExtInit
  25.         STDMETHODIMP Initialize(LPCITEMIDLIST pIDFolder, LPDATAOBJECT pDataObj, HKEY hKeyID);
  26.         // IWABExtInit
  27.         STDMETHODIMP Initialize(LPWABEXTDISPLAY pWED);
  28.         // IShellPropSheetExt
  29.         STDMETHODIMP AddPages(LPFNADDPROPSHEETPAGE pAddPageProc, LPARAM lParam);
  30.         STDMETHODIMP ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE pReplacePageFunc, LPARAM lParam);
  31.         // IContextMenu
  32.         STDMETHODIMP QueryContextMenu(HMENU hMenu, UINT uIndex, UINT uIDFirst, UINT uIDLast, UINT uFlags);
  33.         STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO pCMI);
  34.         STDMETHODIMP GetCommandString(UINT_PTR uID, UINT uFlags, UINT FAR* reserved, LPSTR pName, UINT ccMax);
  35.         // IObjectWithSite
  36.         STDMETHODIMP SetSite(IUnknown* punk);
  37.         STDMETHODIMP GetSite(REFIID riid, void **ppv);
  38. };
  39. //
  40. // To handle the conversion from a IWABExtInit to an IShellExtInit we must
  41. // provide an IDataObject implementation that supports this.  This doesn't need
  42. // to be too public, therefore lets define it here.
  43. //
  44. class CWABDataObject : public IDataObject, CUnknown
  45. {
  46.     private:
  47.         LPWSTR _pPath;
  48.         IADs* _pDsObject;
  49.     public:
  50.         CWABDataObject(LPWSTR pDN);
  51.         ~CWABDataObject();
  52.         // IUnknown
  53.         STDMETHOD(QueryInterface)(REFIID riid, LPVOID* ppvObject);
  54.         STDMETHOD_(ULONG, AddRef)();
  55.         STDMETHOD_(ULONG, Release)();
  56.         // IDataObject
  57.         STDMETHODIMP GetData(FORMATETC *pformatetcIn, STGMEDIUM *pmedium);
  58.         STDMETHODIMP GetDataHere(FORMATETC *pformatetc, STGMEDIUM *pmedium);
  59.         STDMETHODIMP QueryGetData(FORMATETC *pformatetc);
  60.         STDMETHODIMP GetCanonicalFormatEtc(FORMATETC *pformatectIn, FORMATETC *pformatetcOut);
  61.         STDMETHODIMP SetData(FORMATETC *pformatetc, STGMEDIUM *pmedium, BOOL fRelease);
  62.         STDMETHODIMP EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ppenumFormatEtc);
  63.         STDMETHODIMP DAdvise(FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection);
  64.         STDMETHODIMP DUnadvise(DWORD dwConnection);
  65.         STDMETHODIMP EnumDAdvise(IEnumSTATDATA **ppenumAdvise);
  66. };
  67. //
  68. // clipboard formats exposed
  69. //
  70. CLIPFORMAT g_cfDsObjectNames = 0;
  71. CLIPFORMAT g_cfDsDispSpecOptions = 0;
  72. //
  73. // Having extracted the menu item handler list from the cache we then
  74. // convert it DSA made of the following items.  For
  75. //
  76. typedef struct
  77. {
  78.     INT           cAdded;                   // number of verbs added
  79.     IContextMenu* pContextMenu;             // IContextMenu handler interface / = NULL
  80.     LPTSTR        pCaption;                 // Display text for the command, used for the help text
  81.     LPTSTR        pCommand;                 // Command line passed to shell execute
  82. } DSMENUITEM, * LPDSMENUITEM;
  83. /*----------------------------------------------------------------------------
  84. / Helper functions
  85. /----------------------------------------------------------------------------*/
  86. /*-----------------------------------------------------------------------------
  87. / _FreeMenuItem
  88. / -------------
  89. /   Tidy up a DSMENUITEM structure, releasing all memory, interfaces etc.
  90. /
  91. / In:
  92. /   pItem -> item to be released
  93. /
  94. / Out:
  95. /   VOID
  96. /----------------------------------------------------------------------------*/
  97. VOID _FreeMenuItem(LPDSMENUITEM pItem)
  98. {
  99.     TraceEnter(TRACE_UI, "_FreeMenuItem");
  100.     DoRelease(pItem->pContextMenu);
  101.     LocalFreeString(&pItem->pCaption);
  102.     LocalFreeString(&pItem->pCommand);
  103.     TraceLeave();
  104. }
  105. //
  106. // Helper for DSA destruction
  107. //
  108. INT _FreeMenuItemCB(LPVOID pVoid, LPVOID pData)
  109. {
  110.     LPDSMENUITEM pItem = (LPDSMENUITEM)pVoid;
  111.     TraceAssert(pItem);
  112.     TraceEnter(TRACE_UI, "_FreeMenuItemCB");
  113.     _FreeMenuItem(pItem);
  114.     TraceLeaveValue(TRUE);
  115. }
  116. /*----------------------------------------------------------------------------
  117. / CDsPropertyPages implementation
  118. /----------------------------------------------------------------------------*/
  119. /*----------------------------------------------------------------------------
  120. / IUnknown
  121. /----------------------------------------------------------------------------*/
  122. CDsPropertyPages::CDsPropertyPages()
  123. {
  124.     _punkSite = NULL;
  125.     _pDataObject = NULL;
  126.     _hdsaMenuItems = NULL;
  127. }
  128. CDsPropertyPages::~CDsPropertyPages()
  129. {
  130.     DoRelease(_punkSite);
  131.     DoRelease(_pDataObject);
  132.     if ( _hdsaMenuItems )
  133.         DSA_DestroyCallback(_hdsaMenuItems, _FreeMenuItemCB, NULL);
  134. }
  135. // IUnknown bits
  136. #undef CLASS_NAME
  137. #define CLASS_NAME CDsPropertyPages
  138. #include "unknown.inc"
  139. STDMETHODIMP CDsPropertyPages::QueryInterface(REFIID riid, LPVOID* ppvObject)
  140. {
  141.     INTERFACES iface[] =
  142.     {
  143.         &IID_IShellExtInit, (LPSHELLEXTINIT)this,
  144.         &IID_IShellPropSheetExt, (LPSHELLPROPSHEETEXT)this,
  145.         &IID_IContextMenu, (LPCONTEXTMENU)this,
  146.         &IID_IWABExtInit, (IWABExtInit*)this,
  147.         &IID_IObjectWithSite, (IObjectWithSite*)this,
  148.     };
  149.     return HandleQueryInterface(riid, ppvObject, iface, ARRAYSIZE(iface));
  150. }
  151. //
  152. // handle create instance
  153. //
  154. STDAPI CDsPropertyPages_CreateInstance(IUnknown* punkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
  155. {
  156.     CDsPropertyPages *pdpp = new CDsPropertyPages();
  157.     if ( !pdpp )
  158.         return E_OUTOFMEMORY;
  159.     HRESULT hres = pdpp->QueryInterface(IID_IUnknown, (void **)ppunk);
  160.     pdpp->Release();
  161.     return hres;
  162. }
  163. /*-----------------------------------------------------------------------------
  164. / CDsPropertyPages::AddMenuItem
  165. / -----------------------------
  166. /   This object maintains a DSA containing the currently active menu item list,
  167. /   this adds a menu item to that list and also merges with the specified
  168. /   hMenu.  We are given a string which reperesnets the menu to add, this
  169. /   can either be a GUID, or "display text,command" which we then parse
  170. /   and make a suitable entry for.
  171. /
  172. /   The DSA reflects the items that we add and contains the IContextMenu
  173. /   handler iface pointers for the things we drag in.
  174. /
  175. / In:
  176. /   hMenu = menu to merge into
  177. /   pMenuReference -> string defining item to add
  178. /   index = index to insert the item at
  179. /   uIDFirst, uIDLast, uFlags = IContextMenu::QueryContextMenu parameters
  180. /
  181. / Out:
  182. /   SHORT = the number of items merged
  183. /----------------------------------------------------------------------------*/
  184. SHORT CDsPropertyPages::AddMenuItem(HMENU hMenu, LPWSTR pMenuReference, UINT index, UINT uIDFirst, UINT uIDLast, UINT uFlags)
  185. {
  186.     HRESULT hres;
  187.     GUID guid;
  188.     WCHAR szCaption[MAX_PATH];
  189.     WCHAR szCommand[MAX_PATH];
  190.     DSMENUITEM item;
  191.     IShellExtInit* pShellExtInit = NULL;
  192.     IObjectWithSite *pows = NULL;
  193.     USES_CONVERSION;
  194.     TraceEnter(TRACE_UI, "CDsPropertyPages::AddMenuItem");
  195.     // initialize the item structure we are going to keep, then try and crack the
  196.     // item information we have been given
  197.     if ( !hMenu )
  198.         ExitGracefully(hres, E_INVALIDARG, "Bad arguments to _AddMenuItem");
  199.     item.cAdded = 0;
  200.     item.pContextMenu = NULL;
  201.     item.pCaption = NULL;
  202.     item.pCommand = NULL;
  203.     if ( GetGUIDFromStringW(pMenuReference, &guid) )
  204.     {
  205.         // its a GUID, therefore lets pull in the Win32 extension that provides it, and allow it
  206.         // to add in its verbs.  We then hang onto the IContextMenu interface so that we can
  207.         // pass further requests to it (InvokeCommand, GetCommandString).
  208.         hres = CoCreateInstance(guid, NULL, CLSCTX_INPROC_SERVER, IID_IContextMenu, (LPVOID*)&item.pContextMenu);
  209.         FailGracefully(hres, "Failed to get IContextMenu from the GUID");
  210.         if ( _punkSite && 
  211.                 SUCCEEDED(item.pContextMenu->QueryInterface(IID_IObjectWithSite,(void**)&pows)) )
  212.         {
  213.             hres = pows->SetSite(_punkSite);
  214.             FailGracefully(hres, "Failed to ::SetSite on the extension object");
  215.         }
  216.         if ( SUCCEEDED(item.pContextMenu->QueryInterface(IID_IShellExtInit, (LPVOID*)&pShellExtInit)) )
  217.         {
  218.             hres = pShellExtInit->Initialize(NULL, _pDataObject, NULL);
  219.             FailGracefully(hres, "Failed when calling IShellExtInit::Initialize");
  220.         }
  221.         hres = item.pContextMenu->QueryContextMenu(hMenu, index, uIDFirst, uIDLast, uFlags);
  222.         FailGracefully(hres, "Failed when calling QueryContextMenu");
  223.         item.cAdded = ShortFromResult(hres);
  224.     }
  225.     else
  226.     {
  227.         // its not a GUID therefore lets pull apart the string we have, it should
  228.         // consist of the display text for the menu item, and then a command to pass
  229.         // to ShellExecute.
  230.         Trace(TEXT("Parsing: %s"), W2T(pMenuReference));
  231.         if ( SUCCEEDED(GetStringElementW(pMenuReference, 0, szCaption, ARRAYSIZE(szCaption))) && 
  232.              SUCCEEDED(GetStringElementW(pMenuReference, 1, szCommand, ARRAYSIZE(szCommand))) )
  233.         {
  234.             hres = LocalAllocStringW2T(&item.pCaption, szCaption);
  235.             FailGracefully(hres, "Failed to add 'prompt' to structure");
  236.             hres = LocalAllocStringW2T(&item.pCommand, szCommand);
  237.             FailGracefully(hres, "Failed to add 'command' to structure");
  238.             Trace(TEXT("uID: %08x, Caption: %s, Command: %s"), 
  239.                             uIDFirst, item.pCaption, item.pCommand);
  240.             if ( !InsertMenu(hMenu, index, MF_BYPOSITION|MF_STRING, uIDFirst, item.pCaption) )
  241.                ExitGracefully(hres, E_FAIL, "Failed to add the menu item to hMenu");
  242.             item.cAdded = 1;
  243.         }
  244.     }
  245.     
  246.     hres = S_OK;              // success
  247. exit_gracefully:
  248.     
  249.     if ( SUCCEEDED(hres) )
  250.     {
  251.         if ( -1 == DSA_AppendItem(_hdsaMenuItems, &item) )
  252.             ExitGracefully(hres, E_FAIL, "Failed to add the item to the DSA");
  253.     }
  254.     else
  255.     {
  256.         _FreeMenuItem(&item);           // make sure we tidy up
  257.     }
  258.     DoRelease(pows);
  259.     DoRelease(pShellExtInit);
  260.     TraceLeaveValue((SHORT)item.cAdded);
  261. }
  262. /*----------------------------------------------------------------------------
  263. / IShellExtInit
  264. /----------------------------------------------------------------------------*/
  265. STDMETHODIMP CDsPropertyPages::Initialize(LPCITEMIDLIST pIDFolder, LPDATAOBJECT pDataObj, HKEY hKeyID)
  266. {
  267.     HRESULT hres;
  268.     TraceEnter(TRACE_UI, "CDsPropertyPages::Initialize (IShellExtInit)");
  269.     // Release the previous data object and then pick up the new one that
  270.     // we are going to be using.
  271.     DoRelease(_pDataObject);
  272.     if ( !pDataObj )
  273.         ExitGracefully(hres, E_INVALIDARG, "Failed because we don't have a data object");
  274.     pDataObj->AddRef();
  275.     _pDataObject = pDataObj;
  276.     // Check that we have the clipboard format correctly registered so that we
  277.     // can collect a DSOBJECTNAMES structure
  278.     if ( !g_cfDsObjectNames )
  279.     {
  280.         g_cfDsObjectNames = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DSOBJECTNAMES);
  281.         g_cfDsDispSpecOptions = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DSDISPLAYSPECOPTIONS);
  282.         if ( !g_cfDsObjectNames || !g_cfDsDispSpecOptions )
  283.         {
  284.             ExitGracefully(hres, E_FAIL, "No clipboard form registered");
  285.         }
  286.     }
  287.     hres = S_OK;              // success
  288. exit_gracefully:
  289.     TraceLeaveResult(hres);
  290. }
  291. /*----------------------------------------------------------------------------
  292. / IWABExtInit
  293. /----------------------------------------------------------------------------*/
  294. #define WAB_PREFIX     L"ldap:///"
  295. #define CCH_WAB_PREFIX 8
  296. STDMETHODIMP CDsPropertyPages::Initialize(LPWABEXTDISPLAY pWED)
  297. {
  298.     HRESULT hres;
  299.     WCHAR szDecodedURL[INTERNET_MAX_URL_LENGTH];
  300.     LPWSTR pszDecodedURL = szDecodedURL;
  301.     INT cchDecodedURL;
  302.     DWORD dwLen = ARRAYSIZE(szDecodedURL);
  303.     IDataObject* pDataObject = NULL;
  304.     LPWSTR pszPath = NULL;
  305.     LPWSTR pURL = (LPWSTR)pWED->lpsz;
  306.     INT i;
  307.     USES_CONVERSION;
  308.     TraceEnter(TRACE_UI, "CDsPropertyPages::Initialize (IWABExtInit)");
  309.     if ( !(pWED->ulFlags & WAB_DISPLAY_ISNTDS) )
  310.         ExitGracefully(hres, E_FAIL, "The URL is not from NTDS, therefore ignoring");
  311.     if ( !pURL )
  312.         ExitGracefully(hres, E_FAIL, "URL pointer is NULL");
  313.     Trace(TEXT("LDAP URL is: %s"), W2T(pURL));
  314.     //
  315.     // we must now convert from a RFC LDAP URL to something that ADSI can handle, because
  316.     // although they both have the LDAP scheme they don't really mean the same thing.
  317.     //
  318.     // WAB will pass us an encoded URL, this we need to decode, strip the scheme name and
  319.     // then remove the tripple slash,
  320.     //
  321.     // eg: "LDAP:///dn%20dn" becomes, "LDAP://dn dn"
  322.     //
  323.     hres = UrlUnescapeW(pURL, szDecodedURL, &dwLen, 0);
  324.     FailGracefully(hres, "Failed to convert URL to decoded format");
  325.     Trace(TEXT("Decoded URL is: %s"), W2T(szDecodedURL));
  326.     pszDecodedURL += CCH_WAB_PREFIX;         // skip the LDAP:///
  327.     //
  328.     // now tail the URL removing all trailing slashes from it
  329.     //
  330.     for ( cchDecodedURL = lstrlenW(pszDecodedURL); 
  331.                 (cchDecodedURL > 0) && (pszDecodedURL[cchDecodedURL] == L'/'); 
  332.                     cchDecodedURL-- )
  333.     {
  334.         pszDecodedURL[cchDecodedURL] = L'';
  335.     }
  336.     
  337.     if ( !cchDecodedURL )
  338.         ExitGracefully(hres, E_UNEXPECTED, "URL is now NULL");
  339.     //
  340.     // so we have a DN, so lets allocate a IDataObject using it so that we
  341.     // can pass this into the real initialize method for shell extensions.
  342.     //
  343.     Trace(TEXT("DN from the LDAP URL we were given: %s"), W2T(pszDecodedURL));
  344.     pDataObject = new CWABDataObject(pszDecodedURL);
  345.     TraceAssert(pDataObject);
  346.     if ( !pDataObject )
  347.         ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate the data object");
  348.     hres = Initialize(NULL, pDataObject, NULL);
  349.     FailGracefully(hres, "Failed to initialize with the IDataObject");
  350.     // hres = S_OK;           // success
  351. exit_gracefully:
  352.     DoRelease(pDataObject);
  353.     TraceLeaveResult(hres);
  354. }
  355. /*----------------------------------------------------------------------------
  356. / IShellPropSheetExt
  357. /----------------------------------------------------------------------------*/
  358. HRESULT TabCollector_Collect(IUnknown *punkSite, IDataObject* pDataObject, LPFNADDPROPSHEETPAGE pAddPageProc, LPARAM lParam);
  359. STDMETHODIMP CDsPropertyPages::AddPages(LPFNADDPROPSHEETPAGE pAddPageProc, LPARAM lParam)
  360. {
  361.     HRESULT hres;
  362.     
  363.     TraceEnter(TRACE_UI, "CDsPropertyPages::AddPages");
  364.     hres = TabCollector_Collect(_punkSite, _pDataObject, pAddPageProc, lParam);
  365.     FailGracefully(hres, "Failed when calling the collector");
  366.     //hres = S_OK;              // success
  367. exit_gracefully:
  368.     TraceLeaveResult(hres);
  369. }
  370. /*---------------------------------------------------------------------------*/
  371. STDMETHODIMP CDsPropertyPages::ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE lpfnReplaceWith, LPARAM lParam)
  372. {
  373.     TraceEnter(TRACE_UI, "CDsPropertyPages::ReplacePage");
  374.     TraceLeaveResult(E_NOTIMPL);
  375. }
  376. /*----------------------------------------------------------------------------
  377. / IContextMenu
  378. /----------------------------------------------------------------------------*/
  379. STDMETHODIMP CDsPropertyPages::QueryContextMenu(HMENU hMenu, UINT index, UINT uIDFirst, UINT uIDLast, UINT uFlags)
  380. {
  381.     HRESULT hres;
  382.     STGMEDIUM medium = { TYMED_NULL };
  383.     FORMATETC fmte = {g_cfDsObjectNames, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  384.     LPDSOBJECTNAMES pDsObjectNames;
  385.     LPWSTR pPath;
  386.     LPWSTR pObjectClass;
  387.     CLASSCACHEGETINFO ccgi = { 0 };
  388.     LPCLASSCACHEENTRY pCacheEntry = NULL;
  389.     INT i;
  390.     INT cAdded = 0;
  391.     USES_CONVERSION;
  392.     
  393.     TraceEnter(TRACE_UI, "CDsPropertyPages::QueryContextMenu");
  394.     if ( !hMenu || !_pDataObject )
  395.         ExitGracefully(hres, E_FAIL, "Either no IDataObject or no hMenu");
  396.     // Get the bits of information we need from the data object, we are not
  397.     // interested in a attributePrefix, therefore we skip that bit
  398.     // and then look up the menu list in the cache.
  399.     hres = _pDataObject->GetData(&fmte, &medium);
  400.     FailGracefully(hres, "Failed to GetData using CF_DSOBJECTNAMES");
  401.     pDsObjectNames = (LPDSOBJECTNAMES)medium.hGlobal;
  402.     if ( pDsObjectNames->cItems < 1 )
  403.         ExitGracefully(hres, E_FAIL, "Not enough objects in DSOBJECTNAMES structure");
  404.     pPath = (LPWSTR)ByteOffset(pDsObjectNames, pDsObjectNames->aObjects[0].offsetName);
  405.     pObjectClass = (LPWSTR)ByteOffset(pDsObjectNames, pDsObjectNames->aObjects[0].offsetClass);
  406.     // fill the CLASSCACHEGETINFO record so we can cache the information from the
  407.     // display specifiers.
  408.     ccgi.dwFlags = CLASSCACHE_CONTEXTMENUS;
  409.     ccgi.pPath = pPath;
  410.     ccgi.pObjectClass = pObjectClass;
  411.     ccgi.pDataObject = _pDataObject;
  412.     hres = GetServerAndCredentails(&ccgi);
  413.     FailGracefully(hres, "Failed to get the server name");
  414.     hres = GetAttributePrefix(&ccgi.pAttributePrefix, _pDataObject);
  415.     FailGracefully(hres, "Failed to get attributePrefix");
  416.     Trace(TEXT("Class: %s; Attribute Prefix: %s; Server: %s"), 
  417.                 W2T(pObjectClass), W2T(ccgi.pAttributePrefix), ccgi.pServer ? W2T(ccgi.pServer):TEXT("<none>"));
  418.     hres = ClassCache_GetClassInfo(&ccgi, &pCacheEntry);
  419.     FailGracefully(hres, "Failed to get page list (via the cache)");
  420.     // did we get a menu list?  If so lets pull it a part and generate a DSA
  421.     // which lists the menu items we are going to be displaying.   
  422.     if ( (pCacheEntry->dwCached & CLASSCACHE_CONTEXTMENUS) && pCacheEntry->hdsaMenuHandlers )
  423.     {
  424.         if ( _hdsaMenuItems )
  425.             DSA_DestroyCallback(_hdsaMenuItems, _FreeMenuItemCB, NULL);
  426.         _hdsaMenuItems = DSA_Create(SIZEOF(DSMENUITEM), 4);
  427.         if ( !_hdsaMenuItems )
  428.             ExitGracefully(hres, E_OUTOFMEMORY, "Failed to construct DSA for menu items");
  429.         for ( i = DSA_GetItemCount(pCacheEntry->hdsaMenuHandlers) ; --i >= 0 ; )
  430.         {
  431.             LPDSMENUHANDLER pHandlerItem = (LPDSMENUHANDLER)DSA_GetItemPtr(pCacheEntry->hdsaMenuHandlers, i);
  432.             TraceAssert(pHandlerItem);
  433.             cAdded += AddMenuItem(hMenu, pHandlerItem->pMenuReference,
  434.                                         index, uIDFirst+cAdded, uIDLast, uFlags);
  435.         }
  436.     }
  437.     hres = S_OK;              // success
  438. exit_gracefully:
  439.     LocalFreeStringW(&ccgi.pAttributePrefix);
  440.     LocalFreeStringW(&ccgi.pUserName);
  441.     LocalFreeStringW(&ccgi.pPassword);
  442.     LocalFreeStringW(&ccgi.pServer);
  443.     ClassCache_ReleaseClassInfo(&pCacheEntry);
  444.     ReleaseStgMedium(&medium);
  445.     TraceLeaveResult(ResultFromShort(cAdded));
  446. }
  447. /*---------------------------------------------------------------------------*/
  448. STDMETHODIMP CDsPropertyPages::InvokeCommand(LPCMINVOKECOMMANDINFO pCMI)
  449. {
  450.     HRESULT hres;
  451.     STGMEDIUM medium = { TYMED_NULL };
  452.     FORMATETC fmte = {g_cfDsObjectNames, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  453.     LPTSTR pArguments = NULL;
  454.     LPDSOBJECTNAMES pDsObjectNames;
  455.     LPTSTR pPath;
  456.     LPWSTR pObjectClass;
  457.     DWORD object;
  458.     INT i, id; 
  459.     USES_CONVERSION;
  460.     
  461.     TraceEnter(TRACE_UI, "CDsPropertyPages::InvokeCommand");
  462.     // Walk the DSA until we find an item in it that contains the range of
  463.     // items we are looking for, this will either involve invoking the
  464.     // command (via IContextMenu::InvokeCommand) or calling ShellExecute
  465.     // for the objects in the selection.
  466.     if ( HIWORD(pCMI->lpVerb) )
  467.         ExitGracefully(hres, E_FAIL, "Bad lpVerb value for this handler");
  468.     if ( !_hdsaMenuItems )
  469.         ExitGracefully(hres, E_INVALIDARG, "No menu item DSA");
  470.     for ( id = LOWORD(pCMI->lpVerb), i = 0 ; i < DSA_GetItemCount(_hdsaMenuItems) ; i++ )
  471.     {
  472.         LPDSMENUITEM pItem = (LPDSMENUITEM)DSA_GetItemPtr(_hdsaMenuItems, i);
  473.         TraceAssert(pItem);
  474.         Trace(TEXT("id %08x, cAdded %d"), id, pItem->cAdded);
  475.         
  476.         if ( id < pItem->cAdded )
  477.         {
  478.             if ( pItem->pContextMenu )
  479.             {
  480.                 CMINVOKECOMMANDINFO cmi = *pCMI;
  481.                 cmi.lpVerb = (LPCSTR)id;
  482.                 Trace(TEXT("Calling IContextMenu iface with ID %d"), id);
  483.                 hres = pItem->pContextMenu->InvokeCommand(&cmi);
  484.                 FailGracefully(hres, "Failed when calling context menu handler (InvokeCommand)");
  485.             }
  486.             else
  487.             {
  488.                 // the command is not serviced via an IContextMenu handler, therefore lets for
  489.                 // each object in the IDataObject call the command passing the arguments of
  490.                 // the ADsPath and the class.
  491.                 hres = _pDataObject->GetData(&fmte, &medium);
  492.                 FailGracefully(hres, "Failed to GetData using CF_DSOBJECTNAMES");
  493.                 pDsObjectNames = (LPDSOBJECTNAMES)medium.hGlobal;
  494.                 if ( pDsObjectNames->cItems < 1 )
  495.                     ExitGracefully(hres, E_FAIL, "Not enough objects in DSOBJECTNAMES structure");
  496.                 Trace(TEXT("Calling ShellExecute for ID %d (%s)"), id, pItem->pCommand);
  497.                 for ( object = 0 ; object < pDsObjectNames->cItems ; object++ )
  498.                 {
  499.                     pPath = W2T((LPWSTR)ByteOffset(pDsObjectNames, pDsObjectNames->aObjects[object].offsetName));
  500.                     pObjectClass = (LPWSTR)ByteOffset(pDsObjectNames, pDsObjectNames->aObjects[object].offsetClass);
  501.                     hres = LocalAllocStringLen(&pArguments, lstrlen(pPath)+lstrlenW(pObjectClass)+5);    // nb: +5 for space and quotes
  502.                     FailGracefully(hres, "Failed to allocate buffer for arguments");
  503.                     //
  504.                     // does the object path have a space?  if so then lets wrap it in quotes
  505.                     //
  506.                     if ( StrChr(pPath, TEXT(' ')) )
  507.                     {
  508.                         StrCpy(pArguments, TEXT("""));
  509.                         StrCat(pArguments, pPath);
  510.                         StrCat(pArguments, TEXT("""));
  511.                     }
  512.                     else
  513.                     {
  514.                         StrCpy(pArguments, pPath);
  515.                     }
  516.                     StrCat(pArguments, TEXT(" "));
  517.                     StrCat(pArguments, W2T(pObjectClass));
  518.                     Trace(TEXT("Executing: %s"), pItem->pCommand);
  519.                     Trace(TEXT("Arguments: %s"), pArguments);
  520.                     ShellExecute(NULL, NULL, pItem->pCommand, pArguments, NULL, SW_SHOWNORMAL);
  521.                     LocalFreeString(&pArguments);                    
  522.                 }
  523.             }
  524.             break;
  525.         }
  526.         id -= pItem->cAdded;
  527.     }
  528.     hres = (i < DSA_GetItemCount(_hdsaMenuItems)) ? S_OK:E_FAIL;
  529. exit_gracefully:
  530.     LocalFreeString(&pArguments);
  531.     ReleaseStgMedium(&medium);
  532.     TraceLeaveResult(hres);
  533. }
  534. /*---------------------------------------------------------------------------*/
  535. STDMETHODIMP CDsPropertyPages::GetCommandString(UINT_PTR uID, UINT uFlags, UINT FAR* reserved, LPSTR pName, UINT ccNameMax)
  536. {
  537.     HRESULT hres;
  538.     INT i;
  539.     INT id = (INT)uID;
  540.     TraceEnter(TRACE_UI, "CDsPropertyPages::GetCommandString");
  541.     // Walk down the list of the menu items looking for one that matches the
  542.     // item we are trying get the command string from.  If it is an IContextMenu
  543.     // handler then we must call down to that.
  544.     if ( !_hdsaMenuItems )
  545.         ExitGracefully(hres, E_INVALIDARG, "No menu item DSA");
  546.     for ( i = 0 ; i < DSA_GetItemCount(_hdsaMenuItems) ; i++ )
  547.     {
  548.         LPDSMENUITEM pItem = (LPDSMENUITEM)DSA_GetItemPtr(_hdsaMenuItems, i);
  549.         TraceAssert(pItem);
  550.         Trace(TEXT("id %08x, cAdded %d"), id, pItem->cAdded);
  551.         
  552.         if ( id < pItem->cAdded )
  553.         {
  554.             if ( pItem->pContextMenu )
  555.             {
  556.                 hres = pItem->pContextMenu->GetCommandString(id, uFlags, reserved, pName, ccNameMax);
  557.                 FailGracefully(hres, "Failed when calling context menu handler (GetCommandString)");
  558.             }
  559.             else
  560.             {
  561.                 if ( uFlags != GCS_HELPTEXT )
  562.                     ExitGracefully(hres, E_FAIL, "We only respond to GCS_HELPTEXT");
  563.                 Trace(TEXT("GCS_HELPTEXT returns for non-IContextMenu item: %s"), pItem->pCaption);
  564.                 lstrcpyn((LPTSTR)pName, pItem->pCaption, ccNameMax);               
  565.             }
  566.             break;
  567.         }
  568.         id -= pItem->cAdded;
  569.     }        
  570.     hres = (i < DSA_GetItemCount(_hdsaMenuItems)) ? S_OK:E_FAIL;
  571. exit_gracefully:
  572.     TraceLeaveResult(hres);
  573. }
  574. /*----------------------------------------------------------------------------
  575. / IObjectWithSite
  576. /----------------------------------------------------------------------------*/
  577. STDMETHODIMP CDsPropertyPages::SetSite(IUnknown* punk)
  578. {
  579.     HRESULT hres = S_OK;
  580.     TraceEnter(TRACE_UI, "CDsPropertyPages::SetSite");
  581.     DoRelease(_punkSite);
  582.     if ( punk )
  583.     {
  584.         TraceMsg("QIing for IUnknown from the site object");
  585.         hres = punk->QueryInterface(IID_IUnknown, (void **)&_punkSite);
  586.         FailGracefully(hres, "Failed to get IUnknown from the site object");
  587.     }
  588. exit_gracefully:
  589.     TraceLeaveResult(hres);
  590. }
  591. /*---------------------------------------------------------------------------*/
  592. STDMETHODIMP CDsPropertyPages::GetSite(REFIID riid, void **ppv)
  593. {
  594.     HRESULT hres;
  595.     
  596.     TraceEnter(TRACE_UI, "CDsPropertyPages::GetSite");
  597.     if ( !_punkSite )
  598.         ExitGracefully(hres, E_NOINTERFACE, "No site to QI from");
  599.     hres = _punkSite->QueryInterface(riid, ppv);
  600.     FailGracefully(hres, "QI failed on the site unknown object");
  601. exit_gracefully:
  602.     TraceLeaveResult(hres);
  603. }
  604. /*-----------------------------------------------------------------------------
  605. / CWABDataObject
  606. /----------------------------------------------------------------------------*/
  607. CWABDataObject::CWABDataObject(LPWSTR pDN)
  608. {
  609.     USES_CONVERSION;
  610.     TraceEnter(TRACE_WAB, "CWABDataObject::CWABDataObject");
  611.     
  612.     TraceAssert(_pPath==NULL);
  613.     TraceAssert(_pDsObject==NULL);
  614.     if ( SUCCEEDED(LocalAllocStringLenW(&_pPath, lstrlenW(pDN)+7)) )
  615.     {
  616.         StrCpyW(_pPath, L"LDAP://");
  617.         StrCatW(_pPath, pDN);
  618.         Trace(TEXT("DN converted to an ADSI path: %s"), W2T(_pPath));
  619.     }
  620.     TraceLeave();
  621. }
  622. CWABDataObject::~CWABDataObject()
  623. {
  624.     TraceEnter(TRACE_WAB, "CWABDataObject::~CWABDataObject");
  625.     LocalFreeStringW(&_pPath);
  626.     DoRelease(_pDsObject);
  627.     TraceLeave();
  628. }
  629. // IUnknown
  630. #undef CLASS_NAME
  631. #define CLASS_NAME CWABDataObject
  632. #include "unknown.inc"
  633. STDMETHODIMP CWABDataObject::QueryInterface(REFIID riid, LPVOID* ppvObject)
  634. {
  635.     INTERFACES iface[]=
  636.     {
  637.         &IID_IDataObject, (LPDATAOBJECT)this,
  638.     };
  639.     return HandleQueryInterface(riid, ppvObject, iface, ARRAYSIZE(iface));
  640. }
  641. /*-----------------------------------------------------------------------------
  642. / IDataObject methods
  643. /----------------------------------------------------------------------------*/
  644. STDMETHODIMP CWABDataObject::GetData(FORMATETC* pFmt, STGMEDIUM* pMedium)
  645. {
  646.     HRESULT hres;
  647.     BSTR bstrObjectClass = NULL;
  648.     DWORD cbStruct = SIZEOF(DSOBJECTNAMES);
  649.     DWORD offset = SIZEOF(DSOBJECTNAMES);
  650.     LPDSOBJECTNAMES pDsObjectNames = NULL;
  651.     CLASSCACHEGETINFO ccgi = { 0 };
  652.     CLASSCACHEENTRY *pcce = NULL;
  653.     USES_CONVERSION;
  654.     TraceEnter(TRACE_WAB, "CWABDataObject::GetData");
  655.     if ( !g_cfDsObjectNames )
  656.         ExitGracefully(hres, E_FAIL, "g_cfDsObjectNames == NULL, therefore GetData cannot work");
  657.     if ( !_pPath )
  658.         ExitGracefully(hres, E_FAIL, "No _pPath set in data object");
  659.     if ( pFmt->cfFormat == g_cfDsObjectNames )
  660.     {
  661.         // do we have the ADsObject that represents this path yet?  If not then
  662.         // lets grab it, but only do that once otherwise we will continually hit
  663.         // the wire.
  664.         if ( !_pDsObject )
  665.         {
  666.             Trace(TEXT("Caching IADs for %s"), W2T(_pPath));
  667.             hres = ADsOpenObject(_pPath, NULL, NULL, ADS_SECURE_AUTHENTICATION, IID_IADs, (LPVOID*)&_pDsObject);
  668.             FailGracefully(hres, "Failed to get IADs for ADsPath we have");
  669.         }
  670.         // lets allocate a storage medium, put in the only object we have
  671.         // and then return that to the caller.
  672.         hres = _pDsObject->get_Class(&bstrObjectClass);
  673.         FailGracefully(hres, "Failed to get the class of the object");
  674.         // we have the information we need so lets allocate the storage medium and 
  675.         // return the DSOBJECTNAMES structure to the caller.
  676.         cbStruct += StringByteSizeW(_pPath);
  677.         cbStruct += StringByteSizeW(bstrObjectClass);
  678.         hres = AllocStorageMedium(pFmt, pMedium, cbStruct, (LPVOID*)&pDsObjectNames);
  679.         FailGracefully(hres, "Failed to allocate storage medium");
  680.         pDsObjectNames->clsidNamespace = CLSID_MicrosoftDS;
  681.         pDsObjectNames->cItems = 1;
  682.         pDsObjectNames->aObjects[0].dwFlags = 0;
  683.         // check to see if the object is a container, if it is then set the attributes
  684.         // accordingly.
  685.         ccgi.dwFlags = CLASSCACHE_CONTAINER|CLASSCACHE_TREATASLEAF;
  686.         ccgi.pPath = _pPath;
  687.         ccgi.pObjectClass = bstrObjectClass;
  688.         hres = ClassCache_GetClassInfo(&ccgi, &pcce);
  689.         if ( SUCCEEDED(hres) )
  690.         {
  691.             if ( _IsClassContainer(pcce, FALSE) ) 
  692.             {
  693.                 TraceMsg("Flagging the object as a container");
  694.                 pDsObjectNames->aObjects[0].dwFlags |= DSOBJECT_ISCONTAINER;
  695.             }
  696.             
  697.             ClassCache_ReleaseClassInfo(&pcce);
  698.         }
  699.         pDsObjectNames->aObjects[0].dwProviderFlags = 0;
  700.         pDsObjectNames->aObjects[0].offsetName = offset;
  701.         StringByteCopyW(pDsObjectNames, offset, _pPath);
  702.         offset += StringByteSizeW(_pPath);
  703.         pDsObjectNames->aObjects[0].offsetClass = offset;
  704.         StringByteCopyW(pDsObjectNames, offset, bstrObjectClass);
  705.         offset += StringByteSizeW(bstrObjectClass);
  706.     }
  707.     else if ( pFmt->cfFormat == g_cfDsDispSpecOptions )
  708.     {
  709.         PDSDISPLAYSPECOPTIONS pOptions;
  710.         DWORD cbSize = SIZEOF(DSDISPLAYSPECOPTIONS)+StringByteSizeW(DS_PROP_SHELL_PREFIX);
  711.         // return the display spec options so we can indicate that WAB is involved
  712.         // in the menus.
  713.         hres = AllocStorageMedium(pFmt, pMedium, cbSize, (LPVOID*)&pOptions);
  714.         FailGracefully(hres, "Failed to allocate the storage medium");
  715.         pOptions->dwSize = cbSize;
  716.         pOptions->dwFlags = DSDSOF_INVOKEDFROMWAB;                      // invoked from WAB however
  717.         pOptions->offsetAttribPrefix = SIZEOF(DSDISPLAYSPECOPTIONS);
  718.         StringByteCopyW(pOptions, pOptions->offsetAttribPrefix, DS_PROP_SHELL_PREFIX);
  719.     }
  720.     else 
  721.     {
  722.         ExitGracefully(hres, DV_E_FORMATETC, "Bad format passed to GetData");
  723.     }
  724.     hres = S_OK;              // success
  725. exit_gracefully:
  726.     if ( FAILED(hres) )
  727.         ReleaseStgMedium(pMedium);
  728.     SysFreeString(bstrObjectClass);
  729.     TraceLeaveResult(hres);
  730. }
  731. /*---------------------------------------------------------------------------*/
  732. STDMETHODIMP CWABDataObject::GetDataHere(FORMATETC* pFmt, STGMEDIUM* pMedium)
  733. {
  734.     TraceEnter(TRACE_WAB, "CWABDataObject::GetDataHere");
  735.     TraceLeaveResult(E_NOTIMPL);
  736. }
  737. /*---------------------------------------------------------------------------*/
  738. STDMETHODIMP CWABDataObject::QueryGetData(FORMATETC* pFmt)
  739. {
  740.     TraceEnter(TRACE_WAB, "CWABDataObject::QueryGetData");
  741.     TraceLeaveResult(E_NOTIMPL);
  742. }
  743. /*---------------------------------------------------------------------------*/
  744. STDMETHODIMP CWABDataObject::GetCanonicalFormatEtc(FORMATETC* pFmtIn, FORMATETC *pFmtOut)
  745. {
  746.     TraceEnter(TRACE_WAB, "CWABDataObject::GetCanonicalFormatEtc");
  747.     TraceLeaveResult(E_NOTIMPL);
  748. }
  749. /*---------------------------------------------------------------------------*/
  750. STDMETHODIMP CWABDataObject::SetData(FORMATETC* pFormatEtc, STGMEDIUM* pMedium, BOOL fRelease)
  751. {
  752.     TraceEnter(TRACE_WAB, "CWABDataObject::SetData");
  753.     TraceLeaveResult(E_NOTIMPL);
  754. }
  755. /*---------------------------------------------------------------------------*/
  756. STDMETHODIMP CWABDataObject::EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC** ppEnumFormatEtc)
  757. {
  758.     TraceEnter(TRACE_WAB, "CWABDataObject::EnumFormatEtc");
  759.     TraceLeaveResult(E_NOTIMPL);
  760. }
  761. /*---------------------------------------------------------------------------*/
  762. STDMETHODIMP CWABDataObject::DAdvise(FORMATETC* pFormatEtc, DWORD advf, IAdviseSink* pAdvSink, DWORD* pdwConnection)
  763. {
  764.     TraceEnter(TRACE_WAB, "CWABDataObject::DAdvise");
  765.     TraceLeaveResult(E_NOTIMPL);
  766. }
  767. /*---------------------------------------------------------------------------*/
  768. STDMETHODIMP CWABDataObject::DUnadvise(DWORD dwConnection)
  769. {
  770.     TraceEnter(TRACE_WAB, "CWABDataObject::DUnadvise");
  771.     TraceLeaveResult(E_NOTIMPL);
  772. }
  773. /*---------------------------------------------------------------------------*/
  774. STDMETHODIMP CWABDataObject::EnumDAdvise(IEnumSTATDATA** ppenumAdvise)
  775. {
  776.     TraceEnter(TRACE_WAB, "CWABDataObject::EnumDAdvise");
  777.     TraceLeaveResult(E_NOTIMPL);
  778. }