Source code of Windows XP (NT5)
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.

1254 lines
40 KiB

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