windows-nt/Source/XPSP1/NT/shell/cpls/appwzdui/worker.cpp
2020-09-26 16:20:57 +08:00

359 lines
8.8 KiB
C++

// worker.cpp
//
// Implementation of the worker thread object
//
#include "priv.h"
// Do not build this file if on Win9X or NT4
#ifndef DOWNLEVEL_PLATFORM
#include "resource.h"
#include "worker.h"
//--------------------------------------------------------------------
//
//
// CWorkerThread class
//
//
//--------------------------------------------------------------------
// CWorkerThread constructor
CWorkerThread::CWorkerThread() : _cRef(1)
{
ASSERT(NULL == _hthreadWorker);
ASSERT(NULL == _hwndWorker);
ASSERT(FALSE == _fKillWorker);
ASSERT(NULL == _pwe);
ASSERT(0 == _cRefLockWorker);
InitializeCriticalSection(&_csWorker);
DllAddRef();
}
// CWorkerThread destructor
CWorkerThread::~CWorkerThread()
{
ASSERT(0 == _cRefLockWorker);
if (IsWindow(_hwndWorker))
{
DestroyWindow(_hwndWorker);
}
SetListenerWT(NULL);
DeleteCriticalSection(&_csWorker);
DllRelease();
}
BOOL CWorkerThread::PostWorkerMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
BOOL bRet = FALSE;
_LockWorker();
// Only if we are not being killed, and the worker window is valid we
// post the message, we don't want to post to NULL window (desktop)
if (!_fKillWorker && _hwndWorker)
bRet = PostMessage(_hwndWorker, uMsg, wParam, lParam);
_UnlockWorker();
return bRet;
}
/*--------------------------------------------------------------------
Purpose: IUnknown::QueryInterface
*/
STDMETHODIMP CWorkerThread::QueryInterface(REFIID riid, LPVOID * ppvObj)
{
static const QITAB qit[] = {
QITABENT(CWorkerThread, IARPWorker),
{ 0 },
};
return QISearch(this, (LPCQITAB)qit, riid, ppvObj);
}
STDMETHODIMP_(ULONG) CWorkerThread::AddRef()
{
LONG cRef = InterlockedIncrement(&_cRef);
TraceAddRef(CWorkerThread, cRef);
return cRef;
}
STDMETHODIMP_(ULONG) CWorkerThread::Release()
{
LONG cRef = InterlockedDecrement(&_cRef);
TraceRelease(CWorkerThread, cRef);
if (cRef)
return cRef;
delete this;
return 0;
}
/*-------------------------------------------------------------------------
Purpose: Sets the event listener so the worker thread can fire
events incrementally.
*/
HRESULT CWorkerThread::SetListenerWT(IWorkerEvent * pwe)
{
_LockWorker();
{
// We have to protect _pwe because it can be accessed by this
// thread and the main thread.
// Don't AddRef the event listener or we'll have a circular
// reference.
_pwe = pwe;
}
_UnlockWorker();
return S_OK;
}
/*-------------------------------------------------------------------------
Purpose: Start the thread
*/
HRESULT CWorkerThread::StartWT(int iPriority)
{
DWORD thid; // Not used but we have to pass something in
// Create a hidden top-level window to post messages to from the
// worker thread
_hwndWorker = SHCreateWorkerWindow(_WorkerWndProcWrapper, NULL, 0, 0, NULL, this);
if (_hwndWorker)
{
AddRef(); // AddRef myself, the background thread is responsible of releasing
// this ref count
// Kick off the worker thread to do the slow enumeration.
_hthreadWorker = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE)_ThreadStartProcWrapper,
(LPVOID)this, CREATE_SUSPENDED, &thid);
if (_hthreadWorker)
{
// Demote the priority so it doesn't interfere with the
// initial HTML databinding.
SetThreadPriority(_hthreadWorker, iPriority);
ResumeThread(_hthreadWorker);
}
else
{
// Release my refcount in case of failure
Release();
// If we can't create the background thread, don't bother with the window
DestroyWindow(_hwndWorker);
_hwndWorker = NULL;
}
}
return (_hthreadWorker != NULL) ? S_OK : E_FAIL;
}
/*-------------------------------------------------------------------------
Purpose: Kills the worker thread if one is around
*/
HRESULT CWorkerThread::KillWT(void)
{
MSG msg;
// I should never call KillWT to kill myself
ASSERT(_hthreadWorker != GetCurrentThread());
TraceMsg(TF_TASKS, "[%x] Killing worker thread...", _dwThreadId);
// Tell the worker thread to stop when it can
// Do this inside the critical section because we don't want random messages
// get posted to the worker window after this.
_LockWorker();
_fKillWorker = TRUE;
_UnlockWorker();
// If we have no worker thread, nothing to do
if (_hthreadWorker)
{
// Now wait for the worker to stop
if (WaitForSingleObject(_hthreadWorker, 10000) == WAIT_TIMEOUT)
TraceMsg(TF_ERROR, "[%x] Worker thread termination wait timed out!", _dwThreadId);
else
TraceMsg(TF_TASKS, "[%x] Worker thread wait exited cleanly", _dwThreadId);
// Now that the thread is stopped, release our hold so all its memory can go away
CloseHandle(_hthreadWorker);
_hthreadWorker = NULL;
}
// Make sure that all messages to our worker HWND get processed
if (_hwndWorker)
{
while (PeekMessage(&msg, _hwndWorker, 0, 0, PM_REMOVE))
DispatchMessage(&msg);
DestroyWindow(_hwndWorker);
_hwndWorker = NULL;
}
SetListenerWT(NULL);
return S_OK;
}
//--------------------------------------------------------------------
// Private methods
void CWorkerThread::_LockWorker(void)
{
EnterCriticalSection(&_csWorker);
DEBUG_CODE( _cRefLockWorker++; )
}
void CWorkerThread::_UnlockWorker(void)
{
DEBUG_CODE( _cRefLockWorker--; )
LeaveCriticalSection(&_csWorker);
}
/*-------------------------------------------------------------------------
Purpose: Static wndproc wrapper. Calls the real non-static WndProc.
*/
LRESULT
CALLBACK
CWorkerThread::_WorkerWndProcWrapper(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
if (uMsg == WM_DESTROY)
SetWindowLongPtr(hwnd, 0, 0);
else
{
CWorkerThread * pWorker = (CWorkerThread*)GetWindowLongPtr(hwnd, 0);
if (pWorker)
return pWorker->_WorkerWndProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
/*-------------------------------------------------------------------------
Purpose: Used to fire events back to Trident since they can't be fired from
the worker thread.
*/
LRESULT
CWorkerThread::_WorkerWndProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
switch (uMsg)
{
case WORKERWIN_FIRE_ROW_READY:
// Posted by worker thread when async data is ready
_LockWorker();
{
if (_pwe)
{
TraceMsg(TF_TASKS, "[%x] Firing event for row #%d", _dwThreadId, wParam);
_pwe->FireOnDataReady((LONG)wParam);
}
}
_UnlockWorker();
return 0;
case WORKERWIN_FIRE_FINISHED:
// Posted by worker thread when thread is finished enumerating
// async data.
_LockWorker();
{
if (_pwe)
{
TraceMsg(TF_TASKS, "[%x] Firing finished", _dwThreadId);
_pwe->FireOnFinished();
}
}
_UnlockWorker();
return 0;
case WORKERWIN_FIRE_DATASETCHANGED:
// Posted by worker thread when matrix array finished enumerating
_LockWorker();
{
if (_pwe)
{
TraceMsg(TF_TASKS, "[%x] Firing DatasetChanged", _dwThreadId);
_pwe->FireOnDatasetChanged();
}
}
_UnlockWorker();
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
/*-------------------------------------------------------------------------
Purpose: Start and exit of worker thread to do slow app information
*/
DWORD
CALLBACK
CWorkerThread::_ThreadStartProcWrapper(
LPVOID lpParam // this pointer of object since wrapper is static
)
{
CWorkerThread * pwt = (CWorkerThread *)lpParam;
pwt->_dwThreadId = GetCurrentThreadId();
return pwt->_ThreadStartProc();
}
/*-------------------------------------------------------------------------
Purpose: Contains the code run on the worker thread where we get the slow
information about applications
*/
DWORD
CWorkerThread::_ThreadStartProc()
{
// Don't bother killing the worker window here, let the main thread take care
// of the life time of the worker window.
// Signal that we don't have a worker thread anymore. Prevents race
// conditions.
_fKillWorker = FALSE;
TraceMsg(TF_TASKS, "[%x] Exiting worker thread", _dwThreadId);
// Release the ref count to "this" object at end of thread, be it CMtxArray or CDataSrc because we
// AddRef()ed before this thread started.
Release();
return 0;
}
#endif //DOWNLEVEL_PLATFORM