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

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