Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

541 lines
14 KiB

  1. /////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) 1996-1999 Microsoft Corporation
  4. //
  5. // Module Name:
  6. // WorkThrd.cpp
  7. //
  8. // Abstract:
  9. // Implementation of the CWorkerThread class.
  10. //
  11. // Author:
  12. // David Potter (davidp) November 17, 1997
  13. //
  14. // Revision History:
  15. //
  16. // Notes:
  17. //
  18. /////////////////////////////////////////////////////////////////////////////
  19. #include "AdmCommonRes.h"
  20. #include "WorkThrd.h"
  21. #include <process.h>
  22. /////////////////////////////////////////////////////////////////////////////
  23. // Global Variables
  24. /////////////////////////////////////////////////////////////////////////////
  25. TCHAR g_szOldWndProc[] = _T("CLADMWIZ_OldWndProc");
  26. TCHAR g_szThreadObj[] = _T("CLADMWIZ_ThreadObj");
  27. /////////////////////////////////////////////////////////////////////////////
  28. // class CWorkerThread
  29. /////////////////////////////////////////////////////////////////////////////
  30. /////////////////////////////////////////////////////////////////////////////
  31. //++
  32. //
  33. // CWorkerThread::CreateThread
  34. //
  35. // Routine Description:
  36. // Create the thread.
  37. //
  38. // Arguments:
  39. // None.
  40. //
  41. // Return Value:
  42. // Any error values returned by CreateMutex(), CreateEvent(), or
  43. // CreateThread().
  44. //
  45. //--
  46. /////////////////////////////////////////////////////////////////////////////
  47. DWORD CWorkerThread::CreateThread( void )
  48. {
  49. ATLASSERT( m_hThread == NULL );
  50. ATLASSERT( m_hMutex == NULL );
  51. ATLASSERT( m_hInputEvent == NULL );
  52. ATLASSERT( m_hOutputEvent == NULL );
  53. ATLASSERT( m_idThread == 0 );
  54. DWORD _sc = ERROR_SUCCESS;
  55. // Loop to avoid goto's.
  56. do
  57. {
  58. //
  59. // Create the mutex.
  60. //
  61. ATLTRACE( _T("CWorkerThread::CreateThread() - Calling CreateMutex()\n") );
  62. m_hMutex = CreateMutex(
  63. NULL, // lpMutexAttributes
  64. FALSE, // bInitialOwner
  65. NULL // lpName
  66. );
  67. if ( m_hMutex == NULL )
  68. {
  69. _sc = GetLastError();
  70. break;
  71. } // if: error creating the mutex
  72. //
  73. // Create the input event.
  74. //
  75. ATLTRACE( _T("CWorkerThread::CreateThread() - Calling CreateEvent() for input event\n") );
  76. m_hInputEvent = CreateEvent(
  77. NULL, // lpEventAttributes
  78. TRUE, // bManualReset
  79. FALSE, // bInitialState
  80. NULL // lpName
  81. );
  82. if ( m_hInputEvent == NULL )
  83. {
  84. _sc = GetLastError();
  85. break;
  86. } // if: error creating the input event
  87. //
  88. // Create the output event.
  89. //
  90. ATLTRACE( _T("CWorkerThread::CreateThread() - Calling CreateEvent() for output event\n") );
  91. m_hOutputEvent = CreateEvent(
  92. NULL, // lpEventAttributes
  93. TRUE, // bManualReset
  94. FALSE, // bInitialState
  95. NULL // lpName
  96. );
  97. if ( m_hOutputEvent == NULL )
  98. {
  99. _sc = GetLastError();
  100. break;
  101. } // if: error creating the output event
  102. //
  103. // Create the thread.
  104. //
  105. ATLTRACE( _T("CWorkerThread::CreateThread() - Calling CreateThread()\n") );
  106. m_hThread = reinterpret_cast< HANDLE >( _beginthreadex(
  107. NULL, // security
  108. 0, // stack_size
  109. S_ThreadProc, // start_address,
  110. (LPVOID) this, // arglist
  111. 0, // initflag
  112. &m_idThread // thrdaddr
  113. ) );
  114. if ( m_hThread == NULL )
  115. {
  116. _sc = GetLastError();
  117. break;
  118. } // if: error creating the thread
  119. } while ( 0 );
  120. //
  121. // Handle errors by cleaning up objects we created.
  122. //
  123. if ( _sc != ERROR_SUCCESS )
  124. {
  125. Cleanup();
  126. } // if: error occurred
  127. return _sc;
  128. } //*** CWorkerThread::CreateThread()
  129. /////////////////////////////////////////////////////////////////////////////
  130. //++
  131. //
  132. // CWorkerThread::PrepareWindowToWait
  133. //
  134. // Routine Description:
  135. // Prepare to wait for a long operation. This involves disabling the
  136. // window and displaying a wait cursor.
  137. //
  138. // Arguments:
  139. // hwnd [IN] Handle for the window to disable while the
  140. // operation is being performed.
  141. //
  142. // Return Value:
  143. // None.
  144. //
  145. //--
  146. /////////////////////////////////////////////////////////////////////////////
  147. void CWorkerThread::PrepareWindowToWait( IN HWND hwnd )
  148. {
  149. m_hCurrentCursor = GetCursor();
  150. if ( hwnd != NULL )
  151. {
  152. //
  153. // Subclass the window procedure so we can set the cursor properly.
  154. //
  155. m_pfnOldWndProc = reinterpret_cast< WNDPROC >( GetWindowLongPtr( hwnd, GWLP_WNDPROC ) );
  156. SetProp( hwnd, g_szOldWndProc, m_pfnOldWndProc );
  157. SetProp( hwnd, g_szThreadObj, this );
  158. SetWindowLongPtr( hwnd, GWLP_WNDPROC, reinterpret_cast< LONG_PTR >( S_ParentWndProc ) );
  159. //
  160. // Disable property sheet and wizard buttons.
  161. //
  162. EnableWindow( hwnd, FALSE );
  163. } // if: parent window specified
  164. } //*** CWorkerThread::PrepareWindowToWait()
  165. /////////////////////////////////////////////////////////////////////////////
  166. //++
  167. //
  168. // CWorkerThread::CleanupWindowAfterWait
  169. //
  170. // Routine Description:
  171. // Prepare to wait for a long operation. This involves disabling the
  172. // window and displaying a wait cursor.
  173. //
  174. // Arguments:
  175. // hwnd [IN] Handle for the window to disable while the
  176. // operation is being performed.
  177. //
  178. // Return Value:
  179. // None.
  180. //
  181. //--
  182. /////////////////////////////////////////////////////////////////////////////
  183. void CWorkerThread::CleanupWindowAfterWait( IN HWND hwnd )
  184. {
  185. if ( hwnd != NULL )
  186. {
  187. //
  188. // Unsubclass the window procedure.
  189. //
  190. SetWindowLongPtr( hwnd, GWLP_WNDPROC, reinterpret_cast< LONG_PTR >( m_pfnOldWndProc ) );
  191. m_pfnOldWndProc = NULL;
  192. RemoveProp( hwnd, g_szOldWndProc );
  193. RemoveProp( hwnd, g_szThreadObj );
  194. //
  195. // Re-enable property sheet and wizard buttons.
  196. //
  197. EnableWindow( hwnd, TRUE );
  198. } // if: parent window specified
  199. m_hCurrentCursor = NULL;
  200. } //*** CWorkerThread::CleanupWindowAfterWait()
  201. /////////////////////////////////////////////////////////////////////////////
  202. //++
  203. //
  204. // CWorkerThread::CallThreadFunction
  205. //
  206. // Routine Description:
  207. // Call a function supported by the thread.
  208. //
  209. // Arguments:
  210. // hwnd [IN] Handle for the window to disable while the
  211. // operation is being performed.
  212. // nFunction [IN] Code representing the function to perform.
  213. // pvParam1 [IN OUT] Parameter 1 with function-specific data.
  214. // pvParam2 [IN OUT] Parameter 2 with function-specific data.
  215. //
  216. // Return Value:
  217. // Any error values returned by AtlWaitWithMessageLoop() or PulseEvent().
  218. // Status return from the function.
  219. //
  220. //--
  221. /////////////////////////////////////////////////////////////////////////////
  222. DWORD CWorkerThread::CallThreadFunction(
  223. IN HWND hwnd,
  224. IN LONG nFunction,
  225. IN OUT PVOID pvParam1, // = NULL,
  226. IN OUT PVOID pvParam2 // = NULL
  227. )
  228. {
  229. ATLASSERT( m_hThread != NULL ); // Thread must already be created.
  230. ATLASSERT( m_hMutex != NULL ); // Mutex must already be created.
  231. ATLASSERT( m_hInputEvent != NULL ); // Input event must already be created.
  232. ATLASSERT( m_hOutputEvent != NULL ); // Output event must already be created.
  233. ATLASSERT( m_pfnOldWndProc == NULL ); // Parent window not already subclassed.
  234. DWORD _sc;
  235. CWaitCursor _wc;
  236. //
  237. // Prepare the window to wait for the thread operation.
  238. //
  239. PrepareWindowToWait( hwnd );
  240. // Loop to avoid goto's.
  241. do
  242. {
  243. //
  244. // Wait for the thread to become available.
  245. //
  246. ATLTRACE( _T("CWorkerThread::CallThreadFunction() - Waiting on mutex\n") );
  247. if ( ! AtlWaitWithMessageLoop( m_hMutex ) )
  248. {
  249. _sc = GetLastError();
  250. break;
  251. } // if: error waiting on the mutex
  252. // Loop to avoid using goto's.
  253. do
  254. {
  255. //
  256. // Pass this caller's data to the thread.
  257. //
  258. ATLASSERT( m_nFunction == 0 );
  259. ATLASSERT( m_pvParam1 == NULL );
  260. ATLASSERT( m_pvParam2 == NULL );
  261. ATLASSERT( m_dwOutputStatus == ERROR_SUCCESS );
  262. m_nFunction = nFunction;
  263. m_pvParam1 = pvParam1;
  264. m_pvParam2 = pvParam2;
  265. //
  266. // Signal the thread that there is work to do.
  267. //
  268. ATLTRACE( _T("CWorkerThread::CallThreadFunction() - Setting input event\n") );
  269. if ( ! SetEvent( m_hInputEvent ) )
  270. {
  271. _sc = GetLastError();
  272. break;
  273. } // if: error pulsing the event
  274. //
  275. // Wait for the thread to complete the function.
  276. //
  277. ATLTRACE( _T("CWorkerThread::CallThreadFunction() - Waiting on output event\n") );
  278. if ( ! AtlWaitWithMessageLoop( m_hOutputEvent ) )
  279. {
  280. _sc = GetLastError();
  281. break;
  282. } // if: error waiting for the event
  283. ATLTRACE( _T("CWorkerThread::CallThreadFunction() - Resetting output event\n") );
  284. ResetEvent( m_hOutputEvent );
  285. //
  286. // Retrieve the results of the function to return
  287. // to the caller.
  288. //
  289. _sc = m_dwOutputStatus;
  290. //
  291. // Clear input parameters.
  292. //
  293. m_nFunction = WTF_NONE;
  294. m_pvParam1 = NULL;
  295. m_pvParam2 = NULL;
  296. m_dwOutputStatus = ERROR_SUCCESS;
  297. } while ( 0 );
  298. ATLTRACE( _T("CWorkerThread::CallThreadFunction() - Releasing mutex\n") );
  299. ReleaseMutex( m_hMutex );
  300. } while ( 0 );
  301. //
  302. // Cleanup windows after the wait operation.
  303. //
  304. CleanupWindowAfterWait( hwnd );
  305. return _sc;
  306. } //*** CWorkerThread::CallThreadFunction()
  307. /////////////////////////////////////////////////////////////////////////////
  308. //++
  309. //
  310. // CWorkerThread::WaitForThreadToExit
  311. //
  312. // Routine Description:
  313. // Wait for the thread to exit.
  314. //
  315. // Arguments:
  316. // hwnd [IN] Handle for the window to disable while the
  317. // operation is being performed.
  318. //
  319. // Return Value:
  320. // ERROR_SUCCESS Operation completed successfully.
  321. // Any error values returned by AtlWaitWithMessageLoop().
  322. //
  323. //--
  324. /////////////////////////////////////////////////////////////////////////////
  325. DWORD CWorkerThread::WaitForThreadToExit( IN HWND hwnd )
  326. {
  327. ATLASSERT( m_hThread != NULL ); // Thread must already be created.
  328. DWORD _sc = ERROR_SUCCESS;
  329. CWaitCursor _wc;
  330. //
  331. // Prepare the window to wait for the thread operation.
  332. //
  333. PrepareWindowToWait( hwnd );
  334. //
  335. // Wait for the thread to exit.
  336. //
  337. AtlWaitWithMessageLoop( m_hThread );
  338. if ( ! AtlWaitWithMessageLoop( m_hThread ) )
  339. {
  340. _sc = GetLastError();
  341. } // if: error waiting for the thread to exit
  342. //
  343. // Cleanup windows after the wait operation.
  344. //
  345. CleanupWindowAfterWait( hwnd );
  346. return _sc;
  347. } //*** CWorkerThread::WaitForThreadToExit()
  348. /////////////////////////////////////////////////////////////////////////////
  349. //++
  350. //
  351. // static
  352. // CWorkerThread::S_ParentWndProc
  353. //
  354. // Routine Description:
  355. // Static parent window procedure. This procedure handles the
  356. // WM_SETCURSOR message while the thread is processing a request.
  357. //
  358. // Arguments:
  359. // hwnd [IN] Identifies the window.
  360. // uMsg [IN] Specifies the message.
  361. // wParam [IN] Specifies additional information based on uMsg.
  362. // lParam [IN] Specifies additional information based on uMsg.
  363. //
  364. // Return Value:
  365. // The result of the message processing.
  366. //
  367. //--
  368. /////////////////////////////////////////////////////////////////////////////
  369. LRESULT WINAPI CWorkerThread::S_ParentWndProc(
  370. IN HWND hwnd,
  371. IN UINT uMsg,
  372. IN WPARAM wParam,
  373. IN LPARAM lParam
  374. )
  375. {
  376. LRESULT lResult = 0;
  377. CWorkerThread * pthread = reinterpret_cast< CWorkerThread * >( GetProp( hwnd, g_szThreadObj ) );
  378. ATLASSERT( pthread != NULL );
  379. if ( pthread != NULL )
  380. {
  381. if ( uMsg == WM_SETCURSOR )
  382. {
  383. if ( GetCursor() != pthread->m_hCurrentCursor )
  384. {
  385. SetCursor( pthread->m_hCurrentCursor );
  386. } // if: cursor is different
  387. lResult = TRUE;
  388. } // if: set cursor message
  389. else
  390. {
  391. lResult = (*pthread->m_pfnOldWndProc)( hwnd, uMsg, wParam, lParam );
  392. } // else: other message
  393. } // if: thread is non-null
  394. return lResult;
  395. } //*** CWorkerThread::S_ParentWndProc()
  396. /////////////////////////////////////////////////////////////////////////////
  397. //++
  398. //
  399. // static
  400. // CWorkerThread::S_ThreadProc
  401. //
  402. // Routine Description:
  403. // Static thread procedure.
  404. //
  405. // Arguments:
  406. // pvThis [IN OUT] this pointer for the CWorkerThread instance.
  407. //
  408. // Return Value:
  409. // None (ignored).
  410. //
  411. //--
  412. /////////////////////////////////////////////////////////////////////////////
  413. UINT __stdcall CWorkerThread::S_ThreadProc( IN OUT LPVOID pvThis )
  414. {
  415. ATLTRACE( _T("CWorkerThread::S_ThreadProc() - Beginning thread\n") );
  416. DWORD _sc;
  417. LONG _nFunction;
  418. CWorkerThread * _pThis = reinterpret_cast< CWorkerThread * >( pvThis );
  419. ATLASSERT( pvThis != NULL );
  420. ATLASSERT( _pThis->m_hMutex != NULL );
  421. ATLASSERT( _pThis->m_hInputEvent != NULL );
  422. ATLASSERT( _pThis->m_hOutputEvent != NULL );
  423. do
  424. {
  425. //
  426. // Wait for work.
  427. //
  428. ATLTRACE( _T("CWorkerThread::S_ThreadProc() - Waiting on input event\n") );
  429. _sc = WaitForSingleObject( _pThis->m_hInputEvent, INFINITE );
  430. if ( _sc == WAIT_FAILED )
  431. {
  432. _sc = GetLastError();
  433. break;
  434. } // if: error waiting for the event
  435. ATLTRACE( _T("CWorkerThread::S_ThreadProc() - Resetting input event\n") );
  436. ResetEvent( _pThis->m_hInputEvent );
  437. //
  438. // Handle the exit request.
  439. //
  440. if ( _pThis->m_nFunction == WTF_EXIT )
  441. {
  442. _pThis->m_bThreadExiting = TRUE;
  443. } // if: exiting
  444. else
  445. {
  446. //
  447. // Call the function handler.
  448. //
  449. ATLTRACE( _T("CWorkerThread::S_ThreadProc() - Calling thread function handler\n") );
  450. ATLASSERT( _pThis->m_nFunction != 0 );
  451. _pThis->m_dwOutputStatus = _pThis->ThreadFunctionHandler(
  452. _pThis->m_nFunction,
  453. _pThis->m_pvParam1,
  454. _pThis->m_pvParam2
  455. );
  456. } // else: not exiting
  457. //
  458. // Save locally data that we need to access once we have signalled
  459. // the caller's event. If we don't do this, we won't be referencing
  460. // the proper function code after that point.
  461. //
  462. _nFunction = _pThis->m_nFunction;
  463. //
  464. // Signal the calling thread that the work has been completed.
  465. //
  466. ATLTRACE( _T("CWorkerThread::S_ThreadProc() - Setting output event\n") );
  467. if ( ! SetEvent( _pThis->m_hOutputEvent ) )
  468. {
  469. _sc = GetLastError();
  470. break;
  471. } // if: error pulsing
  472. //
  473. // Set the status in case we are exiting.
  474. //
  475. _sc = ERROR_SUCCESS;
  476. } while ( _nFunction != WTF_EXIT );
  477. ATLTRACE( _T("CWorkerThread::S_ThreadProc() - Exiting thread\n") );
  478. // Sleep( 10000 ); // Test thread synchronization
  479. return _sc;
  480. } //*** CWorkerThread::S_ThreadProc()