// qryitem.cpp - CQueryItem class #include "stdafx.h" #include "scopenode.h" #include "namemap.h" #include "qryitem.h" #include extern HWND g_hwndMain; UINT CQueryItem::m_cfDisplayName = RegisterClipboardFormat(TEXT("CCF_DISPLAY_NAME")); UINT CQueryItem::m_cfSnapInClsid = RegisterClipboardFormat(TEXT("CCF_SNAPIN_CLSID")); UINT CQueryItem::m_cfNodeType = RegisterClipboardFormat(TEXT("CCF_NODETYPE")); UINT CQueryItem::m_cfszNodeType = RegisterClipboardFormat(TEXT("CCF_SZNODETYPE")); // {68D2DFD9-86A7-4964-8263-BA025C358992} static const GUID GUID_QueryItem = { 0x68d2dfd9, 0x86a7, 0x4964, { 0x82, 0x63, 0xba, 0x2, 0x5c, 0x35, 0x89, 0x92 } }; ///////////////////////////////////////////////////////////////////////////////////////////// // CQueryItem HRESULT CQueryItem::Initialize(CQueryableNode* pQueryNode, CRowItem* pRowItem) { VALIDATE_POINTER( pQueryNode ); VALIDATE_POINTER( pRowItem ); m_spQueryNode = pQueryNode; m_pRowItem = new CRowItem(*pRowItem); if (m_pRowItem == NULL) return E_OUTOFMEMORY; return S_OK; } ///////////////////////////////////////////////////////////////////////////////////////////////// // Notification handlers BEGIN_NOTIFY_MAP(CQueryItem) ON_SELECT() ON_DBLCLICK() ON_NOTIFY(MMCN_CONTEXTHELP, OnHelp) END_NOTIFY_MAP() HRESULT CQueryItem::OnHelp(LPCONSOLE2 pConsole, LPARAM /*arg*/, LPARAM /*param*/) { VALIDATE_POINTER( pConsole ); tstring strHelpFile = _T(""); tstring strHelpTopic = _T(""); tstring strHelpFull = _T(""); strHelpFile = StrLoadString(IDS_HELPFILE); if( strHelpFile.empty() ) return E_FAIL; // Special Hack to get a different help topic for the first two nodes. int nNodeID = m_spQueryNode->GetNodeID(); switch( nNodeID ) { case 2: { // Users Node strHelpTopic = StrLoadString(IDS_USERSHELPTOPIC); break; } case 3: { // Printers Node strHelpTopic = StrLoadString(IDS_PRINTERSHELPTOPIC); break; } default: { strHelpTopic = StrLoadString(IDS_DEFAULTHELPTOPIC); break; } } if( strHelpTopic.empty() ) return E_FAIL; // Build path to %systemroot%\help TCHAR szWindowsDir[MAX_PATH+1] = {0}; UINT nSize = GetSystemWindowsDirectory( szWindowsDir, MAX_PATH ); if( nSize == 0 || nSize > MAX_PATH ) { return E_FAIL; } strHelpFull = szWindowsDir; strHelpFull += _T("\\Help\\"); strHelpFull += strHelpFile; strHelpFull += _T("::/"); strHelpFull += strHelpTopic; // Show the Help topic CComQIPtr spHelp = pConsole; if( !spHelp ) return E_NOINTERFACE; return spHelp->ShowTopic( (LPTSTR)strHelpFull.c_str() ); } HRESULT CQueryItem::OnSelect(LPCONSOLE2 pConsole, BOOL bSelect, BOOL bScope) { VALIDATE_POINTER( pConsole ); ASSERT(!bScope); if( bSelect ) { CComPtr pConsVerb; pConsole->QueryConsoleVerb(&pConsVerb); ASSERT(pConsVerb != NULL); if (pConsVerb != NULL) { // Row item has class display name, so get internal name from class map DisplayNameMap* pNameMap = DisplayNames::GetClassMap(); if (pNameMap == NULL) return E_FAIL; ASSERT(m_pRowItem != NULL && m_pRowItem->size() >= ROWITEM_USER_INDEX); ASSERT(m_spQueryNode != NULL); LPCWSTR pszClass = pNameMap->GetInternalName((*m_pRowItem)[ROWITEM_CLASS_INDEX]); // Get menu items for this class from the owning query node int iDefault; BOOL bPropertyMenu; reinterpret_cast(m_pRowItem->GetOwnerParam())->GetClassMenuItems(pszClass, m_vMenus, &iDefault, &bPropertyMenu); // if property menu enabled if (bPropertyMenu) { // Enable property button and menu item pConsVerb->SetVerbState(MMC_VERB_PROPERTIES, ENABLED, TRUE); pConsVerb->SetVerbState(MMC_VERB_PROPERTIES, HIDDEN, FALSE); // if no default menu item defined, make properties verb the default pConsVerb->SetDefaultVerb( (iDefault >= 0) ? MMC_VERB_NONE : MMC_VERB_PROPERTIES); } } } return S_OK; } HRESULT CQueryItem::OnDblClick(LPCONSOLE2 pConsole) { VALIDATE_POINTER(pConsole); // Row item has class display name, so get internal name from class map DisplayNameMap* pNameMap = DisplayNames::GetClassMap(); if (pNameMap == NULL) return E_FAIL; ASSERT(m_pRowItem != NULL && m_pRowItem->size() >= ROWITEM_USER_INDEX); ASSERT(m_spQueryNode != NULL); LPCWSTR pszClass = pNameMap->GetInternalName((*m_pRowItem)[ROWITEM_CLASS_INDEX]); // Get menu items for this class from the owning query node int iDefault; BOOL bPropMenu; CQueryNode* pQueryNode = reinterpret_cast(m_pRowItem->GetOwnerParam()); if( !pQueryNode ) return E_FAIL; pQueryNode->GetClassMenuItems(pszClass, m_vMenus, &iDefault, &bPropMenu); // if no default menu item, return if (iDefault < 0) return S_FALSE; // if Active directory command, create AD menu extension if (m_vMenus[iDefault]->MenuType() == MENUTYPE_ACTDIR) { // Create a directory extension object and use it to get the actual menu cmds for the selected object // (we might have one already if AddMenuItems was called before) if (m_pADExt == NULL) m_pADExt = new CActDirExt(); if( !m_pADExt ) return E_OUTOFMEMORY; HRESULT hr = m_pADExt->Initialize(pszClass, m_pRowItem->GetObjPath()); RETURN_ON_FAILURE(hr); menu_vector vADMenus; hr = m_pADExt->GetMenuItems(vADMenus); RETURN_ON_FAILURE(hr); if( m_vMenus.size() <= iDefault ) { return E_FAIL; } LPCWSTR pszName = static_cast((CMenuCmd*)m_vMenus[iDefault])->ADName(); LPCWSTR pszNoLocName = static_cast((CMenuCmd*)m_vMenus[iDefault])->ADNoLocName(); if( !pszName || !pszNoLocName ) return E_FAIL; // if the default command is not provided by the extension, return menu_vector::iterator iter; for( iter = vADMenus.begin(); iter != vADMenus.end(); iter++ ) { if( _tcslen(pszNoLocName) ) { if( _tcscmp(iter->strNoLoc.c_str(),pszNoLocName) == 0 ) break; } else if( _tcscmp(iter->strPlain.c_str(), pszName) == 0 ) { break; } } if( iter == vADMenus.end() ) { return S_FALSE; } } // Execute the command as though it had been selected return MenuCommand(pConsole, iDefault); } HRESULT CQueryItem::AddMenuItems(LPCONTEXTMENUCALLBACK pCallback, long* plAllowed) { VALIDATE_POINTER( pCallback ); VALIDATE_POINTER( plAllowed ); if( !m_spQueryNode || !m_pRowItem ) return E_FAIL; HRESULT hr = S_OK; if (!(*plAllowed & CCM_INSERTIONALLOWED_TOP)) return S_OK; CComQIPtr spContext2 = pCallback; if( !spContext2 ) return E_NOINTERFACE; ASSERT( m_pRowItem->size() >= ROWITEM_USER_INDEX ); //--------------------------- *** HACK ALERT *** ----------------------------------------- // One or more AD menu extensions allow window message processing while initializing // and getting menu items. This causes reentrancy problems because MMC message handlers // can execute before this method returns. Specifically, the following can happen: // // 1. The user right clicks in a taskpad list while the focus is elsewhere. // 2. The right button down event causes MMC to call this method to update task buttons. // 3. An AD menu extn processes messages allowing the button up event to go to MMC. // 4. MMC sees this as a context menu event and calls this method recursively. // 5. An AV occurs in nodemgr because a deleted COnContextMenu object is referenced. // // This can be prevented by not processing menu item requests when the right button is down. // The only time this occurs is during the above scenario. The only ill effect is that the // task buttons are not enabled until the mouse button up occurs when MMC gets the menu // items again. //----------------------------------------------------------------------------------------- if (GetKeyState(VK_RBUTTON) < 0) return S_OK; DisplayNameMap* pNameMap = DisplayNames::GetClassMap(); if (pNameMap == NULL) return E_FAIL; LPCWSTR pszClass = pNameMap->GetInternalName((*m_pRowItem)[ROWITEM_CLASS_INDEX]); // Get menu items for this class from the owning query node int iDefault = 0; BOOL bPropertyMenu; reinterpret_cast(m_pRowItem->GetOwnerParam())->GetClassMenuItems(pszClass, m_vMenus, &iDefault, &bPropertyMenu); // Create a directory extension object and use it to get the actual menu cmds for the selected object // (we might have one already if AddMenuItems was called before) if (m_pADExt == NULL) m_pADExt = new CActDirExt(); if( !m_pADExt ) return E_OUTOFMEMORY; hr = m_pADExt->Initialize(pszClass, m_pRowItem->GetObjPath()); RETURN_ON_FAILURE(hr); menu_vector vADMenus; hr = m_pADExt->GetMenuItems(vADMenus); RETURN_ON_FAILURE(hr); ASSERT(vADMenus.size() > 0); ASSERT(vADMenus.begin() != vADMenus.end()); menucmd_vector::iterator itMenu; long lCmdID = 0; for (itMenu = m_vMenus.begin(); itMenu != m_vMenus.end(); ++itMenu, ++lCmdID) { // if AD menu cmd and not enabled by the selected object, skip it if ( (*itMenu)->MenuType() == MENUTYPE_ACTDIR ) { BOOL bFound = FALSE; menu_vector::iterator iter = vADMenus.begin(); while(iter != vADMenus.end()) { LPCWSTR pszName = static_cast((CMenuCmd*)(*itMenu))->ADName(); LPCWSTR pszNoLocName = static_cast((CMenuCmd*)(*itMenu))->ADNoLocName(); if( pszNoLocName && wcslen(pszNoLocName) ) { if( _tcscmp(iter->strNoLoc.c_str(), pszNoLocName) == 0 ) { bFound = TRUE; break; } } else if( pszName && _tcscmp(iter->strPlain.c_str(), pszName) == 0 ) { bFound = TRUE; break; } iter++; } if (!bFound) { continue; } } CONTEXTMENUITEM2 item; OLECHAR szGuid[50] = {0}; ::StringFromGUID2((*itMenu)->NoLocID(), szGuid, 50); item.strName = const_cast((*itMenu)->Name()); item.strStatusBarText = L""; item.lCommandID = lCmdID; item.lInsertionPointID = CCM_INSERTIONPOINTID_PRIMARY_TOP; item.fFlags = 0; item.fSpecialFlags = (lCmdID == iDefault) ? CCM_SPECIAL_DEFAULT_ITEM : 0; item.strLanguageIndependentName = szGuid; hr = spContext2->AddItem(&item); ASSERT(SUCCEEDED(hr)); } return hr; } HRESULT CQueryItem::QueryPagesFor() { ASSERT(m_pRowItem != NULL && m_pRowItem->size() >= ROWITEM_USER_INDEX); ASSERT(m_spQueryNode != NULL); // Row item has class display name, so get internal name from class map DisplayNameMap* pNameMap = DisplayNames::GetClassMap(); if (pNameMap == NULL) return E_FAIL; LPCWSTR pszClass = pNameMap->GetInternalName((*m_pRowItem)[ROWITEM_CLASS_INDEX]); // Create a directory extension object // (we might have one already if AddMenuItems was called before) if (m_pADExt == NULL) m_pADExt = new CActDirExt(); ASSERT(m_pADExt != NULL); if (m_pADExt == NULL) return E_OUTOFMEMORY; HRESULT hr = m_pADExt->Initialize(pszClass, m_pRowItem->GetObjPath()); RETURN_ON_FAILURE(hr); hpage_vector vhPages; hr = m_pADExt->GetPropertyPages(vhPages); if (SUCCEEDED(hr) && vhPages.size() > 0) { CPropertySheet sheet; // Set title to name of item // Can't use SetTitle becuase if wrongly asserts (pszText == NULL) sheet.m_psh.pszCaption = (*m_pRowItem)[ROWITEM_NAME_INDEX]; sheet.m_psh.dwFlags |= PSH_PROPTITLE; hpage_vector::iterator itPage; for (itPage = vhPages.begin(); itPage != vhPages.end(); ++itPage) { BOOL bStat = sheet.AddPage(*itPage); ASSERT(bStat); } sheet.DoModal(g_hwndMain); } return S_FALSE; } class CRefreshCallback : public CEventCallback { public: CRefreshCallback(HANDLE hProcess, CQueryableNode* pQueryNode) : m_hProcess(hProcess), m_spQueryNode(pQueryNode) {} virtual void Execute() { if( m_spQueryNode ) { m_spQueryNode->OnRefresh(NULL); } CloseHandle(m_hProcess); } HANDLE m_hProcess; CQueryableNodePtr m_spQueryNode; }; HRESULT CQueryItem::MenuCommand(LPCONSOLE2 pConsole, long lCommand) { VALIDATE_POINTER(pConsole); ASSERT( lCommand < m_vMenus.size() && lCommand >= 0 ); if( lCommand >= m_vMenus.size() || lCommand < 0 ) return E_INVALIDARG; HRESULT hr = E_FAIL; switch (m_vMenus[lCommand]->MenuType()) { case MENUTYPE_SHELL: { // Create a query Lookup object to translate the command parameters CQueryLookup lookup(m_spQueryNode, m_pRowItem); HANDLE hProcess = NULL; hr = static_cast((CMenuCmd*)m_vMenus[lCommand])->Execute(&lookup, &hProcess); // if process started and auto-refresh wanted, setup event-triggered callback if (SUCCEEDED(hr) && hProcess != NULL && m_vMenus[lCommand]->IsAutoRefresh()) { CallbackOnEvent(hProcess, new CRefreshCallback(hProcess, m_spQueryNode)); } break; } case MENUTYPE_ACTDIR: { ASSERT(m_pADExt != NULL); BOMMENU bmMenu; bmMenu.strPlain = static_cast((CMenuCmd*)m_vMenus[lCommand])->ADName(); bmMenu.strNoLoc = static_cast((CMenuCmd*)m_vMenus[lCommand])->ADNoLocName(); hr = m_pADExt->Execute(&bmMenu); // if commans should auto-refresh, do it now if (SUCCEEDED(hr) && m_vMenus[lCommand]->IsAutoRefresh()) { ASSERT(m_spQueryNode != NULL); m_spQueryNode->OnRefresh(NULL); } break; } default: ASSERT(0 && L"Unhandled menu command type"); } return hr; } HRESULT CQueryItem::GetDataImpl(UINT cf, HGLOBAL* phGlobal) { VALIDATE_POINTER( phGlobal ); HRESULT hr = DV_E_FORMATETC; if (cf == m_cfDisplayName) { hr = DataToGlobal(phGlobal, (*m_pRowItem)[0], wcslen((*m_pRowItem)[0]) * sizeof(WCHAR)); } else if (cf == m_cfSnapInClsid) { hr = DataToGlobal(phGlobal, &CLSID_BOMSnapIn, sizeof(GUID)); } else if (cf == m_cfNodeType) { hr = DataToGlobal(phGlobal, &GUID_QueryItem, sizeof(GUID)); } else if (cf == m_cfszNodeType) { WCHAR szGuid[GUID_STRING_LEN+1]; StringFromGUID2(GUID_QueryItem, szGuid, GUID_STRING_LEN+1); hr = DataToGlobal(phGlobal, szGuid, GUID_STRING_SIZE); } return hr; }