346 lines
9 KiB
C++
346 lines
9 KiB
C++
#ifndef __BKTHREAD_H_INCLUDED
|
|
#define __BKTHREAD_H_INCLUDED
|
|
|
|
#include "tspqueue.h"
|
|
#include "modlock.h"
|
|
|
|
// Base class for all messages
|
|
class CThreadMessage
|
|
{
|
|
private:
|
|
int m_nMessage;
|
|
|
|
private:
|
|
//
|
|
// No implementation
|
|
//
|
|
CThreadMessage(void);
|
|
CThreadMessage( const CThreadMessage & );
|
|
CThreadMessage &operator=( const CThreadMessage & );
|
|
public:
|
|
CThreadMessage( int nMessage )
|
|
: m_nMessage(nMessage)
|
|
{
|
|
}
|
|
|
|
virtual ~CThreadMessage(void)
|
|
{
|
|
}
|
|
|
|
int Message(void) const
|
|
{
|
|
return(m_nMessage);
|
|
}
|
|
int Message( int nMessage )
|
|
{
|
|
return(m_nMessage = nMessage);
|
|
}
|
|
};
|
|
|
|
typedef CThreadSafePriorityQueue<CThreadMessage> CThreadMessageQueue;
|
|
|
|
class CNotifyThreadMessage : public CThreadMessage
|
|
{
|
|
private:
|
|
//
|
|
// No implementation
|
|
//
|
|
CNotifyThreadMessage(void);
|
|
CNotifyThreadMessage( const CNotifyThreadMessage & );
|
|
CNotifyThreadMessage &operator=( const CNotifyThreadMessage & );
|
|
|
|
private:
|
|
HWND m_hWndNotify;
|
|
|
|
public:
|
|
CNotifyThreadMessage( int nMessage, HWND hWndNotify )
|
|
: CThreadMessage(nMessage),
|
|
m_hWndNotify(hWndNotify)
|
|
{
|
|
}
|
|
virtual ~CNotifyThreadMessage(void)
|
|
{
|
|
m_hWndNotify = NULL;
|
|
}
|
|
HWND NotifyWindow(void) const
|
|
{
|
|
return(m_hWndNotify);
|
|
}
|
|
};
|
|
|
|
typedef BOOL (WINAPI *ThreadMessageHandler)( CThreadMessage *pMsg );
|
|
|
|
struct CThreadMessageMap
|
|
{
|
|
int nMessage;
|
|
ThreadMessageHandler pfnHandler;
|
|
};
|
|
|
|
class CBackgroundThread
|
|
{
|
|
private:
|
|
HANDLE m_hThread;
|
|
DWORD m_dwThreadId;
|
|
CThreadMessageQueue *m_pMessageQueue;
|
|
CThreadMessageMap *m_pThreadMessageMap;
|
|
CSimpleEvent m_CancelEvent;
|
|
HINSTANCE m_hInstanceUnlock;
|
|
|
|
private:
|
|
//
|
|
// No implementation
|
|
//
|
|
CBackgroundThread(void);
|
|
CBackgroundThread &operator=( const CBackgroundThread & );
|
|
CBackgroundThread( const CBackgroundThread & );
|
|
|
|
private:
|
|
//
|
|
// Private constructor. This is the only constructor. It is only called from Create.
|
|
//
|
|
CBackgroundThread( CThreadMessageQueue *pMessageQueue, CThreadMessageMap *pThreadMessageMap, HANDLE hCancelEvent )
|
|
: m_pMessageQueue(pMessageQueue),
|
|
m_pThreadMessageMap(pThreadMessageMap),
|
|
m_CancelEvent(hCancelEvent),
|
|
m_hInstanceUnlock(NULL)
|
|
{
|
|
}
|
|
|
|
bool HandleMessage( CThreadMessage *pMsg )
|
|
{
|
|
for (int i=0;pMsg && m_pThreadMessageMap && m_pThreadMessageMap[i].nMessage;i++)
|
|
{
|
|
if (m_pThreadMessageMap[i].nMessage == pMsg->Message())
|
|
{
|
|
//
|
|
// reset the cancel event
|
|
//
|
|
m_CancelEvent.Reset();
|
|
return (m_pThreadMessageMap[i].pfnHandler(pMsg) != FALSE);
|
|
}
|
|
}
|
|
return(true);
|
|
}
|
|
|
|
HRESULT Run()
|
|
{
|
|
//
|
|
// Make sure we got a good message queue
|
|
//
|
|
if (!m_pMessageQueue)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
//
|
|
// Make sure the event handle is good
|
|
//
|
|
if (!m_pMessageQueue->QueueEvent())
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
//
|
|
// Make sure we have a message queue
|
|
//
|
|
PostThreadMessage( GetCurrentThreadId(), WM_NULL, 0, 0 );
|
|
|
|
//
|
|
// Initialize COM on this thread. As a single threaded apartment.
|
|
//
|
|
HRESULT hr = CoInitialize(NULL);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
//
|
|
// We will loop until we get a WM_QUIT message
|
|
//
|
|
while (true)
|
|
{
|
|
//
|
|
// Wait for a message to be place in the priority queue, or a message to be placed in the thread's queue
|
|
//
|
|
HANDLE Handles[1] = {m_pMessageQueue->QueueEvent()};
|
|
DWORD dwRes = MsgWaitForMultipleObjects(1,Handles,FALSE,INFINITE,QS_ALLINPUT|QS_ALLPOSTMESSAGE);
|
|
|
|
//
|
|
// If the event is signalled, there is a message in the queue
|
|
//
|
|
if (WAIT_OBJECT_0==dwRes)
|
|
{
|
|
//
|
|
// Pull the message out of the queue
|
|
//
|
|
CThreadMessage *pMsg = m_pMessageQueue->Dequeue();
|
|
if (pMsg)
|
|
{
|
|
//
|
|
// Call the message handler.
|
|
//
|
|
BOOL bResult = HandleMessage(pMsg);
|
|
|
|
//
|
|
// Delete the message
|
|
//
|
|
delete pMsg;
|
|
|
|
//
|
|
// If the handler returns false, exit the thread.
|
|
//
|
|
if (!bResult)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (WAIT_OBJECT_0+1==dwRes)
|
|
{
|
|
//
|
|
// pull all of the messages out of the queue and process them
|
|
//
|
|
MSG msg;
|
|
while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE ))
|
|
{
|
|
//
|
|
// Break out of the loop
|
|
//
|
|
if (msg.message == WM_QUIT)
|
|
{
|
|
break;
|
|
}
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// Shut down COM
|
|
//
|
|
CoUninitialize();
|
|
}
|
|
|
|
return(hr);
|
|
}
|
|
|
|
static DWORD ThreadProc(PVOID pData)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
HINSTANCE hInstUnlock = NULL;
|
|
CBackgroundThread *pThread = (CBackgroundThread *)pData;
|
|
if (pData)
|
|
{
|
|
hr = pThread->Run();
|
|
hInstUnlock = pThread->m_hInstanceUnlock;
|
|
delete pThread;
|
|
}
|
|
if (hInstUnlock)
|
|
{
|
|
FreeLibraryAndExitThread( hInstUnlock, static_cast<DWORD>(hr) );
|
|
}
|
|
else
|
|
{
|
|
ExitThread( static_cast<DWORD>(hr) );
|
|
}
|
|
}
|
|
public:
|
|
~CBackgroundThread(void)
|
|
{
|
|
//
|
|
// Delete the thread handle
|
|
//
|
|
if (m_hThread)
|
|
{
|
|
CloseHandle(m_hThread);
|
|
m_hThread = 0;
|
|
}
|
|
|
|
//
|
|
// Nuke the message queue
|
|
//
|
|
delete m_pMessageQueue;
|
|
}
|
|
static HANDLE Create( CThreadMessageQueue *pMessageQueue, CThreadMessageMap *pThreadMessageMap, HANDLE hCancelEvent, HINSTANCE hInstLock )
|
|
{
|
|
//
|
|
// Make sure we have valid arguments
|
|
//
|
|
if (!pMessageQueue || !pThreadMessageMap)
|
|
{
|
|
WIA_ERROR((TEXT("!pMessageQueue || !pThreadMessageMap")));
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// The duplicated handle we will be returning
|
|
//
|
|
HANDLE hReturnHandle = NULL;
|
|
|
|
//
|
|
// Create the thread class
|
|
//
|
|
CBackgroundThread *pThread = new CBackgroundThread( pMessageQueue, pThreadMessageMap, hCancelEvent );
|
|
if (pThread)
|
|
{
|
|
//
|
|
// Lock up before we create the thread
|
|
//
|
|
HINSTANCE hInstanceUnlock = NULL;
|
|
if (hInstLock)
|
|
{
|
|
//
|
|
// Get the module name
|
|
//
|
|
TCHAR szModule[MAX_PATH];
|
|
if (GetModuleFileName( hInstLock, szModule, ARRAYSIZE(szModule)))
|
|
{
|
|
//
|
|
// Increment the reference count
|
|
//
|
|
pThread->m_hInstanceUnlock = LoadLibrary( szModule );
|
|
}
|
|
}
|
|
|
|
|
|
pThread->m_hThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc, pThread, 0, &pThread->m_dwThreadId );
|
|
if (pThread->m_hThread)
|
|
{
|
|
//
|
|
// Copy the handle to return to the caller
|
|
//
|
|
DuplicateHandle( GetCurrentProcess(), pThread->m_hThread, GetCurrentProcess(), &hReturnHandle, 0, FALSE, DUPLICATE_SAME_ACCESS );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Unlock the module
|
|
//
|
|
if (pThread->m_hInstanceUnlock)
|
|
{
|
|
FreeLibrary( hInstanceUnlock );
|
|
hInstanceUnlock;
|
|
}
|
|
|
|
//
|
|
// Since we can't start the thread, we have to delete the thread info to prevent a leak
|
|
//
|
|
delete pThread;
|
|
|
|
WIA_ERROR((TEXT("CreateThread failed")));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Since the background thread isn't going to free it, we have to.
|
|
//
|
|
delete pMessageQueue;
|
|
|
|
WIA_ERROR((TEXT("new CBackgroundThread failed")));
|
|
}
|
|
|
|
return hReturnHandle;
|
|
}
|
|
};
|
|
|
|
#endif //__BKTHREAD_H_INCLUDED
|
|
|