aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2022-06-29 10:28:53 -0500
committerSean Hall <r.sean.hall@gmail.com>2022-06-29 15:08:37 -0500
commit7cca75c8e95f129a21c33f1f4568e90e9e397f9d (patch)
treecb9890caa1ac8bc891d444b6376a5e9f997ca1e3
parent3ff6428a068bafd74d8ec072a5fc261c33cc2019 (diff)
downloadwix-7cca75c8e95f129a21c33f1f4568e90e9e397f9d.tar.gz
wix-7cca75c8e95f129a21c33f1f4568e90e9e397f9d.tar.bz2
wix-7cca75c8e95f129a21c33f1f4568e90e9e397f9d.zip
Add AppWaitForSingleObject/MultipleObjects, ThreadWaitForCompletion.
-rw-r--r--src/burn/engine/apply.cpp26
-rw-r--r--src/burn/engine/cabextract.cpp42
-rw-r--r--src/burn/engine/core.cpp54
-rw-r--r--src/burn/engine/core.h14
-rw-r--r--src/burn/engine/elevation.cpp11
-rw-r--r--src/burn/engine/netfxchainer.cpp31
-rw-r--r--src/burn/engine/pipe.cpp34
-rw-r--r--src/burn/engine/precomp.h1
-rw-r--r--src/burn/test/BurnUnitTest/ElevationTest.cpp3
-rw-r--r--src/burn/test/BurnUnitTest/precomp.h1
-rw-r--r--src/libs/dutil/WixToolset.DUtil/apputil.cpp68
-rw-r--r--src/libs/dutil/WixToolset.DUtil/dutil.vcxproj2
-rw-r--r--src/libs/dutil/WixToolset.DUtil/dutil.vcxproj.filters6
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/apputil.h21
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/dutil.h2
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/dutilsources.h1
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/procutil.h2
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/thrdutil.h22
-rw-r--r--src/libs/dutil/WixToolset.DUtil/monutil.cpp34
-rw-r--r--src/libs/dutil/WixToolset.DUtil/precomp.h1
-rw-r--r--src/libs/dutil/WixToolset.DUtil/procutil.cpp42
-rw-r--r--src/libs/dutil/WixToolset.DUtil/thrdutil.cpp46
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/AppUtilTests.cpp60
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj1
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters3
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/precomp.h1
26 files changed, 380 insertions, 149 deletions
diff --git a/src/burn/engine/apply.cpp b/src/burn/engine/apply.cpp
index e5b978f1..dd99a5cd 100644
--- a/src/burn/engine/apply.cpp
+++ b/src/burn/engine/apply.cpp
@@ -2333,6 +2333,8 @@ static HRESULT DoExecuteAction(
2333 2333
2334 HRESULT hr = S_OK; 2334 HRESULT hr = S_OK;
2335 HANDLE rghWait[2] = { }; 2335 HANDLE rghWait[2] = { };
2336 DWORD dwSignaledIndex = 0;
2337 DWORD dwExitCode = 0;
2336 BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; 2338 BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE;
2337 BOOL fRetry = FALSE; 2339 BOOL fRetry = FALSE;
2338 BOOL fStopWusaService = FALSE; 2340 BOOL fStopWusaService = FALSE;
@@ -2356,27 +2358,23 @@ static HRESULT DoExecuteAction(
2356 // wait for cache sync-point 2358 // wait for cache sync-point
2357 rghWait[0] = pExecuteAction->waitCachePackage.pPackage->hCacheEvent; 2359 rghWait[0] = pExecuteAction->waitCachePackage.pPackage->hCacheEvent;
2358 rghWait[1] = pContext->pApplyContext->hCacheThread; 2360 rghWait[1] = pContext->pApplyContext->hCacheThread;
2359 switch (::WaitForMultipleObjects(rghWait[1] ? 2 : 1, rghWait, FALSE, INFINITE)) 2361
2362 hr = AppWaitForMultipleObjects(rghWait[1] ? 2 : 1, rghWait, FALSE, INFINITE, &dwSignaledIndex);
2363 ExitOnFailure(hr, "Failed to wait for cache check-point.");
2364
2365 switch (dwSignaledIndex)
2360 { 2366 {
2361 case WAIT_OBJECT_0: 2367 case 0:
2362 break; 2368 break;
2363 2369 case 1:
2364 case WAIT_OBJECT_0 + 1: 2370 if (!::GetExitCodeThread(pContext->pApplyContext->hCacheThread, &dwExitCode))
2365 if (!::GetExitCodeThread(pContext->pApplyContext->hCacheThread, (DWORD*)&hr))
2366 { 2371 {
2367 ExitWithLastError(hr, "Failed to get cache thread exit code."); 2372 ExitWithLastError(hr, "Failed to get cache thread exit code.");
2368 } 2373 }
2369 2374
2370 if (SUCCEEDED(hr)) 2375 ExitWithRootFailure(hr, E_UNEXPECTED, "Cache thread exited unexpectedly with exit code: %u.", dwExitCode);
2371 {
2372 hr = E_UNEXPECTED;
2373 }
2374 ExitOnFailure(hr, "Cache thread exited unexpectedly.");
2375
2376 case WAIT_FAILED: __fallthrough;
2377 default:
2378 ExitWithLastError(hr, "Failed to wait for cache check-point.");
2379 } 2376 }
2377
2380 break; 2378 break;
2381 2379
2382 case BURN_EXECUTE_ACTION_TYPE_RELATED_BUNDLE: 2380 case BURN_EXECUTE_ACTION_TYPE_RELATED_BUNDLE:
diff --git a/src/burn/engine/cabextract.cpp b/src/burn/engine/cabextract.cpp
index 5a02ff8a..56146a39 100644
--- a/src/burn/engine/cabextract.cpp
+++ b/src/burn/engine/cabextract.cpp
@@ -262,10 +262,8 @@ extern "C" HRESULT CabExtractClose(
262 } 262 }
263 263
264 // wait for thread to terminate 264 // wait for thread to terminate
265 if (WAIT_OBJECT_0 != ::WaitForSingleObject(pContext->Cabinet.hThread, INFINITE)) 265 hr = AppWaitForSingleObject(pContext->Cabinet.hThread, INFINITE);
266 { 266 ExitOnFailure(hr, "Failed to wait for thread to terminate.");
267 ExitWithLastError(hr, "Failed to wait for thread to terminate.");
268 }
269 } 267 }
270 268
271LExit: 269LExit:
@@ -306,29 +304,31 @@ static HRESULT WaitForOperation(
306{ 304{
307 HRESULT hr = S_OK; 305 HRESULT hr = S_OK;
308 HANDLE rghWait[2] = { }; 306 HANDLE rghWait[2] = { };
307 DWORD dwSignaledIndex = 0;
309 308
310 // wait for operation complete event 309 // wait for operation complete event
311 rghWait[0] = pContext->Cabinet.hOperationCompleteEvent; 310 rghWait[0] = pContext->Cabinet.hOperationCompleteEvent;
312 rghWait[1] = pContext->Cabinet.hThread; 311 rghWait[1] = pContext->Cabinet.hThread;
313 switch (::WaitForMultipleObjects(countof(rghWait), rghWait, FALSE, INFINITE)) 312
313 hr = AppWaitForMultipleObjects(countof(rghWait), rghWait, FALSE, INFINITE, &dwSignaledIndex);
314 ExitOnFailure(hr, "Failed to wait for operation complete event.");
315
316 switch (dwSignaledIndex)
314 { 317 {
315 case WAIT_OBJECT_0: 318 case 0:
316 if (!::ResetEvent(pContext->Cabinet.hOperationCompleteEvent)) 319 if (!::ResetEvent(pContext->Cabinet.hOperationCompleteEvent))
317 { 320 {
318 ExitWithLastError(hr, "Failed to reset operation complete event."); 321 ExitWithLastError(hr, "Failed to reset operation complete event.");
319 } 322 }
320 break;
321 323
322 case WAIT_OBJECT_0 + 1: 324 break;
325 case 1:
323 if (!::GetExitCodeThread(pContext->Cabinet.hThread, (DWORD*)&hr)) 326 if (!::GetExitCodeThread(pContext->Cabinet.hThread, (DWORD*)&hr))
324 { 327 {
325 ExitWithLastError(hr, "Failed to get extraction thread exit code."); 328 ExitWithLastError(hr, "Failed to get extraction thread exit code.");
326 } 329 }
327 ExitFunction();
328 330
329 case WAIT_FAILED: __fallthrough; 331 ExitFunction();
330 default:
331 ExitWithLastError(hr, "Failed to wait for operation complete event.");
332 } 332 }
333 333
334 // clear operation 334 // clear operation
@@ -430,10 +430,8 @@ static DWORD WINAPI ExtractThreadProc(
430 } 430 }
431 431
432 // wait for begin operation event 432 // wait for begin operation event
433 if (WAIT_FAILED == ::WaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE)) 433 hr = AppWaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE);
434 { 434 ExitOnFailure(hr, "Failed to wait for begin operation event.");
435 ExitWithLastError(hr, "Failed to wait for begin operation event.");
436 }
437 435
438 if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent)) 436 if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent))
439 { 437 {
@@ -517,10 +515,8 @@ static INT_PTR CopyFileCallback(
517 } 515 }
518 516
519 // wait for begin operation event 517 // wait for begin operation event
520 if (WAIT_FAILED == ::WaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE)) 518 hr = AppWaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE);
521 { 519 ExitOnFailure(hr, "Failed to wait for begin operation event.");
522 ExitWithLastError(hr, "Failed to wait for begin operation event.");
523 }
524 520
525 if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent)) 521 if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent))
526 { 522 {
@@ -552,10 +548,8 @@ static INT_PTR CopyFileCallback(
552 } 548 }
553 549
554 // wait for begin operation event 550 // wait for begin operation event
555 if (WAIT_FAILED == ::WaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE)) 551 hr = AppWaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE);
556 { 552 ExitOnFailure(hr, "Failed to wait for begin operation event.");
557 ExitWithLastError(hr, "Failed to wait for begin operation event.");
558 }
559 553
560 if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent)) 554 if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent))
561 { 555 {
diff --git a/src/burn/engine/core.cpp b/src/burn/engine/core.cpp
index c1e3a12c..716e5af1 100644
--- a/src/burn/engine/core.cpp
+++ b/src/burn/engine/core.cpp
@@ -12,6 +12,9 @@ struct BURN_CACHE_THREAD_CONTEXT
12}; 12};
13 13
14 14
15static PFN_PROCWAITFORCOMPLETION vpfnProcWaitForCompletion = ProcWaitForCompletion;
16
17
15// internal function declarations 18// internal function declarations
16 19
17static HRESULT CoreRecreateCommandLine( 20static HRESULT CoreRecreateCommandLine(
@@ -65,9 +68,6 @@ static HRESULT DetectPackagePayloadsCached(
65static DWORD WINAPI CacheThreadProc( 68static DWORD WINAPI CacheThreadProc(
66 __in LPVOID lpThreadParameter 69 __in LPVOID lpThreadParameter
67 ); 70 );
68static HRESULT WaitForCacheThread(
69 __in HANDLE hCacheThread
70 );
71static void LogPackages( 71static void LogPackages(
72 __in_opt const BURN_PACKAGE* pUpgradeBundlePackage, 72 __in_opt const BURN_PACKAGE* pUpgradeBundlePackage,
73 __in_opt const BURN_PACKAGE* pForwardCompatibleBundlePackage, 73 __in_opt const BURN_PACKAGE* pForwardCompatibleBundlePackage,
@@ -636,6 +636,7 @@ extern "C" HRESULT CoreApply(
636 BURN_APPLY_CONTEXT applyContext = { }; 636 BURN_APPLY_CONTEXT applyContext = { };
637 BOOL fDeleteApplyCs = FALSE; 637 BOOL fDeleteApplyCs = FALSE;
638 BURN_CACHE_THREAD_CONTEXT cacheThreadContext = { }; 638 BURN_CACHE_THREAD_CONTEXT cacheThreadContext = { };
639 DWORD dwCacheExitCode = 0;
639 BOOL fRollbackCache = FALSE; 640 BOOL fRollbackCache = FALSE;
640 DWORD dwPhaseCount = 0; 641 DWORD dwPhaseCount = 0;
641 BOOTSTRAPPER_APPLYCOMPLETE_ACTION applyCompleteAction = BOOTSTRAPPER_APPLYCOMPLETE_ACTION_NONE; 642 BOOTSTRAPPER_APPLYCOMPLETE_ACTION applyCompleteAction = BOOTSTRAPPER_APPLYCOMPLETE_ACTION_NONE;
@@ -744,7 +745,10 @@ extern "C" HRESULT CoreApply(
744 // If we're not caching in parallel, wait for the cache thread to terminate. 745 // If we're not caching in parallel, wait for the cache thread to terminate.
745 if (!pEngineState->fParallelCacheAndExecute) 746 if (!pEngineState->fParallelCacheAndExecute)
746 { 747 {
747 hr = WaitForCacheThread(applyContext.hCacheThread); 748 hr = ThrdWaitForCompletion(applyContext.hCacheThread, INFINITE, &dwCacheExitCode);
749 ExitOnFailure(hr, "Failed to wait for cache thread before execute.");
750
751 hr = (HRESULT)dwCacheExitCode;
748 ExitOnFailure(hr, "Failed while caching, aborting execution."); 752 ExitOnFailure(hr, "Failed while caching, aborting execution.");
749 753
750 ReleaseHandle(applyContext.hCacheThread); 754 ReleaseHandle(applyContext.hCacheThread);
@@ -761,10 +765,12 @@ extern "C" HRESULT CoreApply(
761 // Wait for cache thread to terminate, this should return immediately unless we're waiting for layout to complete. 765 // Wait for cache thread to terminate, this should return immediately unless we're waiting for layout to complete.
762 if (applyContext.hCacheThread) 766 if (applyContext.hCacheThread)
763 { 767 {
764 HRESULT hrCached = WaitForCacheThread(applyContext.hCacheThread); 768 HRESULT hrCached = ThrdWaitForCompletion(applyContext.hCacheThread, INFINITE, &dwCacheExitCode);
769 ExitOnFailure(hrCached, "Failed to wait for cache thread after execute.");
770
765 if (SUCCEEDED(hr)) 771 if (SUCCEEDED(hr))
766 { 772 {
767 hr = hrCached; 773 hr = (HRESULT)dwCacheExitCode;
768 } 774 }
769 } 775 }
770 776
@@ -1940,6 +1946,22 @@ LExit:
1940 return hr; 1946 return hr;
1941} 1947}
1942 1948
1949extern "C" void CoreFunctionOverride(
1950 __in_opt PFN_PROCWAITFORCOMPLETION pfnProcWaitForCompletion
1951 )
1952{
1953 vpfnProcWaitForCompletion = pfnProcWaitForCompletion;
1954}
1955
1956extern "C" HRESULT DAPI CoreWaitForProcCompletion(
1957 __in HANDLE hProcess,
1958 __in DWORD dwTimeout,
1959 __out DWORD* pdwReturnCode
1960 )
1961{
1962 return vpfnProcWaitForCompletion(hProcess, dwTimeout, pdwReturnCode);
1963}
1964
1943// internal helper functions 1965// internal helper functions
1944 1966
1945static HRESULT AppendEscapedArgumentToCommandLine( 1967static HRESULT AppendEscapedArgumentToCommandLine(
@@ -2268,26 +2290,6 @@ LExit:
2268 return (DWORD)hr; 2290 return (DWORD)hr;
2269} 2291}
2270 2292
2271static HRESULT WaitForCacheThread(
2272 __in HANDLE hCacheThread
2273 )
2274{
2275 HRESULT hr = S_OK;
2276
2277 if (WAIT_OBJECT_0 != ::WaitForSingleObject(hCacheThread, INFINITE))
2278 {
2279 ExitWithLastError(hr, "Failed to wait for cache thread to terminate.");
2280 }
2281
2282 if (!::GetExitCodeThread(hCacheThread, (DWORD*)&hr))
2283 {
2284 ExitWithLastError(hr, "Failed to get cache thread exit code.");
2285 }
2286
2287LExit:
2288 return hr;
2289}
2290
2291static void LogPackages( 2293static void LogPackages(
2292 __in_opt const BURN_PACKAGE* pUpgradeBundlePackage, 2294 __in_opt const BURN_PACKAGE* pUpgradeBundlePackage,
2293 __in_opt const BURN_PACKAGE* pForwardCompatibleBundlePackage, 2295 __in_opt const BURN_PACKAGE* pForwardCompatibleBundlePackage,
diff --git a/src/burn/engine/core.h b/src/burn/engine/core.h
index 28d7ea78..14dbabcc 100644
--- a/src/burn/engine/core.h
+++ b/src/burn/engine/core.h
@@ -174,6 +174,12 @@ typedef struct _BURN_APPLY_CONTEXT
174 DWORD dwCacheCheckpoint; 174 DWORD dwCacheCheckpoint;
175} BURN_APPLY_CONTEXT; 175} BURN_APPLY_CONTEXT;
176 176
177typedef HRESULT (DAPI *PFN_PROCWAITFORCOMPLETION)(
178 __in HANDLE hProcess,
179 __in DWORD dwTimeout,
180 __out DWORD* pReturnCode
181 );
182
177 183
178// function declarations 184// function declarations
179 185
@@ -280,6 +286,14 @@ HRESULT CoreParseCommandLine(
280 __inout HANDLE* phSectionFile, 286 __inout HANDLE* phSectionFile,
281 __inout HANDLE* phSourceEngineFile 287 __inout HANDLE* phSourceEngineFile
282 ); 288 );
289void CoreFunctionOverride(
290 __in_opt PFN_PROCWAITFORCOMPLETION pfnProcWaitForCompletion
291 );
292HRESULT DAPI CoreWaitForProcCompletion(
293 __in HANDLE hProcess,
294 __in DWORD dwTimeout,
295 __out_opt DWORD* pdwReturnCode
296 );
283 297
284#if defined(__cplusplus) 298#if defined(__cplusplus)
285} 299}
diff --git a/src/burn/engine/elevation.cpp b/src/burn/engine/elevation.cpp
index 4a5be8ec..e165a257 100644
--- a/src/burn/engine/elevation.cpp
+++ b/src/burn/engine/elevation.cpp
@@ -1687,15 +1687,8 @@ static HRESULT WaitForElevatedChildCacheThread(
1687 HRESULT hr = S_OK; 1687 HRESULT hr = S_OK;
1688 DWORD dwExitCode = ERROR_SUCCESS; 1688 DWORD dwExitCode = ERROR_SUCCESS;
1689 1689
1690 if (WAIT_OBJECT_0 != ::WaitForSingleObject(hCacheThread, BURN_TIMEOUT)) 1690 hr = ThrdWaitForCompletion(hCacheThread, BURN_TIMEOUT, &dwExitCode);
1691 { 1691 ExitOnFailure(hr, "Failed to wait for cache thread to complete.");
1692 ExitWithLastError(hr, "Failed to wait for cache thread to terminate.");
1693 }
1694
1695 if (!::GetExitCodeThread(hCacheThread, &dwExitCode))
1696 {
1697 ExitWithLastError(hr, "Failed to get cache thread exit code.");
1698 }
1699 1692
1700 AssertSz(dwExitCode == dwExpectedExitCode, "Cache thread should have exited with the expected exit code."); 1693 AssertSz(dwExitCode == dwExpectedExitCode, "Cache thread should have exited with the expected exit code.");
1701 1694
diff --git a/src/burn/engine/netfxchainer.cpp b/src/burn/engine/netfxchainer.cpp
index 6f223eed..68ddb093 100644
--- a/src/burn/engine/netfxchainer.cpp
+++ b/src/burn/engine/netfxchainer.cpp
@@ -338,7 +338,8 @@ extern "C" HRESULT NetFxRunChainer(
338 ) 338 )
339{ 339{
340 HRESULT hr = S_OK; 340 HRESULT hr = S_OK;
341 DWORD er = 0; 341 DWORD dwSignaledIndex = 0;
342 BOOL fTimedOut = 0;
342 WCHAR wzGuid[GUID_STRING_LENGTH]; 343 WCHAR wzGuid[GUID_STRING_LENGTH];
343 LPWSTR sczEventName = NULL; 344 LPWSTR sczEventName = NULL;
344 LPWSTR sczSectionName = NULL; 345 LPWSTR sczSectionName = NULL;
@@ -381,9 +382,17 @@ extern "C" HRESULT NetFxRunChainer(
381 382
382 for (;;) 383 for (;;)
383 { 384 {
384 er = ::WaitForMultipleObjects(2, handles, FALSE, 100); 385 hr = AppWaitForMultipleObjects(2, handles, FALSE, 100, &dwSignaledIndex);
385 if (WAIT_OBJECT_0 == er) 386 ExitOnWaitObjectFailure(hr, fTimedOut, "Failed to wait for netfx chainer process to complete");
387
388 if (fTimedOut)
389 {
390 continue;
391 }
392
393 switch (dwSignaledIndex)
386 { 394 {
395 case 0:
387 // Process has exited 396 // Process has exited
388 *pdwExitCode = NetFxGetResult(pNetfxChainer, &hrInternalError); 397 *pdwExitCode = NetFxGetResult(pNetfxChainer, &hrInternalError);
389 if (E_PENDING == *pdwExitCode) 398 if (E_PENDING == *pdwExitCode)
@@ -396,21 +405,17 @@ extern "C" HRESULT NetFxRunChainer(
396 else if (FAILED(hrInternalError)) 405 else if (FAILED(hrInternalError))
397 { 406 {
398 // push internal error message 407 // push internal error message
399 OnNetFxError(pNetfxChainer, hrInternalError, pfnGenericMessageHandler, pvContext); 408 hr = OnNetFxError(pNetfxChainer, hrInternalError, pfnGenericMessageHandler, pvContext);
400 ExitOnFailure(hr, "Failed to send internal error message from netfx chainer."); 409 ExitOnFailure(hr, "Failed to send internal error message from netfx chainer.");
401 } 410 }
402 411
403 break; 412 ExitFunction();
404 } 413 case 1:
405 else if (WAIT_OBJECT_0 + 1 == er)
406 {
407 // Chainee has notified us of a change. 414 // Chainee has notified us of a change.
408 hr = ProcessNetFxMessage(pNetfxChainer, pfnGenericMessageHandler, pvContext); 415 hr = ProcessNetFxMessage(pNetfxChainer, pfnGenericMessageHandler, pvContext);
409 ExitOnFailure(hr, "Failed to process netfx chainer message."); 416 ExitOnFailure(hr, "Failed to process netfx chainer message.");
410 } 417
411 else if (WAIT_FAILED == er) 418 break;
412 {
413 ExitWithLastError(hr, "Failed to wait for netfx chainer process to complete");
414 } 419 }
415 } 420 }
416 421
diff --git a/src/burn/engine/pipe.cpp b/src/burn/engine/pipe.cpp
index e6c9905b..9529ef40 100644
--- a/src/burn/engine/pipe.cpp
+++ b/src/burn/engine/pipe.cpp
@@ -422,6 +422,7 @@ extern "C" HRESULT PipeTerminateChildProcess(
422 HRESULT hr = S_OK; 422 HRESULT hr = S_OK;
423 BYTE* pbData = NULL; 423 BYTE* pbData = NULL;
424 SIZE_T cbData = 0; 424 SIZE_T cbData = 0;
425 BOOL fTimedOut = FALSE;
425 426
426 // Prepare the exit message. 427 // Prepare the exit message.
427 hr = BuffWriteNumber(&pbData, &cbData, dwParentExitCode); 428 hr = BuffWriteNumber(&pbData, &cbData, dwParentExitCode);
@@ -443,31 +444,28 @@ extern "C" HRESULT PipeTerminateChildProcess(
443 // If we were able to get a handle to the other process, wait for it to exit. 444 // If we were able to get a handle to the other process, wait for it to exit.
444 if (pConnection->hProcess) 445 if (pConnection->hProcess)
445 { 446 {
446 if (WAIT_FAILED == ::WaitForSingleObject(pConnection->hProcess, PIPE_WAIT_FOR_CONNECTION * PIPE_RETRY_FOR_CONNECTION)) 447 hr = AppWaitForSingleObject(pConnection->hProcess, PIPE_WAIT_FOR_CONNECTION * PIPE_RETRY_FOR_CONNECTION);
447 { 448 ExitOnWaitObjectFailure(hr, fTimedOut, "Failed to wait for child process exit.");
448 ExitWithLastError(hr, "Failed to wait for child process exit."); 449
449 } 450 AssertSz(!fTimedOut, "Timed out while waiting for child process to exit.");
451 }
450 452
451#ifdef DEBUG 453#ifdef DEBUG
454 if (pConnection->hProcess && !fTimedOut)
455 {
452 DWORD dwChildExitCode = 0; 456 DWORD dwChildExitCode = 0;
453 DWORD dwErrorCode = ERROR_SUCCESS; 457 HRESULT hrDebug = S_OK;
454 BOOL fReturnedExitCode = ::GetExitCodeProcess(pConnection->hProcess, &dwChildExitCode);
455 if (!fReturnedExitCode)
456 {
457 dwErrorCode = ::GetLastError(); // if the other process is elevated and we are not, then we'll get ERROR_ACCESS_DENIED.
458 458
459 // The unit test use a thread instead of a process so try to get the exit code from 459 hrDebug = CoreWaitForProcCompletion(pConnection->hProcess, 0, &dwChildExitCode);
460 // the thread because we failed to get it from the process. 460 if (E_ACCESSDENIED != hrDebug) // if the other process is elevated and we are not, then we'll get ERROR_ACCESS_DENIED.
461 if (ERROR_INVALID_HANDLE == dwErrorCode) 461 {
462 { 462 TraceError(hrDebug, "Failed to wait for child process completion.");
463 fReturnedExitCode = ::GetExitCodeThread(pConnection->hProcess, &dwChildExitCode);
464 }
465 } 463 }
466 AssertSz((fReturnedExitCode && dwChildExitCode == dwParentExitCode) || 464
467 (!fReturnedExitCode && ERROR_ACCESS_DENIED == dwErrorCode), 465 AssertSz(E_ACCESSDENIED == hrDebug || dwChildExitCode == dwParentExitCode,
468 "Child elevated process did not return matching exit code to parent process."); 466 "Child elevated process did not return matching exit code to parent process.");
469#endif
470 } 467 }
468#endif
471 469
472LExit: 470LExit:
473 return hr; 471 return hr;
diff --git a/src/burn/engine/precomp.h b/src/burn/engine/precomp.h
index bc7046f6..e2d1b5cd 100644
--- a/src/burn/engine/precomp.h
+++ b/src/burn/engine/precomp.h
@@ -46,6 +46,7 @@
46#include <srputil.h> 46#include <srputil.h>
47#include <strutil.h> 47#include <strutil.h>
48#include <svcutil.h> 48#include <svcutil.h>
49#include <thrdutil.h>
49#include <userutil.h> 50#include <userutil.h>
50#include <wiutil.h> 51#include <wiutil.h>
51#include <wuautil.h> 52#include <wuautil.h>
diff --git a/src/burn/test/BurnUnitTest/ElevationTest.cpp b/src/burn/test/BurnUnitTest/ElevationTest.cpp
index 97d76b7d..ce34b4b1 100644
--- a/src/burn/test/BurnUnitTest/ElevationTest.cpp
+++ b/src/burn/test/BurnUnitTest/ElevationTest.cpp
@@ -54,7 +54,6 @@ namespace Bootstrapper
54 HRESULT hr = S_OK; 54 HRESULT hr = S_OK;
55 BURN_ENGINE_STATE engineState = { }; 55 BURN_ENGINE_STATE engineState = { };
56 BURN_PIPE_CONNECTION* pConnection = &engineState.companionConnection; 56 BURN_PIPE_CONNECTION* pConnection = &engineState.companionConnection;
57 HANDLE hEvent = NULL;
58 DWORD dwResult = S_OK; 57 DWORD dwResult = S_OK;
59 58
60 engineState.sczBundleEngineWorkingPath = L"tests\\ignore\\this\\path\\to\\burn.exe"; 59 engineState.sczBundleEngineWorkingPath = L"tests\\ignore\\this\\path\\to\\burn.exe";
@@ -62,6 +61,7 @@ namespace Bootstrapper
62 try 61 try
63 { 62 {
64 ShelFunctionOverride(ElevateTest_ShellExecuteExW); 63 ShelFunctionOverride(ElevateTest_ShellExecuteExW);
64 CoreFunctionOverride(ThrdWaitForCompletion);
65 65
66 PipeConnectionInitialize(pConnection); 66 PipeConnectionInitialize(pConnection);
67 67
@@ -87,7 +87,6 @@ namespace Bootstrapper
87 finally 87 finally
88 { 88 {
89 PipeConnectionUninitialize(pConnection); 89 PipeConnectionUninitialize(pConnection);
90 ReleaseHandle(hEvent);
91 } 90 }
92 } 91 }
93 }; 92 };
diff --git a/src/burn/test/BurnUnitTest/precomp.h b/src/burn/test/BurnUnitTest/precomp.h
index 92c8c4c0..69c62cb2 100644
--- a/src/burn/test/BurnUnitTest/precomp.h
+++ b/src/burn/test/BurnUnitTest/precomp.h
@@ -28,6 +28,7 @@
28#include <resrutil.h> 28#include <resrutil.h>
29#include <shelutil.h> 29#include <shelutil.h>
30#include <strutil.h> 30#include <strutil.h>
31#include <thrdutil.h>
31#include <wiutil.h> 32#include <wiutil.h>
32#include <xmlutil.h> 33#include <xmlutil.h>
33#include <dictutil.h> 34#include <dictutil.h>
diff --git a/src/libs/dutil/WixToolset.DUtil/apputil.cpp b/src/libs/dutil/WixToolset.DUtil/apputil.cpp
index 9e75082a..42f589dc 100644
--- a/src/libs/dutil/WixToolset.DUtil/apputil.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/apputil.cpp
@@ -313,6 +313,74 @@ LExit:
313 return hr; 313 return hr;
314} 314}
315 315
316DAPI_(HRESULT) AppWaitForSingleObject(
317 __in HANDLE hHandle,
318 __in DWORD dwMilliseconds
319 )
320{
321 HRESULT hr = S_OK;
322 DWORD dwResult = 0;
323
324 dwResult = ::WaitForSingleObject(hHandle, dwMilliseconds);
325 if (WAIT_TIMEOUT == dwResult)
326 {
327 ExitFunction1(hr = HRESULT_FROM_WIN32(dwResult));
328 }
329 else if (WAIT_ABANDONED == dwResult)
330 {
331 AppExitOnWin32Error(dwResult, hr, "Abandoned wait for single object.");
332 }
333 else if (WAIT_OBJECT_0 != dwResult)
334 {
335 AssertSz(WAIT_FAILED == dwResult, "Unexpected return code from WaitForSingleObject.");
336 AppExitWithLastError(hr, "Failed to wait for single object.");
337 }
338
339LExit:
340 return hr;
341}
342
343DAPI_(HRESULT) AppWaitForMultipleObjects(
344 __in DWORD dwCount,
345 __in const HANDLE* rghHandles,
346 __in BOOL fWaitAll,
347 __in DWORD dwMilliseconds,
348 __out_opt DWORD* pdwSignaledIndex
349 )
350{
351 HRESULT hr = S_OK;
352 DWORD dwResult = 0;
353 DWORD dwSignaledIndex = dwCount;
354
355 dwResult = ::WaitForMultipleObjects(dwCount, rghHandles, fWaitAll, dwMilliseconds);
356 if (WAIT_TIMEOUT == dwResult)
357 {
358 ExitFunction1(hr = HRESULT_FROM_WIN32(dwResult));
359 }
360 else if (WAIT_ABANDONED_0 <= dwResult && (WAIT_ABANDONED_0 + dwCount) > dwResult)
361 {
362 dwSignaledIndex = dwResult - WAIT_ABANDONED_0;
363 AppExitOnWin32Error(dwResult, hr, "Abandoned wait for multiple objects, index: %u.", dwSignaledIndex);
364 }
365 else if (WAIT_OBJECT_0 <= dwResult && (WAIT_OBJECT_0 + dwCount) > dwResult)
366 {
367 dwSignaledIndex = dwResult - WAIT_OBJECT_0;
368 }
369 else
370 {
371 AssertSz(WAIT_FAILED == dwResult, "Unexpected return code from WaitForMultipleObjects.");
372 AppExitWithLastError(hr, "Failed to wait for multiple objects.");
373 }
374
375LExit:
376 if (pdwSignaledIndex)
377 {
378 *pdwSignaledIndex = dwSignaledIndex;
379 }
380
381 return hr;
382}
383
316static HRESULT EscapeCommandLineArgument( 384static HRESULT EscapeCommandLineArgument(
317 __in_z LPCWSTR wzArgument, 385 __in_z LPCWSTR wzArgument,
318 __out_z LPWSTR* psczEscaped 386 __out_z LPWSTR* psczEscaped
diff --git a/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj b/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj
index 3e3c42b7..c84b98fe 100644
--- a/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj
+++ b/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj
@@ -104,6 +104,7 @@
104 <ClCompile Include="strutil.cpp" /> 104 <ClCompile Include="strutil.cpp" />
105 <ClCompile Include="svcutil.cpp" /> 105 <ClCompile Include="svcutil.cpp" />
106 <ClCompile Include="thmutil.cpp" /> 106 <ClCompile Include="thmutil.cpp" />
107 <ClCompile Include="thrdutil.cpp" />
107 <ClCompile Include="timeutil.cpp" /> 108 <ClCompile Include="timeutil.cpp" />
108 <ClCompile Include="uncutil.cpp" /> 109 <ClCompile Include="uncutil.cpp" />
109 <ClCompile Include="uriutil.cpp" /> 110 <ClCompile Include="uriutil.cpp" />
@@ -164,6 +165,7 @@
164 <ClInclude Include="inc\strutil.h" /> 165 <ClInclude Include="inc\strutil.h" />
165 <ClInclude Include="inc\svcutil.h" /> 166 <ClInclude Include="inc\svcutil.h" />
166 <ClInclude Include="inc\thmutil.h" /> 167 <ClInclude Include="inc\thmutil.h" />
168 <ClInclude Include="inc\thrdutil.h" />
167 <ClInclude Include="inc\timeutil.h" /> 169 <ClInclude Include="inc\timeutil.h" />
168 <ClInclude Include="inc\uriutil.h" /> 170 <ClInclude Include="inc\uriutil.h" />
169 <ClInclude Include="inc\userutil.h" /> 171 <ClInclude Include="inc\userutil.h" />
diff --git a/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj.filters b/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj.filters
index 07698f9e..efadef6f 100644
--- a/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj.filters
+++ b/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj.filters
@@ -162,6 +162,9 @@
162 <ClCompile Include="thmutil.cpp"> 162 <ClCompile Include="thmutil.cpp">
163 <Filter>Source Files</Filter> 163 <Filter>Source Files</Filter>
164 </ClCompile> 164 </ClCompile>
165 <ClCompile Include="thrdutil.cpp">
166 <Filter>Source Files</Filter>
167 </ClCompile>
165 <ClCompile Include="timeutil.cpp"> 168 <ClCompile Include="timeutil.cpp">
166 <Filter>Source Files</Filter> 169 <Filter>Source Files</Filter>
167 </ClCompile> 170 </ClCompile>
@@ -344,6 +347,9 @@
344 <ClInclude Include="inc\thmutil.h"> 347 <ClInclude Include="inc\thmutil.h">
345 <Filter>Header Files</Filter> 348 <Filter>Header Files</Filter>
346 </ClInclude> 349 </ClInclude>
350 <ClInclude Include="inc\thrdutil.h">
351 <Filter>Header Files</Filter>
352 </ClInclude>
347 <ClInclude Include="inc\timeutil.h"> 353 <ClInclude Include="inc\timeutil.h">
348 <Filter>Header Files</Filter> 354 <Filter>Header Files</Filter>
349 </ClInclude> 355 </ClInclude>
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/apputil.h b/src/libs/dutil/WixToolset.DUtil/inc/apputil.h
index 95a98e73..e2812ee4 100644
--- a/src/libs/dutil/WixToolset.DUtil/inc/apputil.h
+++ b/src/libs/dutil/WixToolset.DUtil/inc/apputil.h
@@ -84,6 +84,27 @@ HRESULT DAPI AppEscapeCommandLineArgumentFormattedArgs(
84 __in va_list args 84 __in va_list args
85 ); 85 );
86 86
87/********************************************************************
88AppWaitForSingleObject - wrapper for ::WaitForSingleObject.
89
90********************************************************************/
91HRESULT DAPI AppWaitForSingleObject(
92 __in HANDLE hHandle,
93 __in DWORD dwMilliseconds
94 );
95
96/********************************************************************
97AppWaitForMultipleObjects - wrapper for ::WaitForMultipleObjects.
98
99********************************************************************/
100HRESULT DAPI AppWaitForMultipleObjects(
101 __in DWORD dwCount,
102 __in const HANDLE* rghHandles,
103 __in BOOL fWaitAll,
104 __in DWORD dwMilliseconds,
105 __out_opt DWORD* pdwSignaledIndex
106 );
107
87#ifdef __cplusplus 108#ifdef __cplusplus
88} 109}
89#endif 110#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/dutil.h b/src/libs/dutil/WixToolset.DUtil/inc/dutil.h
index 6f099f35..2db64812 100644
--- a/src/libs/dutil/WixToolset.DUtil/inc/dutil.h
+++ b/src/libs/dutil/WixToolset.DUtil/inc/dutil.h
@@ -130,6 +130,7 @@ void DAPI Dutil_RootFailure(__in_z LPCSTR szFile, __in int iLine, __in HRESULT h
130#define ExitOnWin32ErrorSource(d, e, x, s, ...) if (ERROR_SUCCESS != e) { x = HRESULT_FROM_WIN32(e); if (!FAILED(x)) { x = E_FAIL; } Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, s, __VA_ARGS__); goto LExit; } 130#define ExitOnWin32ErrorSource(d, e, x, s, ...) if (ERROR_SUCCESS != e) { x = HRESULT_FROM_WIN32(e); if (!FAILED(x)) { x = E_FAIL; } Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, s, __VA_ARGS__); goto LExit; }
131#define ExitOnOptionalXmlQueryFailureSource(d, x, b, s, ...) { { if (S_FALSE == x || E_NOTFOUND == x) { b = FALSE; x = S_OK; } else { b = SUCCEEDED(x); } }; ExitOnRootFailureSource(d, x, s, __VA_ARGS__); } 131#define ExitOnOptionalXmlQueryFailureSource(d, x, b, s, ...) { { if (S_FALSE == x || E_NOTFOUND == x) { b = FALSE; x = S_OK; } else { b = SUCCEEDED(x); } }; ExitOnRootFailureSource(d, x, s, __VA_ARGS__); }
132#define ExitOnRequiredXmlQueryFailureSource(d, x, s, ...) { if (S_FALSE == x) { x = E_NOTFOUND; } ExitOnRootFailureSource(d, x, s, __VA_ARGS__); } 132#define ExitOnRequiredXmlQueryFailureSource(d, x, s, ...) { if (S_FALSE == x) { x = E_NOTFOUND; } ExitOnRootFailureSource(d, x, s, __VA_ARGS__); }
133#define ExitOnWaitObjectFailureSource(d, x, b, s, ...) { { if (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == x) { b = TRUE; x = S_OK; } else { b = FALSE; } }; ExitOnFailureSource(d, x, s, __VA_ARGS__); }
133 134
134#define ExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__) 135#define ExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__)
135#define ExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__) 136#define ExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__)
@@ -145,6 +146,7 @@ void DAPI Dutil_RootFailure(__in_z LPCSTR szFile, __in int iLine, __in HRESULT h
145#define ExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DEFAULT, e, x, s, __VA_ARGS__) 146#define ExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DEFAULT, e, x, s, __VA_ARGS__)
146#define ExitOnOptionalXmlQueryFailure(x, b, s, ...) ExitOnOptionalXmlQueryFailureSource(DUTIL_SOURCE_DEFAULT, x, b, s, __VA_ARGS__) 147#define ExitOnOptionalXmlQueryFailure(x, b, s, ...) ExitOnOptionalXmlQueryFailureSource(DUTIL_SOURCE_DEFAULT, x, b, s, __VA_ARGS__)
147#define ExitOnRequiredXmlQueryFailure(x, s, ...) ExitOnRequiredXmlQueryFailureSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__) 148#define ExitOnRequiredXmlQueryFailure(x, s, ...) ExitOnRequiredXmlQueryFailureSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__)
149#define ExitOnWaitObjectFailure(x, b, s, ...) ExitOnWaitObjectFailureSource(DUTIL_SOURCE_DEFAULT, x, b, s, __VA_ARGS__)
148 150
149// release macros 151// release macros
150#define ReleaseObject(x) if (x) { x->Release(); } 152#define ReleaseObject(x) if (x) { x->Release(); }
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/dutilsources.h b/src/libs/dutil/WixToolset.DUtil/inc/dutilsources.h
index f1dd5d1a..664c21e5 100644
--- a/src/libs/dutil/WixToolset.DUtil/inc/dutilsources.h
+++ b/src/libs/dutil/WixToolset.DUtil/inc/dutilsources.h
@@ -63,6 +63,7 @@ typedef enum DUTIL_SOURCE
63 DUTIL_SOURCE_VERUTIL, 63 DUTIL_SOURCE_VERUTIL,
64 DUTIL_SOURCE_WNDUTIL, 64 DUTIL_SOURCE_WNDUTIL,
65 DUTIL_SOURCE_ENVUTIL, 65 DUTIL_SOURCE_ENVUTIL,
66 DUTIL_SOURCE_THRDUTIL,
66 67
67 DUTIL_SOURCE_EXTERNAL = 256, 68 DUTIL_SOURCE_EXTERNAL = 256,
68} DUTIL_SOURCE; 69} DUTIL_SOURCE;
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/procutil.h b/src/libs/dutil/WixToolset.DUtil/inc/procutil.h
index d5ab9242..d61d91b5 100644
--- a/src/libs/dutil/WixToolset.DUtil/inc/procutil.h
+++ b/src/libs/dutil/WixToolset.DUtil/inc/procutil.h
@@ -58,7 +58,7 @@ HRESULT DAPI ProcExecute(
58HRESULT DAPI ProcWaitForCompletion( 58HRESULT DAPI ProcWaitForCompletion(
59 __in HANDLE hProcess, 59 __in HANDLE hProcess,
60 __in DWORD dwTimeout, 60 __in DWORD dwTimeout,
61 __out DWORD *pReturnCode 61 __out_opt DWORD* pdwReturnCode
62 ); 62 );
63HRESULT DAPI ProcWaitForIds( 63HRESULT DAPI ProcWaitForIds(
64 __in_ecount(cProcessIds) const DWORD* pdwProcessIds, 64 __in_ecount(cProcessIds) const DWORD* pdwProcessIds,
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/thrdutil.h b/src/libs/dutil/WixToolset.DUtil/inc/thrdutil.h
new file mode 100644
index 00000000..47e159a1
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/thrdutil.h
@@ -0,0 +1,22 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9/********************************************************************
10 ThrdWaitForCompletion - waits for thread to complete and gets return code.
11
12 *******************************************************************/
13HRESULT DAPI ThrdWaitForCompletion(
14 __in HANDLE hThread,
15 __in DWORD dwTimeout,
16 __out_opt DWORD* pdwReturnCode
17 );
18
19#ifdef __cplusplus
20}
21#endif
22
diff --git a/src/libs/dutil/WixToolset.DUtil/monutil.cpp b/src/libs/dutil/WixToolset.DUtil/monutil.cpp
index 6ad75b56..10954164 100644
--- a/src/libs/dutil/WixToolset.DUtil/monutil.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/monutil.cpp
@@ -16,6 +16,7 @@
16#define MonExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_MONUTIL, p, x, s, __VA_ARGS__) 16#define MonExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_MONUTIL, p, x, s, __VA_ARGS__)
17#define MonExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_MONUTIL, e, x, s, __VA_ARGS__) 17#define MonExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_MONUTIL, e, x, s, __VA_ARGS__)
18#define MonExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_MONUTIL, g, x, s, __VA_ARGS__) 18#define MonExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_MONUTIL, g, x, s, __VA_ARGS__)
19#define MonExitOnWaitObjectFailure(x, b, s, ...) ExitOnWaitObjectFailureSource(DUTIL_SOURCE_MONUTIL, x, b, s, __VA_ARGS__)
19 20
20const int MON_THREAD_GROWTH = 5; 21const int MON_THREAD_GROWTH = 5;
21const int MON_ARRAY_GROWTH = 40; 22const int MON_ARRAY_GROWTH = 40;
@@ -1101,11 +1102,12 @@ static DWORD WINAPI WaiterThread(
1101{ 1102{
1102 HRESULT hr = S_OK; 1103 HRESULT hr = S_OK;
1103 HRESULT hrTemp = S_OK; 1104 HRESULT hrTemp = S_OK;
1104 DWORD dwRet = 0;
1105 BOOL fAgain = FALSE; 1105 BOOL fAgain = FALSE;
1106 BOOL fContinue = TRUE; 1106 BOOL fContinue = TRUE;
1107 BOOL fNotify = FALSE; 1107 BOOL fNotify = FALSE;
1108 BOOL fRet = FALSE; 1108 BOOL fRet = FALSE;
1109 BOOL fTimedOut = FALSE;
1110 DWORD dwSignaledIndex = 0;
1109 MSG msg = { }; 1111 MSG msg = { };
1110 MON_ADD_MESSAGE *pAddMessage = NULL; 1112 MON_ADD_MESSAGE *pAddMessage = NULL;
1111 MON_REMOVE_MESSAGE *pRemoveMessage = NULL; 1113 MON_REMOVE_MESSAGE *pRemoveMessage = NULL;
@@ -1128,13 +1130,14 @@ static DWORD WINAPI WaiterThread(
1128 1130
1129 do 1131 do
1130 { 1132 {
1131 dwRet = ::WaitForMultipleObjects(pWaiterContext->cHandles - pWaiterContext->cRequestsFailing, pWaiterContext->rgHandles, FALSE, pWaiterContext->cRequestsPending > 0 ? dwWait : INFINITE); 1133 hr = AppWaitForMultipleObjects(pWaiterContext->cHandles - pWaiterContext->cRequestsFailing, pWaiterContext->rgHandles, FALSE, pWaiterContext->cRequestsPending > 0 ? dwWait : INFINITE, &dwSignaledIndex);
1134 MonExitOnWaitObjectFailure(hr, fTimedOut, "Failed to wait for multiple objects.");
1132 1135
1133 uCurrentTime = ::GetTickCount(); 1136 uCurrentTime = ::GetTickCount();
1134 uDeltaInMs = uCurrentTime - uLastTimeInMs; 1137 uDeltaInMs = uCurrentTime - uLastTimeInMs;
1135 uLastTimeInMs = uCurrentTime; 1138 uLastTimeInMs = uCurrentTime;
1136 1139
1137 if (WAIT_OBJECT_0 == dwRet) 1140 if (!fTimedOut && 0 == dwSignaledIndex)
1138 { 1141 {
1139 do 1142 do
1140 { 1143 {
@@ -1391,10 +1394,10 @@ static DWORD WINAPI WaiterThread(
1391 } 1394 }
1392 } while (fAgain); 1395 } while (fAgain);
1393 } 1396 }
1394 else if (dwRet > WAIT_OBJECT_0 && dwRet - WAIT_OBJECT_0 < pWaiterContext->cHandles) 1397 else if (!fTimedOut)
1395 { 1398 {
1396 // OK a handle fired - only notify if it's the actual target, and not just some parent waiting for the target child to exist 1399 // OK a handle fired - only notify if it's the actual target, and not just some parent waiting for the target child to exist
1397 dwRequestIndex = dwRet - WAIT_OBJECT_0 - 1; 1400 dwRequestIndex = dwSignaledIndex - 1;
1398 fNotify = (pWaiterContext->rgRequests[dwRequestIndex].dwPathHierarchyIndex == pWaiterContext->rgRequests[dwRequestIndex].cPathHierarchy - 1); 1401 fNotify = (pWaiterContext->rgRequests[dwRequestIndex].dwPathHierarchyIndex == pWaiterContext->rgRequests[dwRequestIndex].cPathHierarchy - 1);
1399 1402
1400 // Initiate re-waits before we notify callback, to ensure we don't miss a single update 1403 // Initiate re-waits before we notify callback, to ensure we don't miss a single update
@@ -1426,10 +1429,6 @@ static DWORD WINAPI WaiterThread(
1426 } 1429 }
1427 } 1430 }
1428 } 1431 }
1429 else if (WAIT_TIMEOUT != dwRet)
1430 {
1431 MonExitWithLastError(hr, "Failed to wait for multiple objects with return code %u", dwRet);
1432 }
1433 1432
1434 // OK, now that we've checked all triggered handles (resetting silence period timers appropriately), check for any pending notifications that we can finally fire 1433 // OK, now that we've checked all triggered handles (resetting silence period timers appropriately), check for any pending notifications that we can finally fire
1435 // And set dwWait appropriately so we awaken at the right time to fire the next pending notification (in case no further writes occur during that time) 1434 // And set dwWait appropriately so we awaken at the right time to fire the next pending notification (in case no further writes occur during that time)
@@ -1726,10 +1725,10 @@ static LRESULT CALLBACK MonWndProc(
1726 DEV_BROADCAST_HANDLE *pHandle = NULL; 1725 DEV_BROADCAST_HANDLE *pHandle = NULL;
1727 DEV_BROADCAST_VOLUME *pVolume = NULL; 1726 DEV_BROADCAST_VOLUME *pVolume = NULL;
1728 DWORD dwUnitMask = 0; 1727 DWORD dwUnitMask = 0;
1729 DWORD er = ERROR_SUCCESS;
1730 WCHAR chDrive = L'\0'; 1728 WCHAR chDrive = L'\0';
1731 BOOL fArrival = FALSE; 1729 BOOL fArrival = FALSE;
1732 BOOL fReturnTrue = FALSE; 1730 BOOL fReturnTrue = FALSE;
1731 BOOL fTimedOut = FALSE;
1733 CREATESTRUCT *pCreateStruct = NULL; 1732 CREATESTRUCT *pCreateStruct = NULL;
1734 MON_WAITER_CONTEXT *pWaiterContext = NULL; 1733 MON_WAITER_CONTEXT *pWaiterContext = NULL;
1735 MON_STRUCT *pm = NULL; 1734 MON_STRUCT *pm = NULL;
@@ -1821,24 +1820,23 @@ static LRESULT CALLBACK MonWndProc(
1821 } 1820 }
1822 } 1821 }
1823 1822
1824 er = ::WaitForSingleObject(pm->internalWait.hWait, MON_THREAD_WAIT_REMOVE_DEVICE); 1823 hr = AppWaitForSingleObject(pm->internalWait.hWait, MON_THREAD_WAIT_REMOVE_DEVICE);
1824 MonExitOnWaitObjectFailure(hr, fTimedOut, "WaitForSingleObject failed with non-timeout reason while waiting for response from waiter thread");
1825
1825 // Make sure any waiter thread processing really old messages can immediately know that we're no longer waiting for a response 1826 // Make sure any waiter thread processing really old messages can immediately know that we're no longer waiting for a response
1826 if (WAIT_OBJECT_0 == er) 1827 if (!fTimedOut)
1827 { 1828 {
1828 // If the response ID matches what we sent, we actually got a valid reply! 1829 // If the response ID matches what we sent, we actually got a valid reply!
1829 if (pm->internalWait.dwReceiveIteration != pm->internalWait.dwSendIteration) 1830 if (pm->internalWait.dwReceiveIteration != pm->internalWait.dwSendIteration)
1830 { 1831 {
1831 TraceError(HRESULT_FROM_WIN32(er), "Waiter thread received wrong ID reply"); 1832 TraceError(E_UNEXPECTED, "Waiter thread received wrong ID reply");
1832 } 1833 }
1833 } 1834 }
1834 else if (WAIT_TIMEOUT == er)
1835 {
1836 TraceError(HRESULT_FROM_WIN32(er), "No response from any waiter thread for query remove message");
1837 }
1838 else 1835 else
1839 { 1836 {
1840 MonExitWithLastError(hr, "WaitForSingleObject failed with non-timeout reason while waiting for response from waiter thread"); 1837 TraceError(HRESULT_FROM_WIN32(WAIT_TIMEOUT), "No response from any waiter thread for query remove message");
1841 } 1838 }
1839
1842 ++pm->internalWait.dwSendIteration; 1840 ++pm->internalWait.dwSendIteration;
1843 } 1841 }
1844 } 1842 }
diff --git a/src/libs/dutil/WixToolset.DUtil/precomp.h b/src/libs/dutil/WixToolset.DUtil/precomp.h
index c9e4f74a..b628d271 100644
--- a/src/libs/dutil/WixToolset.DUtil/precomp.h
+++ b/src/libs/dutil/WixToolset.DUtil/precomp.h
@@ -90,6 +90,7 @@
90#include "timeutil.h" 90#include "timeutil.h"
91#include "wndutil.h" 91#include "wndutil.h"
92#include "thmutil.h" 92#include "thmutil.h"
93#include "thrdutil.h"
93#include "uncutil.h" 94#include "uncutil.h"
94#include "uriutil.h" 95#include "uriutil.h"
95#include "userutil.h" 96#include "userutil.h"
diff --git a/src/libs/dutil/WixToolset.DUtil/procutil.cpp b/src/libs/dutil/WixToolset.DUtil/procutil.cpp
index 340a0cda..29f575ae 100644
--- a/src/libs/dutil/WixToolset.DUtil/procutil.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/procutil.cpp
@@ -17,6 +17,7 @@
17#define ProcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__) 17#define ProcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__)
18#define ProcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PROCUTIL, e, x, s, __VA_ARGS__) 18#define ProcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PROCUTIL, e, x, s, __VA_ARGS__)
19#define ProcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PROCUTIL, g, x, s, __VA_ARGS__) 19#define ProcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PROCUTIL, g, x, s, __VA_ARGS__)
20#define ProcExitOnWaitObjectFailure(x, b, s, ...) ExitOnWaitObjectFailureSource(DUTIL_SOURCE_PROCUTIL, x, b, s, __VA_ARGS__)
20 21
21 22
22// private functions 23// private functions
@@ -403,30 +404,25 @@ LExit:
403extern "C" HRESULT DAPI ProcWaitForCompletion( 404extern "C" HRESULT DAPI ProcWaitForCompletion(
404 __in HANDLE hProcess, 405 __in HANDLE hProcess,
405 __in DWORD dwTimeout, 406 __in DWORD dwTimeout,
406 __out DWORD *pReturnCode 407 __out_opt DWORD* pdwReturnCode
407 ) 408 )
408{ 409{
409 HRESULT hr = S_OK; 410 HRESULT hr = S_OK;
410 DWORD er = ERROR_SUCCESS; 411 BOOL fTimedOut = FALSE;
411 412
412 // Wait for everything to finish 413 // Wait for everything to finish.
413 er = ::WaitForSingleObject(hProcess, dwTimeout); 414 hr = AppWaitForSingleObject(hProcess, dwTimeout);
414 if (WAIT_FAILED == er) 415 ProcExitOnWaitObjectFailure(hr, fTimedOut, "Failed to wait for process to complete.");
415 { 416
416 ProcExitWithLastError(hr, "Failed to wait for process to complete."); 417 if (fTimedOut)
417 }
418 else if (WAIT_TIMEOUT == er)
419 { 418 {
420 ExitFunction1(hr = HRESULT_FROM_WIN32(er)); 419 hr = HRESULT_FROM_WIN32(WAIT_TIMEOUT);
421 } 420 }
422 421 else if (pdwReturnCode && !::GetExitCodeProcess(hProcess, pdwReturnCode))
423 if (!::GetExitCodeProcess(hProcess, &er))
424 { 422 {
425 ProcExitWithLastError(hr, "Failed to get process return code."); 423 ProcExitWithLastError(hr, "Failed to get process return code.");
426 } 424 }
427 425
428 *pReturnCode = er;
429
430LExit: 426LExit:
431 return hr; 427 return hr;
432} 428}
@@ -442,10 +438,10 @@ extern "C" HRESULT DAPI ProcWaitForIds(
442 ) 438 )
443{ 439{
444 HRESULT hr = S_OK; 440 HRESULT hr = S_OK;
445 DWORD er = ERROR_SUCCESS;
446 HANDLE hProcess = NULL; 441 HANDLE hProcess = NULL;
447 HANDLE * rghProcesses = NULL; 442 HANDLE* rghProcesses = NULL;
448 DWORD cProcesses = 0; 443 DWORD cProcesses = 0;
444 BOOL fTimedOut = FALSE;
449 445
450 rghProcesses = static_cast<HANDLE*>(MemAlloc(sizeof(DWORD) * cProcessIds, TRUE)); 446 rghProcesses = static_cast<HANDLE*>(MemAlloc(sizeof(DWORD) * cProcessIds, TRUE));
451 ProcExitOnNull(rgdwProcessIds, hr, E_OUTOFMEMORY, "Failed to allocate array for process ID Handles."); 447 ProcExitOnNull(rgdwProcessIds, hr, E_OUTOFMEMORY, "Failed to allocate array for process ID Handles.");
@@ -459,16 +455,14 @@ extern "C" HRESULT DAPI ProcWaitForIds(
459 } 455 }
460 } 456 }
461 457
462 er = ::WaitForMultipleObjects(cProcesses, rghProcesses, TRUE, dwMilliseconds); 458 hr = AppWaitForMultipleObjects(cProcesses, rghProcesses, TRUE, dwMilliseconds, NULL);
463 if (WAIT_FAILED == er) 459 ProcExitOnWaitObjectFailure(hr, fTimedOut, "Failed to wait for processes to complete.");
464 { 460
465 ProcExitWithLastError(hr, "Failed to wait for process to complete."); 461 if (fTimedOut)
466 }
467 else if (WAIT_TIMEOUT == er)
468 { 462 {
469 ProcExitOnWin32Error(er, hr, "Timed out while waiting for process to complete."); 463 ProcExitWithRootFailure(hr, HRESULT_FROM_WIN32(WAIT_TIMEOUT), "Timed out while waiting for processes to complete.");
470 } 464 }
471 465
472LExit: 466LExit:
473 if (rghProcesses) 467 if (rghProcesses)
474 { 468 {
diff --git a/src/libs/dutil/WixToolset.DUtil/thrdutil.cpp b/src/libs/dutil/WixToolset.DUtil/thrdutil.cpp
new file mode 100644
index 00000000..a8933a48
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/thrdutil.cpp
@@ -0,0 +1,46 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6// Exit macros
7#define ThrdExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_THRDUTIL, x, s, __VA_ARGS__)
8#define ThrdExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_THRDUTIL, x, s, __VA_ARGS__)
9#define ThrdExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_THRDUTIL, x, s, __VA_ARGS__)
10#define ThrdExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_THRDUTIL, x, s, __VA_ARGS__)
11#define ThrdExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_THRDUTIL, x, s, __VA_ARGS__)
12#define ThrdExitWithRootFailure(x, e, s, ...) ExitWithRootFailureSource(DUTIL_SOURCE_THRDUTIL, x, e, s, __VA_ARGS__)
13#define ThrdExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_THRDUTIL, x, s, __VA_ARGS__)
14#define ThrdExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_THRDUTIL, p, x, e, s, __VA_ARGS__)
15#define ThrdExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_THRDUTIL, p, x, s, __VA_ARGS__)
16#define ThrdExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_THRDUTIL, p, x, e, s, __VA_ARGS__)
17#define ThrdExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_THRDUTIL, p, x, s, __VA_ARGS__)
18#define ThrdExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_THRDUTIL, e, x, s, __VA_ARGS__)
19#define ThrdExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_THRDUTIL, g, x, s, __VA_ARGS__)
20#define ThrdExitOnWaitObjectFailure(x, b, s, ...) ExitOnWaitObjectFailureSource(DUTIL_SOURCE_THRDUTIL, x, b, s, __VA_ARGS__)
21
22DAPI_(HRESULT) ThrdWaitForCompletion(
23 __in HANDLE hThread,
24 __in DWORD dwTimeout,
25 __out_opt DWORD *pdwReturnCode
26 )
27{
28 HRESULT hr = S_OK;
29 BOOL fTimedOut = FALSE;
30
31 // Wait for everything to finish.
32 hr = AppWaitForSingleObject(hThread, dwTimeout);
33 ThrdExitOnWaitObjectFailure(hr, fTimedOut, "Failed to wait for thread to complete.");
34
35 if (fTimedOut)
36 {
37 hr = HRESULT_FROM_WIN32(WAIT_TIMEOUT);
38 }
39 else if (pdwReturnCode && !::GetExitCodeThread(hThread, pdwReturnCode))
40 {
41 ThrdExitWithLastError(hr, "Failed to get thread return code.");
42 }
43
44LExit:
45 return hr;
46}
diff --git a/src/libs/dutil/test/DUtilUnitTest/AppUtilTests.cpp b/src/libs/dutil/test/DUtilUnitTest/AppUtilTests.cpp
new file mode 100644
index 00000000..e8c23469
--- /dev/null
+++ b/src/libs/dutil/test/DUtilUnitTest/AppUtilTests.cpp
@@ -0,0 +1,60 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5namespace DutilTests
6{
7 using namespace System;
8 using namespace Xunit;
9 using namespace WixBuildTools::TestSupport;
10
11 public ref class AppUtil
12 {
13 public:
14 [Fact]
15 void WaitForMultipleObjectsTest()
16 {
17 HRESULT hr = S_OK;
18 HANDLE hOne = NULL;
19 HANDLE hTwo = NULL;
20 HANDLE rghHandles[2] = { };
21 DWORD dwSignaledIndex = 0;
22
23 try
24 {
25 hOne = ::CreateEventW(NULL, TRUE, FALSE, NULL);
26 if (!hOne)
27 {
28 hr = HRESULT_FROM_WIN32(::GetLastError());
29 NativeAssert::Succeeded(FAILED(hr) ? hr : E_FAIL, "Failed to create event.");
30 }
31
32 hTwo = ::CreateEventW(NULL, TRUE, TRUE, NULL);
33 if (!hTwo)
34 {
35 hr = HRESULT_FROM_WIN32(::GetLastError());
36 NativeAssert::Succeeded(FAILED(hr) ? hr : E_FAIL, "Failed to create event.");
37 }
38
39 rghHandles[0] = hOne;
40 rghHandles[1] = hTwo;
41
42 hr = AppWaitForMultipleObjects(countof(rghHandles), rghHandles, FALSE, 0, &dwSignaledIndex);
43 NativeAssert::Succeeded(hr, "Failed to wait for multiple objects.");
44 Assert::Equal<DWORD>(1, dwSignaledIndex);
45
46 rghHandles[0] = hTwo;
47 rghHandles[1] = hOne;
48
49 hr = AppWaitForMultipleObjects(countof(rghHandles), rghHandles, FALSE, 0, &dwSignaledIndex);
50 NativeAssert::Succeeded(hr, "Failed to wait for multiple objects.");
51 Assert::Equal<DWORD>(0, dwSignaledIndex);
52 }
53 finally
54 {
55 ReleaseHandle(hOne);
56 ReleaseHandle(hTwo);
57 }
58 }
59 };
60}
diff --git a/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj b/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj
index a1f13239..210f50f5 100644
--- a/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj
+++ b/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj
@@ -44,6 +44,7 @@
44 </PropertyGroup> 44 </PropertyGroup>
45 45
46 <ItemGroup> 46 <ItemGroup>
47 <ClCompile Include="AppUtilTests.cpp" />
47 <ClCompile Include="ApupUtilTests.cpp" /> 48 <ClCompile Include="ApupUtilTests.cpp" />
48 <ClCompile Include="AssemblyInfo.cpp" /> 49 <ClCompile Include="AssemblyInfo.cpp" />
49 <ClCompile Include="DictUtilTest.cpp" /> 50 <ClCompile Include="DictUtilTest.cpp" />
diff --git a/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters b/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters
index cb0c8a73..f1d9c307 100644
--- a/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters
+++ b/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters
@@ -15,6 +15,9 @@
15 </Filter> 15 </Filter>
16 </ItemGroup> 16 </ItemGroup>
17 <ItemGroup> 17 <ItemGroup>
18 <ClCompile Include="AppUtilTests.cpp">
19 <Filter>Source Files</Filter>
20 </ClCompile>
18 <ClCompile Include="ApupUtilTests.cpp"> 21 <ClCompile Include="ApupUtilTests.cpp">
19 <Filter>Source Files</Filter> 22 <Filter>Source Files</Filter>
20 </ClCompile> 23 </ClCompile>
diff --git a/src/libs/dutil/test/DUtilUnitTest/precomp.h b/src/libs/dutil/test/DUtilUnitTest/precomp.h
index ac57cdd4..a5542774 100644
--- a/src/libs/dutil/test/DUtilUnitTest/precomp.h
+++ b/src/libs/dutil/test/DUtilUnitTest/precomp.h
@@ -13,6 +13,7 @@
13#include <dutil.h> 13#include <dutil.h>
14 14
15#include <verutil.h> 15#include <verutil.h>
16#include <apputil.h>
16#include <atomutil.h> 17#include <atomutil.h>
17#include <dictutil.h> 18#include <dictutil.h>
18#include <dirutil.h> 19#include <dirutil.h>