Leaked source code of windows server 2003
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.

849 lines
25 KiB

  1. /*++
  2. Copyright (c) 1998 Seagate Software, Inc. All rights reserved.
  3. Module Name:
  4. rslaunch.cpp
  5. Abstract:
  6. HSM Remote Storage Job Launch Program.
  7. This program is used by the HSM Remote Storage system to submit
  8. user-requested jobs to the NT Task Scheduler. This standalone command
  9. line program has two primary functions: to start the HSM job specified
  10. and not return until the job has completed; and to call into the HSM
  11. Engine to either update secondary storage copy set media, or to
  12. re-create a master secondary storage media from its most recent copy.
  13. NOTE: This program is linked as a windows program, but has no visible
  14. window. It creates an invisible window so it can get the WM_CLOSE
  15. message from the Task Scheduler if the user wants to cancel the job.
  16. ALSO NOTE: This program has no correspoinding header file.
  17. --*/
  18. #include "stdafx.h"
  19. #include "windows.h"
  20. #include "stdio.h"
  21. #include "wsb.h"
  22. #include "hsmeng.h"
  23. #include "fsa.h"
  24. #include "job.h"
  25. #include "rms.h"
  26. #include "hsmconn.h"
  27. HINSTANCE g_hInstance;
  28. //#define RSL_TRACE
  29. #if defined(RSL_TRACE)
  30. #define LTRACE(x) WsbTracef x
  31. #else
  32. #define LTRACE(x)
  33. #endif
  34. #define TRACE_FILE L"RsLaunch.trc"
  35. #define WINDOW_CLASS L"RsLaunchWin"
  36. // Typedefs
  37. typedef enum { // Type of work requested
  38. WORK_NONE,
  39. WORK_RUN,
  40. WORK_RECREATE,
  41. WORK_SYNCH
  42. } WORK_TYPE;
  43. typedef struct { // For passing data to/from DoWork
  44. WCHAR * pCmdLine;
  45. WORK_TYPE wtype;
  46. HRESULT hr;
  47. IHsmJob * pJob;
  48. } DO_WORK_DATA;
  49. // Global data
  50. CComModule _Module;
  51. // Local data
  52. // Local functions
  53. static HRESULT CancelWork(DO_WORK_DATA* pWork);
  54. static HRESULT ConnectToServer(IHsmServer** ppServer);
  55. static BOOL CreateOurWindow(HINSTANCE hInstance);
  56. static DWORD DoWork(void* pVoid);
  57. static HRESULT RecreateMaster(GUID oldMasterMediaId,
  58. OLECHAR* oldMasterMediaName, USHORT copySet);
  59. static void ReportError(HRESULT hr);
  60. static HRESULT RunJob(OLECHAR* jobName, IHsmJob** ppJob);
  61. static HRESULT SynchronizeMedia(OLECHAR* poolName, USHORT copySet);
  62. static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
  63. LPARAM lParam);
  64. //****************************** Functions:
  65. static HRESULT
  66. CancelWork(
  67. IN DO_WORK_DATA* pWork
  68. )
  69. /*++
  70. Routine Description:
  71. Try to cancel the current work.
  72. Arguments:
  73. None.
  74. Return Value:
  75. S_OK - Success
  76. --*/
  77. {
  78. HRESULT hr = E_FAIL;
  79. LTRACE((L"CancelWork: entry\n"));
  80. try {
  81. CComPtr<IHsmServer> pServer;
  82. WsbAffirmHr(ConnectToServer(&pServer));
  83. // Because of a possible timing problem, we may have to wait
  84. // for the operation to start before we can cancel it
  85. for (int i = 0; i < 60; i++) {
  86. if (WORK_RUN == pWork->wtype) {
  87. LTRACE((L"CancelWork: wtype = WORK_RUN, pJob = %p\n",
  88. pWork->pJob));
  89. if (pWork->pJob) {
  90. LTRACE((L"CancelWork: cancelling job\n"));
  91. hr = pWork->pJob->Cancel(HSM_JOB_PHASE_ALL);
  92. break;
  93. }
  94. } else {
  95. LTRACE((L"CancelWork: cancelling copy media operation\n"));
  96. if (S_OK == pServer->CancelCopyMedia()) {
  97. hr = S_OK;
  98. break;
  99. }
  100. }
  101. Sleep(1000);
  102. }
  103. } WsbCatch(hr);
  104. LTRACE((L"CancelWork: exit = %ls\n", WsbHrAsString(hr)));
  105. return(hr);
  106. }
  107. static HRESULT
  108. ConnectToServer(
  109. IN OUT IHsmServer** ppServer
  110. )
  111. /*++
  112. Routine Description:
  113. Connect to the server that will do the work.
  114. Arguments:
  115. ppServer - Pointer to pointer to server.
  116. Return Value:
  117. S_OK - Success
  118. --*/
  119. {
  120. HRESULT hr = S_OK;
  121. LTRACE((L"ConnectToServer: entry\n"));
  122. try {
  123. CWsbStringPtr tmpString;
  124. WsbAffirm(ppServer, E_POINTER);
  125. WsbAffirm(!(*ppServer), E_FAIL);
  126. // Store of the name of the server.
  127. WsbAffirmHr( WsbGetComputerName( tmpString ) );
  128. // Find the Hsm to get it's id.
  129. WsbAffirmHr(HsmConnectFromName(HSMCONN_TYPE_HSM, tmpString, IID_IHsmServer,
  130. (void**) ppServer));
  131. } WsbCatch(hr);
  132. LTRACE((L"ConnectToServer: exit = %ls\n", WsbHrAsString(hr)));
  133. return(hr);
  134. }
  135. static BOOL
  136. CreateOurWindow(
  137. HINSTANCE hInstance
  138. )
  139. /*++
  140. Routine Description:
  141. Create our invisible window.
  142. NOTE: If the Task Scheduler ever gets smarter and can send the WM_CLOSE
  143. message to an application without a window, the invisible window may
  144. not be needed.
  145. Arguments:
  146. hInstance - Handle for this instance of the program.
  147. Return Value:
  148. TRUE - Everything worked.
  149. FALSE - Something went wrong.
  150. --*/
  151. {
  152. BOOL bRet = FALSE;
  153. WNDCLASS wc;
  154. // Register our window type
  155. wc.style = 0;
  156. wc.lpfnWndProc = &WindowProc;
  157. wc.cbClsExtra = 0;
  158. wc.cbWndExtra = 0;
  159. wc.hInstance = hInstance;
  160. wc.hIcon = NULL;
  161. wc.hCursor = NULL;
  162. wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
  163. wc.lpszMenuName = NULL;
  164. wc.lpszClassName = WINDOW_CLASS;
  165. if (RegisterClass(&wc)) {
  166. // Create the window (invisible by default)
  167. if (CreateWindowEx( 0,
  168. WINDOW_CLASS,
  169. L"RsLaunch",
  170. WS_OVERLAPPEDWINDOW,
  171. CW_USEDEFAULT,
  172. CW_USEDEFAULT,
  173. CW_USEDEFAULT,
  174. CW_USEDEFAULT,
  175. NULL,
  176. NULL,
  177. hInstance,
  178. NULL)) {
  179. bRet = TRUE;
  180. } else {
  181. LTRACE((L"CreateWindowEx failed\n"));
  182. }
  183. } else {
  184. LTRACE((L"RegisterClass failed\n"));
  185. }
  186. return(bRet);
  187. }
  188. static DWORD
  189. DoWork(
  190. IN void* pVoid
  191. )
  192. /*++
  193. Routine Description:
  194. Process the command line and start the processing.
  195. Arguments:
  196. pVoid - A pointer (cast to void*) to a DO_WORK_DATA structure.
  197. Return Value:
  198. The return value from the job
  199. --*/
  200. {
  201. HRESULT hr = S_OK;
  202. DO_WORK_DATA * pWork;
  203. LTRACE((L"DoWork: entry\n"));
  204. pWork = static_cast<DO_WORK_DATA*>(pVoid);
  205. try {
  206. WCHAR delims[] = L" \r\n\t\"";
  207. WCHAR delims2[] = L" \t";
  208. WCHAR delims3[] = L"\"";
  209. WCHAR * pToken;
  210. WsbAssert(pWork, E_POINTER);
  211. WsbAssert(pWork->pCmdLine, E_POINTER);
  212. LTRACE((L"DoWork: CmdLine = %ls\n", pWork->pCmdLine));
  213. // Validate we have a parameter
  214. pToken = wcstok(pWork->pCmdLine, delims);
  215. WsbAssert(pToken, E_INVALIDARG);
  216. // What type of request is it?
  217. if (_wcsicmp(pToken, OLESTR("run")) == 0) {
  218. CWsbStringPtr jobName;
  219. // 'run' option passed in
  220. pWork->wtype = WORK_RUN;
  221. // The job name can have embedded spaces so it may be in quotes.
  222. // This means that using wcstok may not work correctly.
  223. pToken = pToken + wcslen(pToken) + 1; // Skip "run" & NULL
  224. pToken = pToken + wcsspn(pToken, delims2); // Skip spaces
  225. if (L'\"' == *pToken) {
  226. // Job name is in quotes
  227. jobName = wcstok(pToken, delims3);
  228. } else {
  229. jobName = wcstok(pToken, delims);
  230. }
  231. WsbAssert(jobName, E_INVALIDARG);
  232. LTRACE((L"DoWork: calling RunJob(%ls)\n", jobName));
  233. WsbAffirmHr(RunJob(jobName, &(pWork->pJob)));
  234. } else if (_wcsicmp(pToken, OLESTR("sync")) == 0) {
  235. CWsbStringPtr poolName;
  236. USHORT copySet = 1;
  237. WCHAR * pTemp;
  238. // 'sync' (update a copy set) option passed in
  239. pWork->wtype = WORK_SYNCH;
  240. pToken = wcstok(NULL, delims);
  241. WsbAssert(pToken, E_INVALIDARG);
  242. pTemp = wcstok(NULL, delims);
  243. if (!pTemp) {
  244. // will pass NULL for poolName if no pool name specified
  245. copySet = (USHORT) _wtoi(pToken);
  246. } else {
  247. poolName = pToken;
  248. copySet = (USHORT) _wtoi(pTemp);
  249. }
  250. WsbAffirmHr(SynchronizeMedia(poolName, copySet));
  251. } else if (_wcsicmp(pToken, OLESTR("recreate")) == 0) {
  252. USHORT copySet = 0;
  253. CWsbStringPtr mediaName;
  254. GUID mediaId = GUID_NULL;
  255. // 'recreate' (re-create a master media) option passed in
  256. pWork->wtype = WORK_RECREATE;
  257. pToken = wcstok(NULL, delims);
  258. WsbAssert(pToken, E_INVALIDARG);
  259. if ( _wcsicmp(pToken, OLESTR("-i")) == 0 ) {
  260. // media id was passed in, convert from string to GUID
  261. pToken = wcstok(NULL, delims);
  262. WsbAssert(pToken, E_INVALIDARG);
  263. WsbAffirmHr(WsbGuidFromString( pToken, &mediaId ));
  264. } else if ( _wcsicmp(pToken, OLESTR("-n")) == 0 ) {
  265. // Media description (name) was passed in.
  266. // The function RecreateMaster() will look up its id (GUID).
  267. pToken = wcstok(NULL, delims);
  268. WsbAssert(pToken, E_INVALIDARG);
  269. mediaName = pToken;
  270. }
  271. // Get copySet number
  272. pToken = wcstok(NULL, delims);
  273. if (pToken && _wcsicmp(pToken, OLESTR("-c")) == 0) {
  274. pToken = wcstok(NULL, delims);
  275. WsbAssert(pToken, E_INVALIDARG);
  276. copySet = (USHORT) _wtoi(pToken);
  277. }
  278. WsbAffirmHr( RecreateMaster( mediaId, mediaName, copySet ));
  279. } else {
  280. WsbThrow(E_INVALIDARG);
  281. }
  282. } WsbCatch(hr);
  283. if (pWork) {
  284. pWork->hr = hr;
  285. }
  286. LTRACE((L"DoWork: exit = %ls\n", WsbHrAsString(hr)));
  287. return(static_cast<DWORD>(hr));
  288. }
  289. static HRESULT
  290. RecreateMaster(
  291. IN GUID oldMasterMediaId,
  292. IN OLECHAR* oldMasterMediaName,
  293. IN USHORT copySet
  294. )
  295. /*++
  296. Routine Description:
  297. This routine implements the method that will cause a Remote Storage master media
  298. to be re-created by calling the appropraite method on the Remote Storage engine.
  299. The master will be re-created from the specified copy or its most recent copy.
  300. Arguments:
  301. oldMasterMediaId - The GUID of the current master media which is to be re-created.
  302. Normally passed, but an option exists where if the master's
  303. description is passed, the id (GUID) will be looked up by this
  304. method prior to invoking the engine. See below.
  305. oldMasterMediaName - A wide character string representing the master media's
  306. description (display name). If this argument is passed with a valid
  307. string, the string is used to look up the oldMasterMediaId above.
  308. copySet - The copyset number of the copy to use for the recreation or zero, which
  309. indicates that the Engine should just use the most recent copy
  310. Return Value:
  311. S_OK - The call succeeded (the specified master was re-created).
  312. E_FAIL - Could not get host computer's name (highly unexpected error).
  313. E_UNEXPECTED - The argument 'oldMasterMediaId' equaled GUID_NULL just prior to
  314. calling the HSM Engine to re-create a media master. This argument should
  315. either be received with a valid value (the norm), or it should be set by this
  316. method if a valid media description was passed in as 'oldMasterMediaName'.
  317. Any other value - The call failed because one of the Remote Storage API calls
  318. contained internally in this method failed. The error value returned is
  319. specific to the API call which failed.
  320. --*/
  321. {
  322. HRESULT hr = S_OK;
  323. try {
  324. CComPtr<IHsmServer> pServer;
  325. WsbAffirmHr(ConnectToServer(&pServer));
  326. // If we were passed a media name, find its id. Since the name option is
  327. // presently only used internally and it bypasses the UI, also mark
  328. // the media record for re-creation (normally done by the UI) otherwise
  329. // the RecreateMaster() call below will fail.
  330. // If the string is not null...
  331. if ( oldMasterMediaName != 0 ) {
  332. // and if the 1st character of the string is not the null terminator
  333. if ( *oldMasterMediaName != 0 ) {
  334. WsbAffirmHr(pServer->FindMediaIdByDescription(oldMasterMediaName,
  335. &oldMasterMediaId));
  336. WsbAffirmHr(pServer->MarkMediaForRecreation(oldMasterMediaId));
  337. }
  338. }
  339. // Ensure we have a non-null media id
  340. WsbAffirm( oldMasterMediaId != GUID_NULL, E_UNEXPECTED );
  341. // Re-create the master media.
  342. WsbAffirmHr(pServer->RecreateMaster( oldMasterMediaId, copySet ));
  343. } WsbCatch(hr);
  344. return(hr);
  345. }
  346. static void
  347. ReportError(
  348. IN HRESULT hr
  349. )
  350. /*++
  351. Routine Description:
  352. Report errors.
  353. Arguments:
  354. hr - The error.
  355. Return Value:
  356. None.
  357. --*/
  358. {
  359. CWsbStringPtr BoxTitle;
  360. CWsbStringPtr BoxString;
  361. CWsbStringPtr BoxString1;
  362. CWsbStringPtr BoxString2;
  363. CWsbStringPtr BoxString3;
  364. CWsbStringPtr BoxString4;
  365. CWsbStringPtr BoxString5;
  366. CWsbStringPtr BoxString6;
  367. BOOL displayMsg = FALSE;
  368. UINT style = MB_OK;
  369. #if DBG
  370. if (E_INVALIDARG == hr) {
  371. // If this is a Debug build then command line invocation is allowed.
  372. // (Debug build implies Development/Test usage.)
  373. // Tell them the valid command lines. Since this program, originally
  374. // written as a console app, is now linked as a Windows program, pop
  375. // this up as a message box.
  376. // define the lines of text to appear in the message box
  377. BoxString = L"Remote Storage Launch Program\r\n";
  378. BoxString1 = L"allowable command line options:\r\n\n";
  379. BoxString2 = L" RSLAUNCH run <job name>\r\n";
  380. BoxString3 = L" RSLAUNCH sync <copyset number>\r\n";
  381. BoxString4 = L" RSLAUNCH sync <pool name> <copyset number>\r\n";
  382. BoxString5 = L" RSLAUNCH recreate -i <media id> [-c <copyset number>]\r\n";
  383. BoxString6 = L" RSLAUNCH recreate -n <media name> [-c <copyset number>]\r\n";
  384. // display the Help message box
  385. style = MB_OK | MB_ICONEXCLAMATION | MB_SETFOREGROUND;
  386. displayMsg = TRUE;
  387. } else {
  388. // message box text lines
  389. BoxString = L"An error occurred while Remote Storage Launch was launching a job.\n";
  390. BoxString1 = WsbHrAsString(hr);
  391. // display the Error message box
  392. style = MB_OK | MB_ICONERROR | MB_TOPMOST;
  393. displayMsg = TRUE;
  394. }
  395. #else
  396. if (E_INVALIDARG == hr) {
  397. // error message box if the Release version:
  398. // message box text lines
  399. BoxString.LoadFromRsc( g_hInstance, IDS_INVALID_PARAMETER );
  400. // display the Error message box
  401. style = MB_OK | MB_ICONERROR | MB_SETFOREGROUND;
  402. displayMsg = TRUE;
  403. }
  404. #endif // DBG
  405. if (displayMsg) {
  406. // concatenate all text lines
  407. BoxString.Append( BoxString1 );
  408. BoxString.Append( BoxString2 );
  409. BoxString.Append( BoxString3 );
  410. BoxString.Append( BoxString4 );
  411. BoxString.Append( BoxString5 );
  412. BoxString.Append( BoxString6 );
  413. WsbAffirm(0 != (WCHAR *)BoxString, E_OUTOFMEMORY);
  414. // message box title line
  415. WsbAffirmHr(BoxTitle.LoadFromRsc( g_hInstance, IDS_APPLICATION_TITLE ));
  416. // display the Help message box
  417. MessageBox( NULL, BoxString, BoxTitle, style);
  418. }
  419. }
  420. static HRESULT
  421. RunJob(
  422. IN OLECHAR* jobName,
  423. OUT IHsmJob** ppJob
  424. )
  425. /*++
  426. Routine Description:
  427. This routine implements the method for running a Remote Storage job.
  428. Arguments:
  429. jobName - A wide character string containing the name of the job to run.
  430. ppJob - Pointer to pointer to Job interface obtained from the server
  431. Return Value:
  432. S_OK - The call succeeded (the specified job ran successfully).
  433. E_POINTER - Input argument 'jobName' is null.
  434. E_FAIL - Used to indicate 2 error conditions:
  435. 1. could not get host computer's name (highly unexpected error);
  436. 2. the job run by this method returned an HRESULT other than S_OK.
  437. Any other value - The call failed because one of the Remote Storage API calls
  438. contained internally in this method failed. The error value returned is
  439. specific to the API call which failed.
  440. --*/
  441. {
  442. HRESULT hr = S_OK;
  443. try {
  444. CComPtr<IHsmServer> pServer;
  445. WsbAssert(0 != jobName, E_POINTER);
  446. WsbAssert(ppJob, E_POINTER);
  447. WsbAffirmHr(ConnectToServer(&pServer));
  448. // Find the job, start the job, wait for the job to complete.
  449. WsbAffirmHr(pServer->FindJobByName(jobName, ppJob));
  450. WsbAffirmHr((*ppJob)->Start());
  451. WsbAffirmHr((*ppJob)->WaitUntilDone());
  452. } WsbCatch(hr);
  453. return(hr);
  454. }
  455. static HRESULT
  456. SynchronizeMedia(
  457. IN OLECHAR* poolName,
  458. IN USHORT copySet
  459. )
  460. /*++
  461. Routine Description:
  462. This routine implements the method that will cause the updating (synchronizing) of
  463. an entire copy set by calling the appropriate method on the Remote Storage engine.
  464. Specifically, this method causes all copy media belonging to a specified copy set
  465. to be checked for synchronization (being up to date) with each of their respective
  466. master media. Those out of date will be brought up to date. Running this method
  467. assumes that Remote Storage has already been configured for a certain number of
  468. copy sets.
  469. Arguments:
  470. poolName - A wide character string containing the name of a specific storage pool
  471. that the user wants the specified copy set synchronized for. If this
  472. argument is passed as NULL then all storage pools will have the specified
  473. copy set synchronized.
  474. copySet - A number indicating which copy set is to be synchronized.
  475. Return Value:
  476. S_OK - The call succeeded (the specified copy set of the specified storage pool was
  477. updated).
  478. E_FAIL - Could not get host computer's name (highly unexpected error).
  479. Any other value - The call failed because one of the Remote Storage API calls
  480. contained internally in this method failed. The error value returned is
  481. specific to the API call which failed.
  482. --*/
  483. {
  484. HRESULT hr = S_OK;
  485. GUID poolId = GUID_NULL;
  486. CComPtr<IHsmStoragePool> pPool;
  487. try {
  488. CComPtr<IHsmServer> pServer;
  489. WsbAffirmHr(ConnectToServer(&pServer));
  490. // If they specified a pool, then find it's id.
  491. if ( poolName != 0 ) {
  492. if ( *poolName != 0 ) {
  493. CWsbStringPtr tmpString;
  494. WsbAffirmHr(pServer->FindStoragePoolByName(poolName, &pPool));
  495. WsbAffirmHr(pPool->GetMediaSet(&poolId, &tmpString));
  496. }
  497. }
  498. // Synchronize the media. Note that if no pool name was passed in, we pass
  499. // GUID_NULL as the pool id.
  500. WsbAffirmHr(pServer->SynchronizeMedia(poolId, copySet));
  501. } WsbCatch(hr);
  502. return(hr);
  503. }
  504. // WindowProc - Needed for our invisible window
  505. static LRESULT CALLBACK
  506. WindowProc(
  507. HWND hwnd,
  508. UINT uMsg,
  509. WPARAM wParam,
  510. LPARAM lParam
  511. )
  512. {
  513. LTRACE((L"WindowProc: msg = %4.4x\n", uMsg));
  514. return(DefWindowProc(hwnd, uMsg, wParam, lParam));
  515. }
  516. //****************************** MAIN *********************************
  517. extern "C"
  518. int WINAPI wWinMain(HINSTANCE hInstance,
  519. HINSTANCE /*hPrevInstance*/,
  520. LPTSTR lpCmdLine,
  521. int /*nShowCmd*/
  522. )
  523. {
  524. HRESULT hr = S_OK;
  525. #if defined(RSL_TRACE)
  526. CComPtr<IWsbTrace> pTrace;
  527. #endif
  528. // Store our instance handle so it can be used by called code.
  529. g_hInstance = hInstance;
  530. try {
  531. HANDLE hJobThread[1] = { NULL };
  532. DO_WORK_DATA workData = { NULL, WORK_NONE, E_FAIL, NULL };
  533. // Register & create our invisible window
  534. WsbAssert(CreateOurWindow(hInstance), E_FAIL);
  535. // Initialize COM
  536. WsbAffirmHr(CoInitializeEx(NULL, COINIT_MULTITHREADED));
  537. // This provides a NULL DACL - RsNotify is only a COM client therefore this ACL is not important
  538. // If a COm object is ever implemented here, the ACL should match potential clients.
  539. CSecurityDescriptor sd;
  540. sd.InitializeFromThreadToken();
  541. WsbAffirmHr(CoInitializeSecurity(sd, -1, NULL, NULL,
  542. RPC_C_AUTHN_LEVEL_CONNECT, RPC_C_IMP_LEVEL_IDENTIFY, NULL,
  543. EOAC_NONE, NULL));
  544. try {
  545. DWORD ThreadId = 0;
  546. #if defined(RSL_TRACE)
  547. // Start tracing
  548. CoCreateInstance(CLSID_CWsbTrace, 0, CLSCTX_SERVER, IID_IWsbTrace,
  549. (void **) &pTrace);
  550. pTrace->DirectOutput(WSB_TRACE_OUT_DEBUG_SCREEN | WSB_TRACE_OUT_FILE);
  551. pTrace->SetTraceFileControls(TRACE_FILE, FALSE, 3000000, NULL);
  552. pTrace->SetOutputFormat(TRUE, TRUE, TRUE);
  553. pTrace->SetTraceSettings(0xffffffffffffffffL);
  554. pTrace->StartTrace();
  555. #endif
  556. LTRACE((L"Main: lpCmdLine = %ls\n", lpCmdLine));
  557. workData.pCmdLine = lpCmdLine;
  558. // Create a thread to start the work and wait for it
  559. // to finish
  560. LTRACE((L"Main: creating thread for DoWork\n"));
  561. hJobThread[0] = CreateThread(0, 0, DoWork,
  562. static_cast<void*>(&workData), 0, &ThreadId);
  563. if (!hJobThread[0]) {
  564. LTRACE((L"Main: CreateThread failed\n"));
  565. WsbThrow(HRESULT_FROM_WIN32(GetLastError()));
  566. }
  567. // Don't exit if we're waiting for work to complete
  568. while (TRUE) {
  569. DWORD exitcode;
  570. DWORD waitStatus;
  571. // Wait for a message or thread to end
  572. LTRACE((L"Main: waiting for multiple objects\n"));
  573. waitStatus = MsgWaitForMultipleObjects(1, hJobThread, FALSE,
  574. INFINITE, QS_ALLINPUT);
  575. // Find out which event happened
  576. if (WAIT_OBJECT_0 == waitStatus) {
  577. // The thread ended; get it's exit code
  578. LTRACE((L"Main: got event on thread\n"));
  579. if (GetExitCodeThread(hJobThread[0], &exitcode)) {
  580. if (STILL_ACTIVE == exitcode) {
  581. // This shouldn't happen; don't know what to do!
  582. } else {
  583. WsbThrow(static_cast<HRESULT>(exitcode));
  584. }
  585. } else {
  586. WsbThrow(HRESULT_FROM_WIN32(GetLastError()));
  587. }
  588. } else if ((WAIT_OBJECT_0 + 1) == waitStatus) {
  589. // Message in queue
  590. MSG msg;
  591. while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
  592. LTRACE((L"Main: message = %4.4x\n", msg.message));
  593. if (WM_CLOSE == msg.message) {
  594. // Cancel the job since someone cancelled us.
  595. // (Should we kill the thread that is waiting?)
  596. LTRACE((L"Main: got WM_CLOSE\n"));
  597. WsbThrow(CancelWork(&workData));
  598. }
  599. DispatchMessage(&msg);
  600. }
  601. } else if (0xFFFFFFFF == waitStatus) {
  602. // Error in MsgWaitForMultipleObjects
  603. WsbThrow(HRESULT_FROM_WIN32(GetLastError()));
  604. } else {
  605. // This shouldn't happend; don't know what to do
  606. }
  607. }
  608. } WsbCatch(hr);
  609. if (hJobThread[0]) {
  610. CloseHandle(hJobThread[0]);
  611. }
  612. if (workData.pJob) {
  613. workData.pJob->Release();
  614. }
  615. // Cleanup COM
  616. CoUninitialize();
  617. } WsbCatch(hr);
  618. LTRACE((L"Main: exit hr = %ls\n", WsbHrAsString(hr)));
  619. if (FAILED(hr)) {
  620. ReportError(hr);
  621. }
  622. return(hr);
  623. }