//+-------------------------------------------------------------------------
//
//  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<CMTContainerNode*>(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<CMTContainerNode*>(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<CMTContainerNode*>(wParam);
	DWORD dwErr = static_cast<DWORD>(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<CPropertyPageHolderBase*>(wParam);
	ASSERT(pPPHolder != NULL);
	HWND hWnd = reinterpret_cast<HWND>(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<CPropertyPageHolderBase*>(wParam);
	ASSERT(pPPHolder != NULL);
	CTreeNode* pNode = reinterpret_cast<CTreeNode*>(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<CExecContext*>(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<CMTContainerNode*>(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<void**>(&m_pConsoleNameSpace));
	ASSERT(hr == S_OK);
	ASSERT(m_pConsoleNameSpace != NULL);
  hr = pUnknown->QueryInterface(IID_IConsole2, reinterpret_cast<void**>(&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<long>(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<CDataObject>* pObject;

  CComObject<CDataObject>::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<CTreeNode*>(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<void**>(ppDataObject));
}


STDMETHODIMP CComponentDataObject::GetDisplayInfo(SCOPEDATAITEM* pScopeDataItem)
{
	ASSERT(pScopeDataItem != NULL);
  CTreeNode* pNode = reinterpret_cast<CTreeNode*>(pScopeDataItem->lParam);
	ASSERT(pNode != NULL);
	ASSERT(pNode->IsContainer());

	ASSERT(pScopeDataItem->mask & SDI_STR);
  pScopeDataItem->displayname = const_cast<LPWSTR>(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<CContainerNode*>(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<LONG_PTR>(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<MMC_EXPANDSYNC_STRUCT*>(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<CPropertyPageHolderBase*>(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<IPropertySheetProvider> spSheetProvider;
	hr = m_pConsole->QueryInterface(IID_IPropertySheetProvider,(void**)&spSheetProvider);
	ASSERT(SUCCEEDED(hr));
	ASSERT(spSheetProvider != NULL);

  //
	// get an interface to a sheet callback
  //
	CComPtr<IPropertySheetCallback> spSheetCallback;
	hr = m_pConsole->QueryInterface(IID_IPropertySheetCallback,(void**)&spSheetCallback);
	ASSERT(SUCCEEDED(hr));
	ASSERT(spSheetCallback != NULL);


  //
	// get a sheet
  //
  MMC_COOKIE cookie = reinterpret_cast<MMC_COOKIE>(pNode);
  DATA_OBJECT_TYPES type = (pNode->IsContainer()) ? CCT_SCOPE : CCT_RESULT;

  CComPtr<IDataObject> 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<LONG_PTR>(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<IContextMenuCallback2> 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<CDummyDataObject>* pObject;
  CComObject<CDummyDataObject>::CreateInstance(&pObject);
  ASSERT(pObject != NULL);

	IDataObject* pDataObject;
  HRESULT hr = pObject->QueryInterface(IID_IDataObject, reinterpret_cast<void**>(&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<LONG_PTR>(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<LONG_PTR>(pNode), REPAINT_RESULT_PANE);
}

HRESULT CComponentDataObject::DeleteAllResultPaneItems(CContainerNode* pNode)
{
	ASSERT(pNode != NULL);
	return UpdateAllViewsHelper(reinterpret_cast<LONG_PTR>(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<LPARAM>(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<LPARAM>(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<LONG_PTR>(pNodeToAdd), ADD_RESULT_ITEM);
}

HRESULT CComponentDataObject::DeleteLeafNode(CLeafNode* pNodeToDelete)
{
	// will have to broadcast to all views
	ASSERT(pNodeToDelete != NULL);
	return UpdateAllViewsHelper(reinterpret_cast<LONG_PTR>(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<LONG_PTR>(pNodeToChange), changeMask);
}

HRESULT CComponentDataObject::UpdateVerbState(CTreeNode* pNodeToChange)
{
	// will have to broadcast to all views
	ASSERT(pNodeToChange != NULL);
	return UpdateAllViewsHelper(reinterpret_cast<LONG_PTR>(pNodeToChange), UPDATE_VERB_STATE);
}

HRESULT CComponentDataObject::SetDescriptionBarText(CTreeNode* pTreeNode)
{
  ASSERT(pTreeNode != NULL);
  return UpdateAllViewsHelper(reinterpret_cast<LONG_PTR>(pTreeNode), UPDATE_DESCRIPTION_BAR);
}

HRESULT CComponentDataObject::SortResultPane(CContainerNode* pContainerNode)
{
	ASSERT(pContainerNode != NULL);
	return UpdateAllViewsHelper(reinterpret_cast<LONG_PTR>(pContainerNode), SORT_RESULT_PANE);
}

HRESULT CComponentDataObject::UpdateResultPaneView(CContainerNode* pContainerNode)
{
  ASSERT(pContainerNode != NULL);
  return UpdateAllViewsHelper(reinterpret_cast<LONG_PTR>(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<UINT>(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<void**>(&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<void**>(&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<ULONG>(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<CTreeNode*>(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<CDataObject>* pObject;
  CComObject<CDataObject>::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<CTreeNode*>(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<CTreeNode*>(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<MMC_VISIBLE_COLUMNS*>(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<CPropertyPageHolderBase*>(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<CContainerNode*>(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<CContainerNode*>(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<DWORD>(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<void**>(&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<CContainerNode*>(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<CNodeList*>(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<CLeafNode*>(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<CLeafNode*>(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<CTreeNode*>(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<CTreeNode*>(data);
    ASSERT(pTreeNode != NULL);
    SetDescriptionBarText(pTreeNode);
  }
  else if (hint == UPDATE_RESULT_PANE_VIEW)
  {
    CContainerNode* pNode = reinterpret_cast<CContainerNode*>(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<LPARAM>(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<LPARAM>(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<LPARAM>(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<LPARAM>(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<void**>(&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<void**>(&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<void**>(&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<IContextMenuCallback2> 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<void**>(&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<void**>(&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<int>(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<int>(param), 
                                                      (CComponentDataObject*)m_pComponentData,
                                                      &nodeList);
          }
          else if (ifc.GetCookieCount() == 1) // single selection
          {
            hr = pNode->ToolbarNotify(static_cast<int>(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<CTreeNode*>(prdc->prdch1->cookie);
	CTreeNode* pNodeB = reinterpret_cast<CTreeNode*>(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)
   {
   }
}