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.

619 lines
21 KiB

  1. /**********************************************************************************
  2. *
  3. *
  4. * contxtmnu.c - contains functions for handling/creating context menu extension
  5. *
  6. * Created - 9/97 - vikramm
  7. *
  8. **********************************************************************************/
  9. #include "_apipch.h"
  10. static const TCHAR szActionPropsRegKey[] = TEXT("Software\\Microsoft\\WAB\\WAB4\\ExtContext");
  11. BOOL fContextExtCoinit = FALSE;
  12. //$$//////////////////////////////////////////////////////////////////////
  13. //
  14. // UninitContextExtInfo
  15. //
  16. // OLE Unintialization
  17. //
  18. //////////////////////////////////////////////////////////////////////////
  19. void UninitContextExtInfo()
  20. {
  21. if(fContextExtCoinit)
  22. {
  23. CoUninitialize();
  24. fContextExtCoinit = FALSE;
  25. }
  26. }
  27. /*
  28. - FreeActionItemList
  29. -
  30. * Frees up the Action Items list cached on the IAB object
  31. *
  32. */
  33. void FreeActionItemList(LPIAB lpIAB)
  34. {
  35. LPWABACTIONITEM lpItem = lpIAB->lpActionList;
  36. while(lpItem)
  37. {
  38. lpIAB->lpActionList = lpItem->lpNext;
  39. SafeRelease(lpItem->lpWABExtInit);
  40. SafeRelease(lpItem->lpContextMenu);
  41. LocalFree(lpItem);
  42. lpItem = lpIAB->lpActionList;
  43. }
  44. lpIAB->lpActionList = NULL;
  45. }
  46. /*
  47. - HrUpdateActionItemList
  48. -
  49. * Apps can register with the WAB for rt-click and toolbar Action items
  50. * We load a list of registered action items here upfront and cache it on
  51. * the IAB object.
  52. *
  53. */
  54. HRESULT HrUpdateActionItemList(LPIAB lpIAB)
  55. {
  56. HRESULT hr = E_FAIL;
  57. HKEY hKey = NULL;
  58. LPWABACTIONITEM lpList = NULL;
  59. DWORD dwIndex = 0, dwSize = 0;
  60. int nCmdID = IDM_EXTENDED_START, nActionItems = 0;
  61. EnterCriticalSection(&lpIAB->cs);
  62. if(lpIAB->lpActionList)
  63. FreeActionItemList(lpIAB);
  64. lpIAB->lpActionList = NULL;
  65. //
  66. // We will look in the registry under HKLM\Software\Microsoft\WAB\WAB4\Actions
  67. // If this key exists, we get all the key values under it - these key values
  68. // are all GUIDs
  69. // The format for this key is
  70. //
  71. // HKLM\Software\Microsoft\WAB\WAB4\Action Extensions
  72. // GUID1
  73. // GUID2
  74. // GUID3 etc
  75. //
  76. if(ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  77. szActionPropsRegKey,
  78. 0, KEY_READ,&hKey))
  79. {
  80. goto out;
  81. }
  82. {
  83. // Enumerate the GUIDs under this key one by one
  84. //
  85. TCHAR szGUIDName[MAX_PATH];
  86. DWORD dwGUIDIndex = 0, dwGUIDSize = CharSizeOf(szGUIDName), dwType = 0;
  87. *szGUIDName = '\0';
  88. while(ERROR_SUCCESS == RegEnumValue(hKey, dwGUIDIndex,
  89. szGUIDName, &dwGUIDSize,
  90. 0, &dwType,
  91. NULL, NULL))
  92. {
  93. // The values under this entry are all GUIDs
  94. // Read the GUID string and translate it into a GUID
  95. //
  96. GUID guidTmp = {0};
  97. WCHAR szW[MAX_PATH];
  98. StrCpyNW(szW, szGUIDName, ARRAYSIZE(szW));
  99. if( !(HR_FAILED(hr = CLSIDFromString(szW, &guidTmp))) )
  100. {
  101. LPWABACTIONITEM lpTemp = LocalAlloc(LMEM_ZEROINIT, sizeof(WABACTIONITEM));
  102. if(!lpTemp)
  103. {
  104. hr = MAPI_E_NOT_ENOUGH_MEMORY;
  105. goto out;
  106. }
  107. // Temporarily cache the GUID
  108. CopyMemory(&(lpTemp->guidContextMenu), &guidTmp, sizeof(GUID));
  109. lpTemp->lpNext = lpList;
  110. lpList = lpTemp;
  111. }
  112. dwGUIDIndex++;
  113. *szGUIDName = '\0';
  114. dwGUIDSize = CharSizeOf(szGUIDName);
  115. }
  116. }
  117. if(lpList)
  118. {
  119. // If we have a list of GUIDs from the registry, we now
  120. // need to open CoCreateInstance them one by one and get a handle
  121. // to their method pointers
  122. LPWABACTIONITEM lpItem = lpList;
  123. if (CoInitialize(NULL) == S_FALSE)
  124. CoUninitialize(); // Already initialized, undo the extra.
  125. else
  126. fContextExtCoinit = TRUE;
  127. while(lpItem)
  128. {
  129. hr = CoCreateInstance( &(lpItem->guidContextMenu),
  130. NULL,
  131. CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,
  132. &IID_IContextMenu,
  133. (LPVOID *)&(lpItem->lpContextMenu));
  134. if(lpItem->lpContextMenu && !HR_FAILED(hr))
  135. {
  136. // Found an IContextMenu object, aslo want a IWABExtInit object
  137. hr = lpItem->lpContextMenu->lpVtbl->QueryInterface(lpItem->lpContextMenu,
  138. &IID_IWABExtInit,
  139. (LPVOID *)&(lpItem->lpWABExtInit));
  140. if(HR_FAILED(hr) || !lpItem->lpWABExtInit)
  141. {
  142. // Can't work without an IWABExtInit object
  143. SafeRelease(lpItem->lpContextMenu);
  144. }
  145. }
  146. else
  147. {
  148. hr = S_OK; //ignore error
  149. lpItem->lpContextMenu = NULL;
  150. }
  151. lpItem = lpItem->lpNext;
  152. }
  153. }
  154. lpIAB->lpActionList = lpList;
  155. hr = S_OK;
  156. out:
  157. if(hKey)
  158. RegCloseKey(hKey);
  159. LeaveCriticalSection(&lpIAB->cs);
  160. return hr;
  161. }
  162. /*
  163. - GetActionAdrList
  164. -
  165. * Based on the parameters for a particular rt-click action,
  166. * scans the entries in the list view and creates an adrlist
  167. * for the entries
  168. *
  169. * If only one entry is selected and it is an LDAP entry, then
  170. * also creates an LDAP URL representing that entry .. this way
  171. * if we are displaying properties or doing actions on a single
  172. * entry, the property sheet extenstions can determine if they
  173. * want to do anything extra for the entry. People most interested
  174. * in this are the NTDS
  175. *
  176. * For now,we only look at the selected items in the list view
  177. *
  178. lpAdrBook - IAB object
  179. hWndLV - the list view on which this action was initiated
  180. *lppAdrList - created AdrList
  181. *lpURL - returned URL
  182. *lpbIsNTDSEntry - flag NTDS entries so they can be special treated
  183. Note performance suffers for a large number of entries so we want to
  184. really only return a list of entryids
  185. */
  186. HRESULT HrGetActionAdrList(LPADRBOOK lpAdrBook,
  187. HWND hWndLV,
  188. LPADRLIST * lppAdrList,
  189. LPTSTR * lppURL, BOOL * lpbIsNTDSEntry)
  190. {
  191. HRESULT hr = S_OK;
  192. LPADRLIST lpAdrList = NULL;
  193. int i = 0, iItemIndex = 0, nIndex= 0;
  194. int nSel = ListView_GetSelectedCount(hWndLV);
  195. SCODE sc;
  196. if(!nSel)
  197. goto out;
  198. sc = MAPIAllocateBuffer(sizeof(ADRLIST) + nSel * sizeof(ADRENTRY), &lpAdrList);
  199. if(sc)
  200. {
  201. hr = ResultFromScode(sc);
  202. goto out;
  203. }
  204. // Get index of selected item
  205. iItemIndex = ListView_GetNextItem(hWndLV,-1,LVNI_SELECTED);
  206. while(iItemIndex != -1)
  207. {
  208. LPRECIPIENT_INFO lpItem = GetItemFromLV(hWndLV, iItemIndex);
  209. if(lpItem)
  210. {
  211. ULONG ulObjType = 0;
  212. LPSPropValue lpProps = NULL;
  213. LPMAILUSER lpEntry = NULL;
  214. ULONG cValues = 0;
  215. if(lpItem->cbEntryID && lpItem->lpEntryID)
  216. {
  217. if (hr = lpAdrBook->lpVtbl->OpenEntry(lpAdrBook,
  218. lpItem->cbEntryID,
  219. lpItem->lpEntryID,
  220. NULL, // interface
  221. 0, // flags
  222. &ulObjType,
  223. (LPUNKNOWN *)&lpEntry))
  224. {
  225. goto out;
  226. }
  227. hr = lpEntry->lpVtbl->GetProps( lpEntry, NULL, MAPI_UNICODE,
  228. &cValues, &lpProps);
  229. if(HR_FAILED(hr))
  230. {
  231. UlRelease(lpEntry);
  232. goto out;
  233. }
  234. lpAdrList->aEntries[nIndex].cValues = cValues;
  235. lpAdrList->aEntries[nIndex].rgPropVals = lpProps;
  236. nIndex++;
  237. UlRelease(lpEntry);
  238. if(nSel == 1 && lppURL)
  239. {
  240. CreateLDAPURLFromEntryID(lpItem->cbEntryID, lpItem->lpEntryID,
  241. lppURL, lpbIsNTDSEntry);
  242. }
  243. }
  244. }
  245. iItemIndex = ListView_GetNextItem(hWndLV,iItemIndex,LVNI_SELECTED);
  246. }
  247. lpAdrList->cEntries = nIndex;
  248. *lppAdrList = lpAdrList;
  249. lpAdrList = NULL;
  250. hr = S_OK;
  251. out:
  252. if (lpAdrList)
  253. MAPIFreeBuffer(lpAdrList);
  254. return hr;
  255. }
  256. extern void MAILUSERAssociateContextData(LPMAILUSER lpMailUser, LPWABEXTDISPLAY lpWEC);
  257. /*
  258. - HrCreateContextDataStruct
  259. -
  260. * Creates the data necessary to initialize a ContextMenu implementor
  261. * This structure is passed into the IWABExtInit::Initialize call
  262. *
  263. hWndLV - ListView containing the WAB entries
  264. lppWABExt - returned data
  265. */
  266. HRESULT HrCreateContextDataStruct( LPIAB lpIAB,
  267. HWND hWndLV,
  268. LPWABEXTDISPLAY * lppWABExt)
  269. {
  270. LPADRLIST lpAdrList = NULL;
  271. LPWABEXTDISPLAY lpWEC = NULL;
  272. LPMAILUSER lpMailUser = NULL;
  273. LPTSTR lpURL = NULL;
  274. BOOL bIsNTDSEntry = FALSE;
  275. HRESULT hr = E_FAIL;
  276. // Get an AdrList Corresponding to the LV contents
  277. hr = HrGetActionAdrList((LPADRBOOK)lpIAB, hWndLV, &lpAdrList, &lpURL, &bIsNTDSEntry);
  278. if(HR_FAILED(hr) || !lpAdrList || !lpAdrList->cEntries)
  279. goto out; //dont bother invoking
  280. // Create a dummy mailuser so callers can call GetIDsFromNames
  281. // on this dummy mailuser - saves them the trouble of creating entries
  282. // just to call GetIDsFromNames
  283. hr = HrCreateNewObject((LPADRBOOK)lpIAB, NULL, MAPI_MAILUSER, CREATE_CHECK_DUP_STRICT, (LPMAPIPROP *) &lpMailUser);
  284. if(HR_FAILED(hr))
  285. goto out; //dont bother invoking
  286. lpWEC = LocalAlloc(LMEM_ZEROINIT, sizeof(WABEXTDISPLAY));
  287. if(!lpWEC)
  288. {
  289. hr = MAPI_E_NOT_ENOUGH_MEMORY;
  290. goto out;
  291. }
  292. lpWEC->cbSize = sizeof(WABEXTDISPLAY);
  293. lpWEC->ulFlags = WAB_CONTEXT_ADRLIST; // indicates ADRLIST is valid and in the lpv member
  294. lpWEC->lpAdrBook = (LPADRBOOK) lpIAB;
  295. lpWEC->lpWABObject = (LPWABOBJECT) lpIAB->lpWABObject;
  296. lpWEC->lpPropObj = (LPMAPIPROP) lpMailUser;
  297. lpWEC->lpv = (LPVOID) lpAdrList;
  298. if(lpURL && lstrlen(lpURL))
  299. {
  300. lpWEC->lpsz = lpURL;
  301. lpWEC->ulFlags |= WAB_DISPLAY_LDAPURL;
  302. lpWEC->ulFlags |= MAPI_UNICODE;
  303. if(bIsNTDSEntry)
  304. lpWEC->ulFlags |= WAB_DISPLAY_ISNTDS;
  305. }
  306. // We associate this entire WABEXT structure with the mailuser
  307. // so that when we get the IMailUser release, we can go ahead and clean
  308. // up all the WABEXT memory .. since there is no other good time that we can
  309. // free the memory since we won't know who is doing what withthe structure.
  310. // Freeing it at IMailUser::Release time works out quite well
  311. MAILUSERAssociateContextData(lpMailUser, lpWEC);
  312. //
  313. // Cache this mailUser on the IAB object
  314. //
  315. // At any given point of time, we are going to cache one ContextMenu related MailUser
  316. // This is because we set this up before QueryCommandMenu and then we dont know whether
  317. // or not the user actually managed to select a menu item, or went off and did something new.
  318. // We set the data on a MailUser and we don't release the MailUser till the next time we are
  319. // here. If someone is currently using the MailUser, they will addref it - the memory attached
  320. // to the MailUser will be freed up with the last caller so we dont leak it ..
  321. // If we never come back here, the mailuser will be released at shutdown
  322. //
  323. UlRelease(lpIAB->lpCntxtMailUser);
  324. lpIAB->lpCntxtMailUser = lpMailUser;
  325. *lppWABExt = lpWEC;
  326. hr = S_OK;
  327. out:
  328. if(HR_FAILED(hr))
  329. {
  330. UlRelease(lpMailUser);
  331. if(lpAdrList)
  332. FreePadrlist(lpAdrList);
  333. if(lpWEC)
  334. LocalFree(lpWEC);
  335. }
  336. return hr;
  337. }
  338. //$$////////////////////////////////////////////////////////////////////////////
  339. //
  340. // AddExtendedMenuItems - Creates a list of extension menu items and adds them
  341. // to the specified menu
  342. //
  343. // hMenuAction - menu on which to add this item
  344. // bUpdateStatus - if TRUE, means only find the existing item in the menu and update
  345. // its status - if FALSE, means add to the menu
  346. // bAddSendMailToItems - if TRUE, means attempt to modify the SendMailTo menu item
  347. //
  348. //////////////////////////////////////////////////////////////////////////////
  349. void AddExtendedMenuItems( LPADRBOOK lpAdrBook,
  350. HWND hWndLV,
  351. HMENU hMenuAction,
  352. BOOL bUpdateStatus,
  353. BOOL bAddSendMailToItems)
  354. {
  355. HRESULT hr = S_OK;
  356. LPWABEXTDISPLAY lpWEC = NULL;
  357. LPIAB lpIAB = (LPIAB) lpAdrBook;
  358. // Intialize the context menu extensions
  359. if(!lpIAB->lpActionList)
  360. HrUpdateActionItemList(lpIAB);
  361. if(bUpdateStatus)
  362. {
  363. // This is only set to true from the call from ui_abook.c which means this
  364. // is the Tools Menu item we are talking about.
  365. // To update the status of the Tools menu item, we need to remove all the
  366. // items we added before and then re-add them
  367. // For indexing purposes, we will assume that the last pre-configured item in
  368. // this list is the Internet Call item (since this menu list is quite variable)
  369. // First get the position of the IDM_TOOLS_INTERNET_CALL item
  370. int i, nPos = -1, nId = 0;
  371. int nCmdCount = GetMenuItemCount(hMenuAction); // Append all items at end of this menu
  372. for(i=0;i<nCmdCount;i++)
  373. {
  374. if(GetMenuItemID(hMenuAction, i) == IDM_TOOLS_INTERNET_CALL)
  375. {
  376. nPos = i;
  377. break;
  378. }
  379. }
  380. if(nPos >= 0 && nPos < nCmdCount-1)
  381. {
  382. for(i=nPos+1;i<nCmdCount;i++)
  383. {
  384. DeleteMenu(hMenuAction, nPos+1, MF_BYPOSITION);
  385. }
  386. }
  387. }
  388. // Do any special treatment we need to do for SendMailTo
  389. AddExtendedSendMailToItems(lpAdrBook, hWndLV, hMenuAction, bAddSendMailToItems);
  390. // Before we can call QueryContextMenu - we must already have a list of all
  391. // the selected items from the ListView and provide such items to the ContextMenu
  392. // implementors so that they can decide how to handle the data being provided to them
  393. // (e.g. they may want to disable their item for multi-selections etc)...
  394. //
  395. HrCreateContextDataStruct(lpIAB, hWndLV, &lpWEC);
  396. if(lpIAB->lpActionList && lpWEC && lpWEC->lpv)
  397. {
  398. LPWABACTIONITEM lpItem = lpIAB->lpActionList;
  399. int nCmdIdPos = GetMenuItemCount(hMenuAction); // Append all items at end of this menu
  400. while(lpItem)
  401. {
  402. if(lpItem->lpContextMenu && lpItem->lpWABExtInit)// && !bUpdateStatus)
  403. {
  404. int nNumCmd = 0;
  405. // Get the menu item added to this menu
  406. hr = lpItem->lpWABExtInit->lpVtbl->Initialize(lpItem->lpWABExtInit, lpWEC);
  407. if(!HR_FAILED(hr))
  408. {
  409. hr = lpItem->lpContextMenu->lpVtbl->QueryContextMenu(lpItem->lpContextMenu,
  410. hMenuAction,
  411. nCmdIdPos,
  412. lpItem->nCmdIdFirst ? lpItem->nCmdIdFirst : IDM_EXTENDED_START+nCmdIdPos,
  413. IDM_EXTENDED_END,
  414. CMF_NODEFAULT | CMF_NORMAL);
  415. if(!HR_FAILED(hr))
  416. {
  417. nNumCmd = HRESULT_CODE(hr);
  418. if(nNumCmd)
  419. {
  420. // Record the range of IDs taken up by this menu ext implementor
  421. if(!lpItem->nCmdIdFirst)
  422. lpItem->nCmdIdFirst = nCmdIdPos+IDM_EXTENDED_START;
  423. if(!lpItem->nCmdIdLast)
  424. lpItem->nCmdIdLast = lpItem->nCmdIdFirst + nNumCmd - 1;
  425. }
  426. // Update the next available starting pos
  427. nCmdIdPos = nCmdIdPos+nNumCmd;
  428. }
  429. }
  430. }
  431. lpItem = lpItem->lpNext;
  432. }
  433. }
  434. }
  435. /*
  436. - ProcessActionCommands
  437. -
  438. * Process a WM_COMMAND message to see if it matches any of the extended
  439. * rt-click action items ...
  440. *
  441. * Also process SendMailTo extended email-address mail processing here since
  442. * this is a convenient place to do so ..
  443. *
  444. */
  445. LRESULT ProcessActionCommands(LPIAB lpIAB, HWND hWndLV, HWND hWnd,
  446. UINT uMsg, WPARAM wParam, LPARAM lParam)
  447. {
  448. int nCmdID = GET_WM_COMMAND_ID(wParam, lParam);
  449. int i = 0;
  450. switch(nCmdID)
  451. {
  452. case IDM_DIALDLG_START:
  453. HrExecDialDlg(hWndLV, (LPADRBOOK)lpIAB);
  454. return 0;
  455. break;
  456. case IDM_LVCONTEXT_INTERNET_CALL:
  457. case IDM_TOOLS_INTERNET_CALL:
  458. HrShellExecInternetCall((LPADRBOOK)lpIAB, hWndLV);
  459. return 0;
  460. break;
  461. case IDM_LVCONTEXT_SENDMAIL:
  462. case IDM_FILE_SENDMAIL:
  463. HrSendMailToSelectedContacts(hWndLV, (LPADRBOOK)lpIAB, 0);
  464. break;
  465. }
  466. if( (nCmdID>=IDM_SENDMAILTO_START) && (nCmdID<=IDM_SENDMAILTO_START+IDM_SENDMAILTO_MAX))
  467. {
  468. HrSendMailToSelectedContacts(hWndLV, (LPADRBOOK)lpIAB, nCmdID - IDM_SENDMAILTO_START);
  469. return 0;
  470. }
  471. // Check if this is any of the context menu extensions ..
  472. if(lpIAB->lpActionList)
  473. {
  474. LPWABACTIONITEM lpListItem = lpIAB->lpActionList;
  475. while(lpListItem)
  476. {
  477. if(nCmdID >= lpListItem->nCmdIdFirst && nCmdID <= lpListItem->nCmdIdLast)
  478. {
  479. CMINVOKECOMMANDINFO cmici = {0};
  480. cmici.cbSize = sizeof(CMINVOKECOMMANDINFO);
  481. cmici.fMask = 0;
  482. cmici.hwnd = hWnd;
  483. cmici.lpVerb = (LPCSTR) IntToPtr(nCmdID - lpListItem->nCmdIdFirst);
  484. cmici.lpParameters = NULL;
  485. cmici.lpDirectory = NULL;
  486. cmici.nShow = SW_SHOWNORMAL;
  487. lpListItem->lpContextMenu->lpVtbl->InvokeCommand(lpListItem->lpContextMenu,
  488. &cmici);
  489. return 0;
  490. }
  491. lpListItem = lpListItem->lpNext;
  492. }
  493. }
  494. return DefWindowProc(hWnd,uMsg,wParam,lParam);
  495. }
  496. /*
  497. - GetContextMenuExtCommandString
  498. -
  499. * gets the status bar helptext for context menu extensions
  500. *
  501. */
  502. void GetContextMenuExtCommandString(LPIAB lpIAB, int nCmdId, LPTSTR sz, ULONG cbsz)
  503. {
  504. int nStringID = 0;
  505. switch(nCmdId)
  506. {
  507. case IDM_DIALDLG_START:
  508. nStringID = idsMenuDialer;
  509. break;
  510. case IDM_LVCONTEXT_INTERNET_CALL:
  511. case IDM_TOOLS_INTERNET_CALL:
  512. nStringID = idsMenuInternetCall;
  513. break;
  514. case IDM_LVCONTEXT_SENDMAIL:
  515. case IDM_FILE_SENDMAIL:
  516. nStringID = idsMenuSendMail;
  517. break;
  518. }
  519. if(nStringID)
  520. {
  521. LoadString(hinstMapiX, nStringID, sz, cbsz);
  522. return;
  523. }
  524. if(lpIAB->lpActionList)
  525. {
  526. LPWABACTIONITEM lpListItem = lpIAB->lpActionList;
  527. while(lpListItem)
  528. {
  529. if(nCmdId >= lpListItem->nCmdIdFirst && nCmdId <= lpListItem->nCmdIdLast)
  530. {
  531. char szC[MAX_PATH];
  532. ULONG cbszC = CharSizeOf(szC);
  533. lpListItem->lpContextMenu->lpVtbl->GetCommandString(lpListItem->lpContextMenu,
  534. nCmdId - lpListItem->nCmdIdFirst,
  535. GCS_HELPTEXT,
  536. NULL,
  537. szC,
  538. cbszC);
  539. {
  540. LPTSTR lp = ConvertAtoW(szC);
  541. if(lp)
  542. {
  543. StrCpyN(sz, lp, cbsz);
  544. LocalFreeAndNull(&lp);
  545. }
  546. }
  547. break;
  548. }
  549. lpListItem = lpListItem->lpNext;
  550. }
  551. }
  552. }