403 lines
12 KiB
C++
403 lines
12 KiB
C++
// Copyright (c) 1998-1999 Microsoft Corporation
|
|
|
|
#include "StdAfx.h"
|
|
#include <windows.h>
|
|
#include <process.h>
|
|
#include <atlbase.h>
|
|
|
|
#include "DataObj.h"
|
|
#include "CompData.h"
|
|
#include "DataSrc.h"
|
|
#include "SysInfo.h"
|
|
#include "gathint.h"
|
|
#include "gather.h"
|
|
#include "thread.h"
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Construction and destruction. Cancel any refresh in progress while
|
|
// destructing the object.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
CThreadingRefresh::CThreadingRefresh(CDataGatherer * pGatherer) : m_pGatherer(pGatherer)
|
|
{
|
|
InitializeCriticalSection(&m_csThreadRefresh);
|
|
|
|
// Generate a system wide unique name for the events (in case there are multiple
|
|
// instances of MSInfo running). If we can't generate a GUID for this, use the tick count.
|
|
// Unique name generation fixes bug 394884.
|
|
|
|
CString strEvent(_T(""));
|
|
GUID guid;
|
|
|
|
if (SUCCEEDED(::CoCreateGuid(&guid)))
|
|
{
|
|
LPOLESTR lpGUID;
|
|
|
|
if (SUCCEEDED(StringFromCLSID(guid, &lpGUID)))
|
|
{
|
|
strEvent = lpGUID;
|
|
CoTaskMemFree(lpGUID);
|
|
}
|
|
}
|
|
|
|
if (strEvent.IsEmpty())
|
|
strEvent.Format(_T("%08x"), ::GetTickCount());
|
|
|
|
m_pThreadInfo = new SThreadInfo;
|
|
m_pThreadInfo->m_eventDone = CreateEvent(NULL, TRUE, TRUE, CString(_T("MSInfoDone")) + strEvent);
|
|
m_pThreadInfo->m_eventStart = CreateEvent(NULL, TRUE, FALSE, CString(_T("MSInfoStart")) + strEvent);
|
|
m_pThreadInfo->m_pThreadRefresh = this;
|
|
|
|
m_hThread = NULL;
|
|
}
|
|
|
|
CThreadingRefresh::~CThreadingRefresh()
|
|
{
|
|
KillRefreshThread();
|
|
DeleteCriticalSection(&m_csThreadRefresh);
|
|
CloseHandle(m_pThreadInfo->m_eventDone);
|
|
CloseHandle(m_pThreadInfo->m_eventStart);
|
|
|
|
delete m_pThreadInfo;
|
|
m_pThreadInfo = NULL;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// This function lets the part of the other thread which updates the results
|
|
// pane whether or not it's safe to do so. If the refresh thread is currently
|
|
// updating the display, the UI thread should leave it alone. This is used
|
|
// by SetRefreshing().
|
|
//
|
|
// It's a global function, it's not pretty, but it's for functionality added
|
|
// to the system after it was done.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
BOOL CThreadingRefresh::ResultsPaneNotAvailable()
|
|
{
|
|
return ((WAIT_TIMEOUT != ::WaitForSingleObject(m_pThreadInfo->m_eventDone, 0)) && m_pThreadInfo->m_fShowData);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Is there currently a thread doing a refresh?
|
|
//-----------------------------------------------------------------------------
|
|
|
|
BOOL CThreadingRefresh::IsRefreshing()
|
|
{
|
|
return (WAIT_TIMEOUT == ::WaitForSingleObject(m_pThreadInfo->m_eventDone, 0));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Wait until the current refresh is done (or a long timeout period passes).
|
|
//-----------------------------------------------------------------------------
|
|
|
|
BOOL CThreadingRefresh::WaitForRefresh()
|
|
{
|
|
if (IsRefreshing())
|
|
return (WAIT_TIMEOUT != ::WaitForSingleObject(m_pThreadInfo->m_eventDone, 600000));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// What is the percentage complete? Not implemented at this time.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
int CThreadingRefresh::PerchentageComplete()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Refresh all of the data recursively from pFolder, not returning until the
|
|
// refresh is complete.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void CThreadingRefresh::RefreshAll(CFolder * pFolder, volatile BOOL * pfCancel)
|
|
{
|
|
CWaitCursor waitcursor;
|
|
|
|
if (pFolder && pFolder->GetType() == CDataSource::GATHERER)
|
|
{
|
|
RefreshFolderAsync(pFolder, NULL, TRUE, FALSE);
|
|
|
|
while (IsRefreshing())
|
|
{
|
|
if (pfCancel && *pfCancel)
|
|
{
|
|
CancelRefresh();
|
|
break;
|
|
}
|
|
|
|
Sleep(250);
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// A simple synchronous refresh on a folder. We don't return until it's done.
|
|
// We'll cancel any existing refresh in progress, though.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void CThreadingRefresh::RefreshFolder(CFolder * pFolder, BOOL fRecursive, BOOL fSoftRefresh)
|
|
{
|
|
if (IsRefreshing() && pFolder == m_pThreadInfo->m_pFolder)
|
|
return;
|
|
|
|
RefreshFolderAsync(pFolder, NULL, fRecursive, fSoftRefresh);
|
|
WaitForRefresh();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// This is an asynchronous refresh for a specified folder. It starts a thread
|
|
// to perform the refresh, and returns immediately.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
DWORD WINAPI ThreadRefresh(void * pArg);
|
|
void CThreadingRefresh::RefreshFolderAsync(CFolder * pFolder, CSystemInfo * pSysInfo, BOOL fRecursive, BOOL fSoftRefresh)
|
|
{
|
|
if (!m_pGatherer || !pFolder || pFolder->GetType() != CDataSource::GATHERER)
|
|
return;
|
|
|
|
// If we're currently refreshing this folder, don't bother.
|
|
|
|
if (m_pThreadInfo->m_pFolder == pFolder && IsRefreshing())
|
|
return;
|
|
|
|
// Stop any refresh in progress.
|
|
|
|
CancelRefresh();
|
|
|
|
// If this is a soft refresh, if the folder has already been refreshed,
|
|
// bounce out of here.
|
|
|
|
CWBEMFolder * pWBEMFolder = reinterpret_cast<CWBEMFolder *>(pFolder);
|
|
if (fSoftRefresh)
|
|
{
|
|
if (!pWBEMFolder || pWBEMFolder->m_fBeenRefreshed)
|
|
return;
|
|
|
|
if (pWBEMFolder->m_pCategory && pWBEMFolder->m_pCategory->HasBeenRefreshed())
|
|
return;
|
|
}
|
|
|
|
// Give some visual indicators that we're refreshing.
|
|
|
|
if (pSysInfo)
|
|
{
|
|
pSysInfo->ClearResultsPane();
|
|
pSysInfo->SetStatusText(IDS_REFRESHING_MSG);
|
|
if (pFolder && pFolder->GetChildNode() == NULL)
|
|
pSysInfo->SetRefreshing(pSysInfo->lparamRefreshIndicator);
|
|
}
|
|
|
|
// Set the fields in the shared memory. We know the thread is in a paused
|
|
// state (isn't using these fields) because of the CancelRefresh().
|
|
|
|
m_pThreadInfo->m_fShowData = FALSE;
|
|
m_pThreadInfo->m_fCancel = FALSE;
|
|
m_pThreadInfo->m_fQuit = FALSE;
|
|
m_pThreadInfo->m_pFolder = pFolder;
|
|
m_pThreadInfo->m_pSysInfo = pSysInfo;
|
|
m_pThreadInfo->m_pGatherer = m_pGatherer;
|
|
m_pThreadInfo->m_fRecursive = fRecursive;
|
|
m_pThreadInfo->m_fSoftRefresh = fSoftRefresh;
|
|
|
|
// If the thread hasn't been created, create it now. It will do the refresh,
|
|
// then pause waiting for m_eventStart to be triggered. Otherwise, just
|
|
// do the trigger so the thread wakes up and refreshes.
|
|
|
|
if (m_hThread == NULL)
|
|
{
|
|
m_pThreadInfo->m_fShowData = TRUE;
|
|
::ResetEvent(m_pThreadInfo->m_eventDone);
|
|
::ResetEvent(m_pThreadInfo->m_eventStart);
|
|
m_hThread = ::CreateThread(NULL, 0, ThreadRefresh, (LPVOID) m_pThreadInfo, 0, &m_dwThreadID);
|
|
}
|
|
else
|
|
{
|
|
::ResetEvent(m_pThreadInfo->m_eventDone);
|
|
::SetEvent(m_pThreadInfo->m_eventStart);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cancel a refresh in progress by setting the flag and waiting for thread to
|
|
// signal that it's done.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void CThreadingRefresh::CancelRefresh(BOOL fUpdateUI)
|
|
{
|
|
if (IsRefreshing())
|
|
{
|
|
if (fUpdateUI)
|
|
{
|
|
CWaitCursor waitcursor;
|
|
|
|
m_pThreadInfo->m_fCancel = TRUE;
|
|
if (!WaitForRefresh())
|
|
; // Something bad has happened.
|
|
}
|
|
else
|
|
{
|
|
m_pThreadInfo->m_fCancel = TRUE;
|
|
if (!WaitForRefresh())
|
|
; // Something bad has happened.
|
|
}
|
|
|
|
if (fUpdateUI && m_pThreadInfo->m_pSysInfo)
|
|
m_pThreadInfo->m_pSysInfo->SetStatusText(_T(""));
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Terminates the thread which does the refresh, presumably before exiting.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void CThreadingRefresh::KillRefreshThread()
|
|
{
|
|
CancelRefresh();
|
|
m_pThreadInfo->m_fQuit = TRUE;
|
|
m_pThreadInfo->m_fCancel = TRUE;
|
|
|
|
::SetEvent(m_pThreadInfo->m_eventStart);
|
|
if (WAIT_TIMEOUT == ::WaitForSingleObject(m_hThread, 60000))
|
|
::TerminateThread(m_hThread, 0);
|
|
|
|
::CloseHandle(m_hThread);
|
|
m_hThread = NULL;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// The thread function which actually performs the work.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
DWORD WINAPI ThreadRefresh(void * pArg)
|
|
{
|
|
SThreadInfo * pThreadInfo = (SThreadInfo *) pArg;
|
|
if (pThreadInfo == NULL)
|
|
return 0;
|
|
|
|
CoInitialize(NULL);
|
|
|
|
CDataProvider * pOurProvider = NULL;
|
|
CDataProvider * pLastExternalProvider = NULL;
|
|
CString strLastExternalComputer(_T(""));
|
|
|
|
while (!pThreadInfo->m_fQuit)
|
|
{
|
|
if (pThreadInfo->m_pFolder && pThreadInfo->m_pGatherer)
|
|
{
|
|
// If the provider pointer in the gatherer object has changed since
|
|
// we last did a refresh, we should recreate our own provider.
|
|
|
|
if (pLastExternalProvider != pThreadInfo->m_pGatherer->m_pProvider ||
|
|
strLastExternalComputer.CompareNoCase(pThreadInfo->m_pGatherer->m_strDeferredProvider) != 0)
|
|
{
|
|
// Get the computer name to which we connect.
|
|
|
|
strLastExternalComputer = pThreadInfo->m_pGatherer->m_strDeferredProvider;
|
|
if (pThreadInfo->m_pGatherer->m_pProvider)
|
|
strLastExternalComputer = pThreadInfo->m_pGatherer->m_pProvider->m_strComputer;
|
|
|
|
// Create a new CDataProvider for that computer.
|
|
|
|
if (pOurProvider)
|
|
delete pOurProvider;
|
|
|
|
pOurProvider = new CDataProvider;
|
|
if (pOurProvider)
|
|
pOurProvider->Create(strLastExternalComputer, pThreadInfo->m_pGatherer);
|
|
}
|
|
|
|
// Save the external provider pointer, and replace it with ours.
|
|
// The main thread shouldn't be touching it while we are running
|
|
// (i.e. when m_eventDone is not set).
|
|
|
|
pLastExternalProvider = pThreadInfo->m_pGatherer->m_pProvider;
|
|
pThreadInfo->m_pGatherer->m_pProvider = pOurProvider;
|
|
|
|
// Do the refresh on the folder.
|
|
|
|
if (pThreadInfo->m_pFolder->GetType() == CDataSource::GATHERER)
|
|
{
|
|
if (pThreadInfo->m_pGatherer && !pThreadInfo->m_fSoftRefresh)
|
|
pThreadInfo->m_pGatherer->SetLastError(GATH_ERR_NOERROR);
|
|
|
|
CWBEMFolder * pWBEMFolder = reinterpret_cast<CWBEMFolder *>(pThreadInfo->m_pFolder);
|
|
if (pWBEMFolder && pWBEMFolder->m_pCategory != NULL)
|
|
pWBEMFolder->m_pCategory->Refresh(pThreadInfo->m_fRecursive, &(pThreadInfo->m_fCancel), pThreadInfo->m_fSoftRefresh);
|
|
|
|
if (!pThreadInfo->m_fCancel)
|
|
pWBEMFolder->m_fBeenRefreshed = TRUE;
|
|
}
|
|
|
|
// Restore the external provider pointer.
|
|
|
|
pOurProvider = pThreadInfo->m_pGatherer->m_pProvider;
|
|
pThreadInfo->m_pGatherer->m_pProvider = pLastExternalProvider;
|
|
|
|
// Signal that we're done, to release the UI thread (if it's waiting to cancel us and start
|
|
// a new refresh).
|
|
|
|
::SetEvent(pThreadInfo->m_eventDone);
|
|
|
|
// Make sure we've cleared the UI thread updating the results pane by entering and immediately
|
|
// leaving the critical section.
|
|
|
|
if (pThreadInfo->m_pThreadRefresh)
|
|
{
|
|
pThreadInfo->m_pThreadRefresh->EnterCriticalSection();
|
|
pThreadInfo->m_pThreadRefresh->LeaveCriticalSection();
|
|
}
|
|
|
|
// If the refresh wasn't cancelled, and the user hasn't selected a
|
|
// different category while we were refreshing, then update the
|
|
// display. Note, we have to indicate that we're done before doing
|
|
// this.
|
|
|
|
if (!pThreadInfo->m_fCancel && pThreadInfo->m_pSysInfo && pThreadInfo->m_pSysInfo->m_pConsole2)
|
|
{
|
|
pThreadInfo->m_pSysInfo->SetStatusText(_T(""));
|
|
if (pThreadInfo->m_fShowData)
|
|
{
|
|
if (pThreadInfo->m_pSysInfo->m_pLastRefreshedFolder == pThreadInfo->m_pFolder)
|
|
{
|
|
pThreadInfo->m_pSysInfo->ClearResultsPane();
|
|
pThreadInfo->m_pSysInfo->SetResultHeaderColumns(pThreadInfo->m_pFolder);
|
|
pThreadInfo->m_pSysInfo->EnumerateValues(pThreadInfo->m_pFolder);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Invalidate the refresh (didn't get to display it).
|
|
|
|
CWBEMFolder * pWBEMFolder = reinterpret_cast<CWBEMFolder *>(pThreadInfo->m_pFolder);
|
|
if (pWBEMFolder)
|
|
pWBEMFolder->m_fBeenRefreshed = FALSE;
|
|
}
|
|
}
|
|
else
|
|
::SetEvent(pThreadInfo->m_eventDone);
|
|
|
|
pThreadInfo->m_fShowData = FALSE;
|
|
|
|
// Go to sleep until it's time to return to work.
|
|
|
|
::WaitForSingleObject(pThreadInfo->m_eventStart, INFINITE);
|
|
::ResetEvent(pThreadInfo->m_eventStart);
|
|
::ResetEvent(pThreadInfo->m_eventDone);
|
|
|
|
pThreadInfo->m_fShowData = TRUE;
|
|
|
|
if (!pThreadInfo->m_fCancel && !pThreadInfo->m_fQuit && pThreadInfo->m_pSysInfo)
|
|
pThreadInfo->m_pSysInfo->SetStatusText(IDS_REFRESHING_MSG);
|
|
}
|
|
|
|
if (pOurProvider)
|
|
delete pOurProvider;
|
|
|
|
CoUninitialize();
|
|
return 0;
|
|
}
|