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.

1288 lines
43 KiB

  1. #include "pch.h"
  2. #include <atlbase.h>
  3. #pragma hdrstop
  4. /*----------------------------------------------------------------------------
  5. / Static data for mapping verbs to intersting information
  6. /----------------------------------------------------------------------------*/
  7. //
  8. // Menu item stored in the DSA to map from external IDs to internal
  9. //
  10. typedef struct
  11. {
  12. INT iMenuItem; // index into menu_items array
  13. } MENUITEM, * LPMENUITEM;
  14. //
  15. // This table maps classes to verbs that should be added to the menu
  16. // we then add menu item data structures as required.
  17. //
  18. #define MENUCMD_INITITEM 0x0001 // called per menu item
  19. #define MENUCMD_INVOKE 0x0002 // called to invoke the command
  20. //
  21. // Handlers
  22. //
  23. typedef struct
  24. {
  25. DWORD dwFlags;
  26. HDPA hdpaSelection;
  27. LPWSTR pszUserName;
  28. LPWSTR pszPassword;
  29. } VERBINFO, * LPVERBINFO;
  30. typedef HRESULT (*LPMENUITEMCB)(UINT uCmd, HWND hWnd, HMENU hMenu, LPARAM uID, LPVERBINFO pvi, UINT uFlags);
  31. HRESULT _UserVerbCB(UINT uCmd, HWND hWnd, HMENU hMenu, LPARAM uID, LPVERBINFO pvi, UINT uFlags);
  32. HRESULT _VolumeVerbCB(UINT uCmd, HWND hWnd, HMENU hMenu, LPARAM uID, LPVERBINFO pvi, UINT uFlags);
  33. HRESULT _ComputerVerbCB(UINT uCmd, HWND hWnd, HMENU hMenu, LPARAM uID, LPVERBINFO pvi, UINT uFlags);
  34. HRESULT _PrinterVerbCB(UINT uCmd, HWND hWnd, HMENU hMenu, LPARAM uID, LPVERBINFO pvi, UINT uFlags);
  35. struct
  36. {
  37. BOOL fNotValidInWAB:1; // =1 => verb is NOT valid when invoked from WAB
  38. LPWSTR pObjectClass; // class name
  39. UINT uID; // name to add for verb
  40. UINT idsHelp; // help text for this verb
  41. LPMENUITEMCB pItemCB; // menu item callback
  42. }
  43. menu_items[] =
  44. {
  45. 0, L"user", IDC_USER_OPENHOMEPAGE, IDS_USER_OPENHOMEPAGE, _UserVerbCB,
  46. 1, L"user", IDC_USER_MAILTO, IDS_USER_MAILTO, _UserVerbCB,
  47. 0, L"inetOrgPerson",IDC_USER_OPENHOMEPAGE, IDS_USER_OPENHOMEPAGE, _UserVerbCB,
  48. 1, L"inetOrgPerson",IDC_USER_MAILTO, IDS_USER_MAILTO, _UserVerbCB,
  49. 0, L"contact", IDC_USER_OPENHOMEPAGE, IDS_USER_OPENHOMEPAGE, _UserVerbCB,
  50. 1, L"contact", IDC_USER_MAILTO, IDS_USER_MAILTO, _UserVerbCB,
  51. 1, L"group", IDC_USER_MAILTO, IDS_USER_MAILTO, _UserVerbCB,
  52. 0, L"volume", IDC_VOLUME_OPEN, IDS_VOLUME_OPEN, _VolumeVerbCB,
  53. 0, L"volume", IDC_VOLUME_EXPLORE, IDS_VOLUME_EXPLORE, _VolumeVerbCB,
  54. 0, L"volume", IDC_VOLUME_FIND, IDS_VOLUME_FIND, _VolumeVerbCB,
  55. 0, L"volume", IDC_VOLUME_MAPNETDRIVE, IDS_VOLUME_MAPNETDRIVE, _VolumeVerbCB,
  56. 0, L"computer", IDC_COMPUTER_MANAGE, IDS_COMPUTER_MANAGE, _ComputerVerbCB,
  57. 0, L"printQueue", IDC_PRINTER_INSTALL, IDS_PRINTER_INSTALL, _PrinterVerbCB,
  58. 0, L"printQueue", IDC_PRINTER_OPEN, IDS_PRINTER_OPEN, _PrinterVerbCB,
  59. };
  60. //
  61. // Our class for implementing the standard verbs
  62. //
  63. class CDsVerbs : public IShellExtInit, IContextMenu
  64. {
  65. private:
  66. LONG _cRef;
  67. IDataObject* _pDataObject;
  68. HDSA _hdsaItems; // entry per verb on menu
  69. VERBINFO _vi;
  70. //
  71. // This public data is used by the verb handlers, they are passed a CDsVerbs*
  72. // as one of their parameters, so using this we then allow them to store what
  73. // they need in here.
  74. //
  75. public:
  76. CDsVerbs();
  77. ~CDsVerbs();
  78. // IUnknown members
  79. STDMETHODIMP_(ULONG) AddRef();
  80. STDMETHODIMP_(ULONG) Release();
  81. STDMETHODIMP QueryInterface(REFIID, LPVOID FAR*);
  82. // IShellExtInit
  83. STDMETHODIMP Initialize(LPCITEMIDLIST pIDFolder, LPDATAOBJECT pDataObj, HKEY hKeyID);
  84. // IContextMenu
  85. STDMETHODIMP QueryContextMenu(HMENU hMenu, UINT uIndex, UINT uIDFirst, UINT uIDLast, UINT uFlags);
  86. STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO pCMI);
  87. STDMETHODIMP GetCommandString(UINT_PTR uID, UINT uFlags, UINT FAR* reserved, LPSTR pName, UINT ccMax);
  88. private:
  89. VOID FreeMenuStateData(VOID);
  90. };
  91. static HRESULT _OpenObject(LPCWSTR pszPath, REFIID riid, void **ppv, LPVERBINFO pvi)
  92. {
  93. return OpenDsObject(pszPath, pvi->pszUserName, pvi->pszPassword, riid, ppv,
  94. (pvi->dwFlags & DSDSOF_SIMPLEAUTHENTICATE),
  95. (pvi->dwFlags & DSDSOF_DONTSIGNSEAL));
  96. }
  97. /*----------------------------------------------------------------------------
  98. / CDsVerbs implementation
  99. /----------------------------------------------------------------------------*/
  100. /*----------------------------------------------------------------------------
  101. / IUnknown
  102. /----------------------------------------------------------------------------*/
  103. CDsVerbs::CDsVerbs() :
  104. _cRef(1),
  105. _pDataObject(NULL),
  106. _hdsaItems(NULL)
  107. {
  108. _vi.dwFlags = 0;
  109. _vi.hdpaSelection = NULL;
  110. _vi.pszUserName = NULL;
  111. _vi.pszPassword = NULL;
  112. DllAddRef();
  113. }
  114. CDsVerbs::~CDsVerbs()
  115. {
  116. DoRelease(_pDataObject);
  117. FreeMenuStateData();
  118. SecureLocalFreeStringW(&_vi.pszUserName);
  119. SecureLocalFreeStringW(&_vi.pszPassword);
  120. DllRelease();
  121. }
  122. // IUnknown bits
  123. ULONG CDsVerbs::AddRef()
  124. {
  125. return InterlockedIncrement(&_cRef);
  126. }
  127. ULONG CDsVerbs::Release()
  128. {
  129. Assert(0 != _cRef);
  130. ULONG cRef = InterlockedDecrement(&_cRef);
  131. if (0 == cRef)
  132. {
  133. delete this;
  134. }
  135. return cRef;
  136. }
  137. HRESULT CDsVerbs::QueryInterface(REFIID riid, void **ppv)
  138. {
  139. static const QITAB qit[] =
  140. {
  141. QITABENT(CDsVerbs, IShellExtInit), // IID_IShellExtInit
  142. QITABENT(CDsVerbs, IContextMenu), // IID_IContextMenu
  143. {0, 0 },
  144. };
  145. return QISearch(this, qit, riid, ppv);
  146. }
  147. // handle create instance
  148. STDAPI CDsVerbs_CreateInstance(IUnknown* punkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
  149. {
  150. CDsVerbs *pdv = new CDsVerbs();
  151. if (!pdv)
  152. return E_OUTOFMEMORY;
  153. HRESULT hres = pdv->QueryInterface(IID_IUnknown, (void **)ppunk);
  154. pdv->Release();
  155. return hres;
  156. }
  157. /*----------------------------------------------------------------------------
  158. / IShellExtInit
  159. /----------------------------------------------------------------------------*/
  160. STDMETHODIMP CDsVerbs::Initialize(LPCITEMIDLIST pIDFolder, LPDATAOBJECT pDataObject, HKEY hKeyID)
  161. {
  162. HRESULT hr;
  163. TraceEnter(TRACE_VERBS, "CDsVerbs::Initialize");
  164. // take a copy of the IDataObject if we are given one
  165. if (!pDataObject)
  166. ExitGracefully(hr, E_FAIL, "No IDataObject to interact with");
  167. DoRelease(_pDataObject);
  168. _pDataObject = pDataObject;
  169. _pDataObject->AddRef();
  170. hr = S_OK; // sucess
  171. exit_gracefully:
  172. TraceLeaveResult(hr);
  173. }
  174. /*----------------------------------------------------------------------------
  175. / IContextMenu
  176. /----------------------------------------------------------------------------*/
  177. STDMETHODIMP CDsVerbs::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
  178. {
  179. HRESULT hr;
  180. FORMATETC fmte = {(CLIPFORMAT)0, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  181. STGMEDIUM mediumDsObjects = { TYMED_NULL };
  182. STGMEDIUM mediumDispSpecOptions = { TYMED_NULL };
  183. LPDSOBJECTNAMES pDsObjectNames = NULL;
  184. LPDSDISPLAYSPECOPTIONS pDispSpecOptions = NULL;
  185. MENUITEM item;
  186. INT i, iVerb;
  187. TCHAR szBuffer[MAX_PATH];
  188. BOOL fInWAB = FALSE;
  189. TraceEnter(TRACE_VERBS, "CDsVerbs::QueryContextMenu");
  190. FreeMenuStateData();
  191. // Get the selection from the IDataObject we have been given. This structure
  192. // contains the object class, ADsPath and other information.
  193. if (!_pDataObject)
  194. ExitGracefully(hr, E_FAIL, "No IDataObject to use");
  195. fmte.cfFormat = g_cfDsObjectNames;
  196. hr = _pDataObject->GetData(&fmte, &mediumDsObjects);
  197. FailGracefully(hr, "Failed to get the DSOBJECTNAMES from IDataObject");
  198. pDsObjectNames = (LPDSOBJECTNAMES)GlobalLock(mediumDsObjects.hGlobal);
  199. TraceAssert(pDsObjectNames);
  200. fmte.cfFormat = g_cfDsDispSpecOptions;
  201. if (SUCCEEDED(_pDataObject->GetData(&fmte, &mediumDispSpecOptions)))
  202. {
  203. pDispSpecOptions = (LPDSDISPLAYSPECOPTIONS)GlobalLock(mediumDispSpecOptions.hGlobal);
  204. TraceAssert(pDispSpecOptions);
  205. TraceMsg("Retrieved the CF_DISPSPECOPTIONS from the IDataObject");
  206. fInWAB = (pDispSpecOptions->dwFlags & DSDSOF_INVOKEDFROMWAB) == DSDSOF_INVOKEDFROMWAB;
  207. Trace(TEXT("Invoked from WAB == %d"), fInWAB);
  208. // copy credential and other information for the verbs to invoke with
  209. _vi.dwFlags = pDispSpecOptions->dwFlags;
  210. if (_vi.dwFlags & DSDSOF_HASUSERANDSERVERINFO)
  211. {
  212. TraceMsg("Copying user and credential information from clipboard block");
  213. if (pDispSpecOptions->offsetUserName)
  214. {
  215. LPWSTR pszUserName = (LPWSTR)ByteOffset(pDispSpecOptions, pDispSpecOptions->offsetUserName);
  216. Trace(TEXT("pszUserName: %s"), pszUserName);
  217. hr = LocalAllocStringW(&_vi.pszUserName, pszUserName);
  218. FailGracefully(hr, "Failed to copy the user name");
  219. }
  220. if (pDispSpecOptions->offsetPassword)
  221. {
  222. LPWSTR pszPassword = (LPWSTR)ByteOffset(pDispSpecOptions, pDispSpecOptions->offsetPassword);
  223. Trace(TEXT("pszPassword: %s"), pszPassword);
  224. hr = LocalAllocStringW(&_vi.pszPassword, pszPassword);
  225. FailGracefully(hr, "Failed to copy the password");
  226. }
  227. }
  228. }
  229. // Take the first item of the selection, compare all the objects in the
  230. // rest of the DSOBJECTNAMES, all those who have the same class.
  231. _hdsaItems = DSA_Create(SIZEOF(MENUITEM), 4);
  232. TraceAssert(_hdsaItems);
  233. _vi.hdpaSelection = DPA_Create(4);
  234. TraceAssert(_vi.hdpaSelection);
  235. if (!_vi.hdpaSelection || !_hdsaItems)
  236. ExitGracefully(hr, E_OUTOFMEMORY, "Failed to allocate the selection DPA");
  237. for (i = 0 ; i < (INT)pDsObjectNames->cItems ; i++)
  238. {
  239. LPCWSTR pObjectClass0 = (LPCWSTR)ByteOffset(pDsObjectNames, pDsObjectNames->aObjects[0].offsetClass);
  240. LPWSTR pPath = (LPWSTR)ByteOffset(pDsObjectNames, pDsObjectNames->aObjects[i].offsetName);
  241. LPCWSTR pObjectClass = (LPCWSTR)ByteOffset(pDsObjectNames, pDsObjectNames->aObjects[i].offsetClass);
  242. Trace(TEXT("ADsPath of object %d is %s"), i, pPath);
  243. Trace(TEXT("objectClass of object %d is %s"), i, pObjectClass);
  244. if (!StrCmpW(pObjectClass0, pObjectClass))
  245. {
  246. Trace(TEXT("Adding item %d to the selection DPA"), i);
  247. hr = StringDPA_AppendStringW(_vi.hdpaSelection, pPath, NULL);
  248. FailGracefully(hr, "Failed to copy selection to selection DPA");
  249. }
  250. }
  251. // Walk the list of menu items, lets see which ones we need to add to the
  252. // menu.
  253. if (DPA_GetPtrCount(_vi.hdpaSelection))
  254. {
  255. LPCWSTR pObjectClass0 = (LPCWSTR)ByteOffset(pDsObjectNames, pDsObjectNames->aObjects[0].offsetClass);
  256. for (i = 0 ; i < ARRAYSIZE(menu_items); i++)
  257. {
  258. if (menu_items[i].fNotValidInWAB && fInWAB)
  259. {
  260. TraceMsg("Skipping verb not valid for WAB");
  261. continue;
  262. }
  263. if (!StrCmpW(pObjectClass0, menu_items[i].pObjectClass))
  264. {
  265. Trace(TEXT("Adding the verb at index %d to the menu"), i);
  266. // now fill in the MENUITEM structure and add it to the DSA list,
  267. // then add the menu item itself, calling the callback so it can
  268. // enable/disable itself.
  269. item.iMenuItem = i;
  270. iVerb = DSA_AppendItem(_hdsaItems, &item);
  271. TraceAssert(iVerb != -1);
  272. if (iVerb != -1)
  273. {
  274. Trace(TEXT("iVerb is %d"), iVerb);
  275. LoadString(GLOBAL_HINSTANCE, menu_items[i].uID, szBuffer, ARRAYSIZE(szBuffer));
  276. InsertMenu(hMenu, iVerb+indexMenu, MF_BYPOSITION|MF_STRING, iVerb+idCmdFirst, szBuffer);
  277. menu_items[i].pItemCB(MENUCMD_INITITEM,
  278. NULL,
  279. hMenu,
  280. MAKELPARAM(menu_items[i].uID, iVerb+idCmdFirst),
  281. &_vi,
  282. uFlags);
  283. }
  284. }
  285. }
  286. }
  287. hr = S_OK;
  288. exit_gracefully:
  289. if (SUCCEEDED(hr))
  290. {
  291. Trace(TEXT("%d items added by QueryContextMenu"), DSA_GetItemCount(_hdsaItems));
  292. hr = ResultFromShort(DSA_GetItemCount(_hdsaItems));
  293. }
  294. if (pDsObjectNames)
  295. GlobalUnlock(mediumDsObjects.hGlobal);
  296. if (pDispSpecOptions)
  297. GlobalUnlock(mediumDispSpecOptions.hGlobal);
  298. ReleaseStgMedium(&mediumDsObjects);
  299. ReleaseStgMedium(&mediumDispSpecOptions);
  300. TraceLeaveResult(hr);
  301. }
  302. /*---------------------------------------------------------------------------*/
  303. STDMETHODIMP CDsVerbs::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
  304. {
  305. HRESULT hr;
  306. UINT uID = LOWORD(lpcmi->lpVerb);
  307. LPMENUITEM pMenuItem;
  308. TraceEnter(TRACE_VERBS, "CDsVerbs::InvokeCommand");
  309. // Dreference the menu item to get the index's into both the item list and the
  310. // menu table. With both of these we can then invoke the command.
  311. Trace(TEXT("uID %d (DSA contains %d)"), uID, DSA_GetItemCount(_hdsaItems));
  312. if (!_hdsaItems)
  313. ExitGracefully(hr, E_UNEXPECTED, "No _hdasItems");
  314. pMenuItem = (LPMENUITEM)DSA_GetItemPtr(_hdsaItems, (UINT)uID);
  315. TraceAssert(pMenuItem);
  316. if (!pMenuItem || !menu_items[pMenuItem->iMenuItem].pItemCB)
  317. ExitGracefully(hr, E_UNEXPECTED, "Failed because pItem == NULL");
  318. hr = menu_items[pMenuItem->iMenuItem].pItemCB(MENUCMD_INVOKE,
  319. lpcmi->hwnd,
  320. NULL,
  321. MAKELPARAM(menu_items[pMenuItem->iMenuItem].uID, 0),
  322. &_vi,
  323. 0);
  324. exit_gracefully:
  325. TraceLeaveResult(S_OK);
  326. }
  327. /*---------------------------------------------------------------------------*/
  328. STDMETHODIMP CDsVerbs::GetCommandString(UINT_PTR uID, UINT uFlags, UINT FAR* reserved, LPSTR pszName, UINT ccMax)
  329. {
  330. HRESULT hr = E_NOTIMPL;
  331. INT cc;
  332. TraceEnter(TRACE_VERBS, "CDsVerbs::GetCommandString");
  333. if (_hdsaItems)
  334. {
  335. LPMENUITEM pMenuItem = (LPMENUITEM)DSA_GetItemPtr(_hdsaItems, (INT)uID);
  336. TraceAssert(pMenuItem);
  337. if (!pMenuItem)
  338. ExitGracefully(hr, E_FAIL, "Failed to get menu item");
  339. if (uFlags == GCS_HELPTEXT)
  340. {
  341. // Get the menu item and look up the resource for this verb
  342. // and return it into the callers buffer.
  343. if (!LoadString(GLOBAL_HINSTANCE, menu_items[pMenuItem->iMenuItem].idsHelp, (LPTSTR)pszName, ccMax))
  344. ExitGracefully(hr, E_FAIL, "Failed to load string for help text");
  345. }
  346. else
  347. {
  348. ExitGracefully(hr, E_FAIL, "Failed to get command string");
  349. }
  350. }
  351. hr = S_OK;
  352. exit_gracefully:
  353. TraceLeaveResult(hr);
  354. }
  355. /*-----------------------------------------------------------------------------
  356. / CDsVerbs::FreeMenuStateData
  357. / ---------------------------
  358. / Release the verb state data for the CDsVerbs class, this can be called
  359. / (and is) during the destructor and during the context menu construction
  360. / to ensure a consistent state.
  361. /
  362. / In:
  363. / Out:
  364. / HRESULT
  365. /----------------------------------------------------------------------------*/
  366. VOID CDsVerbs::FreeMenuStateData(VOID)
  367. {
  368. TraceEnter(TRACE_VERBS, "CDsVerbs::FreeMenuStateData");
  369. if (_hdsaItems)
  370. {
  371. DSA_Destroy(_hdsaItems);
  372. _hdsaItems = NULL;
  373. }
  374. StringDPA_Destroy(&_vi.hdpaSelection);
  375. LocalFreeStringW(&_vi.pszUserName);
  376. LocalFreeStringW(&_vi.pszPassword);
  377. TraceLeave();
  378. }
  379. /*----------------------------------------------------------------------------
  380. / User object verbs
  381. /----------------------------------------------------------------------------*/
  382. HRESULT _UserVerbCB(UINT uCmd, HWND hWnd, HMENU hMenu, LPARAM uID, LPVERBINFO pvi, UINT uFlags)
  383. {
  384. HRESULT hr;
  385. HDPA hdpaMailTo = NULL;
  386. LPTSTR pURL = NULL;
  387. IADs* pDsObject = NULL;
  388. VARIANT variant;
  389. SHELLEXECUTEINFO sei = { 0 };
  390. INT i;
  391. DECLAREWAITCURSOR = GetCursor();
  392. TraceEnter(TRACE_VERBS, "_UserVerbCB");
  393. VariantInit(&variant);
  394. switch (uCmd)
  395. {
  396. case MENUCMD_INITITEM:
  397. {
  398. // if this is a map network drive/find volume verb then lets ensure we only handle
  399. // a single selection.
  400. switch (LOWORD(uID))
  401. {
  402. case IDC_USER_OPENHOMEPAGE:
  403. {
  404. if (DPA_GetPtrCount(pvi->hdpaSelection) != 1)
  405. {
  406. TraceMsg("Disabling as selection > 1");
  407. EnableMenuItem(hMenu, HIWORD(uID), MF_BYCOMMAND|MF_GRAYED);
  408. }
  409. break;
  410. }
  411. }
  412. break;
  413. }
  414. case MENUCMD_INVOKE:
  415. {
  416. // if we have a selection and the user has picked a verb then we
  417. // need to get the UNC"s from the objects we are trying to invoke,
  418. // therefore lets build a DPA containing them.
  419. SetWaitCursor();
  420. for (i = 0 ; i < DPA_GetPtrCount(pvi->hdpaSelection); i++)
  421. {
  422. LPWSTR pPath = (LPWSTR)DPA_GetPtr(pvi->hdpaSelection, i);
  423. TraceAssert(pPath);
  424. DoRelease(pDsObject);
  425. VariantClear(&variant);
  426. Trace(TEXT("Binding to %s"), pPath);
  427. if (FAILED(_OpenObject(pPath,IID_PPV_ARG(IADs, &pDsObject), pvi)))
  428. {
  429. TraceMsg("Failed to bind to the object");
  430. continue;
  431. }
  432. if (LOWORD(uID) == IDC_USER_OPENHOMEPAGE)
  433. {
  434. // get the web address of the object and store it, this should
  435. // only happen once.
  436. if (FAILED(pDsObject->Get(CComBSTR(L"wWWHomePage"), &variant)))
  437. continue;
  438. if (V_VT(&variant) == VT_BSTR)
  439. {
  440. PARSEDURLW pu = {0};
  441. pu.cbSize = sizeof(pu);
  442. if (SUCCEEDED(ParseURLW(variant.bstrVal, &pu)))
  443. {
  444. if ((pu.nScheme == URL_SCHEME_HTTP) || (pu.nScheme == URL_SCHEME_HTTPS))
  445. {
  446. Trace(TEXT("Storing URL %s"), V_BSTR(&variant));
  447. hr = LocalAllocStringW(&pURL, V_BSTR(&variant));
  448. FailGracefully(hr, "Failed to store the URL");
  449. }
  450. else
  451. {
  452. TraceMsg("URL scheme not HTTP/HTTPS so ignoring");
  453. }
  454. }
  455. else
  456. {
  457. TraceMsg("URL wasn't even an URL");
  458. }
  459. }
  460. }
  461. else
  462. {
  463. // ensure we have a DPA for storing the mail addresses of the
  464. // objects we are invoked on.
  465. if (!hdpaMailTo)
  466. {
  467. hdpaMailTo = DPA_Create(4);
  468. TraceAssert(hdpaMailTo);
  469. if (!hdpaMailTo)
  470. ExitGracefully(hr, E_OUTOFMEMORY, "Failed to create the DPA for mail addresses");
  471. }
  472. if (FAILED(pDsObject->Get(CComBSTR(L"mail"), &variant)))
  473. continue;
  474. if (V_VT(&variant) == VT_BSTR)
  475. {
  476. Trace(TEXT("Adding mail address %s to DPA"), V_BSTR(&variant));
  477. StringDPA_AppendString(hdpaMailTo, V_BSTR(&variant), NULL);
  478. }
  479. }
  480. }
  481. // now process the argument list that we have built.
  482. ResetWaitCursor();
  483. sei.cbSize = SIZEOF(sei);
  484. sei.hwnd = hWnd;
  485. sei.nShow = SW_SHOWNORMAL;
  486. switch (LOWORD(uID))
  487. {
  488. case IDC_USER_OPENHOMEPAGE:
  489. {
  490. // if we have a URL then lets pass it to shell execute,
  491. // otherwise report the failure to the user.
  492. if (!pURL)
  493. {
  494. FormatMsgBox(hWnd, GLOBAL_HINSTANCE, IDS_TITLE, IDS_ERR_NOHOMEPAGE, MB_OK|MB_ICONERROR);
  495. ExitGracefully(hr, E_FAIL, "No URL defined");
  496. }
  497. Trace(TEXT("Executing URL %s"), pURL);
  498. sei.lpFile = pURL;
  499. if (!ShellExecuteEx(&sei))
  500. ExitGracefully(hr, E_UNEXPECTED, "Failed in ShellExecuteEx");
  501. break;
  502. }
  503. case IDC_USER_MAILTO:
  504. {
  505. // If every single bind operation failed above,
  506. // hdpaMailTo didn't get defined, and we'll fault
  507. // if we try to use it.
  508. if (hdpaMailTo)
  509. {
  510. // build a command line we can use for the mail to verb.
  511. if (DPA_GetPtrCount(hdpaMailTo) <= 0)
  512. {
  513. FormatMsgBox(hWnd, GLOBAL_HINSTANCE, IDS_TITLE, IDS_ERR_NOMAILADDR, MB_OK|MB_ICONERROR);
  514. ExitGracefully(hr, E_FAIL, "No mail addresses defined");
  515. }
  516. TCHAR szMailTo[1800] = {0};
  517. int cchMailTo = ARRAYSIZE(szMailTo)-8; // -8 for mailto: + terminator
  518. StrCpyN(szMailTo, TEXT("mailto:"), ARRAYSIZE(szMailTo));
  519. for (i = 0 ; (i < DPA_GetPtrCount(hdpaMailTo)) && (cchMailTo > 0); i++)
  520. {
  521. LPTSTR pszName = (LPTSTR)DPA_GetPtr(hdpaMailTo, i);
  522. cchMailTo -= lstrlen(pszName) +1; // +1 for seperator
  523. if (cchMailTo < 0)
  524. {
  525. LPTSTR pszFirstName = (LPTSTR)DPA_GetPtr(hdpaMailTo, 0);
  526. LPTSTR pszLastName = (LPTSTR)DPA_GetPtr(hdpaMailTo, max(0, i-1));
  527. if (IDNO == FormatMsgBox(hWnd, GLOBAL_HINSTANCE, IDS_TITLE, IDS_ERR_EMAILTOOLONG, MB_YESNO|MB_ICONERROR, pszFirstName, pszLastName))
  528. {
  529. ExitGracefully(hr, E_UNEXPECTED, "mailto: line too long");
  530. }
  531. }
  532. else
  533. {
  534. if (i > 0)
  535. StrCatBuff(szMailTo, TEXT(";"), ARRAYSIZE(szMailTo));
  536. StrCatBuff(szMailTo, pszName, ARRAYSIZE(szMailTo));
  537. }
  538. }
  539. sei.lpFile = szMailTo;
  540. if (!ShellExecuteEx(&sei))
  541. ExitGracefully(hr, E_UNEXPECTED, "Failed in ShellExecuteEx");
  542. }
  543. else
  544. {
  545. //FEATURE: We need an error message, here
  546. // FormatMsgBox(hWnd, GLOBAL_HINSTANCE, IDS_TITLE, IDS_ERR_NOMAILADDR, MB_OK|MB_ICONERROR);
  547. ExitGracefully(hr, E_FAIL, "hdpaMailTo never initialized!");
  548. }
  549. break;
  550. }
  551. }
  552. }
  553. }
  554. hr = S_OK; // success
  555. exit_gracefully:
  556. DoRelease(pDsObject);
  557. VariantClear(&variant);
  558. LocalFreeString(&pURL);
  559. StringDPA_Destroy(&hdpaMailTo);
  560. ResetWaitCursor();
  561. TraceLeaveResult(hr);
  562. }
  563. /*----------------------------------------------------------------------------
  564. / Volume object verbs
  565. /----------------------------------------------------------------------------*/
  566. HRESULT _VolumeVerbCB(UINT uCmd, HWND hWnd, HMENU hMenu, LPARAM uID, LPVERBINFO pvi, UINT uFlags)
  567. {
  568. HRESULT hr;
  569. HDPA hdpaUNC = NULL;
  570. IADs* pDsObject = NULL;
  571. VARIANT variant;
  572. INT i;
  573. LPITEMIDLIST pidl;
  574. DECLAREWAITCURSOR = GetCursor();
  575. TraceEnter(TRACE_VERBS, "_VolumeVerbCB");
  576. VariantInit(&variant);
  577. switch (uCmd)
  578. {
  579. case MENUCMD_INITITEM:
  580. {
  581. // if this is a map network drive/find volume verb then lets ensure we only handle
  582. // a single selection.
  583. switch (LOWORD(uID))
  584. {
  585. case IDC_VOLUME_FIND:
  586. case IDC_VOLUME_MAPNETDRIVE:
  587. {
  588. if (DPA_GetPtrCount(pvi->hdpaSelection) != 1)
  589. {
  590. TraceMsg("Disabling as selection > 1");
  591. EnableMenuItem(hMenu, HIWORD(uID), MF_BYCOMMAND|MF_GRAYED);
  592. }
  593. // we remove the find verb if we the restrictions apply to remove it.
  594. if (LOWORD(uID) == IDC_VOLUME_FIND)
  595. {
  596. if (SHRestricted(REST_NOFIND))
  597. {
  598. TraceMsg("Restriction says 'no find', so deleting the find verb");
  599. DeleteMenu(hMenu, HIWORD(uID), MF_BYCOMMAND);
  600. }
  601. }
  602. break;
  603. }
  604. case IDC_VOLUME_OPEN:
  605. {
  606. if (!(uFlags & CMF_EXPLORE))
  607. {
  608. TraceMsg("Not exploring, so making open the default verb");
  609. SetMenuDefaultItem(hMenu, HIWORD(uID), MF_BYCOMMAND);
  610. }
  611. break;
  612. }
  613. case IDC_VOLUME_EXPLORE:
  614. {
  615. if (uFlags & CMF_EXPLORE)
  616. {
  617. TraceMsg("Exploring so making explore the default verb");
  618. SetMenuDefaultItem(hMenu, HIWORD(uID), MF_BYCOMMAND);
  619. }
  620. break;
  621. }
  622. }
  623. break;
  624. }
  625. case MENUCMD_INVOKE:
  626. {
  627. // if we have a selection and the user has picked a verb then we
  628. // need to get the UNC"s from the objects we are trying to invoke,
  629. // therefore lets build a DPA containing them.
  630. SetWaitCursor();
  631. hdpaUNC = DPA_Create(4);
  632. TraceAssert(hdpaUNC);
  633. if (!hdpaUNC)
  634. ExitGracefully(hr, E_OUTOFMEMORY, "Failed to get UNC DPA");
  635. for (i = 0 ; i < DPA_GetPtrCount(pvi->hdpaSelection); i++)
  636. {
  637. LPWSTR pPath = (LPWSTR)DPA_GetPtr(pvi->hdpaSelection, i);
  638. TraceAssert(pPath);
  639. DoRelease(pDsObject);
  640. VariantClear(&variant);
  641. Trace(TEXT("Binding to %s"), pPath);
  642. if (FAILED(_OpenObject(pPath, IID_PPV_ARG(IADs, &pDsObject), pvi)))
  643. {
  644. TraceMsg("Failed to bind to the object");
  645. continue;
  646. }
  647. if (FAILED(pDsObject->Get(CComBSTR(L"uNCName"), &variant)))
  648. continue;
  649. if (V_VT(&variant) == VT_BSTR)
  650. {
  651. Trace(TEXT("Adding UNC %s to DPA"), V_BSTR(&variant));
  652. StringDPA_AppendString(hdpaUNC, V_BSTR(&variant), NULL);
  653. }
  654. }
  655. ResetWaitCursor();
  656. // we now have the selection stored in the DPA, so lets invoke the command
  657. // by walking the list of UNC's and calling the relevant invoke logic.
  658. Trace(TEXT("UNC DPA contains %d entries"), DPA_GetPtrCount(hdpaUNC));
  659. if (!DPA_GetPtrCount(hdpaUNC))
  660. {
  661. FormatMsgBox(hWnd, GLOBAL_HINSTANCE, IDS_TITLE, IDS_ERR_NOUNC, MB_OK|MB_ICONERROR);
  662. ExitGracefully(hr, E_FAIL, "No UNC paths defined");
  663. }
  664. for (i = 0 ; i < DPA_GetPtrCount(hdpaUNC); i++)
  665. {
  666. LPTSTR pUNC = (LPTSTR)DPA_GetPtr(hdpaUNC, i);
  667. TraceAssert(pUNC);
  668. Trace(TEXT("pUNC is %s"), pUNC);
  669. switch (LOWORD(uID))
  670. {
  671. // explore and open we pass onto the shell.
  672. case IDC_VOLUME_OPEN:
  673. case IDC_VOLUME_EXPLORE:
  674. {
  675. SHELLEXECUTEINFO sei = { 0 }; // clears the structure
  676. TraceMsg("Trying to open/explore to UNC");
  677. sei.cbSize = SIZEOF(sei);
  678. sei.hwnd = hWnd;
  679. sei.lpFile = pUNC;
  680. sei.nShow = SW_SHOWNORMAL;
  681. if (uID == IDC_VOLUME_EXPLORE)
  682. sei.lpVerb = TEXT("explore");
  683. ShellExecuteEx(&sei);
  684. break;
  685. }
  686. // find we show the find UI by building an ITEMIDLIST for the UNC we
  687. // have and then call the shells find UI.
  688. case IDC_VOLUME_FIND:
  689. {
  690. TraceMsg("Invoking find on the UNC");
  691. if (SUCCEEDED(SHILCreateFromPath(pUNC, &pidl, NULL)))
  692. {
  693. SHFindFiles(pidl, NULL);
  694. ILFree(pidl);
  695. }
  696. break;
  697. }
  698. // lets get a net connection from SHStartNetConnection...
  699. case IDC_VOLUME_MAPNETDRIVE:
  700. {
  701. Trace(TEXT("Invoking Map Network Drive for: %s"), pUNC);
  702. SHStartNetConnectionDialog(hWnd, pUNC, RESOURCETYPE_DISK);
  703. break;
  704. }
  705. default:
  706. {
  707. TraceAssert(FALSE);
  708. ExitGracefully(hr, E_UNEXPECTED, "Failed to invoke, bad uID");
  709. }
  710. }
  711. }
  712. }
  713. }
  714. hr = S_OK; // success
  715. exit_gracefully:
  716. DoRelease(pDsObject);
  717. VariantClear(&variant);
  718. StringDPA_Destroy(&hdpaUNC);
  719. ResetWaitCursor();
  720. TraceLeaveResult(hr);
  721. }
  722. /*----------------------------------------------------------------------------
  723. / Computer object verbs
  724. /----------------------------------------------------------------------------*/
  725. HRESULT _ComputerVerbCB(UINT uCmd, HWND hWnd, HMENU hMenu, LPARAM uID, LPVERBINFO pvi, UINT uFlags)
  726. {
  727. HRESULT hr;
  728. IADs * pDsObject = NULL;
  729. LPTSTR pArguments = NULL;
  730. LPTSTR pComputer = NULL;
  731. TCHAR szBuffer[MAX_PATH];
  732. DECLAREWAITCURSOR = GetCursor();
  733. TraceEnter(TRACE_VERBS, "_ComputerVerbCB");
  734. if (LOWORD(uID) != IDC_COMPUTER_MANAGE)
  735. ExitGracefully(hr, E_INVALIDARG, "Not computer manange, so bailing");
  736. switch (uCmd)
  737. {
  738. case MENUCMD_INITITEM:
  739. {
  740. if (DPA_GetPtrCount(pvi->hdpaSelection) != 1)
  741. {
  742. TraceMsg("Selection is != 1, so disabling verb");
  743. EnableMenuItem(hMenu, HIWORD(uID), MF_BYCOMMAND|MF_GRAYED);
  744. }
  745. break;
  746. }
  747. case MENUCMD_INVOKE:
  748. {
  749. LPWSTR pPath = (LPWSTR)DPA_GetPtr(pvi->hdpaSelection, 0); // selection always 0
  750. TraceAssert(pPath);
  751. hr = _OpenObject(pPath, IID_PPV_ARG(IADs, &pDsObject), pvi);
  752. if(FAILED(hr))
  753. {
  754. PWSTR pszError = NULL;
  755. StringErrorFromHr(hr, &pszError, TRUE);
  756. if(pszError)
  757. {
  758. FormatMsgBox(hWnd, GLOBAL_HINSTANCE, IDS_TITLE, IDS_ERR_COMPUTER_MANAGE, MB_OK|MB_ICONERROR, pszError);
  759. delete[] pszError;
  760. }
  761. FailGracefully(hr, "Failed to bind to computer object");
  762. }
  763. VARIANT vNetAddr;
  764. hr = pDsObject->Get(CComBSTR(L"dNSHostName"), &vNetAddr);
  765. if (SUCCEEDED(hr)) {
  766. hr = LocalAllocString (&pComputer, vNetAddr.bstrVal);
  767. FailGracefully(hr, "Failed to copy computer address somewhere interesting");
  768. } else {
  769. if (hr == E_ADS_PROPERTY_NOT_FOUND) {
  770. hr = pDsObject->Get(CComBSTR(L"sAMAccountName"), &vNetAddr);
  771. if (SUCCEEDED(hr)) {
  772. hr = LocalAllocString(&pComputer, vNetAddr.bstrVal);
  773. FailGracefully(hr, "Failed to copy SAM account name somewhere interesting");
  774. // To make the computer name useful we must remove the trailing dollar if
  775. // there is one. Therefore scan to the end of the string and nuke the
  776. // last character.
  777. INT i = lstrlen(pComputer);
  778. TraceAssert(i > 1);
  779. if ((i > 1) && (pComputer[i-1] == TEXT('$')))
  780. {
  781. pComputer[i-1] = TEXT('\0');
  782. Trace(TEXT("Fixed computer name: %s"), pComputer);
  783. }
  784. } else
  785. FailGracefully (hr, "Failed to find a usable machine address");
  786. }
  787. }
  788. hr = FormatMsgResource(&pArguments, g_hInstance, IDS_COMPUTER_MANAGECMD, pComputer);
  789. FailGracefully(hr, "Failed to format MMC cmd line");
  790. ExpandEnvironmentStrings(pArguments, szBuffer, ARRAYSIZE(szBuffer));
  791. Trace(TEXT("MMC cmd line: mmc.exe %s"), szBuffer);
  792. ResetWaitCursor();
  793. SHELLEXECUTEINFO sei = {0};
  794. sei.cbSize = sizeof(sei);
  795. sei.fMask = SEE_MASK_DOENVSUBST;
  796. sei.lpFile = TEXT("%SystemRoot%\\System32\\mmc.exe");
  797. sei.lpParameters = szBuffer;
  798. sei.nShow = SW_SHOWNORMAL;
  799. ShellExecuteEx(&sei);
  800. }
  801. }
  802. hr = S_OK; // success
  803. exit_gracefully:
  804. DoRelease(pDsObject);
  805. LocalFreeString (&pComputer);
  806. TraceLeaveResult(hr);
  807. }
  808. /*----------------------------------------------------------------------------
  809. / printQueue object verb implementations
  810. /----------------------------------------------------------------------------*/
  811. //
  812. // Windows 2000 (and beyond) use a RunDll32 entry point.
  813. //
  814. #define PRINT_FMT TEXT("printui.dll,PrintUIEntry /n \"%s\" ")
  815. #define PRINT_SWITCH_OPEN TEXT("/o ")
  816. #define PRINT_SWITCH_INSTALL TEXT("/in ")
  817. BOOL _PrinterCheckRestrictions(HWND hwnd, RESTRICTIONS rest)
  818. {
  819. if (SHRestricted(rest))
  820. {
  821. FormatMsgBox(hwnd, GLOBAL_HINSTANCE, IDS_RESTRICTIONSTITLE, IDS_RESTRICTIONS, MB_OK|MB_ICONERROR);
  822. return TRUE;
  823. }
  824. return FALSE;
  825. }
  826. HRESULT _PrinterRunDLLCountAtSymbols(LPCTSTR pszPrinterName, UINT *puCount)
  827. {
  828. HRESULT hr = E_FAIL;
  829. if(pszPrinterName && puCount)
  830. {
  831. *puCount = 0;
  832. while(*pszPrinterName)
  833. {
  834. if(TEXT('@') == *pszPrinterName++)
  835. {
  836. (*puCount) ++;
  837. }
  838. }
  839. hr = S_OK;
  840. }
  841. return hr;
  842. }
  843. HRESULT _PrinterRunDLLFormatAtSymbols(LPTSTR pszBuffer, UINT uBufSize, LPCTSTR pszPrinterName)
  844. {
  845. HRESULT hr = E_FAIL;
  846. if(pszPrinterName && pszBuffer && uBufSize)
  847. {
  848. // the buffer end - where we will put the zero terminator
  849. LPTSTR pszBufEnd = pszBuffer + uBufSize - 1;
  850. // format the printer name quoting the @ symbols
  851. while(*pszPrinterName)
  852. {
  853. if(TEXT('@') == *pszPrinterName)
  854. {
  855. // check the buffer size
  856. if((pszBuffer+1) >= pszBufEnd)
  857. break; // not enough space
  858. // we have space in the buffer
  859. *pszBuffer++ = TEXT('\\');
  860. *pszBuffer++ = *pszPrinterName++;
  861. }
  862. else
  863. {
  864. // check the buffer size
  865. if(pszBuffer >= pszBufEnd)
  866. break; // not enough space
  867. // we have space in the buffer
  868. *pszBuffer++ = *pszPrinterName++;
  869. }
  870. }
  871. if(0 == *pszPrinterName)
  872. {
  873. // the buffer is long enough
  874. hr = S_OK;
  875. }
  876. else
  877. {
  878. // we hit the insufficent buffer error
  879. hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
  880. }
  881. // put the zero terminator
  882. *pszBuffer = 0;
  883. }
  884. return hr;
  885. }
  886. HRESULT _PrinterVerbCB(UINT uCmd, HWND hWnd, HMENU hMenu, LPARAM uID, LPVERBINFO pvi, UINT uFlags)
  887. {
  888. HRESULT hr;
  889. IADs* pDsObject = NULL;
  890. LPTSTR pPrinterUNC = NULL;
  891. LPTSTR pBuffer = NULL;
  892. LPTSTR pPrinterName = NULL;
  893. UINT uAtSymbolsCount, uBufSize;
  894. INT i;
  895. VARIANT variant;
  896. DECLAREWAITCURSOR = GetCursor();
  897. TraceEnter(TRACE_VERBS, "_ComputerVerbCB");
  898. VariantInit(&variant);
  899. switch (uCmd)
  900. {
  901. case MENUCMD_INITITEM:
  902. {
  903. // printers want the open verb as their default.
  904. if (LOWORD(uID) == IDC_PRINTER_INSTALL)
  905. {
  906. TraceMsg("Install should be the default verb for printQueue objects");
  907. SetMenuDefaultItem(hMenu, HIWORD(uID), MF_BYCOMMAND);
  908. }
  909. // printer verbs only work on a single selection.
  910. if (DPA_GetPtrCount(pvi->hdpaSelection) != 1)
  911. {
  912. TraceMsg("Selection is != 1, so disabling verb");
  913. EnableMenuItem(hMenu, HIWORD(uID), MF_BYCOMMAND|MF_GRAYED);
  914. }
  915. break;
  916. }
  917. case MENUCMD_INVOKE:
  918. {
  919. LPWSTR pPath = (LPWSTR)DPA_GetPtr(pvi->hdpaSelection, 0); // selection always 0
  920. TraceAssert(pPath);
  921. SetWaitCursor();
  922. hr = _OpenObject(pPath, IID_PPV_ARG(IADs, &pDsObject), pvi);
  923. FailGracefully(hr, "Failed to get pDsObject");
  924. // for Windows NT we can grab the UNC name and build a command line
  925. // we invoke the printUI dll using.
  926. hr = pDsObject->Get(CComBSTR(L"uNCName"), &variant);
  927. FailGracefully(hr, "Failed to get UNC from the printer object");
  928. if (V_VT(&variant) != VT_BSTR)
  929. ExitGracefully(hr, E_FAIL, "UNC is not a BSTR - whats with that?");
  930. hr = LocalAllocStringW(&pPrinterUNC, V_BSTR(&variant));
  931. FailGracefully(hr, "Failed to copy the printerUNC");
  932. Trace(TEXT("printQueue object UNC: %s"), pPrinterUNC);
  933. // if this is the downlevel shell then load the PRINUI code and then
  934. // invoke the handler accordingly.
  935. hr = _PrinterRunDLLCountAtSymbols(pPrinterUNC, &uAtSymbolsCount);
  936. FailGracefully(hr, "Failed to count the @ symbols");
  937. uBufSize = lstrlen(pPrinterUNC) + uAtSymbolsCount + 1;
  938. hr = LocalAllocStringLen(&pPrinterName, uBufSize);
  939. FailGracefully(hr, "Failed to copy the printerName");
  940. hr = _PrinterRunDLLFormatAtSymbols(pPrinterName, uBufSize, pPrinterUNC);
  941. FailGracefully(hr, "Failed to format printerName @ symbols ");
  942. // allocate the format buffer.
  943. int cchBuffer = lstrlen(PRINT_FMT) +
  944. lstrlen(PRINT_SWITCH_OPEN) +
  945. lstrlen(PRINT_SWITCH_INSTALL) +
  946. lstrlen(pPrinterName) + 1;
  947. hr = LocalAllocStringLen(&pBuffer, cchBuffer);
  948. FailGracefully(hr, "Failed to allocate format buffer");
  949. wnsprintf(pBuffer, cchBuffer, PRINT_FMT, pPrinterName); // now format the line...
  950. switch (LOWORD(uID))
  951. {
  952. case IDC_PRINTER_OPEN:
  953. StrCatBuff(pBuffer, PRINT_SWITCH_OPEN, cchBuffer);
  954. break;
  955. case IDC_PRINTER_INSTALL:
  956. StrCatBuff(pBuffer, PRINT_SWITCH_INSTALL, cchBuffer);
  957. break;
  958. }
  959. ResetWaitCursor();
  960. BOOL bRunCommand = TRUE;
  961. if(IDC_PRINTER_INSTALL == LOWORD(uID) && _PrinterCheckRestrictions(hWnd, REST_NOPRINTERADD))
  962. bRunCommand = FALSE;
  963. if(bRunCommand)
  964. {
  965. Trace(TEXT("Invoking: rundll32.exe %s"), pBuffer);
  966. SHELLEXECUTEINFO sei = {0};
  967. sei.cbSize = sizeof(sei);
  968. sei.fMask = SEE_MASK_DOENVSUBST;
  969. sei.lpFile = TEXT("%SystemRoot%\\System32\\rundll32.exe");
  970. sei.lpParameters = pBuffer;
  971. sei.nShow = SW_SHOWNORMAL;
  972. ShellExecuteEx(&sei);
  973. }
  974. }
  975. }
  976. hr = S_OK; // success
  977. exit_gracefully:
  978. if (FAILED(hr))
  979. {
  980. // we need to tell something to the user here.
  981. FormatMsgBox(hWnd, GLOBAL_HINSTANCE, IDS_TITLE, IDS_ERR_DSOPENOBJECT, MB_OK|MB_ICONERROR);
  982. }
  983. VariantClear(&variant);
  984. DoRelease(pDsObject);
  985. LocalFreeString(&pPrinterUNC);
  986. LocalFreeString(&pPrinterName);
  987. LocalFreeString(&pBuffer);
  988. ResetWaitCursor();
  989. TraceLeaveResult(hr);
  990. }