#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 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(hr) ); } else { ExitThread( static_cast(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