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
500 lines
16 KiB
// qryitem.cpp - CQueryItem class
|
|
|
|
#include "stdafx.h"
|
|
#include "scopenode.h"
|
|
#include "namemap.h"
|
|
#include "qryitem.h"
|
|
|
|
#include <algorithm>
|
|
|
|
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<IDisplayHelp> 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<IConsoleVerb> 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<CQueryNode*>(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<CQueryNode*>(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<CActDirMenuCmd*>((CMenuCmd*)m_vMenus[iDefault])->ADName();
|
|
LPCWSTR pszNoLocName = static_cast<CActDirMenuCmd*>((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<IContextMenuCallback2> 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<CQueryNode*>(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<CActDirMenuCmd*>((CMenuCmd*)(*itMenu))->ADName();
|
|
LPCWSTR pszNoLocName = static_cast<CActDirMenuCmd*>((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<LPWSTR>((*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<CShellMenuCmd*>((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<CActDirMenuCmd*>((CMenuCmd*)m_vMenus[lCommand])->ADName();
|
|
bmMenu.strNoLoc = static_cast<CActDirMenuCmd*>((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;
|
|
}
|
|
|