///////////////////////////////////////////////////////////////////////////// // // Copyright (c) 1996-1999 Microsoft Corporation // // Module Name: // WorkThrd.cpp // // Abstract: // Implementation of the CWorkerThread class. // // Author: // David Potter (davidp) November 17, 1997 // // Revision History: // // Notes: // ///////////////////////////////////////////////////////////////////////////// #include "AdmCommonRes.h" #include "WorkThrd.h" #include ///////////////////////////////////////////////////////////////////////////// // Global Variables ///////////////////////////////////////////////////////////////////////////// TCHAR g_szOldWndProc[] = _T("CLADMWIZ_OldWndProc"); TCHAR g_szThreadObj[] = _T("CLADMWIZ_ThreadObj"); ///////////////////////////////////////////////////////////////////////////// // class CWorkerThread ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// //++ // // CWorkerThread::CreateThread // // Routine Description: // Create the thread. // // Arguments: // None. // // Return Value: // Any error values returned by CreateMutex(), CreateEvent(), or // CreateThread(). // //-- ///////////////////////////////////////////////////////////////////////////// DWORD CWorkerThread::CreateThread( void ) { ATLASSERT( m_hThread == NULL ); ATLASSERT( m_hMutex == NULL ); ATLASSERT( m_hInputEvent == NULL ); ATLASSERT( m_hOutputEvent == NULL ); ATLASSERT( m_idThread == 0 ); DWORD _sc = ERROR_SUCCESS; // Loop to avoid goto's. do { // // Create the mutex. // ATLTRACE( _T("CWorkerThread::CreateThread() - Calling CreateMutex()\n") ); m_hMutex = CreateMutex( NULL, // lpMutexAttributes FALSE, // bInitialOwner NULL // lpName ); if ( m_hMutex == NULL ) { _sc = GetLastError(); break; } // if: error creating the mutex // // Create the input event. // ATLTRACE( _T("CWorkerThread::CreateThread() - Calling CreateEvent() for input event\n") ); m_hInputEvent = CreateEvent( NULL, // lpEventAttributes TRUE, // bManualReset FALSE, // bInitialState NULL // lpName ); if ( m_hInputEvent == NULL ) { _sc = GetLastError(); break; } // if: error creating the input event // // Create the output event. // ATLTRACE( _T("CWorkerThread::CreateThread() - Calling CreateEvent() for output event\n") ); m_hOutputEvent = CreateEvent( NULL, // lpEventAttributes TRUE, // bManualReset FALSE, // bInitialState NULL // lpName ); if ( m_hOutputEvent == NULL ) { _sc = GetLastError(); break; } // if: error creating the output event // // Create the thread. // ATLTRACE( _T("CWorkerThread::CreateThread() - Calling CreateThread()\n") ); m_hThread = reinterpret_cast< HANDLE >( _beginthreadex( NULL, // security 0, // stack_size S_ThreadProc, // start_address, (LPVOID) this, // arglist 0, // initflag &m_idThread // thrdaddr ) ); if ( m_hThread == NULL ) { _sc = GetLastError(); break; } // if: error creating the thread } while ( 0 ); // // Handle errors by cleaning up objects we created. // if ( _sc != ERROR_SUCCESS ) { Cleanup(); } // if: error occurred return _sc; } //*** CWorkerThread::CreateThread() ///////////////////////////////////////////////////////////////////////////// //++ // // CWorkerThread::PrepareWindowToWait // // Routine Description: // Prepare to wait for a long operation. This involves disabling the // window and displaying a wait cursor. // // Arguments: // hwnd [IN] Handle for the window to disable while the // operation is being performed. // // Return Value: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CWorkerThread::PrepareWindowToWait( IN HWND hwnd ) { m_hCurrentCursor = GetCursor(); if ( hwnd != NULL ) { // // Subclass the window procedure so we can set the cursor properly. // m_pfnOldWndProc = reinterpret_cast< WNDPROC >( GetWindowLongPtr( hwnd, GWLP_WNDPROC ) ); SetProp( hwnd, g_szOldWndProc, m_pfnOldWndProc ); SetProp( hwnd, g_szThreadObj, this ); SetWindowLongPtr( hwnd, GWLP_WNDPROC, reinterpret_cast< LONG_PTR >( S_ParentWndProc ) ); // // Disable property sheet and wizard buttons. // EnableWindow( hwnd, FALSE ); } // if: parent window specified } //*** CWorkerThread::PrepareWindowToWait() ///////////////////////////////////////////////////////////////////////////// //++ // // CWorkerThread::CleanupWindowAfterWait // // Routine Description: // Prepare to wait for a long operation. This involves disabling the // window and displaying a wait cursor. // // Arguments: // hwnd [IN] Handle for the window to disable while the // operation is being performed. // // Return Value: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CWorkerThread::CleanupWindowAfterWait( IN HWND hwnd ) { if ( hwnd != NULL ) { // // Unsubclass the window procedure. // SetWindowLongPtr( hwnd, GWLP_WNDPROC, reinterpret_cast< LONG_PTR >( m_pfnOldWndProc ) ); m_pfnOldWndProc = NULL; RemoveProp( hwnd, g_szOldWndProc ); RemoveProp( hwnd, g_szThreadObj ); // // Re-enable property sheet and wizard buttons. // EnableWindow( hwnd, TRUE ); } // if: parent window specified m_hCurrentCursor = NULL; } //*** CWorkerThread::CleanupWindowAfterWait() ///////////////////////////////////////////////////////////////////////////// //++ // // CWorkerThread::CallThreadFunction // // Routine Description: // Call a function supported by the thread. // // Arguments: // hwnd [IN] Handle for the window to disable while the // operation is being performed. // nFunction [IN] Code representing the function to perform. // pvParam1 [IN OUT] Parameter 1 with function-specific data. // pvParam2 [IN OUT] Parameter 2 with function-specific data. // // Return Value: // Any error values returned by AtlWaitWithMessageLoop() or PulseEvent(). // Status return from the function. // //-- ///////////////////////////////////////////////////////////////////////////// DWORD CWorkerThread::CallThreadFunction( IN HWND hwnd, IN LONG nFunction, IN OUT PVOID pvParam1, // = NULL, IN OUT PVOID pvParam2 // = NULL ) { ATLASSERT( m_hThread != NULL ); // Thread must already be created. ATLASSERT( m_hMutex != NULL ); // Mutex must already be created. ATLASSERT( m_hInputEvent != NULL ); // Input event must already be created. ATLASSERT( m_hOutputEvent != NULL ); // Output event must already be created. ATLASSERT( m_pfnOldWndProc == NULL ); // Parent window not already subclassed. DWORD _sc; CWaitCursor _wc; // // Prepare the window to wait for the thread operation. // PrepareWindowToWait( hwnd ); // Loop to avoid goto's. do { // // Wait for the thread to become available. // ATLTRACE( _T("CWorkerThread::CallThreadFunction() - Waiting on mutex\n") ); if ( ! AtlWaitWithMessageLoop( m_hMutex ) ) { _sc = GetLastError(); break; } // if: error waiting on the mutex // Loop to avoid using goto's. do { // // Pass this caller's data to the thread. // ATLASSERT( m_nFunction == 0 ); ATLASSERT( m_pvParam1 == NULL ); ATLASSERT( m_pvParam2 == NULL ); ATLASSERT( m_dwOutputStatus == ERROR_SUCCESS ); m_nFunction = nFunction; m_pvParam1 = pvParam1; m_pvParam2 = pvParam2; // // Signal the thread that there is work to do. // ATLTRACE( _T("CWorkerThread::CallThreadFunction() - Setting input event\n") ); if ( ! SetEvent( m_hInputEvent ) ) { _sc = GetLastError(); break; } // if: error pulsing the event // // Wait for the thread to complete the function. // ATLTRACE( _T("CWorkerThread::CallThreadFunction() - Waiting on output event\n") ); if ( ! AtlWaitWithMessageLoop( m_hOutputEvent ) ) { _sc = GetLastError(); break; } // if: error waiting for the event ATLTRACE( _T("CWorkerThread::CallThreadFunction() - Resetting output event\n") ); ResetEvent( m_hOutputEvent ); // // Retrieve the results of the function to return // to the caller. // _sc = m_dwOutputStatus; // // Clear input parameters. // m_nFunction = WTF_NONE; m_pvParam1 = NULL; m_pvParam2 = NULL; m_dwOutputStatus = ERROR_SUCCESS; } while ( 0 ); ATLTRACE( _T("CWorkerThread::CallThreadFunction() - Releasing mutex\n") ); ReleaseMutex( m_hMutex ); } while ( 0 ); // // Cleanup windows after the wait operation. // CleanupWindowAfterWait( hwnd ); return _sc; } //*** CWorkerThread::CallThreadFunction() ///////////////////////////////////////////////////////////////////////////// //++ // // CWorkerThread::WaitForThreadToExit // // Routine Description: // Wait for the thread to exit. // // Arguments: // hwnd [IN] Handle for the window to disable while the // operation is being performed. // // Return Value: // ERROR_SUCCESS Operation completed successfully. // Any error values returned by AtlWaitWithMessageLoop(). // //-- ///////////////////////////////////////////////////////////////////////////// DWORD CWorkerThread::WaitForThreadToExit( IN HWND hwnd ) { ATLASSERT( m_hThread != NULL ); // Thread must already be created. DWORD _sc = ERROR_SUCCESS; CWaitCursor _wc; // // Prepare the window to wait for the thread operation. // PrepareWindowToWait( hwnd ); // // Wait for the thread to exit. // AtlWaitWithMessageLoop( m_hThread ); if ( ! AtlWaitWithMessageLoop( m_hThread ) ) { _sc = GetLastError(); } // if: error waiting for the thread to exit // // Cleanup windows after the wait operation. // CleanupWindowAfterWait( hwnd ); return _sc; } //*** CWorkerThread::WaitForThreadToExit() ///////////////////////////////////////////////////////////////////////////// //++ // // static // CWorkerThread::S_ParentWndProc // // Routine Description: // Static parent window procedure. This procedure handles the // WM_SETCURSOR message while the thread is processing a request. // // Arguments: // hwnd [IN] Identifies the window. // uMsg [IN] Specifies the message. // wParam [IN] Specifies additional information based on uMsg. // lParam [IN] Specifies additional information based on uMsg. // // Return Value: // The result of the message processing. // //-- ///////////////////////////////////////////////////////////////////////////// LRESULT WINAPI CWorkerThread::S_ParentWndProc( IN HWND hwnd, IN UINT uMsg, IN WPARAM wParam, IN LPARAM lParam ) { LRESULT lResult = 0; CWorkerThread * pthread = reinterpret_cast< CWorkerThread * >( GetProp( hwnd, g_szThreadObj ) ); ATLASSERT( pthread != NULL ); if ( pthread != NULL ) { if ( uMsg == WM_SETCURSOR ) { if ( GetCursor() != pthread->m_hCurrentCursor ) { SetCursor( pthread->m_hCurrentCursor ); } // if: cursor is different lResult = TRUE; } // if: set cursor message else { lResult = (*pthread->m_pfnOldWndProc)( hwnd, uMsg, wParam, lParam ); } // else: other message } // if: thread is non-null return lResult; } //*** CWorkerThread::S_ParentWndProc() ///////////////////////////////////////////////////////////////////////////// //++ // // static // CWorkerThread::S_ThreadProc // // Routine Description: // Static thread procedure. // // Arguments: // pvThis [IN OUT] this pointer for the CWorkerThread instance. // // Return Value: // None (ignored). // //-- ///////////////////////////////////////////////////////////////////////////// UINT __stdcall CWorkerThread::S_ThreadProc( IN OUT LPVOID pvThis ) { ATLTRACE( _T("CWorkerThread::S_ThreadProc() - Beginning thread\n") ); DWORD _sc; LONG _nFunction; CWorkerThread * _pThis = reinterpret_cast< CWorkerThread * >( pvThis ); ATLASSERT( pvThis != NULL ); ATLASSERT( _pThis->m_hMutex != NULL ); ATLASSERT( _pThis->m_hInputEvent != NULL ); ATLASSERT( _pThis->m_hOutputEvent != NULL ); do { // // Wait for work. // ATLTRACE( _T("CWorkerThread::S_ThreadProc() - Waiting on input event\n") ); _sc = WaitForSingleObject( _pThis->m_hInputEvent, INFINITE ); if ( _sc == WAIT_FAILED ) { _sc = GetLastError(); break; } // if: error waiting for the event ATLTRACE( _T("CWorkerThread::S_ThreadProc() - Resetting input event\n") ); ResetEvent( _pThis->m_hInputEvent ); // // Handle the exit request. // if ( _pThis->m_nFunction == WTF_EXIT ) { _pThis->m_bThreadExiting = TRUE; } // if: exiting else { // // Call the function handler. // ATLTRACE( _T("CWorkerThread::S_ThreadProc() - Calling thread function handler\n") ); ATLASSERT( _pThis->m_nFunction != 0 ); _pThis->m_dwOutputStatus = _pThis->ThreadFunctionHandler( _pThis->m_nFunction, _pThis->m_pvParam1, _pThis->m_pvParam2 ); } // else: not exiting // // Save locally data that we need to access once we have signalled // the caller's event. If we don't do this, we won't be referencing // the proper function code after that point. // _nFunction = _pThis->m_nFunction; // // Signal the calling thread that the work has been completed. // ATLTRACE( _T("CWorkerThread::S_ThreadProc() - Setting output event\n") ); if ( ! SetEvent( _pThis->m_hOutputEvent ) ) { _sc = GetLastError(); break; } // if: error pulsing // // Set the status in case we are exiting. // _sc = ERROR_SUCCESS; } while ( _nFunction != WTF_EXIT ); ATLTRACE( _T("CWorkerThread::S_ThreadProc() - Exiting thread\n") ); // Sleep( 10000 ); // Test thread synchronization return _sc; } //*** CWorkerThread::S_ThreadProc()