//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1998 - 1999 // // File: compbas_.cpp // //-------------------------------------------------------------------------- // initialize to the thread ID of the thread that loads the snapin // that is the main thread extern DWORD _MainThreadId = ::GetCurrentThreadId(); const TCHAR NODE_TYPES_KEY[] = TEXT("Software\\Microsoft\\MMC\\NodeTypes"); const TCHAR SNAPINS_KEY[] = TEXT("Software\\Microsoft\\MMC\\SnapIns"); const TCHAR g_szNodeType[] = TEXT("NodeType"); const TCHAR g_szNameString[] = TEXT("NameString"); const TCHAR g_szNameStringIndirect[] = TEXT("NameStringIndirect"); const TCHAR g_szStandaloneSnap[] = TEXT("Standalone"); const TCHAR g_szExtensionSnap[] = TEXT("Extension"); const TCHAR g_szNodeTypes[] = TEXT("NodeTypes"); const TCHAR g_szExtensions[] = TEXT("Extensions"); const TCHAR g_szDynamicExtensions[] = TEXT("Dynamic Extensions"); const TCHAR g_szVersion[] = TEXT("Version"); const TCHAR g_szProvider[] = _T("Provider"); const TCHAR g_szAbout[] = _T("About"); HRESULT RegisterSnapin(const GUID* pSnapinCLSID, const GUID* pStaticNodeGUID, const GUID* pAboutGUID, LPCTSTR lpszNameString, LPCTSTR lpszVersion, LPCTSTR lpszProvider, BOOL bExtension, _NODE_TYPE_INFO_ENTRY* pNodeTypeInfoEntryArray, UINT nSnapinNameID) { OLECHAR szSnapinClassID[128] = {0}, szStaticNodeGuid[128] = {0}, szAboutGuid[128] = {0}; ::StringFromGUID2(*pSnapinCLSID,szSnapinClassID,128); ::StringFromGUID2(*pStaticNodeGUID,szStaticNodeGuid,128); ::StringFromGUID2(*pAboutGUID,szAboutGuid,128); CRegKey regkeySnapins; LONG lRes = regkeySnapins.Open(HKEY_LOCAL_MACHINE, SNAPINS_KEY); ASSERT(lRes == ERROR_SUCCESS); if (lRes != ERROR_SUCCESS) return HRESULT_FROM_WIN32(lRes); // failed to open CRegKey regkeyThisSnapin; lRes = regkeyThisSnapin.Create(regkeySnapins, szSnapinClassID); ASSERT(lRes == ERROR_SUCCESS); if (lRes != ERROR_SUCCESS) return HRESULT_FROM_WIN32(lRes); // failed to create lRes = regkeyThisSnapin.SetValue(lpszNameString, g_szNameString); if (lRes != ERROR_SUCCESS) return HRESULT_FROM_WIN32(lRes); // JeffJon 6/12/00 100624: MUI: MMC: Shared Folders snap-in // stores its display information in the registry if (nSnapinNameID != 0) { CString str; WCHAR szModule[_MAX_PATH]; ::GetModuleFileName(AfxGetInstanceHandle(), szModule, _MAX_PATH); str.Format( _T("@%s,-%d"), szModule, nSnapinNameID ); lRes = regkeyThisSnapin.SetValue(str, g_szNameStringIndirect); if (lRes != ERROR_SUCCESS) return HRESULT_FROM_WIN32(lRes); } lRes = regkeyThisSnapin.SetValue(szAboutGuid, g_szAbout); if (lRes != ERROR_SUCCESS) return HRESULT_FROM_WIN32(lRes); lRes = regkeyThisSnapin.SetValue(szStaticNodeGuid, g_szNodeType); if (lRes != ERROR_SUCCESS) return HRESULT_FROM_WIN32(lRes); lRes = regkeyThisSnapin.SetValue(lpszProvider, g_szProvider); if (lRes != ERROR_SUCCESS) return HRESULT_FROM_WIN32(lRes); lRes = regkeyThisSnapin.SetValue(lpszVersion, g_szVersion); if (lRes != ERROR_SUCCESS) return HRESULT_FROM_WIN32(lRes); CRegKey regKeyStandaloneorExtension; lRes = regKeyStandaloneorExtension.Create(regkeyThisSnapin, bExtension ? g_szExtensionSnap : g_szStandaloneSnap); if (lRes != ERROR_SUCCESS) return HRESULT_FROM_WIN32(lRes); CRegKey regKeyNodeTypes; lRes = regKeyNodeTypes.Create(regkeyThisSnapin, g_szNodeTypes); if (lRes != ERROR_SUCCESS) { return HRESULT_FROM_WIN32(lRes); } OLECHAR szNodeGUID[128]; for (_NODE_TYPE_INFO_ENTRY* pCurrEntry = pNodeTypeInfoEntryArray; pCurrEntry->m_pNodeGUID != NULL; pCurrEntry++) { ::StringFromGUID2(*(pCurrEntry->m_pNodeGUID),szNodeGUID,128); CRegKey regKeyNode; lRes = regKeyNode.Create(regKeyNodeTypes, szNodeGUID); if (lRes != ERROR_SUCCESS) { return HRESULT_FROM_WIN32(lRes); } } return HRESULT_FROM_WIN32(lRes); } HRESULT UnregisterSnapin(const GUID* pSnapinCLSID) { OLECHAR szSnapinClassID[128]; ::StringFromGUID2(*pSnapinCLSID,szSnapinClassID,128); CRegKey regkeySnapins; LONG lRes = regkeySnapins.Open(HKEY_LOCAL_MACHINE, SNAPINS_KEY); ASSERT(lRes == ERROR_SUCCESS); if (lRes != ERROR_SUCCESS) { return HRESULT_FROM_WIN32(lRes); // failed to open } lRes = regkeySnapins.RecurseDeleteKey(szSnapinClassID); ASSERT(lRes == ERROR_SUCCESS); return HRESULT_FROM_WIN32(lRes); } HRESULT RegisterNodeType(const GUID* pGuid, LPCTSTR lpszNodeDescription) { OLECHAR szNodeGuid[128]; ::StringFromGUID2(*pGuid,szNodeGuid,128); CRegKey regkeyNodeTypes; LONG lRes = regkeyNodeTypes.Open(HKEY_LOCAL_MACHINE, NODE_TYPES_KEY); ASSERT(lRes == ERROR_SUCCESS); if (lRes != ERROR_SUCCESS) { return HRESULT_FROM_WIN32(lRes); // failed to open } CRegKey regkeyThisNodeType; lRes = regkeyThisNodeType.Create(regkeyNodeTypes, szNodeGuid); ASSERT(lRes == ERROR_SUCCESS); if (lRes != ERROR_SUCCESS) { return HRESULT_FROM_WIN32(lRes); // failed to create } lRes = regkeyThisNodeType.SetValue(lpszNodeDescription); ASSERT(lRes == ERROR_SUCCESS); return HRESULT_FROM_WIN32(lRes); } HRESULT UnregisterNodeType(const GUID* pGuid) { OLECHAR szNodeGuid[128]; ::StringFromGUID2(*pGuid,szNodeGuid,128); CRegKey regkeyNodeTypes; LONG lRes = regkeyNodeTypes.Open(HKEY_LOCAL_MACHINE, NODE_TYPES_KEY); ASSERT(lRes == ERROR_SUCCESS); if (lRes != ERROR_SUCCESS) { return HRESULT_FROM_WIN32(lRes); // failed to open } lRes = regkeyNodeTypes.RecurseDeleteKey(szNodeGuid); ASSERT(lRes == ERROR_SUCCESS); return HRESULT_FROM_WIN32(lRes); } HRESULT RegisterNodeExtension(const GUID* pNodeGuid, LPCTSTR lpszExtensionType, const GUID* pExtensionSnapinCLSID, LPCTSTR lpszDescription, BOOL bDynamic) { OLECHAR szNodeGuid[128], szExtensionSnapinCLSID[128]; ::StringFromGUID2(*pNodeGuid, szNodeGuid,128); ::StringFromGUID2(*pExtensionSnapinCLSID, szExtensionSnapinCLSID,128); // compose full path of key up to the node GUID WCHAR szKeyPath[2048]; wsprintf(szKeyPath, L"%s\\%s", NODE_TYPES_KEY, szNodeGuid); CRegKey regkeyNodeTypesNode; LONG lRes = regkeyNodeTypesNode.Open(HKEY_LOCAL_MACHINE, szKeyPath); ASSERT(lRes == ERROR_SUCCESS); if (lRes != ERROR_SUCCESS) { return HRESULT_FROM_WIN32(lRes); // failed to open } CRegKey regkeyExtensions; lRes = regkeyExtensions.Create(regkeyNodeTypesNode, g_szExtensions); ASSERT(lRes == ERROR_SUCCESS); if (lRes != ERROR_SUCCESS) { return HRESULT_FROM_WIN32(lRes); // failed to create } CRegKey regkeyExtensionType; lRes = regkeyExtensionType.Create(regkeyExtensions, lpszExtensionType); ASSERT(lRes == ERROR_SUCCESS); if (lRes != ERROR_SUCCESS) { return HRESULT_FROM_WIN32(lRes); // failed to create } lRes = regkeyExtensionType.SetValue(lpszDescription, szExtensionSnapinCLSID); ASSERT(lRes == ERROR_SUCCESS); if (lRes != ERROR_SUCCESS) { return HRESULT_FROM_WIN32(lRes); // failed to set value } if (bDynamic) { // create a subkey under the node GUID CRegKey regkeyDynamicExtensions; lRes = regkeyDynamicExtensions.Create(regkeyNodeTypesNode, g_szDynamicExtensions); ASSERT(lRes == ERROR_SUCCESS); if (lRes != ERROR_SUCCESS) return HRESULT_FROM_WIN32(lRes); // failed to create // set value (same value as the extension type above) lRes = regkeyDynamicExtensions.SetValue(lpszDescription, szExtensionSnapinCLSID); ASSERT(lRes == ERROR_SUCCESS); if (lRes != ERROR_SUCCESS) { return HRESULT_FROM_WIN32(lRes); // failed to set value } } return HRESULT_FROM_WIN32(lRes); } HRESULT UnregisterNodeExtension(const GUID* pNodeGuid, LPCTSTR lpszExtensionType, const GUID* pExtensionSnapinCLSID, BOOL bDynamic) { OLECHAR szNodeGuid[128], szExtensionSnapinCLSID[128]; ::StringFromGUID2(*pNodeGuid, szNodeGuid,128); ::StringFromGUID2(*pExtensionSnapinCLSID, szExtensionSnapinCLSID,128); // compose full path of key up to the node GUID WCHAR szKeyPath[2048]; wsprintf(szKeyPath, L"%s\\%s", NODE_TYPES_KEY, szNodeGuid); CRegKey regkeyNodeTypesNode; LONG lRes = regkeyNodeTypesNode.Open(HKEY_LOCAL_MACHINE, szKeyPath); ASSERT(lRes == ERROR_SUCCESS); if (lRes != ERROR_SUCCESS) return HRESULT_FROM_WIN32(lRes); // failed to open lRes = ERROR_SUCCESS; // open the key for the Dynamic extensions if (bDynamic) { CRegKey regkeyDynamicExtensions; lRes = regkeyDynamicExtensions.Open(regkeyNodeTypesNode, g_szDynamicExtensions); if (lRes == ERROR_SUCCESS) { lRes = regkeyDynamicExtensions.DeleteValue(szExtensionSnapinCLSID); } } else { // // Open the extensions key // CRegKey regkeyExtensions; lRes = regkeyExtensions.Open(regkeyNodeTypesNode, g_szExtensions); if (lRes == ERROR_SUCCESS) { CRegKey regkeyExtensionType; lRes = regkeyExtensionType.Open(regkeyExtensions, lpszExtensionType); if (lRes == ERROR_SUCCESS) { lRes = regkeyExtensionType.DeleteValue(szExtensionSnapinCLSID); } } } lRes = ERROR_SUCCESS; return HRESULT_FROM_WIN32(lRes); } ///////////////////////////////////////////////////////////////////////////// // CTimerThread BOOL CTimerThread::Start(HWND hWnd) { ASSERT(m_hWnd == NULL); ASSERT(::IsWindow(hWnd)); m_hWnd = hWnd; return CreateThread(); } BOOL CTimerThread::PostMessageToWnd(WPARAM wParam, LPARAM lParam) { ASSERT(::IsWindow(m_hWnd)); return ::PostMessage(m_hWnd, CHiddenWnd::s_TimerThreadMessage, wParam, lParam); } ///////////////////////////////////////////////////////////////////////////// // CWorkerThread CWorkerThread::CWorkerThread() { m_bAutoDelete = FALSE; m_bAbandoned = FALSE; m_hEventHandle = NULL; ExceptionPropagatingInitializeCriticalSection(&m_cs); m_hWnd = NULL; } CWorkerThread::~CWorkerThread() { ::DeleteCriticalSection(&m_cs); if (m_hEventHandle != NULL) { VERIFY(::CloseHandle(m_hEventHandle)); m_hEventHandle = NULL; } } BOOL CWorkerThread::Start(HWND hWnd) { ASSERT(m_hWnd == NULL); ASSERT(::IsWindow(hWnd)); m_hWnd = hWnd; ASSERT(m_hEventHandle == NULL); // cannot call start twice or reuse the same C++ object m_hEventHandle = ::CreateEvent(NULL,TRUE /*bManualReset*/,FALSE /*signalled*/, NULL); if (m_hEventHandle == NULL) { return FALSE; } return CreateThread(); } void CWorkerThread::Abandon() { Lock(); OnAbandon(); m_bAutoDelete = TRUE; m_bAbandoned = TRUE; Unlock(); } BOOL CWorkerThread::IsAbandoned() { Lock(); BOOL b = m_bAbandoned; Unlock(); return b; } BOOL CWorkerThread::PostMessageToWnd(UINT Msg, WPARAM wParam, LPARAM lParam) { BOOL b = IsAbandoned(); if (b) { return TRUE; // no need to post } ASSERT(::IsWindow(m_hWnd)); return ::PostMessage(m_hWnd, Msg, wParam, lParam); } void CWorkerThread::WaitForExitAcknowledge() { BOOL b = IsAbandoned(); if (b) { return; } VERIFY(WAIT_OBJECT_0 == ::WaitForSingleObject(m_hEventHandle,INFINITE)); } ///////////////////////////////////////////////////////////////////////////// // CHiddenWnd const UINT CHiddenWnd::s_NodeThreadHaveDataNotificationMessage = WM_USER + 1; const UINT CHiddenWnd::s_NodeThreadErrorNotificationMessage = WM_USER + 2; const UINT CHiddenWnd::s_NodeThreadExitingNotificationMessage = WM_USER + 3; const UINT CHiddenWnd::s_NodePropertySheetCreateMessage = WM_USER + 4; const UINT CHiddenWnd::s_NodePropertySheetDeleteMessage = WM_USER + 5; const UINT CHiddenWnd::s_ExecCommandMessage = WM_USER + 6; const UINT CHiddenWnd::s_ForceEnumerationMessage = WM_USER + 7; const UINT CHiddenWnd::s_TimerThreadMessage = WM_USER + 8; CHiddenWnd::CHiddenWnd(CComponentDataObject* pComponentDataObject) { m_pComponentDataObject = pComponentDataObject; m_nTimerID = 0; } LRESULT CHiddenWnd::OnNodeThreadHaveDataNotification(UINT, WPARAM wParam, LPARAM, BOOL&) { //TRACE(_T("CHiddenWnd::OnNodeThreadHaveDataNotification()\n")); ASSERT(m_pComponentDataObject != NULL); // call into the CTreeNode code CMTContainerNode* pNode = reinterpret_cast(wParam); ASSERT(pNode); ASSERT(pNode->IsContainer()); pNode->OnThreadHaveDataNotification(m_pComponentDataObject); return 1; } LRESULT CHiddenWnd::OnNodeThreadExitingNotification(UINT, WPARAM wParam, LPARAM lParam, BOOL&) { //TRACE(_T("CHiddenWnd::OnNodeThreadExitingNotification()\n")); ASSERT(m_pComponentDataObject != NULL); // call into the CTreeNode code CMTContainerNode* pNode = reinterpret_cast(wParam); ASSERT(pNode); ASSERT(pNode->IsContainer()); pNode->OnThreadExitingNotification(m_pComponentDataObject); // notify anybody interested in this event m_pComponentDataObject->GetNotificationSinkTable()->Notify( CHiddenWnd::s_NodeThreadExitingNotificationMessage ,wParam,lParam); return 1; } LRESULT CHiddenWnd::OnNodeThreadErrorNotification(UINT, WPARAM wParam, LPARAM lParam, BOOL&) { ASSERT(m_pComponentDataObject != NULL); // call into the CTreeNode code CMTContainerNode* pNode = reinterpret_cast(wParam); DWORD dwErr = static_cast(lParam); ASSERT(pNode); ASSERT(pNode->IsContainer()); pNode->OnThreadErrorNotification(dwErr, m_pComponentDataObject); return 1; } LRESULT CHiddenWnd::OnNodePropertySheetCreate(UINT, WPARAM wParam, LPARAM lParam, BOOL&) { //TRACE(_T("CHiddenWnd::OnNodePropertySheetCreate()\n")); ASSERT(m_pComponentDataObject != NULL); CPropertyPageHolderBase* pPPHolder = reinterpret_cast(wParam); ASSERT(pPPHolder != NULL); HWND hWnd = reinterpret_cast(lParam); ASSERT(::IsWindow(hWnd)); m_pComponentDataObject->GetPropertyPageHolderTable()->AddWindow(pPPHolder, hWnd); return 1; } LRESULT CHiddenWnd::OnNodePropertySheetDelete(UINT, WPARAM wParam, LPARAM lParam, BOOL&) { //TRACE(_T("CHiddenWnd::OnNodePropertySheetDestroy()\n")); ASSERT(m_pComponentDataObject != NULL); CPropertyPageHolderBase* pPPHolder = reinterpret_cast(wParam); ASSERT(pPPHolder != NULL); CTreeNode* pNode = reinterpret_cast(lParam); ASSERT(pNode != NULL); m_pComponentDataObject->GetPropertyPageHolderTable()->Remove(pPPHolder); pNode->OnDeleteSheet(); return 1; } LRESULT CHiddenWnd::OnExecCommand(UINT, WPARAM wParam, LPARAM lParam, BOOL&) { //TRACE(_T("CHiddenWnd::OnExecCommand()\n")); ASSERT(m_pComponentDataObject != NULL); CExecContext* pExec = reinterpret_cast(wParam); ASSERT(pExec != NULL); pExec->Execute((long)lParam); // execute code TRACE(_T("CHiddenWnd::BeforeDone()\n")); pExec->Done(); // let the secondary thread proceed return 1; } LRESULT CHiddenWnd::OnForceEnumeration(UINT, WPARAM wParam, LPARAM, BOOL&) { TRACE(_T("CHiddenWnd::OnForceEnumeration()\n")); ASSERT(m_pComponentDataObject != NULL); // call into the CTreeNode code CMTContainerNode* pNode = reinterpret_cast(wParam); ASSERT(pNode); ASSERT(pNode->GetContainer() != NULL); // not the root!!! ASSERT(pNode->IsContainer()); pNode->ForceEnumeration(m_pComponentDataObject); return 1; } LRESULT CHiddenWnd::OnTimerThread(UINT, WPARAM wParam, LPARAM lParam, BOOL&) { //TRACE(_T("CHiddenWnd::OnTimerThread()\n")); ASSERT(m_pComponentDataObject != NULL); // NULL arguments means that the thread acknowledge it is running properly // only to be called once if ((wParam == 0) && (lParam == 0)) { ASSERT(!m_pComponentDataObject->m_bTimerThreadStarted); m_pComponentDataObject->m_bTimerThreadStarted = TRUE; } else { // got some object specific message m_pComponentDataObject->OnTimerThread(wParam, lParam); } return 1; } LRESULT CHiddenWnd::OnTimer(UINT, WPARAM, LPARAM, BOOL&) { ASSERT(m_pComponentDataObject != NULL); m_pComponentDataObject->OnTimer(); return 1; } //////////////////////////////////////////////////////////////////////////////////// // CRunningThreadTable #define RUNNING_THREAD_ARRAY_DEF_SIZE (4) CRunningThreadTable::CRunningThreadTable(CComponentDataObject* pComponentData) { m_pComponentData = pComponentData; m_pEntries = (CMTContainerNode**)malloc(sizeof(CMTContainerNode*) * RUNNING_THREAD_ARRAY_DEF_SIZE); if (m_pEntries != NULL) { memset(m_pEntries,NULL, sizeof(CMTContainerNode*) * RUNNING_THREAD_ARRAY_DEF_SIZE); } m_nSize = RUNNING_THREAD_ARRAY_DEF_SIZE; } CRunningThreadTable::~CRunningThreadTable() { #ifdef _DEBUG for (int k=0; k < m_nSize; k++) { ASSERT(m_pEntries[k] == NULL); } #endif free(m_pEntries); } void CRunningThreadTable::Add(CMTContainerNode* pNode) { ASSERT(pNode != NULL); for (int k=0; k < m_nSize; k++) { if (m_pEntries[k] == NULL) // get the first empty spot { pNode->IncrementThreadLockCount(); m_pEntries[k] = pNode; return; } } // all full, need to grow the array int nAlloc = m_nSize*2; m_pEntries = (CMTContainerNode**)realloc(m_pEntries, sizeof(CMTContainerNode*)*nAlloc); memset(&m_pEntries[m_nSize], NULL, sizeof(CMTContainerNode*)*m_nSize); pNode->IncrementThreadLockCount(); m_pEntries[m_nSize] = pNode; m_nSize = nAlloc; } BOOL CRunningThreadTable::IsPresent(CMTContainerNode* pNode) { ASSERT(pNode != NULL); for (int k=0; k < m_nSize; k++) { if (m_pEntries[k] == pNode) { return TRUE; } } return FALSE; } void CRunningThreadTable::Remove(CMTContainerNode* pNode) { ASSERT(pNode != NULL); for (int k=0; k < m_nSize; k++) { if (m_pEntries[k] == pNode) { m_pEntries[k] = NULL; pNode->DecrementThreadLockCount(); return; // assume no more that one holder entry } } } void CRunningThreadTable::RemoveAll() { for (int k=0; k < m_nSize; k++) { if (m_pEntries[k] != NULL) { m_pEntries[k]->AbandonThread(m_pComponentData); m_pEntries[k] = NULL; } } } //////////////////////////////////////////////////////////////////////////////////// // CExecContext CExecContext::CExecContext() { m_hEventHandle = ::CreateEvent(NULL,TRUE /*bManualReset*/,FALSE /*signalled*/, NULL); ASSERT(m_hEventHandle != NULL); } CExecContext::~CExecContext() { ASSERT(m_hEventHandle != NULL); VERIFY(::CloseHandle(m_hEventHandle)); } void CExecContext::Done() { VERIFY(0 != ::SetEvent(m_hEventHandle)); } void CExecContext::Wait() { ASSERT(m_hEventHandle != NULL); VERIFY(WAIT_OBJECT_0 == ::WaitForSingleObject(m_hEventHandle,INFINITE)); } //////////////////////////////////////////////////////////////////////////////////// // CNotificationSinkEvent CNotificationSinkEvent::CNotificationSinkEvent() { m_hEventHandle = ::CreateEvent(NULL,TRUE /*bManualReset*/,FALSE /*signalled*/, NULL); ASSERT(m_hEventHandle != NULL); } CNotificationSinkEvent::~CNotificationSinkEvent() { ASSERT(m_hEventHandle != NULL); VERIFY(::CloseHandle(m_hEventHandle)); } void CNotificationSinkEvent::OnNotify(DWORD, WPARAM, LPARAM) { TRACE(_T("CNotificationSinkEvent::OnNotify()\n")); VERIFY(0 != ::SetEvent(m_hEventHandle)); } void CNotificationSinkEvent::Wait() { TRACE(_T("CNotificationSinkEvent::Wait()\n")); ASSERT(m_hEventHandle != NULL); VERIFY(WAIT_OBJECT_0 == ::WaitForSingleObject(m_hEventHandle,INFINITE)); } //////////////////////////////////////////////////////////////////////////////////// // CNotificationSinkTable #define NOTIFICATION_SINK_ARRAY_DEF_SIZE (4) CNotificationSinkTable::CNotificationSinkTable() { ExceptionPropagatingInitializeCriticalSection(&m_cs); m_pEntries = (CNotificationSinkBase**)malloc(sizeof(CNotificationSinkBase*) * NOTIFICATION_SINK_ARRAY_DEF_SIZE); if (m_pEntries != NULL) { memset(m_pEntries,NULL, sizeof(CNotificationSinkBase*) * NOTIFICATION_SINK_ARRAY_DEF_SIZE); } m_nSize = NOTIFICATION_SINK_ARRAY_DEF_SIZE; } CNotificationSinkTable::~CNotificationSinkTable() { free(m_pEntries); ::DeleteCriticalSection(&m_cs); } void CNotificationSinkTable::Advise(CNotificationSinkBase* p) { Lock(); ASSERT(p != NULL); for (int k=0; k < m_nSize; k++) { if (m_pEntries[k] == NULL) // get the first empty spot { m_pEntries[k] = p; Unlock(); return; } } // all full, need to grow the array int nAlloc = m_nSize*2; m_pEntries = (CNotificationSinkBase**)realloc(m_pEntries, sizeof(CNotificationSinkBase*)*nAlloc); memset(&m_pEntries[m_nSize], NULL, sizeof(CNotificationSinkBase*)*m_nSize); m_pEntries[m_nSize] = p; m_nSize = nAlloc; Unlock(); } void CNotificationSinkTable::Unadvise(CNotificationSinkBase* p) { Lock(); ASSERT(p != NULL); for (int k=0; k < m_nSize; k++) { if (m_pEntries[k] == p) { m_pEntries[k] = NULL; Unlock(); return; // assume no more that one holder entry } } Unlock(); } void CNotificationSinkTable::Notify(DWORD dwEvent, WPARAM dwArg1, LPARAM dwArg2) { Lock(); for (int k=0; k < m_nSize; k++) { if (m_pEntries[k] != NULL) { m_pEntries[k]->OnNotify(dwEvent, dwArg1, dwArg2); } } Unlock(); } /////////////////////////////////////////////////////////////////////////////// // CWatermarkInfoState (private class) class CWatermarkInfoState { public: CWatermarkInfoState() { m_pWatermarkInfo = NULL; m_hBanner = m_hWatermark = NULL; } ~CWatermarkInfoState() { DeleteBitmaps(); if (m_pWatermarkInfo != NULL) { delete m_pWatermarkInfo; } } void DeleteBitmaps() { if (m_hBanner != NULL) { ::DeleteObject(m_hBanner); m_hBanner = NULL; } if (m_hWatermark != NULL) { ::DeleteObject(m_hWatermark); m_hWatermark = NULL; } } void LoadBitmaps() { ASSERT(m_pWatermarkInfo != NULL); if (m_hBanner == NULL) { m_hBanner = ::LoadBitmap(AfxGetInstanceHandle(), MAKEINTRESOURCE(m_pWatermarkInfo->m_nIDBanner)); } if (m_hWatermark == NULL) { m_hWatermark = ::LoadBitmap(AfxGetInstanceHandle(), MAKEINTRESOURCE(m_pWatermarkInfo->m_nIDWatermark)); } } CWatermarkInfo* m_pWatermarkInfo; HBITMAP m_hBanner; HBITMAP m_hWatermark; }; /////////////////////////////////////////////////////////////////////////////// // CComponentDataObject implementation: helpers #ifdef _DEBUG_REFCOUNT unsigned int CComponentDataObject::m_nOustandingObjects = 0; #endif // _DEBUG_REFCOUNT CComponentDataObject::CComponentDataObject() : m_hiddenWnd((CComponentDataObject*)this), // initialize backpointer m_pTimerThreadObj(NULL), m_PPHTable(this), m_RTTable(this), m_pConsole(NULL), m_pConsoleNameSpace(NULL), m_pRootData(NULL), m_hWnd(NULL), m_nTimerThreadID(0x0), m_bTimerThreadStarted(FALSE), m_dwTimerInterval(1), m_dwTimerTime(0), m_pWatermarkInfoState(NULL), m_bExtensionSnapin(FALSE) { ExceptionPropagatingInitializeCriticalSection(&m_cs); #ifdef _DEBUG_REFCOUNT dbg_cRef = 0; ++m_nOustandingObjects; TRACE(_T("CComponentDataObject(), count = %d\n"),m_nOustandingObjects); #endif // _DEBUG_REFCOUNT } CComponentDataObject::~CComponentDataObject() { ::DeleteCriticalSection(&m_cs); #ifdef _DEBUG_REFCOUNT --m_nOustandingObjects; TRACE(_T("~CComponentDataObject(), count = %d\n"),m_nOustandingObjects); #endif // _DEBUG_REFCOUNT ASSERT(m_pConsole == NULL); ASSERT(m_pConsoleNameSpace == NULL); ASSERT(m_pRootData == NULL); } HRESULT CComponentDataObject::FinalConstruct() { AFX_MANAGE_STATE(AfxGetStaticModuleState()); if (!m_hiddenWnd.Create()) { TRACE(_T("Failed to create hidden window\n")); return E_FAIL; } m_hWnd = m_hiddenWnd.m_hWnd; m_pRootData = OnCreateRootData(); ASSERT(m_pRootData != NULL); return S_OK; } void CComponentDataObject::FinalRelease() { if (m_hiddenWnd.m_hWnd != NULL) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); VERIFY(m_hiddenWnd.DestroyWindow()); } // delete data if(m_pRootData != NULL) { delete m_pRootData; m_pRootData = NULL; } if (m_pWatermarkInfoState != NULL) { delete m_pWatermarkInfoState; } m_ColList.RemoveAndDeleteAllColumnSets(); if (log_instance != NULL) { log_instance->KillInstance(); } } /////////////////////////////////////////////////////////////////////////////// // CComponentDataObject::IComponentData members STDMETHODIMP CComponentDataObject::Initialize(LPUNKNOWN pUnknown) { ASSERT(m_pRootData != NULL); ASSERT(pUnknown != NULL); HRESULT hr = E_FAIL; AFX_MANAGE_STATE(AfxGetStaticModuleState()); // MMC should only call ::Initialize once! ASSERT(m_pConsole == NULL); ASSERT(m_pConsoleNameSpace == NULL); // get the pointers we need to hold on to hr = pUnknown->QueryInterface(IID_IConsoleNameSpace2, reinterpret_cast(&m_pConsoleNameSpace)); ASSERT(hr == S_OK); ASSERT(m_pConsoleNameSpace != NULL); hr = pUnknown->QueryInterface(IID_IConsole2, reinterpret_cast(&m_pConsole)); ASSERT(hr == S_OK); ASSERT(m_pConsole != NULL); // add the images for the scope tree LPIMAGELIST lpScopeImage; hr = m_pConsole->QueryScopeImageList(&lpScopeImage); ASSERT(hr == S_OK); // Set the images hr = OnSetImages(lpScopeImage); // Load the bitmaps from the dll ASSERT(hr == S_OK); lpScopeImage->Release(); OnInitialize(); return S_OK; } STDMETHODIMP CComponentDataObject::Notify(LPDATAOBJECT lpDataObject, MMC_NOTIFY_TYPE event, LPARAM arg, LPARAM param) { ASSERT(m_pConsoleNameSpace != NULL); HRESULT hr = S_OK; // Since it's my folder it has an internal format. // Design Note: for extension. I can use the fact, that the data object doesn't have // my internal format and I should look at the node type and see how to extend it. AFX_MANAGE_STATE(AfxGetStaticModuleState()); if (event == MMCN_PROPERTY_CHANGE) { ASSERT(lpDataObject == NULL); hr = OnPropertyChange(param, static_cast(arg)); } else { CInternalFormatCracker ifc; ifc.Extract(lpDataObject); if (ifc.GetCookieCount() == 0) { if ((event == MMCN_EXPAND) && (arg == TRUE) && IsExtensionSnapin()) { return OnExtensionExpand(lpDataObject, param); // this is a namespace extension, need to add // the root of the snapin CContainerNode* pContNode = GetRootData(); HSCOPEITEM pParent = param; pContNode->SetScopeID(pParent); pContNode->MarkExpanded(); return AddContainerNode(pContNode, pParent); } else if ((event == MMCN_REMOVE_CHILDREN) && IsExtensionSnapin()) { hr = OnRemoveChildren(lpDataObject, arg); } return S_OK; // Extensions not supported } switch(event) { case MMCN_PASTE: break; case MMCN_DELETE: hr = OnDeleteVerbHandler(ifc, NULL); break; case MMCN_REFRESH: hr = OnRefreshVerbHandler(ifc); break; case MMCN_RENAME: hr = OnRename(ifc, arg, param); break; case MMCN_EXPAND: hr = OnExpand(ifc, arg, param); break; case MMCN_EXPANDSYNC: hr = OnExpand(ifc, arg, param, FALSE); break; case MMCN_BTN_CLICK: break; case MMCN_SELECT: hr = OnSelect(ifc, arg, param); break; default: break; } // switch } // if return hr; } STDMETHODIMP CComponentDataObject::Destroy() { InternalAddRef(); TRACE(_T("CComponentDataObject::Destroy()\n")); OnDestroy(); SAFE_RELEASE(m_pConsoleNameSpace); SAFE_RELEASE(m_pConsole); AFX_MANAGE_STATE(AfxGetStaticModuleState()); VERIFY(m_hiddenWnd.DestroyWindow()); InternalRelease(); return S_OK; } BOOL CComponentDataObject::PostExecMessage(CExecContext* pExec, LPARAM arg) { ASSERT(pExec != NULL); ASSERT(::IsWindow(m_hWnd)); return ::PostMessage(m_hWnd, CHiddenWnd::s_ExecCommandMessage, (WPARAM)pExec, (LPARAM)arg); } BOOL CComponentDataObject::PostForceEnumeration(CMTContainerNode* pContainerNode) { ASSERT(::IsWindow(m_hWnd)); return ::PostMessage(m_hWnd, CHiddenWnd::s_ForceEnumerationMessage, (WPARAM)pContainerNode, (LPARAM)0); } BOOL CComponentDataObject::OnCreateSheet(CPropertyPageHolderBase* pPPHolder, HWND hWnd) { ASSERT(pPPHolder != NULL); ASSERT(::IsWindow(hWnd)); ASSERT(::IsWindow(m_hWnd)); TRACE(_T("\nCComponentDataObject::OnCreateSheet()\n")); return ::PostMessage(m_hWnd, CHiddenWnd::s_NodePropertySheetCreateMessage, (WPARAM)pPPHolder, (LPARAM)hWnd); } BOOL CComponentDataObject::OnDeleteSheet(CPropertyPageHolderBase* pPPHolder, CTreeNode* pNode) { ASSERT(pPPHolder != NULL); ASSERT(pNode != NULL); ASSERT(::IsWindow(m_hWnd)); TRACE(_T("\nCComponentDataObject::OnDeleteSheet()\n")); return ::PostMessage(m_hWnd, CHiddenWnd::s_NodePropertySheetDeleteMessage, (WPARAM)pPPHolder, (LPARAM)pNode); } void CComponentDataObject::OnInitialize() { VERIFY(StartTimerThread()); } void CComponentDataObject::OnDestroy() { // stop timer and worker thread ShutDownTimerThread(); // detach all the threads that might be still running GetRunningThreadTable()->RemoveAll(); // tell all the open property sheets to shut down // shut down property sheets, if any GetPropertyPageHolderTable()->WaitForAllToShutDown(); } STDMETHODIMP CComponentDataObject::QueryDataObject(MMC_COOKIE cookie, DATA_OBJECT_TYPES type, LPDATAOBJECT* ppDataObject) { ASSERT(ppDataObject != NULL); CComObject* pObject; CComObject::CreateInstance(&pObject); ASSERT(pObject != NULL); // Save cookie and type for delayed rendering pObject->SetType(type); CTreeNode* pNode; // // -1 is an uninitialized data object, just ignore // if (cookie != -1) { if (cookie == NULL) { pNode = GetRootData(); } else { pNode = reinterpret_cast(cookie); } ASSERT(pNode != NULL); pObject->AddCookie(pNode); } // save a pointer to "this" IUnknown* pUnkComponentData = GetUnknown(); // no addref ASSERT(pUnkComponentData != NULL); pObject->SetComponentData(pUnkComponentData); // will addref it return pObject->QueryInterface(IID_IDataObject, reinterpret_cast(ppDataObject)); } STDMETHODIMP CComponentDataObject::GetDisplayInfo(SCOPEDATAITEM* pScopeDataItem) { ASSERT(pScopeDataItem != NULL); CTreeNode* pNode = reinterpret_cast(pScopeDataItem->lParam); ASSERT(pNode != NULL); ASSERT(pNode->IsContainer()); ASSERT(pScopeDataItem->mask & SDI_STR); pScopeDataItem->displayname = const_cast(pNode->GetDisplayName()); ASSERT(pScopeDataItem->displayname != NULL); return S_OK; } STDMETHODIMP CComponentDataObject::CompareObjects(LPDATAOBJECT lpDataObjectA, LPDATAOBJECT lpDataObjectB) { ASSERT(lpDataObjectA != NULL); ASSERT(lpDataObjectB != NULL); CInternalFormatCracker ifcA, ifcB; VERIFY(SUCCEEDED(ifcA.Extract(lpDataObjectA))); VERIFY(SUCCEEDED(ifcB.Extract(lpDataObjectB))); CTreeNode* pNodeA = ifcA.GetCookieAt(0); CTreeNode* pNodeB = ifcB.GetCookieAt(0); ASSERT(pNodeA != NULL); ASSERT(pNodeB != NULL); if ( (pNodeA == NULL) || (pNodeB == NULL) ) { return E_FAIL; } return (pNodeA == pNodeB) ? S_OK : S_FALSE; } /////////////////////////////////////////////////////////////////////////////// // Message handlers for CComponentDataObject::IComponentData::Notify() HRESULT CComponentDataObject::OnAdd(CTreeNode*, LPARAM, LPARAM) { return E_UNEXPECTED; } HRESULT CComponentDataObject::OnRemoveChildren(LPDATAOBJECT lpDataObject, LPARAM) { CInternalFormatCracker ifc; HRESULT hr = S_OK; hr = ifc.Extract(lpDataObject); if (SUCCEEDED(hr)) { if (ifc.GetCookieCount() == 1) { CTreeNode* pNode = ifc.GetCookieAt(0); if (pNode != NULL) { if (pNode->IsContainer()) { CContainerNode* pContainerNode = dynamic_cast(pNode); if (pContainerNode != NULL) { pContainerNode->RemoveAllChildrenFromList(); } } } } else { ASSERT(FALSE); } } return hr; } HRESULT CComponentDataObject::OnRename(CInternalFormatCracker& ifc, LPARAM, LPARAM param) { HRESULT hr = S_FALSE; CTreeNode* pNode = ifc.GetCookieAt(0); ASSERT(pNode != NULL); hr = pNode->OnRename(this, (LPOLESTR)param); if (hr == S_OK) { UpdateAllViewsHelper(reinterpret_cast(pNode), CHANGE_RESULT_ITEM); } return hr; } HRESULT CComponentDataObject::OnExpand(CInternalFormatCracker& ifc, LPARAM arg, LPARAM param, BOOL bAsync) { if (arg == TRUE) { // Did Initialize get called? ASSERT(m_pConsoleNameSpace != NULL); // // I shouldn't have to deal with multiple select here... // ASSERT(ifc.GetCookieCount() == 1); CTreeNode* pNode = ifc.GetCookieAt(0); if (pNode == NULL) { ASSERT(pNode != NULL); return S_FALSE; } EnumerateScopePane(pNode, param, bAsync); } else if (!bAsync) { ASSERT(m_pConsoleNameSpace != NULL); // // I shouldn't have to deal with multiple select here... // ASSERT(ifc.GetCookieCount() == 1); CTreeNode* pNode = ifc.GetCookieAt(0); ASSERT(pNode != NULL); if (pNode && pNode->CanExpandSync()) { MMC_EXPANDSYNC_STRUCT* pExpandStruct = reinterpret_cast(param); if (pExpandStruct && pExpandStruct->bExpanding) { EnumerateScopePane(pNode, pExpandStruct->hItem, bAsync); pExpandStruct->bHandled = TRUE; } } else { return S_FALSE; } } return S_OK; } HRESULT CComponentDataObject::OnSelect(CInternalFormatCracker&, LPARAM, LPARAM) { return E_UNEXPECTED; } HRESULT CComponentDataObject::OnContextMenu(CTreeNode*, LPARAM, LPARAM) { return S_OK; } HRESULT CComponentDataObject::OnPropertyChange(LPARAM param, long fScopePane) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); TRACE(_T("CComponentDataObject::OnPropertyChange()\n")); ASSERT(param != NULL); CPropertyPageHolderBase* pPPHolder = reinterpret_cast(param); ASSERT(pPPHolder != NULL); CTreeNode* pNode = pPPHolder->GetTreeNode(); ASSERT(pNode != NULL); // allow both types in the result pane, but only scope items in the scope pane ASSERT(!fScopePane || (fScopePane && pNode->IsContainer()) ); long changeMask = CHANGE_RESULT_ITEM; // default, the holder can change it BOOL bUpdate = pPPHolder->OnPropertyChange(fScopePane, &changeMask); // fire event to let the property page thread proceed pPPHolder->AcknowledgeNotify(); if (bUpdate) { pNode->OnPropertyChange(this, fScopePane, changeMask); } return S_OK; } ///////////////////////////////////////////////////////////////////////////// // CComponentDataObject::IExtendPropertySheet2 memebers STDMETHODIMP CComponentDataObject::CreatePropertyPages(LPPROPERTYSHEETCALLBACK lpProvider, LONG_PTR handle, LPDATAOBJECT lpIDataObject) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); CInternalFormatCracker ifc; HRESULT hr = ifc.Extract(lpIDataObject); if (FAILED(hr)) { return hr; } // // this was an object created by the modal wizard, do nothing // if (ifc.GetCookieType() == CCT_UNINITIALIZED) { return hr; } if (ifc.GetCookieType() == CCT_SNAPIN_MANAGER) { return SnapinManagerCreatePropertyPages(lpProvider,handle); } CTreeNode* pNode = ifc.GetCookieAt(0); ASSERT(ifc.GetCookieType() == CCT_SCOPE || ifc.GetCookieType() == CCT_RESULT); CNodeList nodeList; ifc.GetCookieList(nodeList); if (nodeList.GetCount() > 1) // multiple selection { // // Delegate to the container // ASSERT(pNode->GetContainer() != NULL); hr = pNode->GetContainer()->CreatePropertyPages(lpProvider, handle, &nodeList); } else if (nodeList.GetCount() == 1) // single selection { // // Delegate to the node // ASSERT(pNode != NULL); hr = pNode->CreatePropertyPages(lpProvider, handle, &nodeList); } else { hr = E_FAIL; } return hr; } STDMETHODIMP CComponentDataObject::QueryPagesFor(LPDATAOBJECT lpDataObject) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); CTreeNode* pNode; DATA_OBJECT_TYPES type; CInternalFormatCracker ifc; HRESULT hr = ifc.Extract(lpDataObject); if (FAILED(hr)) { return hr; } type = ifc.GetCookieType(); pNode = ifc.GetCookieAt(0); // // Retrieve node list and count // CNodeList nodeList; ifc.GetCookieList(nodeList); // // this was an object created by the modal wizard, do nothing // if (type == CCT_UNINITIALIZED) { return hr; } if (type == CCT_SNAPIN_MANAGER) { return HasPropertyPages(type) ? S_OK : S_FALSE; } // // we have a node, so delegate to it // ASSERT(pNode != NULL); BOOL bDummy; if (nodeList.GetCount() == 1) // single selection { ASSERT((type == CCT_SCOPE) || (type == CCT_RESULT)); if (pNode->GetSheetCount() > 0) { pNode->ShowPageForNode(this); return S_FALSE; } else if (pNode->DelegatesPPToContainer() && pNode->GetContainer()->GetSheetCount() > 0) { // // Find the page and bring it to foreground // pNode->ShowPageForNode(this); return S_FALSE; } if (pNode->HasPropertyPages(type, &bDummy, &nodeList)) { hr = S_OK; } else { hr = S_FALSE; } } else if (nodeList.GetCount() > 1) // multiple selection { ASSERT(pNode->GetContainer() != NULL); if (pNode->GetContainer()->HasPropertyPages(type, &bDummy, &nodeList)) { hr = S_OK; } else { hr = S_FALSE; } } return hr; } HRESULT CComponentDataObject::CreatePropertySheet(CTreeNode* pNode, HWND hWndParent, LPCWSTR lpszTitle) { HRESULT hr = S_OK; HWND hWnd = hWndParent; if (hWnd == NULL) { hr = m_pConsole->GetMainWindow(&hWnd); if (FAILED(hr)) { ASSERT(FALSE); return hr; } } // // get an interface to a sheet provider // CComPtr spSheetProvider; hr = m_pConsole->QueryInterface(IID_IPropertySheetProvider,(void**)&spSheetProvider); ASSERT(SUCCEEDED(hr)); ASSERT(spSheetProvider != NULL); // // get an interface to a sheet callback // CComPtr spSheetCallback; hr = m_pConsole->QueryInterface(IID_IPropertySheetCallback,(void**)&spSheetCallback); ASSERT(SUCCEEDED(hr)); ASSERT(spSheetCallback != NULL); // // get a sheet // MMC_COOKIE cookie = reinterpret_cast(pNode); DATA_OBJECT_TYPES type = (pNode->IsContainer()) ? CCT_SCOPE : CCT_RESULT; CComPtr spDataObject; hr = QueryDataObject(cookie, type, &spDataObject); ASSERT(SUCCEEDED(hr)); ASSERT(spDataObject != NULL); hr = spSheetProvider->CreatePropertySheet(lpszTitle, TRUE, cookie, spDataObject, 0x0 /*dwOptions*/); ASSERT(SUCCEEDED(hr)); hr = spSheetProvider->AddPrimaryPages(GetUnknown(), TRUE /*bCreateHandle*/, hWnd, pNode->IsContainer() /* bScopePane*/); hr = spSheetProvider->AddExtensionPages(); ASSERT(SUCCEEDED(hr)); hr = spSheetProvider->Show(reinterpret_cast(hWnd), 0); ASSERT(SUCCEEDED(hr)); return hr; } CWatermarkInfo* CComponentDataObject::SetWatermarkInfo(CWatermarkInfo* pWatermarkInfo) { if (m_pWatermarkInfoState == NULL) { m_pWatermarkInfoState = new CWatermarkInfoState; } CWatermarkInfo* pOldWatermarkInfo = m_pWatermarkInfoState->m_pWatermarkInfo; m_pWatermarkInfoState->m_pWatermarkInfo = pWatermarkInfo; // we changed info, so dump the old bitmap handles m_pWatermarkInfoState->DeleteBitmaps(); return pOldWatermarkInfo; } STDMETHODIMP CComponentDataObject::GetWatermarks(LPDATAOBJECT, HBITMAP* lphWatermark, HBITMAP* lphHeader, HPALETTE* lphPalette, BOOL* pbStretch) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); *lphHeader = NULL; *lphWatermark = NULL; *lphPalette = NULL; *pbStretch = TRUE; if ((m_pWatermarkInfoState == NULL) || (m_pWatermarkInfoState->m_pWatermarkInfo == NULL)) { return E_FAIL; } *pbStretch = m_pWatermarkInfoState->m_pWatermarkInfo->m_bStretch; *lphPalette = m_pWatermarkInfoState->m_pWatermarkInfo->m_hPalette; // load bitmaps if not loaded yet m_pWatermarkInfoState->LoadBitmaps(); *lphHeader = m_pWatermarkInfoState->m_hBanner; if (*lphHeader == NULL) { return E_FAIL; } *lphWatermark = m_pWatermarkInfoState->m_hWatermark; if (*lphWatermark == NULL) { return E_FAIL; } return S_OK; } ///////////////////////////////////////////////////////////////////////////// // CComponentDataObject::IExtendContextMenu memebers STDMETHODIMP CComponentDataObject::AddMenuItems(LPDATAOBJECT pDataObject, LPCONTEXTMENUCALLBACK pContextMenuCallback, long *pInsertionAllowed) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); HRESULT hr = S_OK; CTreeNode* pNode; DATA_OBJECT_TYPES type; CInternalFormatCracker ifc; hr = ifc.Extract(pDataObject); if (FAILED(hr)) { return hr; } type = ifc.GetCookieType(); pNode = ifc.GetCookieAt(0); ASSERT(pNode != NULL); if (pNode == NULL) { return hr; } CComPtr spContextMenuCallback2; hr = pContextMenuCallback->QueryInterface(IID_IContextMenuCallback2, (PVOID*)&spContextMenuCallback2); if (FAILED(hr)) { return hr; } CNodeList nodeList; ifc.GetCookieList(nodeList); if (nodeList.GetCount() > 1) // multiple selection { ASSERT(pNode->GetContainer() != NULL); hr = pNode->GetContainer()->OnAddMenuItems(spContextMenuCallback2, type, pInsertionAllowed, &nodeList); } else if (nodeList.GetCount() == 1) // single selection { hr = pNode->OnAddMenuItems(spContextMenuCallback2, type, pInsertionAllowed, &nodeList); } else { hr = E_FAIL; } return hr; } STDMETHODIMP CComponentDataObject::Command(long nCommandID, LPDATAOBJECT pDataObject) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); CInternalFormatCracker ifc; HRESULT hr = ifc.Extract(pDataObject); if (FAILED(hr)) { return hr; } CTreeNode* pNode = ifc.GetCookieAt(0); ASSERT(pNode != NULL); // // Retrieve node list and count // CNodeList nodeList; ifc.GetCookieList(nodeList); if (nodeList.GetCount() > 1) // multiple selection { // // Delegate the command to the container // ASSERT(pNode->GetContainer() != NULL); hr = pNode->GetContainer()->OnCommand(nCommandID, ifc.GetCookieType(), this, &nodeList); } else if (nodeList.GetCount() == 1) // single selection { // // Let the node take care of it // hr = pNode->OnCommand(nCommandID, ifc.GetCookieType(), this, &nodeList); } else { hr = E_FAIL; } return hr; } ///////////////////////////////////////////////////////////////////////////// // CComponentDataObject::IPersistStream members STDMETHODIMP CComponentDataObject::IsDirty() { // forward to the root of the tree CRootData* pRootData = GetRootData(); ASSERT(pRootData != NULL); return pRootData->IsDirty(); } STDMETHODIMP CComponentDataObject::Load(IStream __RPC_FAR *pStm) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); // forward to the root of the tree CRootData* pRootData = GetRootData(); ASSERT(pRootData != NULL); return pRootData->Load(pStm); } STDMETHODIMP CComponentDataObject::Save(IStream __RPC_FAR *pStm, BOOL fClearDirty) { // forward to the root of the tree CRootData* pRootData = GetRootData(); ASSERT(pRootData != NULL); return pRootData->Save(pStm,fClearDirty); } ///////////////////////////////////////////////////////////////////////////// // CComponentDataObject::ISnapinHelp2 memebers STDMETHODIMP CComponentDataObject::GetHelpTopic(LPOLESTR* lpCompiledHelpFile) { if (lpCompiledHelpFile == NULL) { return E_INVALIDARG; } LPCWSTR lpszHelpFileName = GetHTMLHelpFileName(); if (lpszHelpFileName == NULL) { *lpCompiledHelpFile = NULL; return E_NOTIMPL; } CString szHelpFilePath; LPTSTR lpszBuffer = szHelpFilePath.GetBuffer(2*MAX_PATH); UINT nLen = ::GetSystemWindowsDirectory(lpszBuffer, 2*MAX_PATH); if (nLen == 0) { return E_FAIL; } wcscpy(&lpszBuffer[nLen], lpszHelpFileName); szHelpFilePath.ReleaseBuffer(); UINT nBytes = (szHelpFilePath.GetLength()+1) * sizeof(WCHAR); *lpCompiledHelpFile = (LPOLESTR)::CoTaskMemAlloc(nBytes); if (*lpCompiledHelpFile != NULL) { memcpy(*lpCompiledHelpFile, (LPCWSTR)szHelpFilePath, nBytes); } else { return E_OUTOFMEMORY; } return S_OK; } HRESULT CComponentDataObject::GetLinkedTopics(LPOLESTR*) { return S_FALSE; } /////////////////////////////////////////////////////////////////////////////// // CComponentDataObject Helpers HRESULT CComponentDataObject::UpdateAllViewsHelper(LPARAM data, LONG_PTR hint) { ASSERT(m_pConsole != NULL); CComObject* pObject; CComObject::CreateInstance(&pObject); ASSERT(pObject != NULL); IDataObject* pDataObject; HRESULT hr = pObject->QueryInterface(IID_IDataObject, reinterpret_cast(&pDataObject)); ASSERT(SUCCEEDED(hr)); ASSERT(pDataObject != NULL); hr = m_pConsole->UpdateAllViews(pDataObject,data, hint); pDataObject->Release(); return hr; } void CComponentDataObject::HandleStandardVerbsHelper(CComponentObject* pComponentObj, LPCONSOLEVERB pConsoleVerb, BOOL bScope, BOOL bSelect, LPDATAOBJECT lpDataObject) { // You should crack the data object and enable/disable/hide standard // commands appropriately. The standard commands are reset everytime you get // called. So you must reset them back. ASSERT(pConsoleVerb != NULL); ASSERT(pComponentObj != NULL); ASSERT(lpDataObject != NULL); // reset the selection pComponentObj->SetSelectedNode(NULL, CCT_UNINITIALIZED); CInternalFormatCracker ifc; VERIFY(SUCCEEDED(ifc.Extract(lpDataObject))); CTreeNode* pNode = ifc.GetCookieAt(0); if (pNode == NULL) { return; } // // Retrieve node list and count // CNodeList nodeList; ifc.GetCookieList(nodeList); if (nodeList.GetCount() > 1) // multiple selection { // // Delegate to the container // ASSERT(pNode->GetContainer() != NULL); pNode->GetContainer()->OnSetVerbState(pConsoleVerb, ifc.GetCookieType(), &nodeList); } else if (nodeList.GetCount() == 1) // single selection { // // set selection, if any // if (bSelect) { pComponentObj->SetSelectedNode(pNode, ifc.GetCookieType()); } ASSERT((ifc.GetCookieType() == CCT_SCOPE) || (ifc.GetCookieType() == CCT_RESULT)); TRACE(_T("HandleStandardVerbsHelper: Node <%s> bScope = %d bSelect = %d, type = %s\n"), pNode->GetDisplayName(), bScope, bSelect, (ifc.GetCookieType() == CCT_SCOPE) ? _T("CCT_SCOPE") : _T("CCT_RESULT")); pConsoleVerb->SetDefaultVerb(pNode->GetDefaultVerb(ifc.GetCookieType(), &nodeList)); pNode->OnSetVerbState(pConsoleVerb, ifc.GetCookieType(), &nodeList); } } void CComponentDataObject::EnumerateScopePane(CTreeNode* cookie, HSCOPEITEM pParent, BOOL bAsync) { ASSERT(m_pConsoleNameSpace != NULL); // make sure we QI'ed for the interface // find the node corresponding to the cookie ASSERT(cookie != NULL); ASSERT(cookie->IsContainer()); CContainerNode* pContNode = (CContainerNode*)cookie; pContNode->MarkExpanded(); if (pContNode == GetRootData()) { pContNode->SetScopeID(pParent); } // allow the node to enumerate its children, if not enumerated yet if (!pContNode->IsEnumerated()) { BOOL bAddChildrenNow = pContNode->OnEnumerate(this, bAsync); pContNode->MarkEnumerated(); if (!bAddChildrenNow) { return; } } // scan the list of children, looking for containers and add them ASSERT(pParent != NULL); CNodeList* pChildList = pContNode->GetContainerChildList(); ASSERT(pChildList != NULL); POSITION pos; for( pos = pChildList->GetHeadPosition(); pos != NULL; ) { CContainerNode* pCurrChildNode = (CContainerNode*)pChildList->GetNext(pos); ASSERT(pCurrChildNode != NULL); if (pCurrChildNode->IsVisible()) { AddContainerNode(pCurrChildNode, pParent); } } } HRESULT CComponentDataObject::OnDeleteVerbHandler(CInternalFormatCracker& ifc, CComponentObject*) { HRESULT hr = S_OK; CTreeNode* pNode = ifc.GetCookieAt(0); ASSERT(pNode != NULL); // // Retrieve the cookie list and count // CNodeList nodeList; ifc.GetCookieList(nodeList); if (nodeList.GetCount() > 1) // multiple selection { ASSERT(pNode->GetContainer() != NULL); pNode->GetContainer()->OnDelete(this, &nodeList); } else if (nodeList.GetCount() == 1) // single selection { pNode->OnDelete(this, &nodeList); } else { hr = E_FAIL; } return hr; } HRESULT CComponentDataObject::OnRefreshVerbHandler(CInternalFormatCracker& ifc) { HRESULT hr = S_OK; CTreeNode* pNode = ifc.GetCookieAt(0); ASSERT(pNode != NULL); // // Retrieve the node list and the count // CNodeList nodeList; ifc.GetCookieList(nodeList); if (nodeList.GetCount() > 1) // multiple selection { ASSERT(pNode->GetContainer() != NULL); pNode->GetContainer()->OnRefresh(this, &nodeList); } else if (nodeList.GetCount() == 1) // single selection { pNode->OnRefresh(this, &nodeList); } else { hr = E_FAIL; } return hr; } HRESULT CComponentDataObject::OnHelpHandler(CInternalFormatCracker& ifc, CComponentObject* pComponentObject) { // // responding to MMCN_CONTEXTHELP // ASSERT(pComponentObject != NULL); HRESULT hr = S_OK; CTreeNode* pNode = ifc.GetCookieAt(0); ASSERT(pNode != NULL); // // Retrieve the node list and count // CNodeList nodeList; ifc.GetCookieList(nodeList); if (nodeList.GetCount() > 1) // Multiple selection { ASSERT(pNode->GetContainer() != NULL); OnNodeContextHelp(&nodeList); } else if (nodeList.GetCount() == 1) // Single selection { OnNodeContextHelp(&nodeList); } else { hr = E_FAIL; } return hr; } BOOL CComponentDataObject::WinHelp(LPCTSTR lpszHelpFileName, // file, no path UINT uCommand, // type of Help DWORD dwData // additional data ) { HWND hWnd; GetConsole()->GetMainWindow(&hWnd); CString szHelpFilePath; LPTSTR lpszBuffer = szHelpFilePath.GetBuffer(2*MAX_PATH); UINT nLen = ::GetSystemWindowsDirectory(lpszBuffer, 2*MAX_PATH); if (nLen == 0) { return FALSE; } wcscpy(&lpszBuffer[nLen], lpszHelpFileName); szHelpFilePath.ReleaseBuffer(); return ::WinHelp(hWnd, szHelpFilePath, uCommand, dwData); } HRESULT CComponentDataObject::AddNode(CTreeNode* pNodeToAdd) { ASSERT(pNodeToAdd != NULL); // if the node is hidden, just ignore if (!pNodeToAdd->IsVisible()) return S_OK; if (pNodeToAdd->IsContainer()) { ASSERT(pNodeToAdd->GetContainer() != NULL); HSCOPEITEM pParentScopeItem = pNodeToAdd->GetContainer()->GetScopeID(); ASSERT(pParentScopeItem != NULL); return AddContainerNode((CContainerNode*)pNodeToAdd, pParentScopeItem); } return AddLeafNode((CLeafNode*)pNodeToAdd); } HRESULT CComponentDataObject::AddNodeSorted(CTreeNode* pNodeToAdd) { ASSERT(pNodeToAdd != NULL); // if the node is hidden, just ignore if (!pNodeToAdd->IsVisible()) { return S_OK; } if (pNodeToAdd->IsContainer()) { ASSERT(pNodeToAdd->GetContainer() != NULL); HSCOPEITEM pParentScopeItem = pNodeToAdd->GetContainer()->GetScopeID(); ASSERT(pParentScopeItem != NULL); return AddContainerNodeSorted((CContainerNode*)pNodeToAdd, pParentScopeItem); } return AddLeafNode((CLeafNode*)pNodeToAdd); } HRESULT CComponentDataObject::DeleteNode(CTreeNode* pNodeToDelete) { if (pNodeToDelete->IsContainer()) { return DeleteContainerNode((CContainerNode*)pNodeToDelete); } return DeleteLeafNode((CLeafNode*)pNodeToDelete); } HRESULT CComponentDataObject::DeleteMultipleNodes(CNodeList* pNodeList) { HRESULT hr = S_OK; POSITION pos = pNodeList->GetHeadPosition(); while (pos != NULL) { CTreeNode* pNode = pNodeList->GetNext(pos); if (pNode->IsContainer()) { DeleteContainerNode((CContainerNode*)pNode); } } hr = UpdateAllViewsHelper(reinterpret_cast(pNodeList), DELETE_MULTIPLE_RESULT_ITEMS); return hr; } HRESULT CComponentDataObject::ChangeNode(CTreeNode* pNodeToChange, long changeMask) { if (!pNodeToChange->IsVisible()) { return S_OK; } if (pNodeToChange->IsContainer()) { CContainerNode* pContNode = (CContainerNode*)pNodeToChange; //if (!pContNode->IsExpanded()) // return S_OK; return ChangeContainerNode(pContNode, changeMask); } return ChangeLeafNode((CLeafNode*)pNodeToChange, changeMask); } HRESULT CComponentDataObject::RemoveAllChildren(CContainerNode* pNode) { // if the node is hidden or not expanded yet, just ignore if (!pNode->IsVisible() || !pNode->IsExpanded()) { return S_OK; } ASSERT(pNode != NULL); HSCOPEITEM nID = pNode->GetScopeID(); ASSERT(nID != 0); // remove the container itself HRESULT hr = m_pConsoleNameSpace->DeleteItem(nID, /*fDeleteThis*/ FALSE); ASSERT(SUCCEEDED(hr)); DeleteAllResultPaneItems(pNode); // remove the result items from all the views (will do only if container selected) ASSERT(SUCCEEDED(hr)); return hr; } HRESULT CComponentDataObject::RepaintSelectedFolderInResultPane() { return UpdateAllViewsHelper((long)NULL, REPAINT_RESULT_PANE); } HRESULT CComponentDataObject::RepaintResultPane(CContainerNode* pNode) { ASSERT(pNode != NULL); return UpdateAllViewsHelper(reinterpret_cast(pNode), REPAINT_RESULT_PANE); } HRESULT CComponentDataObject::DeleteAllResultPaneItems(CContainerNode* pNode) { ASSERT(pNode != NULL); return UpdateAllViewsHelper(reinterpret_cast(pNode), DELETE_ALL_RESULT_ITEMS); } HRESULT CComponentDataObject::AddContainerNode(CContainerNode* pNodeToInsert, HSCOPEITEM pParentScopeItem) { ASSERT(pNodeToInsert != NULL); if ((pNodeToInsert != GetRootData()) && (!pNodeToInsert->GetContainer()->IsExpanded())) { return S_OK; } //ASSERT(pNodeToInsert->GetScopeID() == 0); SCOPEDATAITEM scopeDataItem; InitializeScopeDataItem(&scopeDataItem, pParentScopeItem, reinterpret_cast(pNodeToInsert), // lParam, use the node pointer as cookie pNodeToInsert->GetImageIndex(FALSE), // close image pNodeToInsert->GetImageIndex(TRUE), // open image pNodeToInsert->HasChildren()); HRESULT hr = m_pConsoleNameSpace->InsertItem(&scopeDataItem); ASSERT(SUCCEEDED(hr)); if (FAILED(hr)) { return hr; } // Note - On return, the ID member of 'scopeDataItem' // contains the handle to the newly inserted item, so we have to save ASSERT(scopeDataItem.ID != NULL); pNodeToInsert->SetScopeID(scopeDataItem.ID); return hr; } // // Note : This should combined with the function above adding a third parameter that is a compare function, // which is NULL by default. If it is NULL then we just skip the GetChildItem() and the while loop. // HRESULT CComponentDataObject::AddContainerNodeSorted(CContainerNode* pNodeToInsert, HSCOPEITEM pParentScopeItem) { ASSERT(pNodeToInsert != NULL); if ((pNodeToInsert != GetRootData()) && (!pNodeToInsert->GetContainer()->IsExpanded())) { return S_OK; } SCOPEDATAITEM scopeDataItem; InitializeScopeDataItem(&scopeDataItem, pParentScopeItem, reinterpret_cast(pNodeToInsert), // lParam, use the node pointer as cookie pNodeToInsert->GetImageIndex(FALSE), // close image pNodeToInsert->GetImageIndex(TRUE), // open image pNodeToInsert->HasChildren()); HSCOPEITEM pChildScopeItem; CTreeNode* pChildNode = NULL; // Enumerate through the scope node items and insert the new node in sorted order HRESULT hr = m_pConsoleNameSpace->GetChildItem(pParentScopeItem, &pChildScopeItem, (MMC_COOKIE*)&pChildNode); ASSERT(SUCCEEDED(hr)); if (FAILED(hr)) { return hr; } while (pChildNode != NULL) { // REVIEW_JEFFJON : we should probably have a compare function as a parameter and use that here. if (_wcsicoll(pNodeToInsert->GetDisplayName(), pChildNode->GetDisplayName()) < 0) { // Insert the node before the node pointed to by pChildScopeItem scopeDataItem.relativeID = pChildScopeItem; scopeDataItem.mask |= SDI_NEXT; break; } pChildNode = NULL; hr = m_pConsoleNameSpace->GetNextItem(pChildScopeItem, &pChildScopeItem, (MMC_COOKIE*)&pChildNode); ASSERT(SUCCEEDED(hr)); if (FAILED(hr)) { return hr; } } hr = m_pConsoleNameSpace->InsertItem(&scopeDataItem); ASSERT(SUCCEEDED(hr)); if (FAILED(hr)) { return hr; } // Note - On return, the ID member of 'scopeDataItem' // contains the handle to the newly inserted item, so we have to save ASSERT(scopeDataItem.ID != NULL); pNodeToInsert->SetScopeID(scopeDataItem.ID); return hr; } HRESULT CComponentDataObject::DeleteContainerNode(CContainerNode* pNodeToDelete) { ASSERT(pNodeToDelete != NULL); ASSERT(pNodeToDelete->GetContainer() != NULL); HSCOPEITEM nID = pNodeToDelete->GetScopeID(); ASSERT(nID != 0); HRESULT hr = m_pConsoleNameSpace->DeleteItem(nID, /*fDeleteThis*/ TRUE); pNodeToDelete->SetScopeID(0); return hr; } HRESULT CComponentDataObject::ChangeContainerNode(CContainerNode* pNodeToChange, long changeMask) { ASSERT(pNodeToChange != NULL); ASSERT(changeMask & CHANGE_RESULT_ITEM); ASSERT(m_pConsoleNameSpace != NULL); if (!pNodeToChange->AddedToScopePane()) { return S_OK; } SCOPEDATAITEM scopeDataItem; memset(&scopeDataItem, 0, sizeof(SCOPEDATAITEM)); scopeDataItem.ID = pNodeToChange->GetScopeID(); ASSERT(scopeDataItem.ID != 0); if (changeMask & CHANGE_RESULT_ITEM_DATA) { scopeDataItem.mask |= SDI_STR; scopeDataItem.displayname = MMC_CALLBACK; } if (changeMask & CHANGE_RESULT_ITEM_ICON) { scopeDataItem.mask |= SDI_IMAGE; scopeDataItem.nImage = pNodeToChange->GetImageIndex(FALSE); scopeDataItem.mask |= SDI_OPENIMAGE; scopeDataItem.nOpenImage = pNodeToChange->GetImageIndex(TRUE); } return m_pConsoleNameSpace->SetItem(&scopeDataItem); } HRESULT CComponentDataObject::AddLeafNode(CLeafNode* pNodeToAdd) { // will have to broadcast to all views ASSERT(pNodeToAdd != NULL); return UpdateAllViewsHelper(reinterpret_cast(pNodeToAdd), ADD_RESULT_ITEM); } HRESULT CComponentDataObject::DeleteLeafNode(CLeafNode* pNodeToDelete) { // will have to broadcast to all views ASSERT(pNodeToDelete != NULL); return UpdateAllViewsHelper(reinterpret_cast(pNodeToDelete), DELETE_RESULT_ITEM); } HRESULT CComponentDataObject::ChangeLeafNode(CLeafNode* pNodeToChange, long changeMask) { // will have to broadcast to all views ASSERT(pNodeToChange != NULL); return UpdateAllViewsHelper(reinterpret_cast(pNodeToChange), changeMask); } HRESULT CComponentDataObject::UpdateVerbState(CTreeNode* pNodeToChange) { // will have to broadcast to all views ASSERT(pNodeToChange != NULL); return UpdateAllViewsHelper(reinterpret_cast(pNodeToChange), UPDATE_VERB_STATE); } HRESULT CComponentDataObject::SetDescriptionBarText(CTreeNode* pTreeNode) { ASSERT(pTreeNode != NULL); return UpdateAllViewsHelper(reinterpret_cast(pTreeNode), UPDATE_DESCRIPTION_BAR); } HRESULT CComponentDataObject::SortResultPane(CContainerNode* pContainerNode) { ASSERT(pContainerNode != NULL); return UpdateAllViewsHelper(reinterpret_cast(pContainerNode), SORT_RESULT_PANE); } HRESULT CComponentDataObject::UpdateResultPaneView(CContainerNode* pContainerNode) { ASSERT(pContainerNode != NULL); return UpdateAllViewsHelper(reinterpret_cast(pContainerNode), UPDATE_RESULT_PANE_VIEW); } void CComponentDataObject::InitializeScopeDataItem(LPSCOPEDATAITEM pScopeDataItem, HSCOPEITEM pParentScopeItem, LPARAM lParam, int nImage, int nOpenImage, BOOL bHasChildren) { ASSERT(pScopeDataItem != NULL); memset(pScopeDataItem, 0, sizeof(SCOPEDATAITEM)); // set parent scope item pScopeDataItem->mask |= SDI_PARENT; pScopeDataItem->relativeID = pParentScopeItem; // Add node name, we implement callback pScopeDataItem->mask |= SDI_STR; pScopeDataItem->displayname = MMC_CALLBACK; // Add the lParam pScopeDataItem->mask |= SDI_PARAM; pScopeDataItem->lParam = lParam; // Add close image if (nImage != -1) { pScopeDataItem->mask |= SDI_IMAGE; pScopeDataItem->nImage = nImage; } // Add open image if (nOpenImage != -1) { pScopeDataItem->mask |= SDI_OPENIMAGE; pScopeDataItem->nOpenImage = nOpenImage; } // Add button to node if the folder has children if (bHasChildren == TRUE) { pScopeDataItem->mask |= SDI_CHILDREN; pScopeDataItem->cChildren = 1; } } /////////////////////////////////////////////////////////////////////////////// // Timer and Background Thread BOOL CComponentDataObject::StartTimerThread() { ASSERT(::IsWindow(m_hWnd)); m_pTimerThreadObj = OnCreateTimerThread(); if (m_pTimerThreadObj == NULL) { return TRUE; } // start the the thread if (!m_pTimerThreadObj->Start(m_hWnd)) { return FALSE; } ASSERT(m_pTimerThreadObj->m_nThreadID != 0); m_nTimerThreadID = m_pTimerThreadObj->m_nThreadID; WaitForTimerThreadStartAck(); return SetTimer(); } void CComponentDataObject::ShutDownTimerThread() { KillTimer(); PostMessageToTimerThread(WM_QUIT, 0,0); // // Wait for the thread to die or else we could AV since there may be more // messages in the queue than just the WM_QUIT // if (m_pTimerThreadObj != NULL) { DWORD dwRetState = ::WaitForSingleObject(m_pTimerThreadObj->m_hThread,INFINITE); ASSERT(dwRetState != WAIT_FAILED); } // // Threads now gone, delete the thread object // delete m_pTimerThreadObj; m_pTimerThreadObj = NULL; } BOOL CComponentDataObject::PostMessageToTimerThread(UINT Msg, WPARAM wParam, LPARAM lParam) { if (m_nTimerThreadID != 0) { return ::PostThreadMessage(m_nTimerThreadID, Msg, wParam, lParam); } return TRUE; } BOOL CComponentDataObject::SetTimer() { ASSERT(::IsWindow(m_hWnd)); ASSERT(m_hiddenWnd.m_nTimerID == 0); m_dwTimerTime = 0; DWORD dwTimerIntervalMillisec = m_dwTimerInterval*1000; m_hiddenWnd.m_nTimerID = m_hiddenWnd.SetTimer(1, dwTimerIntervalMillisec); return (m_hiddenWnd.m_nTimerID != 0); } void CComponentDataObject::KillTimer() { ASSERT(::IsWindow(m_hWnd)); if (m_hiddenWnd.m_nTimerID != 0) { VERIFY(m_hiddenWnd.KillTimer(static_cast(m_hiddenWnd.m_nTimerID))); m_hiddenWnd.m_nTimerID = 0; } } void CComponentDataObject::WaitForTimerThreadStartAck() { MSG tempMSG; ASSERT(!m_bTimerThreadStarted); while(!m_bTimerThreadStarted) { if (::PeekMessage(&tempMSG,m_hWnd,CHiddenWnd::s_TimerThreadMessage, CHiddenWnd::s_TimerThreadMessage, PM_REMOVE)) { DispatchMessage(&tempMSG); } } } void CComponentDataObject::WaitForThreadExitMessage(CMTContainerNode* pNode) { MSG tempMSG; while(GetRunningThreadTable()->IsPresent(pNode)) { if (::PeekMessage(&tempMSG, m_hiddenWnd.m_hWnd, CHiddenWnd::s_NodeThreadHaveDataNotificationMessage, CHiddenWnd::s_NodeThreadExitingNotificationMessage, PM_REMOVE)) { DispatchMessage(&tempMSG); } } // while } /////////////////////////////////////////////////////////////////////////////// // CComponentObject implementation /////////////////////////////////////////////////////////////////////////////// #ifdef _DEBUG_REFCOUNT unsigned int CComponentObject::m_nOustandingObjects = 0; #endif // _DEBUG_REFCOUNT CComponentObject::CComponentObject() { #ifdef _DEBUG_REFCOUNT dbg_cRef = 0; ++m_nOustandingObjects; TRACE(_T("CComponentObject(), count = %d\n"),m_nOustandingObjects); #endif // _DEBUG_REFCOUNT Construct(); } CComponentObject::~CComponentObject() { #ifdef _DEBUG_REFCOUNT --m_nOustandingObjects; TRACE(_T("~CComponentObject(), count = %d\n"),m_nOustandingObjects); #endif // _DEBUG_REFCOUNT // Make sure the interfaces have been released ASSERT(m_pConsole == NULL); ASSERT(m_pHeader == NULL); //SAFE_RELEASE(m_pComponentData); // QI'ed in IComponentDataImpl::CreateComponent if (m_pComponentData != NULL) { m_pComponentData->Release(); m_pComponentData = NULL; TRACE(_T("~CComponentObject() released m_pCompomentData\n")); } Construct(); } void CComponentObject::Construct() { m_pConsole = NULL; m_pHeader = NULL; m_pResult = NULL; m_pImageResult = NULL; m_pComponentData = NULL; m_pToolbar = NULL; m_pControlbar = NULL; m_pConsoleVerb = NULL; m_pSelectedContainerNode = NULL; m_pSelectedNode = NULL; m_selectedType = CCT_UNINITIALIZED; } /////////////////////////////////////////////////////////////////////////////// // CComponentObject::IComponent members STDMETHODIMP CComponentObject::Initialize(LPCONSOLE lpConsole) { ASSERT(lpConsole != NULL); AFX_MANAGE_STATE(AfxGetStaticModuleState()); // Save the IConsole pointer m_pConsole = lpConsole; m_pConsole->AddRef(); // QI for a IHeaderCtrl HRESULT hr = m_pConsole->QueryInterface(IID_IHeaderCtrl, reinterpret_cast(&m_pHeader)); // Give the console the header control interface pointer if (SUCCEEDED(hr)) { m_pConsole->SetHeader(m_pHeader); } m_pConsole->QueryInterface(IID_IResultData, reinterpret_cast(&m_pResult)); hr = m_pConsole->QueryResultImageList(&m_pImageResult); ASSERT(hr == S_OK); hr = m_pConsole->QueryConsoleVerb(&m_pConsoleVerb); ASSERT(hr == S_OK); return S_OK; } STDMETHODIMP CComponentObject::Notify(LPDATAOBJECT lpDataObject, MMC_NOTIFY_TYPE event, LPARAM arg, LPARAM param) { HRESULT hr = S_OK; AFX_MANAGE_STATE(AfxGetStaticModuleState()); if (event == MMCN_PROPERTY_CHANGE) { ASSERT(lpDataObject == NULL); hr = OnPropertyChange(param, static_cast(arg)); } else if (event == MMCN_VIEW_CHANGE) { hr = OnUpdateView(lpDataObject,arg,param); } else if (event == MMCN_DESELECT_ALL) { TRACE(_T("CComponentObject::Notify -> MMCN_DESELECT_ALL \n")); } else if (event == MMCN_COLUMN_CLICK) { OnColumnSortChanged(arg, param); } else if (event == MMCN_CUTORMOVE) { hr = S_FALSE; } else if (lpDataObject != NULL) { CInternalFormatCracker ifc; ifc.Extract(lpDataObject); if (ifc.GetCookieCount() < 1) { CComponentDataObject* pComponentDataObject = (CComponentDataObject*)m_pComponentData; if ( (event == MMCN_ADD_IMAGES) && pComponentDataObject->IsExtensionSnapin() ) { CTreeNode* pTreeNode = pComponentDataObject->GetRootData(); return InitializeBitmaps(pTreeNode); // cookie for the root } return S_OK; } switch(event) { case MMCN_ACTIVATE: break; case MMCN_CLICK: OnResultItemClk(ifc, FALSE); break; case MMCN_DBLCLICK: hr = S_FALSE; break; case MMCN_ADD_IMAGES: OnAddImages(ifc, arg, param); break; case MMCN_SHOW: hr = OnShow(ifc, arg, param); break; case MMCN_COLUMNS_CHANGED: hr = OnColumnsChanged(ifc, arg, param); break; case MMCN_MINIMIZED: hr = OnMinimize(ifc, arg, param); break; case MMCN_SELECT: HandleStandardVerbs( (BOOL) LOWORD(arg)/*bScope*/, (BOOL) HIWORD(arg)/*bSelect*/,lpDataObject); break; case MMCN_QUERY_PASTE: hr = S_FALSE; break; case MMCN_PASTE: AfxMessageBox(_T("CComponentObject::MMCN_PASTE")); break; case MMCN_DELETE: // just delegate to the component data object hr = ((CComponentDataObject*)m_pComponentData)->OnDeleteVerbHandler( ifc, this); break; case MMCN_REFRESH: // just delegate to the component data object hr = ((CComponentDataObject*)m_pComponentData)->OnRefreshVerbHandler( ifc); // // Once the refresh has begun, update the verbs associated with the // object being refreshed. // HandleStandardVerbs( (BOOL) LOWORD(arg)/*bScope*/, (BOOL) HIWORD(arg)/*bSelect*/,lpDataObject); break; case MMCN_RENAME: // just delegate to the component data object hr = ((CComponentDataObject*)m_pComponentData)->OnRename(ifc, arg, param); break; case MMCN_CONTEXTHELP: // just delegate to the component data object hr = ((CComponentDataObject*)m_pComponentData)->OnHelpHandler(ifc, this); break; default: hr = E_UNEXPECTED; break; } } return hr; } STDMETHODIMP CComponentObject::Destroy(MMC_COOKIE) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); // // Release the interfaces that we QI'ed // if (m_pConsole != NULL) { // // Tell the console to release the header control interface // m_pConsole->SetHeader(NULL); SAFE_RELEASE(m_pHeader); SAFE_RELEASE(m_pToolbar); SAFE_RELEASE(m_pControlbar); SAFE_RELEASE(m_pResult); SAFE_RELEASE(m_pImageResult); SAFE_RELEASE(m_pConsoleVerb); // Release the IConsole interface last SAFE_RELEASE(m_pConsole); } return S_OK; } STDMETHODIMP CComponentObject::GetResultViewType(MMC_COOKIE cookie, LPOLESTR* ppViewType, long* pViewOptions) { CTreeNode* pNode; if (cookie == NULL) { pNode = ((CComponentDataObject*)m_pComponentData)->GetRootData(); } else { pNode = reinterpret_cast(cookie); } ASSERT(pNode != NULL); if (pNode != NULL) { return pNode->GetResultViewType((CComponentDataObject*)m_pComponentData, ppViewType, pViewOptions); } // Use default view if (((CComponentDataObject*)m_pComponentData)->IsMultiSelect()) { *pViewOptions = MMC_VIEW_OPTIONS_MULTISELECT; } else { *pViewOptions = MMC_VIEW_OPTIONS_NONE; } *ppViewType = NULL; return S_FALSE; } STDMETHODIMP CComponentObject::QueryDataObject(MMC_COOKIE cookie, DATA_OBJECT_TYPES type, LPDATAOBJECT* ppDataObject) { HRESULT hr = S_OK; ASSERT(ppDataObject != NULL); CComObject* pObject; CComObject::CreateInstance(&pObject); ASSERT(pObject != NULL); if (pObject != NULL) { CTreeNode* pNode = NULL; if (cookie == MMC_MULTI_SELECT_COOKIE) { TRACE(_T("CDSEvent::GetDataObject() - multi-select.\n")); RESULTDATAITEM rdi; ZeroMemory(&rdi, sizeof(rdi)); rdi.mask = RDI_STATE; rdi.nIndex = -1; rdi.nState = LVIS_SELECTED; do { rdi.lParam = 0; ASSERT(rdi.mask == RDI_STATE); ASSERT(rdi.nState == LVIS_SELECTED); hr = m_pResult->GetNextItem(&rdi); if (hr != S_OK) break; pNode = reinterpret_cast(rdi.lParam); pObject->AddCookie(pNode); } while (1); // addref() the new pointer and return it. pObject->AddRef(); *ppDataObject = pObject; } else { // Delegate it to the IComponentData implementation ASSERT(m_pComponentData != NULL); hr = m_pComponentData->QueryDataObject(cookie, type, ppDataObject); } } return hr; } STDMETHODIMP CComponentObject::GetDisplayInfo(LPRESULTDATAITEM pResultDataItem) { ASSERT(pResultDataItem != NULL); CTreeNode* pNode = reinterpret_cast(pResultDataItem->lParam); ASSERT(pNode != NULL); ASSERT(pResultDataItem->bScopeItem == pNode->IsContainer()); if (pResultDataItem->mask & RDI_STR) { LPCWSTR lpszString = pNode->GetString(pResultDataItem->nCol); if (lpszString != NULL) { pResultDataItem->str = (LPWSTR)lpszString; } } if ((pResultDataItem->mask & RDI_IMAGE) && (pResultDataItem->nCol == 0)) { pResultDataItem->nImage = pNode->GetImageIndex(FALSE); } return S_OK; } STDMETHODIMP CComponentObject::CompareObjects(LPDATAOBJECT lpDataObjectA, LPDATAOBJECT lpDataObjectB) { // Delegate it to the IComponentData implementation ASSERT(m_pComponentData != NULL); return m_pComponentData->CompareObjects(lpDataObjectA, lpDataObjectB); } /////////////////////////////////////////////////////////////////////////////// // Message handlers for CComponentObject::IComponent::Notify() HRESULT CComponentObject::OnFolder(CTreeNode*, LPARAM, LPARAM) { ASSERT(FALSE); return S_OK; } HRESULT CComponentObject::OnShow(CInternalFormatCracker& ifc, LPARAM arg, LPARAM) { HRESULT hr = S_OK; ASSERT(ifc.GetCookieCount() == 1); // // I shouldn't have to deal with multiple select here // CTreeNode* pNode = ifc.GetCookieAt(0); ASSERT(pNode != NULL); ASSERT(pNode->IsContainer()); CContainerNode* pContainerNode = (CContainerNode*)pNode; // Note - arg is TRUE when it is time to enumerate if (arg == TRUE) { long lResultView; LPOLESTR lpoleResultView = NULL; pNode->GetResultViewType((CComponentDataObject*)m_pComponentData, &lpoleResultView, &lResultView); if (lResultView == MMC_VIEW_OPTIONS_NONE || lResultView == MMC_VIEW_OPTIONS_MULTISELECT) { // Show the headers for this nodetype InitializeHeaders(pContainerNode); EnumerateResultPane(pContainerNode); m_pSelectedContainerNode = pContainerNode; SetDescriptionBarText(pContainerNode); } else { m_pSelectedContainerNode = pContainerNode; hr = pNode->OnShow(m_pConsole); } } else { // Removed by JEFFJON : new column header implementation // if we want we can notify ourselves that the focus is being lost // SaveHeadersInfo(pContainerNode); m_pSelectedContainerNode = NULL; // Free data associated with the result pane items, because // your node is no longer being displayed. // Note: The console will remove the items from the result pane } #ifdef _DEBUG if (m_pSelectedContainerNode == NULL) TRACE(_T("NULL selection\n")); else TRACE(_T("Node <%s> selected\n"), m_pSelectedContainerNode->GetDisplayName()); #endif return hr; } HRESULT CComponentObject::OnColumnsChanged(CInternalFormatCracker& ifc, LPARAM, LPARAM param) { CTreeNode* pNode = ifc.GetCookieAt(0); ASSERT(pNode != NULL); ASSERT(pNode->IsContainer()); CContainerNode* pContainerNode = (CContainerNode*)pNode; MMC_VISIBLE_COLUMNS* pVisibleCols = reinterpret_cast(param); pContainerNode->OnColumnsChanged(pVisibleCols->rgVisibleCols, pVisibleCols->nVisibleColumns); return S_OK; } HRESULT CComponentObject::OnColumnSortChanged(LPARAM, LPARAM) { return S_OK; } HRESULT CComponentObject::ForceSort(UINT iCol, DWORD dwDirection) { HRESULT hr = m_pResult->Sort(iCol, dwDirection, NULL); return hr; } HRESULT CComponentObject::OnActivate(CTreeNode*, LPARAM, LPARAM) { ASSERT(FALSE); return S_OK; } HRESULT CComponentObject::OnResultItemClk(CInternalFormatCracker&, BOOL) { return S_OK; } HRESULT CComponentObject::OnMinimize(CInternalFormatCracker&, LPARAM, LPARAM) { return S_OK; } HRESULT CComponentObject::OnPropertyChange(LPARAM param, long fScopePane) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); ASSERT(param != NULL); #ifdef _DEBUG TRACE(_T("CComponentObject::OnPropertyChange()\n")); CPropertyPageHolderBase* pPPHolder = reinterpret_cast(param); ASSERT(pPPHolder != NULL); CTreeNode* pNode = pPPHolder->GetTreeNode(); ASSERT(pNode != NULL); // the item must be a result item and in the result pane ASSERT(!fScopePane); #endif // we delegate the call to the IComponentData implementation CComponentDataObject* pComponentDataObject = (CComponentDataObject*)m_pComponentData; ASSERT(pComponentDataObject != NULL); return pComponentDataObject->OnPropertyChange(param, fScopePane); } HRESULT CComponentObject::OnUpdateView(LPDATAOBJECT, LPARAM data, LONG_PTR hint) { if (m_pSelectedContainerNode == NULL) { return S_OK; // no selection for our IComponentData } if (hint == DELETE_ALL_RESULT_ITEMS) { // data contains the container whose result pane has to be refreshed CContainerNode* pNode = reinterpret_cast(data); ASSERT(pNode != NULL); // do it only if selected and we are using the standard list view, // if not, reselecting will do a delete/enumeration long lResultView; LPOLESTR lpoleResultView = NULL; pNode->GetResultViewType((CComponentDataObject*)m_pComponentData, &lpoleResultView, &lResultView); if (m_pSelectedContainerNode == pNode && (lResultView == MMC_VIEW_OPTIONS_NONE || lResultView == MMC_VIEW_OPTIONS_MULTISELECT)) { ASSERT(m_pResult != NULL); VERIFY(SUCCEEDED(m_pResult->DeleteAllRsltItems())); SetDescriptionBarText(pNode); } } else if (hint == SORT_RESULT_PANE) { // data contains the container whose result pane has to be refreshed CContainerNode* pNode = reinterpret_cast(data); ASSERT(pNode != NULL); // do it only if selected, if not, reselecting will do a delete/enumeration if (m_pSelectedContainerNode == pNode) { MMC_SORT_SET_DATA* pColumnSortData = NULL; // build the column id LPCWSTR lpszColumnID = pNode->GetColumnID(); size_t iLen = wcslen(lpszColumnID); // allocate memory for the struct and add on enough to make the byte[1] into a string // for the column id SColumnSetID* pColumnID = (SColumnSetID*)malloc(sizeof(SColumnSetID) + (iLen * sizeof(WCHAR))); memset(pColumnID, 0, sizeof(SColumnSetID) + (iLen * sizeof(WCHAR))); pColumnID->cBytes = static_cast(iLen * sizeof(WCHAR)); wcscpy((LPWSTR)pColumnID->id, lpszColumnID); // Get the sort column and direction IColumnData* pColumnData = NULL; HRESULT hr = m_pConsole->QueryInterface(IID_IColumnData, reinterpret_cast(&pColumnData)); if (pColumnData != NULL) hr = pColumnData->GetColumnSortData(pColumnID, &pColumnSortData); if (SUCCEEDED(hr)) { if (pColumnSortData != NULL) { UINT iCurrentSortColumn = pColumnSortData->pSortData->nColIndex; DWORD dwCurrentSortDirection = pColumnSortData->pSortData->dwSortOptions; VERIFY(SUCCEEDED(ForceSort(iCurrentSortColumn, dwCurrentSortDirection))); CoTaskMemFree(pColumnSortData); } } if (pColumnData != NULL) pColumnData->Release(); free(pColumnID); } } else if (hint == REPAINT_RESULT_PANE) { // data contains the container whose result pane has to be refreshed CContainerNode* pNode = reinterpret_cast(data); if (pNode == NULL) pNode = m_pSelectedContainerNode; // passing NULL means apply to the current selection // update all the leaf nodes in the result pane CNodeList* pChildList = ((CContainerNode*)pNode)->GetLeafChildList(); for( POSITION pos = pChildList->GetHeadPosition(); pos != NULL; ) { CLeafNode* pCurrentChild = (CLeafNode*)pChildList->GetNext(pos); ChangeResultPaneItem(pCurrentChild,CHANGE_RESULT_ITEM); } } else if ( hint == DELETE_MULTIPLE_RESULT_ITEMS) { CNodeList* pNodeList = reinterpret_cast(data); ASSERT(pNodeList != NULL); POSITION pos = pNodeList->GetHeadPosition(); while (pos != NULL) { CTreeNode* pNode = pNodeList->GetNext(pos); ASSERT(pNode != NULL); if (!pNode->IsContainer()) { DeleteResultPaneItem(static_cast(pNode)); } } SetDescriptionBarText(pNodeList->GetHead()->GetContainer()); } else if ( (hint == ADD_RESULT_ITEM) || (hint == DELETE_RESULT_ITEM) || (hint & CHANGE_RESULT_ITEM)) { // we deal with a leaf node CLeafNode* pNode = reinterpret_cast(data); ASSERT(pNode != NULL); // consider only if the parent is selected, otherwise will enumerate later when selected if (m_pSelectedContainerNode == pNode->GetContainer()) { if (hint & CHANGE_RESULT_ITEM) { ChangeResultPaneItem(pNode,hint); } else if ( hint == ADD_RESULT_ITEM) { AddResultPaneItem(pNode); SetDescriptionBarText(pNode); } else if ( hint == DELETE_RESULT_ITEM) { DeleteResultPaneItem(pNode); SetDescriptionBarText(pNode); } } } else if (hint == UPDATE_VERB_STATE) { CTreeNode* pTreeNode = reinterpret_cast(data); ASSERT(pTreeNode != NULL); if (m_pSelectedNode == pTreeNode) { ASSERT(m_selectedType != CCT_UNINITIALIZED); CNodeList nodeList; nodeList.AddTail(pTreeNode); m_pConsoleVerb->SetDefaultVerb(pTreeNode->GetDefaultVerb(m_selectedType, &nodeList)); pTreeNode->OnSetVerbState(m_pConsoleVerb, m_selectedType, &nodeList); } } else if (hint == UPDATE_DESCRIPTION_BAR) { CTreeNode* pTreeNode = reinterpret_cast(data); ASSERT(pTreeNode != NULL); SetDescriptionBarText(pTreeNode); } else if (hint == UPDATE_RESULT_PANE_VIEW) { CContainerNode* pNode = reinterpret_cast(data); ASSERT(pNode != NULL); HSCOPEITEM hScopeID = pNode->GetScopeID(); if (hScopeID != 0) { m_pConsole->SelectScopeItem(hScopeID); } } return S_OK; } HRESULT CComponentObject::SetDescriptionBarText(CTreeNode* pTreeNode) { ASSERT(pTreeNode != NULL); HRESULT hr = S_OK; if (m_pSelectedContainerNode == pTreeNode) { LPWSTR lpszText = pTreeNode->GetDescriptionBarText(); hr = m_pResult->SetDescBarText(lpszText); } else if (m_pSelectedContainerNode == pTreeNode->GetContainer()) { LPWSTR lpszText = pTreeNode->GetContainer()->GetDescriptionBarText(); hr = m_pResult->SetDescBarText(lpszText); } return hr; } HRESULT CComponentObject::OnAddImages(CInternalFormatCracker& ifc, LPARAM, LPARAM) { CTreeNode* pNode = ifc.GetCookieAt(0); ASSERT(pNode != NULL); return InitializeBitmaps(pNode); } void CComponentObject::HandleStandardVerbs(BOOL bScope, BOOL bSelect, LPDATAOBJECT lpDataObject) { if (lpDataObject == NULL) { return; } ((CComponentDataObject*)m_pComponentData)->HandleStandardVerbsHelper( this, m_pConsoleVerb, bScope, bSelect, lpDataObject); } void CComponentObject::EnumerateResultPane(CContainerNode* pContainerNode) { ASSERT(m_pResult != NULL); // make sure we QI'ed for the interfaces ASSERT(m_pComponentData != NULL); ASSERT(pContainerNode != NULL); // // get the list of children // subfolders already added by console, add only the leaf nodes // CNodeList* pChildList = pContainerNode->GetLeafChildList(); ASSERT(pChildList != NULL); POSITION pos; for( pos = pChildList->GetHeadPosition(); pos != NULL; ) { CTreeNode* pCurrChildNode = pChildList->GetNext(pos); ASSERT(pCurrChildNode != NULL); if(pCurrChildNode->IsVisible()) { VERIFY(SUCCEEDED(AddResultPaneItem((CLeafNode*)pCurrChildNode))); } } } HRESULT CComponentObject::AddResultPaneItem(CLeafNode* pNodeToInsert) { ASSERT(m_pResult != NULL); ASSERT(pNodeToInsert != NULL); RESULTDATAITEM resultItem; memset(&resultItem, 0, sizeof(RESULTDATAITEM)); resultItem.mask = RDI_STR | RDI_IMAGE | RDI_PARAM; resultItem.str = MMC_CALLBACK; //use close image index on result pane resultItem.nImage = pNodeToInsert->GetImageIndex(FALSE); resultItem.lParam = reinterpret_cast(pNodeToInsert); return m_pResult->InsertItem(&resultItem); } HRESULT CComponentObject::DeleteResultPaneItem(CLeafNode* pNodeToDelete) { ASSERT(m_pResult != NULL); ASSERT(pNodeToDelete != NULL); RESULTDATAITEM resultItem; memset(&resultItem, 0, sizeof(RESULTDATAITEM)); HRESULTITEM itemID; HRESULT hr = m_pResult->FindItemByLParam(reinterpret_cast(pNodeToDelete), &itemID); ASSERT(SUCCEEDED(hr)); if (FAILED(hr)) { return hr; } return m_pResult->DeleteItem(itemID,0 /* all cols */); } HRESULT CComponentObject::ChangeResultPaneItem(CLeafNode* pNodeToChange, LONG_PTR changeMask) { ASSERT(changeMask & CHANGE_RESULT_ITEM); ASSERT(m_pResult != NULL); ASSERT(pNodeToChange != NULL); HRESULTITEM itemID; HRESULT hr = m_pResult->FindItemByLParam(reinterpret_cast(pNodeToChange), &itemID); ASSERT(SUCCEEDED(hr)); if (FAILED(hr)) { return hr; } RESULTDATAITEM resultItem; memset(&resultItem, 0, sizeof(RESULTDATAITEM)); resultItem.itemID = itemID; if (changeMask & CHANGE_RESULT_ITEM_DATA) { // // UpdateItem() alone does not allow the // item string buffer to grow and you get "foo..." when // "foo" changes to "foobar" the first time (buffer grows) // resultItem.mask |= RDI_STR; resultItem.str = MMC_CALLBACK; // // this line asserts, use the one above ask Tony // //resultItem.str = (LPWSTR)pNodeToChange->GetDisplayName(); } if (changeMask & CHANGE_RESULT_ITEM_ICON) { resultItem.mask |= RDI_IMAGE; resultItem.nImage = pNodeToChange->GetImageIndex(FALSE); } hr = m_pResult->SetItem(&resultItem); ASSERT(SUCCEEDED(hr)); hr = m_pResult->UpdateItem(itemID); ASSERT(SUCCEEDED(hr)); return hr; } HRESULT CComponentObject::FindResultPaneItemID(CLeafNode* pNode, HRESULTITEM*) { ASSERT(FALSE); ASSERT(m_pResult != NULL); RESULTDATAITEM resultItem; memset(&resultItem, 0, sizeof(RESULTDATAITEM)); resultItem.mask = SDI_PARAM; resultItem.lParam = reinterpret_cast(pNode); HRESULT hr = m_pResult->GetItem(&resultItem); ASSERT(SUCCEEDED(hr)); return E_FAIL; } /////////////////////////////////////////////////////////////////////////////// // CComponentObject::IExtendPropertySheet2 members STDMETHODIMP CComponentObject::CreatePropertyPages(LPPROPERTYSHEETCALLBACK lpProvider, LONG_PTR handle, LPDATAOBJECT lpIDataObject) { // Delegate it to the IComponentData implementation ASSERT(m_pComponentData != NULL); IExtendPropertySheet2* pIExtendPropertySheet2; VERIFY(SUCCEEDED(m_pComponentData->QueryInterface(IID_IExtendPropertySheet2, reinterpret_cast(&pIExtendPropertySheet2)))); ASSERT(pIExtendPropertySheet2 != NULL); HRESULT hr = pIExtendPropertySheet2->CreatePropertyPages(lpProvider, handle, lpIDataObject); pIExtendPropertySheet2->Release(); return hr; } STDMETHODIMP CComponentObject::QueryPagesFor(LPDATAOBJECT lpDataObject) { // Delegate it to the IComponentData implementation ASSERT(m_pComponentData != NULL); IExtendPropertySheet2* pIExtendPropertySheet2; VERIFY(SUCCEEDED(m_pComponentData->QueryInterface(IID_IExtendPropertySheet2, reinterpret_cast(&pIExtendPropertySheet2)))); ASSERT(pIExtendPropertySheet2 != NULL); HRESULT hr = pIExtendPropertySheet2->QueryPagesFor(lpDataObject); pIExtendPropertySheet2->Release(); return hr; } STDMETHODIMP CComponentObject::GetWatermarks(LPDATAOBJECT lpDataObject, HBITMAP* lphWatermark, HBITMAP* lphHeader, HPALETTE* lphPalette, BOOL* pbStretch) { // Delegate it to the IComponentData implementation ASSERT(m_pComponentData != NULL); IExtendPropertySheet2* pIExtendPropertySheet2; VERIFY(SUCCEEDED(m_pComponentData->QueryInterface(IID_IExtendPropertySheet2, reinterpret_cast(&pIExtendPropertySheet2)))); ASSERT(pIExtendPropertySheet2 != NULL); HRESULT hr = pIExtendPropertySheet2->GetWatermarks(lpDataObject, lphWatermark, lphHeader, lphPalette, pbStretch); pIExtendPropertySheet2->Release(); return hr; } /////////////////////////////////////////////////////////////////////////////// // CComponentObject::IExtendContextMenu members STDMETHODIMP CComponentObject::AddMenuItems(LPDATAOBJECT pDataObject, LPCONTEXTMENUCALLBACK pContextMenuCallback, long *pInsertionAllowed) { HRESULT hr = S_OK; CComPtr spContextMenuCallback2; hr = pContextMenuCallback->QueryInterface(IID_IContextMenuCallback2, (PVOID*)&spContextMenuCallback2); if (FAILED(hr)) { return hr; } if (pDataObject == DOBJ_CUSTOMOCX) { // // A custom result pane is being used and we don't know what node it cooresponds to so we assume that it // is the previously selected container. // ASSERT(m_pSelectedContainerNode != NULL); CTreeNode* pNode = (CTreeNode*)m_pSelectedContainerNode; CNodeList nodeList; nodeList.AddTail(pNode); hr = m_pSelectedContainerNode->OnAddMenuItems(spContextMenuCallback2, CCT_UNINITIALIZED, pInsertionAllowed, &nodeList); } else { // // Delegate it to the IComponentData implementation // ASSERT(m_pComponentData != NULL); IExtendContextMenu* pIExtendContextMenu; VERIFY(SUCCEEDED(m_pComponentData->QueryInterface(IID_IExtendContextMenu, reinterpret_cast(&pIExtendContextMenu)))); ASSERT(pIExtendContextMenu != NULL); hr = pIExtendContextMenu->AddMenuItems(pDataObject, pContextMenuCallback, pInsertionAllowed); pIExtendContextMenu->Release(); } return hr; } STDMETHODIMP CComponentObject::Command(long nCommandID, LPDATAOBJECT pDataObject) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); HRESULT hr = S_OK; if (pDataObject == DOBJ_CUSTOMOCX) { // // A custom result pane is being used and we don't know what node it cooresponds to so we assume that it // is the previously selected container. // ASSERT(m_pSelectedContainerNode != NULL); CTreeNode* pNode = (CTreeNode*)m_pSelectedContainerNode; CNodeList nodeList; nodeList.AddTail(pNode); hr = m_pSelectedContainerNode->OnCommand(nCommandID, CCT_UNINITIALIZED, (CComponentDataObject*)m_pComponentData, &nodeList); } else { // Delegate it to the IComponentData implementation ASSERT(m_pComponentData != NULL); IExtendContextMenu* pIExtendContextMenu; VERIFY(SUCCEEDED(m_pComponentData->QueryInterface(IID_IExtendContextMenu, reinterpret_cast(&pIExtendContextMenu)))); ASSERT(pIExtendContextMenu != NULL); hr = pIExtendContextMenu->Command(nCommandID, pDataObject); pIExtendContextMenu->Release(); } return hr; } /////////////////////////////////////////////////////////////////////////////// // CComponentObject::IExtendControlbar members STDMETHODIMP CComponentObject::SetControlbar(LPCONTROLBAR pControlbar) { HRESULT hr = S_OK; if (pControlbar == NULL) { // // Detach the controls here // if (m_pControlbar != NULL && m_pToolbar != NULL) { hr = m_pControlbar->Detach((IUnknown *) m_pToolbar); SAFE_RELEASE(m_pControlbar); } } else { // // Save the controlbar interface pointer // if (m_pControlbar == NULL) { m_pControlbar = pControlbar; m_pControlbar->AddRef(); } // // Do something here that checks to see if we have toolbars // already created and use those. If not then create one // and load everything necessary for it. // // // Create the toolbar // hr = m_pControlbar->Create (TOOLBAR, this, (IUnknown **) &m_pToolbar); if (SUCCEEDED(hr)) { // // Load the toolbar // AFX_MANAGE_STATE(AfxGetStaticModuleState()); hr = InitializeToolbar(m_pToolbar); if (FAILED(hr)) { hr = m_pControlbar->Detach((IUnknown*) m_pToolbar); SAFE_RELEASE(m_pControlbar); } } } return hr; } STDMETHODIMP CComponentObject::ControlbarNotify(MMC_NOTIFY_TYPE event, LPARAM arg, LPARAM param) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); HRESULT hr = S_OK; if (m_pControlbar == NULL) { return hr; } // // MMC provides two events here MMCN_SELECT at the time a node is selected // and MMCN_BTN_CLICK when a toolbar button is pressed // switch (event) { case MMCN_SELECT: { // // Attach the toolbar to the controlbar // hr = m_pControlbar->Attach(TOOLBAR, (IUnknown *) m_pToolbar); if (SUCCEEDED(hr)) { ASSERT(m_pToolbar != NULL); // // bSelect is TRUE if the node was selected, FALSE if the node was deselected // bScope is TRUE if the a scope node is selected, FALSE if a result node was selected // BOOL bSelect = HIWORD(arg); if (bSelect) { CInternalFormatCracker ifc; hr = ifc.Extract((LPDATAOBJECT)param); if (SUCCEEDED(hr)) { CTreeNode* pNode = ifc.GetCookieAt(0); ASSERT(pNode != NULL); CNodeList nodeList; ifc.GetCookieList(nodeList); if (ifc.GetCookieCount() > 1) // multiple selection { ASSERT(pNode->GetContainer() != NULL); hr = pNode->GetContainer()->OnSetToolbarVerbState(m_pToolbar, &nodeList); } else if (ifc.GetCookieCount() == 1) // single selection { hr = pNode->OnSetToolbarVerbState(m_pToolbar, &nodeList); } } } } break; } case MMCN_BTN_CLICK: { // // The arg is -1 for custom views like MessageView // if (DOBJ_CUSTOMOCX == (LPDATAOBJECT)arg) { if (m_pSelectedContainerNode != NULL) { CNodeList nodeList; nodeList.AddTail(m_pSelectedContainerNode); hr = m_pSelectedContainerNode->ToolbarNotify(static_cast(param), (CComponentDataObject*)m_pComponentData, &nodeList); } else { hr = S_FALSE; } } else { CInternalFormatCracker ifc; hr = ifc.Extract((LPDATAOBJECT)arg); CTreeNode* pNode = ifc.GetCookieAt(0); ASSERT(pNode != NULL); CNodeList nodeList; ifc.GetCookieList(nodeList); if (ifc.GetCookieCount() > 1) // multiple selection { ASSERT(pNode->GetContainer() != NULL); hr = pNode->GetContainer()->ToolbarNotify(static_cast(param), (CComponentDataObject*)m_pComponentData, &nodeList); } else if (ifc.GetCookieCount() == 1) // single selection { hr = pNode->ToolbarNotify(static_cast(param), (CComponentDataObject*)m_pComponentData, &nodeList); } else { hr = S_FALSE; } } break; } default: { break; } } return hr; } /////////////////////////////////////////////////////////////////////////////// // CComponentObject::IResultDataCompareEx members // This compare is used to sort the item's in the listview // // Note: Assum sort is ascending when comparing. STDMETHODIMP CComponentObject::Compare(RDCOMPARE* prdc, int* pnResult) { if (pnResult == NULL) { ASSERT(FALSE); return E_POINTER; } if (prdc == NULL) { ASSERT(FALSE); return E_POINTER; } CTreeNode* pNodeA = reinterpret_cast(prdc->prdch1->cookie); CTreeNode* pNodeB = reinterpret_cast(prdc->prdch2->cookie); ASSERT(pNodeA != NULL); ASSERT(pNodeB != NULL); CContainerNode* pContNode = pNodeA->GetContainer(); ASSERT(pContNode != NULL); // delegate the sorting to the container int nCol = prdc->nColumn; *pnResult = pContNode->Compare(pNodeA, pNodeB, nCol, prdc->lUserParam); return S_OK; } /////////////////////////////////////////////////////////////////////////////// // CComponentObject Helpers // This wrapper function required to make prefast shut up when we are // initializing a critical section in a constructor. void ExceptionPropagatingInitializeCriticalSection(LPCRITICAL_SECTION critsec) { __try { ::InitializeCriticalSection(critsec); } // // propagate the exception to our caller. // __except (EXCEPTION_CONTINUE_SEARCH) { } }