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.

500 lines
16 KiB

  1. // qryitem.cpp - CQueryItem class
  2. #include "stdafx.h"
  3. #include "scopenode.h"
  4. #include "namemap.h"
  5. #include "qryitem.h"
  6. #include <algorithm>
  7. extern HWND g_hwndMain;
  8. UINT CQueryItem::m_cfDisplayName = RegisterClipboardFormat(TEXT("CCF_DISPLAY_NAME"));
  9. UINT CQueryItem::m_cfSnapInClsid = RegisterClipboardFormat(TEXT("CCF_SNAPIN_CLSID"));
  10. UINT CQueryItem::m_cfNodeType = RegisterClipboardFormat(TEXT("CCF_NODETYPE"));
  11. UINT CQueryItem::m_cfszNodeType = RegisterClipboardFormat(TEXT("CCF_SZNODETYPE"));
  12. // {68D2DFD9-86A7-4964-8263-BA025C358992}
  13. static const GUID GUID_QueryItem =
  14. { 0x68d2dfd9, 0x86a7, 0x4964, { 0x82, 0x63, 0xba, 0x2, 0x5c, 0x35, 0x89, 0x92 } };
  15. /////////////////////////////////////////////////////////////////////////////////////////////
  16. // CQueryItem
  17. HRESULT CQueryItem::Initialize(CQueryableNode* pQueryNode, CRowItem* pRowItem)
  18. {
  19. VALIDATE_POINTER( pQueryNode );
  20. VALIDATE_POINTER( pRowItem );
  21. m_spQueryNode = pQueryNode;
  22. m_pRowItem = new CRowItem(*pRowItem);
  23. if (m_pRowItem == NULL) return E_OUTOFMEMORY;
  24. return S_OK;
  25. }
  26. /////////////////////////////////////////////////////////////////////////////////////////////////
  27. // Notification handlers
  28. BEGIN_NOTIFY_MAP(CQueryItem)
  29. ON_SELECT()
  30. ON_DBLCLICK()
  31. ON_NOTIFY(MMCN_CONTEXTHELP, OnHelp)
  32. END_NOTIFY_MAP()
  33. HRESULT CQueryItem::OnHelp(LPCONSOLE2 pConsole, LPARAM /*arg*/, LPARAM /*param*/)
  34. {
  35. VALIDATE_POINTER( pConsole );
  36. tstring strHelpFile = _T("");
  37. tstring strHelpTopic = _T("");
  38. tstring strHelpFull = _T("");
  39. strHelpFile = StrLoadString(IDS_HELPFILE);
  40. if( strHelpFile.empty() ) return E_FAIL;
  41. // Special Hack to get a different help topic for the first two nodes.
  42. int nNodeID = m_spQueryNode->GetNodeID();
  43. switch( nNodeID )
  44. {
  45. case 2:
  46. {
  47. // Users Node
  48. strHelpTopic = StrLoadString(IDS_USERSHELPTOPIC);
  49. break;
  50. }
  51. case 3:
  52. {
  53. // Printers Node
  54. strHelpTopic = StrLoadString(IDS_PRINTERSHELPTOPIC);
  55. break;
  56. }
  57. default:
  58. {
  59. strHelpTopic = StrLoadString(IDS_DEFAULTHELPTOPIC);
  60. break;
  61. }
  62. }
  63. if( strHelpTopic.empty() ) return E_FAIL;
  64. // Build path to %systemroot%\help
  65. TCHAR szWindowsDir[MAX_PATH+1] = {0};
  66. UINT nSize = GetSystemWindowsDirectory( szWindowsDir, MAX_PATH );
  67. if( nSize == 0 || nSize > MAX_PATH )
  68. {
  69. return E_FAIL;
  70. }
  71. strHelpFull = szWindowsDir;
  72. strHelpFull += _T("\\Help\\");
  73. strHelpFull += strHelpFile;
  74. strHelpFull += _T("::/");
  75. strHelpFull += strHelpTopic;
  76. // Show the Help topic
  77. CComQIPtr<IDisplayHelp> spHelp = pConsole;
  78. if( !spHelp ) return E_NOINTERFACE;
  79. return spHelp->ShowTopic( (LPTSTR)strHelpFull.c_str() );
  80. }
  81. HRESULT CQueryItem::OnSelect(LPCONSOLE2 pConsole, BOOL bSelect, BOOL bScope)
  82. {
  83. VALIDATE_POINTER( pConsole );
  84. ASSERT(!bScope);
  85. if( bSelect )
  86. {
  87. CComPtr<IConsoleVerb> pConsVerb;
  88. pConsole->QueryConsoleVerb(&pConsVerb);
  89. ASSERT(pConsVerb != NULL);
  90. if (pConsVerb != NULL)
  91. {
  92. // Row item has class display name, so get internal name from class map
  93. DisplayNameMap* pNameMap = DisplayNames::GetClassMap();
  94. if (pNameMap == NULL)
  95. return E_FAIL;
  96. ASSERT(m_pRowItem != NULL && m_pRowItem->size() >= ROWITEM_USER_INDEX);
  97. ASSERT(m_spQueryNode != NULL);
  98. LPCWSTR pszClass = pNameMap->GetInternalName((*m_pRowItem)[ROWITEM_CLASS_INDEX]);
  99. // Get menu items for this class from the owning query node
  100. int iDefault;
  101. BOOL bPropertyMenu;
  102. reinterpret_cast<CQueryNode*>(m_pRowItem->GetOwnerParam())->GetClassMenuItems(pszClass, m_vMenus, &iDefault, &bPropertyMenu);
  103. // if property menu enabled
  104. if (bPropertyMenu)
  105. {
  106. // Enable property button and menu item
  107. pConsVerb->SetVerbState(MMC_VERB_PROPERTIES, ENABLED, TRUE);
  108. pConsVerb->SetVerbState(MMC_VERB_PROPERTIES, HIDDEN, FALSE);
  109. // if no default menu item defined, make properties verb the default
  110. pConsVerb->SetDefaultVerb( (iDefault >= 0) ? MMC_VERB_NONE : MMC_VERB_PROPERTIES);
  111. }
  112. }
  113. }
  114. return S_OK;
  115. }
  116. HRESULT CQueryItem::OnDblClick(LPCONSOLE2 pConsole)
  117. {
  118. VALIDATE_POINTER(pConsole);
  119. // Row item has class display name, so get internal name from class map
  120. DisplayNameMap* pNameMap = DisplayNames::GetClassMap();
  121. if (pNameMap == NULL)
  122. return E_FAIL;
  123. ASSERT(m_pRowItem != NULL && m_pRowItem->size() >= ROWITEM_USER_INDEX);
  124. ASSERT(m_spQueryNode != NULL);
  125. LPCWSTR pszClass = pNameMap->GetInternalName((*m_pRowItem)[ROWITEM_CLASS_INDEX]);
  126. // Get menu items for this class from the owning query node
  127. int iDefault;
  128. BOOL bPropMenu;
  129. CQueryNode* pQueryNode = reinterpret_cast<CQueryNode*>(m_pRowItem->GetOwnerParam());
  130. if( !pQueryNode ) return E_FAIL;
  131. pQueryNode->GetClassMenuItems(pszClass, m_vMenus, &iDefault, &bPropMenu);
  132. // if no default menu item, return
  133. if (iDefault < 0)
  134. return S_FALSE;
  135. // if Active directory command, create AD menu extension
  136. if (m_vMenus[iDefault]->MenuType() == MENUTYPE_ACTDIR)
  137. {
  138. // Create a directory extension object and use it to get the actual menu cmds for the selected object
  139. // (we might have one already if AddMenuItems was called before)
  140. if (m_pADExt == NULL)
  141. m_pADExt = new CActDirExt();
  142. if( !m_pADExt ) return E_OUTOFMEMORY;
  143. HRESULT hr = m_pADExt->Initialize(pszClass, m_pRowItem->GetObjPath());
  144. RETURN_ON_FAILURE(hr);
  145. menu_vector vADMenus;
  146. hr = m_pADExt->GetMenuItems(vADMenus);
  147. RETURN_ON_FAILURE(hr);
  148. if( m_vMenus.size() <= iDefault )
  149. {
  150. return E_FAIL;
  151. }
  152. LPCWSTR pszName = static_cast<CActDirMenuCmd*>((CMenuCmd*)m_vMenus[iDefault])->ADName();
  153. LPCWSTR pszNoLocName = static_cast<CActDirMenuCmd*>((CMenuCmd*)m_vMenus[iDefault])->ADNoLocName();
  154. if( !pszName || !pszNoLocName ) return E_FAIL;
  155. // if the default command is not provided by the extension, return
  156. menu_vector::iterator iter;
  157. for( iter = vADMenus.begin(); iter != vADMenus.end(); iter++ )
  158. {
  159. if( _tcslen(pszNoLocName) )
  160. {
  161. if( _tcscmp(iter->strNoLoc.c_str(),pszNoLocName) == 0 )
  162. break;
  163. }
  164. else if( _tcscmp(iter->strPlain.c_str(), pszName) == 0 )
  165. {
  166. break;
  167. }
  168. }
  169. if( iter == vADMenus.end() )
  170. {
  171. return S_FALSE;
  172. }
  173. }
  174. // Execute the command as though it had been selected
  175. return MenuCommand(pConsole, iDefault);
  176. }
  177. HRESULT CQueryItem::AddMenuItems(LPCONTEXTMENUCALLBACK pCallback, long* plAllowed)
  178. {
  179. VALIDATE_POINTER( pCallback );
  180. VALIDATE_POINTER( plAllowed );
  181. if( !m_spQueryNode || !m_pRowItem ) return E_FAIL;
  182. HRESULT hr = S_OK;
  183. if (!(*plAllowed & CCM_INSERTIONALLOWED_TOP))
  184. return S_OK;
  185. CComQIPtr<IContextMenuCallback2> spContext2 = pCallback;
  186. if( !spContext2 ) return E_NOINTERFACE;
  187. ASSERT( m_pRowItem->size() >= ROWITEM_USER_INDEX );
  188. //--------------------------- *** HACK ALERT *** -----------------------------------------
  189. // One or more AD menu extensions allow window message processing while initializing
  190. // and getting menu items. This causes reentrancy problems because MMC message handlers
  191. // can execute before this method returns. Specifically, the following can happen:
  192. //
  193. // 1. The user right clicks in a taskpad list while the focus is elsewhere.
  194. // 2. The right button down event causes MMC to call this method to update task buttons.
  195. // 3. An AD menu extn processes messages allowing the button up event to go to MMC.
  196. // 4. MMC sees this as a context menu event and calls this method recursively.
  197. // 5. An AV occurs in nodemgr because a deleted COnContextMenu object is referenced.
  198. //
  199. // This can be prevented by not processing menu item requests when the right button is down.
  200. // The only time this occurs is during the above scenario. The only ill effect is that the
  201. // task buttons are not enabled until the mouse button up occurs when MMC gets the menu
  202. // items again.
  203. //-----------------------------------------------------------------------------------------
  204. if (GetKeyState(VK_RBUTTON) < 0)
  205. return S_OK;
  206. DisplayNameMap* pNameMap = DisplayNames::GetClassMap();
  207. if (pNameMap == NULL)
  208. return E_FAIL;
  209. LPCWSTR pszClass = pNameMap->GetInternalName((*m_pRowItem)[ROWITEM_CLASS_INDEX]);
  210. // Get menu items for this class from the owning query node
  211. int iDefault = 0;
  212. BOOL bPropertyMenu;
  213. reinterpret_cast<CQueryNode*>(m_pRowItem->GetOwnerParam())->GetClassMenuItems(pszClass, m_vMenus, &iDefault, &bPropertyMenu);
  214. // Create a directory extension object and use it to get the actual menu cmds for the selected object
  215. // (we might have one already if AddMenuItems was called before)
  216. if (m_pADExt == NULL)
  217. m_pADExt = new CActDirExt();
  218. if( !m_pADExt ) return E_OUTOFMEMORY;
  219. hr = m_pADExt->Initialize(pszClass, m_pRowItem->GetObjPath());
  220. RETURN_ON_FAILURE(hr);
  221. menu_vector vADMenus;
  222. hr = m_pADExt->GetMenuItems(vADMenus);
  223. RETURN_ON_FAILURE(hr);
  224. ASSERT(vADMenus.size() > 0);
  225. ASSERT(vADMenus.begin() != vADMenus.end());
  226. menucmd_vector::iterator itMenu;
  227. long lCmdID = 0;
  228. for (itMenu = m_vMenus.begin(); itMenu != m_vMenus.end(); ++itMenu, ++lCmdID)
  229. {
  230. // if AD menu cmd and not enabled by the selected object, skip it
  231. if ( (*itMenu)->MenuType() == MENUTYPE_ACTDIR )
  232. {
  233. BOOL bFound = FALSE;
  234. menu_vector::iterator iter = vADMenus.begin();
  235. while(iter != vADMenus.end())
  236. {
  237. LPCWSTR pszName = static_cast<CActDirMenuCmd*>((CMenuCmd*)(*itMenu))->ADName();
  238. LPCWSTR pszNoLocName = static_cast<CActDirMenuCmd*>((CMenuCmd*)(*itMenu))->ADNoLocName();
  239. if( pszNoLocName && wcslen(pszNoLocName) )
  240. {
  241. if( _tcscmp(iter->strNoLoc.c_str(), pszNoLocName) == 0 )
  242. {
  243. bFound = TRUE;
  244. break;
  245. }
  246. }
  247. else if( pszName && _tcscmp(iter->strPlain.c_str(), pszName) == 0 )
  248. {
  249. bFound = TRUE;
  250. break;
  251. }
  252. iter++;
  253. }
  254. if (!bFound)
  255. {
  256. continue;
  257. }
  258. }
  259. CONTEXTMENUITEM2 item;
  260. OLECHAR szGuid[50] = {0};
  261. ::StringFromGUID2((*itMenu)->NoLocID(), szGuid, 50);
  262. item.strName = const_cast<LPWSTR>((*itMenu)->Name());
  263. item.strStatusBarText = L"";
  264. item.lCommandID = lCmdID;
  265. item.lInsertionPointID = CCM_INSERTIONPOINTID_PRIMARY_TOP;
  266. item.fFlags = 0;
  267. item.fSpecialFlags = (lCmdID == iDefault) ? CCM_SPECIAL_DEFAULT_ITEM : 0;
  268. item.strLanguageIndependentName = szGuid;
  269. hr = spContext2->AddItem(&item);
  270. ASSERT(SUCCEEDED(hr));
  271. }
  272. return hr;
  273. }
  274. HRESULT
  275. CQueryItem::QueryPagesFor()
  276. {
  277. ASSERT(m_pRowItem != NULL && m_pRowItem->size() >= ROWITEM_USER_INDEX);
  278. ASSERT(m_spQueryNode != NULL);
  279. // Row item has class display name, so get internal name from class map
  280. DisplayNameMap* pNameMap = DisplayNames::GetClassMap();
  281. if (pNameMap == NULL)
  282. return E_FAIL;
  283. LPCWSTR pszClass = pNameMap->GetInternalName((*m_pRowItem)[ROWITEM_CLASS_INDEX]);
  284. // Create a directory extension object
  285. // (we might have one already if AddMenuItems was called before)
  286. if (m_pADExt == NULL)
  287. m_pADExt = new CActDirExt();
  288. ASSERT(m_pADExt != NULL);
  289. if (m_pADExt == NULL)
  290. return E_OUTOFMEMORY;
  291. HRESULT hr = m_pADExt->Initialize(pszClass, m_pRowItem->GetObjPath());
  292. RETURN_ON_FAILURE(hr);
  293. hpage_vector vhPages;
  294. hr = m_pADExt->GetPropertyPages(vhPages);
  295. if (SUCCEEDED(hr) && vhPages.size() > 0)
  296. {
  297. CPropertySheet sheet;
  298. // Set title to name of item
  299. // Can't use SetTitle becuase if wrongly asserts (pszText == NULL)
  300. sheet.m_psh.pszCaption = (*m_pRowItem)[ROWITEM_NAME_INDEX];
  301. sheet.m_psh.dwFlags |= PSH_PROPTITLE;
  302. hpage_vector::iterator itPage;
  303. for (itPage = vhPages.begin(); itPage != vhPages.end(); ++itPage)
  304. {
  305. BOOL bStat = sheet.AddPage(*itPage);
  306. ASSERT(bStat);
  307. }
  308. sheet.DoModal(g_hwndMain);
  309. }
  310. return S_FALSE;
  311. }
  312. class CRefreshCallback : public CEventCallback
  313. {
  314. public:
  315. CRefreshCallback(HANDLE hProcess, CQueryableNode* pQueryNode)
  316. : m_hProcess(hProcess), m_spQueryNode(pQueryNode) {}
  317. virtual void Execute()
  318. {
  319. if( m_spQueryNode )
  320. {
  321. m_spQueryNode->OnRefresh(NULL);
  322. }
  323. CloseHandle(m_hProcess);
  324. }
  325. HANDLE m_hProcess;
  326. CQueryableNodePtr m_spQueryNode;
  327. };
  328. HRESULT
  329. CQueryItem::MenuCommand(LPCONSOLE2 pConsole, long lCommand)
  330. {
  331. VALIDATE_POINTER(pConsole);
  332. ASSERT( lCommand < m_vMenus.size() && lCommand >= 0 );
  333. if( lCommand >= m_vMenus.size() || lCommand < 0 )
  334. return E_INVALIDARG;
  335. HRESULT hr = E_FAIL;
  336. switch (m_vMenus[lCommand]->MenuType())
  337. {
  338. case MENUTYPE_SHELL:
  339. {
  340. // Create a query Lookup object to translate the command parameters
  341. CQueryLookup lookup(m_spQueryNode, m_pRowItem);
  342. HANDLE hProcess = NULL;
  343. hr = static_cast<CShellMenuCmd*>((CMenuCmd*)m_vMenus[lCommand])->Execute(&lookup, &hProcess);
  344. // if process started and auto-refresh wanted, setup event-triggered callback
  345. if (SUCCEEDED(hr) && hProcess != NULL && m_vMenus[lCommand]->IsAutoRefresh())
  346. {
  347. CallbackOnEvent(hProcess, new CRefreshCallback(hProcess, m_spQueryNode));
  348. }
  349. break;
  350. }
  351. case MENUTYPE_ACTDIR:
  352. {
  353. ASSERT(m_pADExt != NULL);
  354. BOMMENU bmMenu;
  355. bmMenu.strPlain = static_cast<CActDirMenuCmd*>((CMenuCmd*)m_vMenus[lCommand])->ADName();
  356. bmMenu.strNoLoc = static_cast<CActDirMenuCmd*>((CMenuCmd*)m_vMenus[lCommand])->ADNoLocName();
  357. hr = m_pADExt->Execute(&bmMenu);
  358. // if commans should auto-refresh, do it now
  359. if (SUCCEEDED(hr) && m_vMenus[lCommand]->IsAutoRefresh())
  360. {
  361. ASSERT(m_spQueryNode != NULL);
  362. m_spQueryNode->OnRefresh(NULL);
  363. }
  364. break;
  365. }
  366. default:
  367. ASSERT(0 && L"Unhandled menu command type");
  368. }
  369. return hr;
  370. }
  371. HRESULT CQueryItem::GetDataImpl(UINT cf, HGLOBAL* phGlobal)
  372. {
  373. VALIDATE_POINTER( phGlobal );
  374. HRESULT hr = DV_E_FORMATETC;
  375. if (cf == m_cfDisplayName)
  376. {
  377. hr = DataToGlobal(phGlobal, (*m_pRowItem)[0], wcslen((*m_pRowItem)[0]) * sizeof(WCHAR));
  378. }
  379. else if (cf == m_cfSnapInClsid)
  380. {
  381. hr = DataToGlobal(phGlobal, &CLSID_BOMSnapIn, sizeof(GUID));
  382. }
  383. else if (cf == m_cfNodeType)
  384. {
  385. hr = DataToGlobal(phGlobal, &GUID_QueryItem, sizeof(GUID));
  386. }
  387. else if (cf == m_cfszNodeType)
  388. {
  389. WCHAR szGuid[GUID_STRING_LEN+1];
  390. StringFromGUID2(GUID_QueryItem, szGuid, GUID_STRING_LEN+1);
  391. hr = DataToGlobal(phGlobal, szGuid, GUID_STRING_SIZE);
  392. }
  393. return hr;
  394. }