@joebigelow / wix / commits / 7cca75c8

Add AppWaitForSingleObject/MultipleObjects, ThreadWaitForCompletion.

Sean Hall committed Jun 29, 2022 at 10:28 UTC 7cca75c8e95f129a21c33f1f4568e90e9e397f9d
26 files changed +380 -149
src/burn/engine/apply.cpp
+12 -14
@@ -2333,6 +2333,8 @@ static HRESULT DoExecuteAction(
2333
2334 HRESULT hr = S_OK;
2335 HANDLE rghWait[2] = { };
2336 + DWORD dwSignaledIndex = 0;
2337 + DWORD dwExitCode = 0;
2338 BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE;
2339 BOOL fRetry = FALSE;
2340 BOOL fStopWusaService = FALSE;
@@ -2356,27 +2358,23 @@ static HRESULT DoExecuteAction(
2358 // wait for cache sync-point
2359 rghWait[0] = pExecuteAction->waitCachePackage.pPackage->hCacheEvent;
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)
2366 {
2361 - case WAIT_OBJECT_0:
2367 + case 0:
2368 break;
2363 -
2364 - case WAIT_OBJECT_0 + 1:
2365 - if (!::GetExitCodeThread(pContext->pApplyContext->hCacheThread, (DWORD*)&hr))
2369 + case 1:
2370 + if (!::GetExitCodeThread(pContext->pApplyContext->hCacheThread, &dwExitCode))
2371 {
2372 ExitWithLastError(hr, "Failed to get cache thread exit code.");
2373 }
2374
2370 - if (SUCCEEDED(hr))
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.");
2375 + ExitWithRootFailure(hr, E_UNEXPECTED, "Cache thread exited unexpectedly with exit code: %u.", dwExitCode);
2376 }
2377 +
2378 break;
2379
2380 case BURN_EXECUTE_ACTION_TYPE_RELATED_BUNDLE:
src/burn/engine/cabextract.cpp
+18 -24
@@ -262,10 +262,8 @@ extern "C" HRESULT CabExtractClose(
262 }
263
264 // wait for thread to terminate
265 - if (WAIT_OBJECT_0 != ::WaitForSingleObject(pContext->Cabinet.hThread, INFINITE))
266 - {
267 - ExitWithLastError(hr, "Failed to wait for thread to terminate.");
268 - }
265 + hr = AppWaitForSingleObject(pContext->Cabinet.hThread, INFINITE);
266 + ExitOnFailure(hr, "Failed to wait for thread to terminate.");
267 }
268
269 LExit:
@@ -306,29 +304,31 @@ static HRESULT WaitForOperation(
304 {
305 HRESULT hr = S_OK;
306 HANDLE rghWait[2] = { };
307 + DWORD dwSignaledIndex = 0;
308
309 // wait for operation complete event
310 rghWait[0] = pContext->Cabinet.hOperationCompleteEvent;
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)
317 {
315 - case WAIT_OBJECT_0:
318 + case 0:
319 if (!::ResetEvent(pContext->Cabinet.hOperationCompleteEvent))
320 {
321 ExitWithLastError(hr, "Failed to reset operation complete event.");
322 }
320 - break;
323
322 - case WAIT_OBJECT_0 + 1:
324 + break;
325 + case 1:
326 if (!::GetExitCodeThread(pContext->Cabinet.hThread, (DWORD*)&hr))
327 {
328 ExitWithLastError(hr, "Failed to get extraction thread exit code.");
329 }
327 - ExitFunction();
330
329 - case WAIT_FAILED: __fallthrough;
330 - default:
331 - ExitWithLastError(hr, "Failed to wait for operation complete event.");
331 + ExitFunction();
332 }
333
334 // clear operation
@@ -430,10 +430,8 @@ static DWORD WINAPI ExtractThreadProc(
430 }
431
432 // wait for begin operation event
433 - if (WAIT_FAILED == ::WaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE))
434 - {
435 - ExitWithLastError(hr, "Failed to wait for begin operation event.");
436 - }
433 + hr = AppWaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE);
434 + ExitOnFailure(hr, "Failed to wait for begin operation event.");
435
436 if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent))
437 {
@@ -517,10 +515,8 @@ static INT_PTR CopyFileCallback(
515 }
516
517 // wait for begin operation event
520 - if (WAIT_FAILED == ::WaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE))
521 - {
522 - ExitWithLastError(hr, "Failed to wait for begin operation event.");
523 - }
518 + hr = AppWaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE);
519 + ExitOnFailure(hr, "Failed to wait for begin operation event.");
520
521 if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent))
522 {
@@ -552,10 +548,8 @@ static INT_PTR CopyFileCallback(
548 }
549
550 // wait for begin operation event
555 - if (WAIT_FAILED == ::WaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE))
556 - {
557 - ExitWithLastError(hr, "Failed to wait for begin operation event.");
558 - }
551 + hr = AppWaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE);
552 + ExitOnFailure(hr, "Failed to wait for begin operation event.");
553
554 if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent))
555 {
src/burn/engine/core.cpp
+28 -26
@@ -12,6 +12,9 @@ struct BURN_CACHE_THREAD_CONTEXT
12 };
13
14
15 +static PFN_PROCWAITFORCOMPLETION vpfnProcWaitForCompletion = ProcWaitForCompletion;
16 +
17 +
18 // internal function declarations
19
20 static HRESULT CoreRecreateCommandLine(
@@ -65,9 +68,6 @@ static HRESULT DetectPackagePayloadsCached(
68 static DWORD WINAPI CacheThreadProc(
69 __in LPVOID lpThreadParameter
70 );
68 -static HRESULT WaitForCacheThread(
69 - __in HANDLE hCacheThread
70 - );
71 static void LogPackages(
72 __in_opt const BURN_PACKAGE* pUpgradeBundlePackage,
73 __in_opt const BURN_PACKAGE* pForwardCompatibleBundlePackage,
@@ -636,6 +636,7 @@ extern "C" HRESULT CoreApply(
636 BURN_APPLY_CONTEXT applyContext = { };
637 BOOL fDeleteApplyCs = FALSE;
638 BURN_CACHE_THREAD_CONTEXT cacheThreadContext = { };
639 + DWORD dwCacheExitCode = 0;
640 BOOL fRollbackCache = FALSE;
641 DWORD dwPhaseCount = 0;
642 BOOTSTRAPPER_APPLYCOMPLETE_ACTION applyCompleteAction = BOOTSTRAPPER_APPLYCOMPLETE_ACTION_NONE;
@@ -744,7 +745,10 @@ extern "C" HRESULT CoreApply(
745 // If we're not caching in parallel, wait for the cache thread to terminate.
746 if (!pEngineState->fParallelCacheAndExecute)
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;
752 ExitOnFailure(hr, "Failed while caching, aborting execution.");
753
754 ReleaseHandle(applyContext.hCacheThread);
@@ -761,10 +765,12 @@ extern "C" HRESULT CoreApply(
765 // Wait for cache thread to terminate, this should return immediately unless we're waiting for layout to complete.
766 if (applyContext.hCacheThread)
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 +
771 if (SUCCEEDED(hr))
772 {
767 - hr = hrCached;
773 + hr = (HRESULT)dwCacheExitCode;
774 }
775 }
776
@@ -1940,6 +1946,22 @@ LExit:
1946 return hr;
1947 }
1948
1949 +extern "C" void CoreFunctionOverride(
1950 + __in_opt PFN_PROCWAITFORCOMPLETION pfnProcWaitForCompletion
1951 + )
1952 +{
1953 + vpfnProcWaitForCompletion = pfnProcWaitForCompletion;
1954 +}
1955 +
1956 +extern "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 +
1965 // internal helper functions
1966
1967 static HRESULT AppendEscapedArgumentToCommandLine(
@@ -2268,26 +2290,6 @@ LExit:
2290 return (DWORD)hr;
2291 }
2292
2271 -static 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 -
2287 -LExit:
2288 - return hr;
2289 -}
2290 -
2293 static void LogPackages(
2294 __in_opt const BURN_PACKAGE* pUpgradeBundlePackage,
2295 __in_opt const BURN_PACKAGE* pForwardCompatibleBundlePackage,
src/burn/engine/core.h
+14
@@ -174,6 +174,12 @@ typedef struct _BURN_APPLY_CONTEXT
174 DWORD dwCacheCheckpoint;
175 } BURN_APPLY_CONTEXT;
176
177 +typedef HRESULT (DAPI *PFN_PROCWAITFORCOMPLETION)(
178 + __in HANDLE hProcess,
179 + __in DWORD dwTimeout,
180 + __out DWORD* pReturnCode
181 + );
182 +
183
184 // function declarations
185
@@ -280,6 +286,14 @@ HRESULT CoreParseCommandLine(
286 __inout HANDLE* phSectionFile,
287 __inout HANDLE* phSourceEngineFile
288 );
289 +void CoreFunctionOverride(
290 + __in_opt PFN_PROCWAITFORCOMPLETION pfnProcWaitForCompletion
291 + );
292 +HRESULT DAPI CoreWaitForProcCompletion(
293 + __in HANDLE hProcess,
294 + __in DWORD dwTimeout,
295 + __out_opt DWORD* pdwReturnCode
296 + );
297
298 #if defined(__cplusplus)
299 }
src/burn/engine/elevation.cpp
+2 -9
@@ -1687,15 +1687,8 @@ static HRESULT WaitForElevatedChildCacheThread(
1687 HRESULT hr = S_OK;
1688 DWORD dwExitCode = ERROR_SUCCESS;
1689
1690 - if (WAIT_OBJECT_0 != ::WaitForSingleObject(hCacheThread, BURN_TIMEOUT))
1691 - {
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 - }
1690 + hr = ThrdWaitForCompletion(hCacheThread, BURN_TIMEOUT, &dwExitCode);
1691 + ExitOnFailure(hr, "Failed to wait for cache thread to complete.");
1692
1693 AssertSz(dwExitCode == dwExpectedExitCode, "Cache thread should have exited with the expected exit code.");
1694
src/burn/engine/netfxchainer.cpp
+18 -13
@@ -338,7 +338,8 @@ extern "C" HRESULT NetFxRunChainer(
338 )
339 {
340 HRESULT hr = S_OK;
341 - DWORD er = 0;
341 + DWORD dwSignaledIndex = 0;
342 + BOOL fTimedOut = 0;
343 WCHAR wzGuid[GUID_STRING_LENGTH];
344 LPWSTR sczEventName = NULL;
345 LPWSTR sczSectionName = NULL;
@@ -381,9 +382,17 @@ extern "C" HRESULT NetFxRunChainer(
382
383 for (;;)
384 {
384 - er = ::WaitForMultipleObjects(2, handles, FALSE, 100);
385 - if (WAIT_OBJECT_0 == er)
385 + hr = AppWaitForMultipleObjects(2, handles, FALSE, 100, &dwSignaledIndex);
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)
394 {
395 + case 0:
396 // Process has exited
397 *pdwExitCode = NetFxGetResult(pNetfxChainer, &hrInternalError);
398 if (E_PENDING == *pdwExitCode)
@@ -396,21 +405,17 @@ extern "C" HRESULT NetFxRunChainer(
405 else if (FAILED(hrInternalError))
406 {
407 // push internal error message
399 - OnNetFxError(pNetfxChainer, hrInternalError, pfnGenericMessageHandler, pvContext);
408 + hr = OnNetFxError(pNetfxChainer, hrInternalError, pfnGenericMessageHandler, pvContext);
409 ExitOnFailure(hr, "Failed to send internal error message from netfx chainer.");
401 - }
410 + }
411
403 - break;
404 - }
405 - else if (WAIT_OBJECT_0 + 1 == er)
406 - {
412 + ExitFunction();
413 + case 1:
414 // Chainee has notified us of a change.
415 hr = ProcessNetFxMessage(pNetfxChainer, pfnGenericMessageHandler, pvContext);
416 ExitOnFailure(hr, "Failed to process netfx chainer message.");
410 - }
411 - else if (WAIT_FAILED == er)
412 - {
413 - ExitWithLastError(hr, "Failed to wait for netfx chainer process to complete");
417 +
418 + break;
419 }
420 }
421
src/burn/engine/pipe.cpp
+16 -18
@@ -422,6 +422,7 @@ extern "C" HRESULT PipeTerminateChildProcess(
422 HRESULT hr = S_OK;
423 BYTE* pbData = NULL;
424 SIZE_T cbData = 0;
425 + BOOL fTimedOut = FALSE;
426
427 // Prepare the exit message.
428 hr = BuffWriteNumber(&pbData, &cbData, dwParentExitCode);
@@ -443,31 +444,28 @@ extern "C" HRESULT PipeTerminateChildProcess(
444 // If we were able to get a handle to the other process, wait for it to exit.
445 if (pConnection->hProcess)
446 {
446 - if (WAIT_FAILED == ::WaitForSingleObject(pConnection->hProcess, PIPE_WAIT_FOR_CONNECTION * PIPE_RETRY_FOR_CONNECTION))
447 - {
448 - ExitWithLastError(hr, "Failed to wait for child process exit.");
449 - }
447 + hr = AppWaitForSingleObject(pConnection->hProcess, PIPE_WAIT_FOR_CONNECTION * PIPE_RETRY_FOR_CONNECTION);
448 + ExitOnWaitObjectFailure(hr, fTimedOut, "Failed to wait for child process exit.");
449 +
450 + AssertSz(!fTimedOut, "Timed out while waiting for child process to exit.");
451 + }
452
453 #ifdef DEBUG
454 + if (pConnection->hProcess && !fTimedOut)
455 + {
456 DWORD dwChildExitCode = 0;
453 - DWORD dwErrorCode = ERROR_SUCCESS;
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.
457 + HRESULT hrDebug = S_OK;
458
459 - // The unit test use a thread instead of a process so try to get the exit code from
460 - // the thread because we failed to get it from the process.
461 - if (ERROR_INVALID_HANDLE == dwErrorCode)
462 - {
463 - fReturnedExitCode = ::GetExitCodeThread(pConnection->hProcess, &dwChildExitCode);
464 - }
459 + hrDebug = CoreWaitForProcCompletion(pConnection->hProcess, 0, &dwChildExitCode);
460 + if (E_ACCESSDENIED != hrDebug) // if the other process is elevated and we are not, then we'll get ERROR_ACCESS_DENIED.
461 + {
462 + TraceError(hrDebug, "Failed to wait for child process completion.");
463 }
466 - AssertSz((fReturnedExitCode && dwChildExitCode == dwParentExitCode) ||
467 - (!fReturnedExitCode && ERROR_ACCESS_DENIED == dwErrorCode),
464 +
465 + AssertSz(E_ACCESSDENIED == hrDebug || dwChildExitCode == dwParentExitCode,
466 "Child elevated process did not return matching exit code to parent process.");
469 -#endif
467 }
468 +#endif
469
470 LExit:
471 return hr;
src/burn/engine/precomp.h
+1
@@ -46,6 +46,7 @@
46 #include <srputil.h>
47 #include <strutil.h>
48 #include <svcutil.h>
49 +#include <thrdutil.h>
50 #include <userutil.h>
51 #include <wiutil.h>
52 #include <wuautil.h>
src/burn/test/BurnUnitTest/ElevationTest.cpp
+1 -2
@@ -54,7 +54,6 @@ namespace Bootstrapper
54 HRESULT hr = S_OK;
55 BURN_ENGINE_STATE engineState = { };
56 BURN_PIPE_CONNECTION* pConnection = &engineState.companionConnection;
57 - HANDLE hEvent = NULL;
57 DWORD dwResult = S_OK;
58
59 engineState.sczBundleEngineWorkingPath = L"tests\\ignore\\this\\path\\to\\burn.exe";
@@ -62,6 +61,7 @@ namespace Bootstrapper
61 try
62 {
63 ShelFunctionOverride(ElevateTest_ShellExecuteExW);
64 + CoreFunctionOverride(ThrdWaitForCompletion);
65
66 PipeConnectionInitialize(pConnection);
67
@@ -87,7 +87,6 @@ namespace Bootstrapper
87 finally
88 {
89 PipeConnectionUninitialize(pConnection);
90 - ReleaseHandle(hEvent);
90 }
91 }
92 };
src/burn/test/BurnUnitTest/precomp.h
+1
@@ -28,6 +28,7 @@
28 #include <resrutil.h>
29 #include <shelutil.h>
30 #include <strutil.h>
31 +#include <thrdutil.h>
32 #include <wiutil.h>
33 #include <xmlutil.h>
34 #include <dictutil.h>
src/libs/dutil/WixToolset.DUtil/apputil.cpp
+68
@@ -313,6 +313,74 @@ LExit:
313 return hr;
314 }
315
316 +DAPI_(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 +
339 +LExit:
340 + return hr;
341 +}
342 +
343 +DAPI_(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 +
375 +LExit:
376 + if (pdwSignaledIndex)
377 + {
378 + *pdwSignaledIndex = dwSignaledIndex;
379 + }
380 +
381 + return hr;
382 +}
383 +
384 static HRESULT EscapeCommandLineArgument(
385 __in_z LPCWSTR wzArgument,
386 __out_z LPWSTR* psczEscaped
src/libs/dutil/WixToolset.DUtil/dutil.vcxproj
+2
@@ -104,6 +104,7 @@
104 <ClCompile Include="strutil.cpp" />
105 <ClCompile Include="svcutil.cpp" />
106 <ClCompile Include="thmutil.cpp" />
107 + <ClCompile Include="thrdutil.cpp" />
108 <ClCompile Include="timeutil.cpp" />
109 <ClCompile Include="uncutil.cpp" />
110 <ClCompile Include="uriutil.cpp" />
@@ -164,6 +165,7 @@
165 <ClInclude Include="inc\strutil.h" />
166 <ClInclude Include="inc\svcutil.h" />
167 <ClInclude Include="inc\thmutil.h" />
168 + <ClInclude Include="inc\thrdutil.h" />
169 <ClInclude Include="inc\timeutil.h" />
170 <ClInclude Include="inc\uriutil.h" />
171 <ClInclude Include="inc\userutil.h" />
src/libs/dutil/WixToolset.DUtil/dutil.vcxproj.filters
+6
@@ -162,6 +162,9 @@
162 <ClCompile Include="thmutil.cpp">
163 <Filter>Source Files</Filter>
164 </ClCompile>
165 + <ClCompile Include="thrdutil.cpp">
166 + <Filter>Source Files</Filter>
167 + </ClCompile>
168 <ClCompile Include="timeutil.cpp">
169 <Filter>Source Files</Filter>
170 </ClCompile>
@@ -344,6 +347,9 @@
347 <ClInclude Include="inc\thmutil.h">
348 <Filter>Header Files</Filter>
349 </ClInclude>
350 + <ClInclude Include="inc\thrdutil.h">
351 + <Filter>Header Files</Filter>
352 + </ClInclude>
353 <ClInclude Include="inc\timeutil.h">
354 <Filter>Header Files</Filter>
355 </ClInclude>
src/libs/dutil/WixToolset.DUtil/inc/apputil.h
+21
@@ -84,6 +84,27 @@ HRESULT DAPI AppEscapeCommandLineArgumentFormattedArgs(
84 __in va_list args
85 );
86
87 +/********************************************************************
88 +AppWaitForSingleObject - wrapper for ::WaitForSingleObject.
89 +
90 +********************************************************************/
91 +HRESULT DAPI AppWaitForSingleObject(
92 + __in HANDLE hHandle,
93 + __in DWORD dwMilliseconds
94 + );
95 +
96 +/********************************************************************
97 +AppWaitForMultipleObjects - wrapper for ::WaitForMultipleObjects.
98 +
99 +********************************************************************/
100 +HRESULT 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 +
108 #ifdef __cplusplus
109 }
110 #endif
src/libs/dutil/WixToolset.DUtil/inc/dutil.h
+2
@@ -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; }
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__); }
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__); }
134
135 #define ExitOnLastError(x, s, ...) ExitOnLastErrorSource(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
146 #define ExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DEFAULT, e, x, s, __VA_ARGS__)
147 #define ExitOnOptionalXmlQueryFailure(x, b, s, ...) ExitOnOptionalXmlQueryFailureSource(DUTIL_SOURCE_DEFAULT, x, b, 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__)
150
151 // release macros
152 #define ReleaseObject(x) if (x) { x->Release(); }
src/libs/dutil/WixToolset.DUtil/inc/dutilsources.h
+1
@@ -63,6 +63,7 @@ typedef enum DUTIL_SOURCE
63 DUTIL_SOURCE_VERUTIL,
64 DUTIL_SOURCE_WNDUTIL,
65 DUTIL_SOURCE_ENVUTIL,
66 + DUTIL_SOURCE_THRDUTIL,
67
68 DUTIL_SOURCE_EXTERNAL = 256,
69 } DUTIL_SOURCE;
src/libs/dutil/WixToolset.DUtil/inc/procutil.h
+1 -1
@@ -58,7 +58,7 @@ HRESULT DAPI ProcExecute(
58 HRESULT DAPI ProcWaitForCompletion(
59 __in HANDLE hProcess,
60 __in DWORD dwTimeout,
61 - __out DWORD *pReturnCode
61 + __out_opt DWORD* pdwReturnCode
62 );
63 HRESULT DAPI ProcWaitForIds(
64 __in_ecount(cProcessIds) const DWORD* pdwProcessIds,
src/libs/dutil/WixToolset.DUtil/inc/thrdutil.h new
+22
@@ -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
6 +extern "C" {
7 +#endif
8 +
9 +/********************************************************************
10 + ThrdWaitForCompletion - waits for thread to complete and gets return code.
11 +
12 + *******************************************************************/
13 +HRESULT DAPI ThrdWaitForCompletion(
14 + __in HANDLE hThread,
15 + __in DWORD dwTimeout,
16 + __out_opt DWORD* pdwReturnCode
17 + );
18 +
19 +#ifdef __cplusplus
20 +}
21 +#endif
22 +
src/libs/dutil/WixToolset.DUtil/monutil.cpp
+16 -18
@@ -16,6 +16,7 @@
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__)
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__)
20
21 const int MON_THREAD_GROWTH = 5;
22 const int MON_ARRAY_GROWTH = 40;
@@ -1101,11 +1102,12 @@ static DWORD WINAPI WaiterThread(
1102 {
1103 HRESULT hr = S_OK;
1104 HRESULT hrTemp = S_OK;
1104 - DWORD dwRet = 0;
1105 BOOL fAgain = FALSE;
1106 BOOL fContinue = TRUE;
1107 BOOL fNotify = FALSE;
1108 BOOL fRet = FALSE;
1109 + BOOL fTimedOut = FALSE;
1110 + DWORD dwSignaledIndex = 0;
1111 MSG msg = { };
1112 MON_ADD_MESSAGE *pAddMessage = NULL;
1113 MON_REMOVE_MESSAGE *pRemoveMessage = NULL;
@@ -1128,13 +1130,14 @@ static DWORD WINAPI WaiterThread(
1130
1131 do
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.");
1135
1136 uCurrentTime = ::GetTickCount();
1137 uDeltaInMs = uCurrentTime - uLastTimeInMs;
1138 uLastTimeInMs = uCurrentTime;
1139
1137 - if (WAIT_OBJECT_0 == dwRet)
1140 + if (!fTimedOut && 0 == dwSignaledIndex)
1141 {
1142 do
1143 {
@@ -1391,10 +1394,10 @@ static DWORD WINAPI WaiterThread(
1394 }
1395 } while (fAgain);
1396 }
1394 - else if (dwRet > WAIT_OBJECT_0 && dwRet - WAIT_OBJECT_0 < pWaiterContext->cHandles)
1397 + else if (!fTimedOut)
1398 {
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;
1401 fNotify = (pWaiterContext->rgRequests[dwRequestIndex].dwPathHierarchyIndex == pWaiterContext->rgRequests[dwRequestIndex].cPathHierarchy - 1);
1402
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(
1429 }
1430 }
1431 }
1429 - else if (WAIT_TIMEOUT != dwRet)
1430 - {
1431 - MonExitWithLastError(hr, "Failed to wait for multiple objects with return code %u", dwRet);
1432 - }
1432
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
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(
1725 DEV_BROADCAST_HANDLE *pHandle = NULL;
1726 DEV_BROADCAST_VOLUME *pVolume = NULL;
1727 DWORD dwUnitMask = 0;
1729 - DWORD er = ERROR_SUCCESS;
1728 WCHAR chDrive = L'\0';
1729 BOOL fArrival = FALSE;
1730 BOOL fReturnTrue = FALSE;
1731 + BOOL fTimedOut = FALSE;
1732 CREATESTRUCT *pCreateStruct = NULL;
1733 MON_WAITER_CONTEXT *pWaiterContext = NULL;
1734 MON_STRUCT *pm = NULL;
@@ -1821,24 +1820,23 @@ static LRESULT CALLBACK MonWndProc(
1820 }
1821 }
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 +
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)
1828 {
1829 // If the response ID matches what we sent, we actually got a valid reply!
1830 if (pm->internalWait.dwReceiveIteration != pm->internalWait.dwSendIteration)
1831 {
1831 - TraceError(HRESULT_FROM_WIN32(er), "Waiter thread received wrong ID reply");
1832 + TraceError(E_UNEXPECTED, "Waiter thread received wrong ID reply");
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 - }
1835 else
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");
1838 }
1839 +
1840 ++pm->internalWait.dwSendIteration;
1841 }
1842 }
src/libs/dutil/WixToolset.DUtil/precomp.h
+1
@@ -90,6 +90,7 @@
90 #include "timeutil.h"
91 #include "wndutil.h"
92 #include "thmutil.h"
93 +#include "thrdutil.h"
94 #include "uncutil.h"
95 #include "uriutil.h"
96 #include "userutil.h"
src/libs/dutil/WixToolset.DUtil/procutil.cpp
+18 -24
@@ -17,6 +17,7 @@
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__)
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__)
21
22
23 // private functions
@@ -403,30 +404,25 @@ LExit:
404 extern "C" HRESULT DAPI ProcWaitForCompletion(
405 __in HANDLE hProcess,
406 __in DWORD dwTimeout,
406 - __out DWORD *pReturnCode
407 + __out_opt DWORD* pdwReturnCode
408 )
409 {
410 HRESULT hr = S_OK;
410 - DWORD er = ERROR_SUCCESS;
411 + BOOL fTimedOut = FALSE;
412
412 - // Wait for everything to finish
413 - er = ::WaitForSingleObject(hProcess, dwTimeout);
414 - if (WAIT_FAILED == er)
415 - {
416 - ProcExitWithLastError(hr, "Failed to wait for process to complete.");
417 - }
418 - else if (WAIT_TIMEOUT == er)
413 + // Wait for everything to finish.
414 + hr = AppWaitForSingleObject(hProcess, dwTimeout);
415 + ProcExitOnWaitObjectFailure(hr, fTimedOut, "Failed to wait for process to complete.");
416 +
417 + if (fTimedOut)
418 {
420 - ExitFunction1(hr = HRESULT_FROM_WIN32(er));
419 + hr = HRESULT_FROM_WIN32(WAIT_TIMEOUT);
420 }
422 -
423 - if (!::GetExitCodeProcess(hProcess, &er))
421 + else if (pdwReturnCode && !::GetExitCodeProcess(hProcess, pdwReturnCode))
422 {
423 ProcExitWithLastError(hr, "Failed to get process return code.");
424 }
425
428 - *pReturnCode = er;
429 -
426 LExit:
427 return hr;
428 }
@@ -442,10 +438,10 @@ extern "C" HRESULT DAPI ProcWaitForIds(
438 )
439 {
440 HRESULT hr = S_OK;
445 - DWORD er = ERROR_SUCCESS;
441 HANDLE hProcess = NULL;
447 - HANDLE * rghProcesses = NULL;
442 + HANDLE* rghProcesses = NULL;
443 DWORD cProcesses = 0;
444 + BOOL fTimedOut = FALSE;
445
446 rghProcesses = static_cast<HANDLE*>(MemAlloc(sizeof(DWORD) * cProcessIds, TRUE));
447 ProcExitOnNull(rgdwProcessIds, hr, E_OUTOFMEMORY, "Failed to allocate array for process ID Handles.");
@@ -459,16 +455,14 @@ extern "C" HRESULT DAPI ProcWaitForIds(
455 }
456 }
457
462 - er = ::WaitForMultipleObjects(cProcesses, rghProcesses, TRUE, dwMilliseconds);
463 - if (WAIT_FAILED == er)
464 - {
465 - ProcExitWithLastError(hr, "Failed to wait for process to complete.");
466 - }
467 - else if (WAIT_TIMEOUT == er)
458 + hr = AppWaitForMultipleObjects(cProcesses, rghProcesses, TRUE, dwMilliseconds, NULL);
459 + ProcExitOnWaitObjectFailure(hr, fTimedOut, "Failed to wait for processes to complete.");
460 +
461 + if (fTimedOut)
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.");
464 }
471 -
465 +
466 LExit:
467 if (rghProcesses)
468 {
src/libs/dutil/WixToolset.DUtil/thrdutil.cpp new
+46
@@ -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 +
22 +DAPI_(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 +
44 +LExit:
45 + return hr;
46 +}
src/libs/dutil/test/DUtilUnitTest/AppUtilTests.cpp new
+60
@@ -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 +
5 +namespace 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 +}
src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj
+1
@@ -44,6 +44,7 @@
44 </PropertyGroup>
45
46 <ItemGroup>
47 + <ClCompile Include="AppUtilTests.cpp" />
48 <ClCompile Include="ApupUtilTests.cpp" />
49 <ClCompile Include="AssemblyInfo.cpp" />
50 <ClCompile Include="DictUtilTest.cpp" />
src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters
+3
@@ -15,6 +15,9 @@
15 </Filter>
16 </ItemGroup>
17 <ItemGroup>
18 + <ClCompile Include="AppUtilTests.cpp">
19 + <Filter>Source Files</Filter>
20 + </ClCompile>
21 <ClCompile Include="ApupUtilTests.cpp">
22 <Filter>Source Files</Filter>
23 </ClCompile>
src/libs/dutil/test/DUtilUnitTest/precomp.h
+1
@@ -13,6 +13,7 @@
13 #include <dutil.h>
14
15 #include <verutil.h>
16 +#include <apputil.h>
17 #include <atomutil.h>
18 #include <dictutil.h>
19 #include <dirutil.h>