Add embedded test.
Sean Hall committed
Jun 29, 2022 at 10:29 UTC
ec413164bd0285d1e9b9d36538974641a109b579
11 files changed
+248
-33
src/burn/engine/core.cpp
+35
@@ -12,6 +12,7 @@ struct BURN_CACHE_THREAD_CONTEXT
12
};
13
14
15
+static PFN_CREATEPROCESSW vpfnCreateProcessW = ::CreateProcessW;
16
static PFN_PROCWAITFORCOMPLETION vpfnProcWaitForCompletion = ProcWaitForCompletion;
17
18
@@ -1947,12 +1948,46 @@ LExit:
1948
}
1949
1950
extern "C" void CoreFunctionOverride(
1951
+ __in_opt PFN_CREATEPROCESSW pfnCreateProcessW,
1952
__in_opt PFN_PROCWAITFORCOMPLETION pfnProcWaitForCompletion
1953
)
1954
{
1955
+ vpfnCreateProcessW = pfnCreateProcessW;
1956
vpfnProcWaitForCompletion = pfnProcWaitForCompletion;
1957
}
1958
1959
+extern "C" HRESULT CoreCreateProcess(
1960
+ __in_opt LPCWSTR wzApplicationName,
1961
+ __inout_opt LPWSTR sczCommandLine,
1962
+ __in BOOL fInheritHandles,
1963
+ __in DWORD dwCreationFlags,
1964
+ __in_opt LPCWSTR wzCurrentDirectory,
1965
+ __in WORD wShowWindow,
1966
+ __out LPPROCESS_INFORMATION pProcessInformation
1967
+ )
1968
+{
1969
+ HRESULT hr = S_OK;
1970
+ STARTUPINFOW si = { };
1971
+ size_t cchCurrentDirectory = 0;
1972
+
1973
+ // CreateProcessW has undocumented MAX_PATH restriction for lpCurrentDirectory even when long path support is enabled.
1974
+ if (wzCurrentDirectory && FAILED(::StringCchLengthW(wzCurrentDirectory, MAX_PATH - 1, &cchCurrentDirectory)))
1975
+ {
1976
+ wzCurrentDirectory = NULL;
1977
+ }
1978
+
1979
+ si.cb = sizeof(si);
1980
+ si.wShowWindow = wShowWindow;
1981
+
1982
+ if (!vpfnCreateProcessW(wzApplicationName, sczCommandLine, NULL, NULL, fInheritHandles, dwCreationFlags, NULL, wzCurrentDirectory, &si, pProcessInformation))
1983
+ {
1984
+ ExitWithLastError(hr, "CreateProcessW failed with return code: %d", Dutil_er);
1985
+ }
1986
+
1987
+LExit:
1988
+ return hr;
1989
+}
1990
+
1991
extern "C" HRESULT DAPI CoreWaitForProcCompletion(
1992
__in HANDLE hProcess,
1993
__in DWORD dwTimeout,
src/burn/engine/core.h
+23
@@ -174,6 +174,19 @@ typedef struct _BURN_APPLY_CONTEXT
174
DWORD dwCacheCheckpoint;
175
} BURN_APPLY_CONTEXT;
176
177
+typedef BOOL (STDAPICALLTYPE *PFN_CREATEPROCESSW)(
178
+ __in_opt LPCWSTR lpApplicationName,
179
+ __inout_opt LPWSTR lpCommandLine,
180
+ __in_opt LPSECURITY_ATTRIBUTES lpProcessAttributes,
181
+ __in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes,
182
+ __in BOOL bInheritHandles,
183
+ __in DWORD dwCreationFlags,
184
+ __in_opt LPVOID lpEnvironment,
185
+ __in_opt LPCWSTR lpCurrentDirectory,
186
+ __in LPSTARTUPINFOW lpStartupInfo,
187
+ __out LPPROCESS_INFORMATION lpProcessInformation
188
+ );
189
+
190
typedef HRESULT (DAPI *PFN_PROCWAITFORCOMPLETION)(
191
__in HANDLE hProcess,
192
__in DWORD dwTimeout,
@@ -287,8 +300,18 @@ HRESULT CoreParseCommandLine(
300
__inout HANDLE* phSourceEngineFile
301
);
302
void CoreFunctionOverride(
303
+ __in_opt PFN_CREATEPROCESSW pfnCreateProcessW,
304
__in_opt PFN_PROCWAITFORCOMPLETION pfnProcWaitForCompletion
305
);
306
+HRESULT CoreCreateProcess(
307
+ __in_opt LPCWSTR wzApplicationName,
308
+ __inout_opt LPWSTR sczCommandLine,
309
+ __in BOOL fInheritHandles,
310
+ __in DWORD dwCreationFlags,
311
+ __in_opt LPCWSTR wzCurrentDirectory,
312
+ __in WORD wShowWindow,
313
+ __out LPPROCESS_INFORMATION pProcessInformation
314
+ );
315
HRESULT DAPI CoreWaitForProcCompletion(
316
__in HANDLE hProcess,
317
__in DWORD dwTimeout,
src/burn/engine/embedded.cpp
+3
-6
@@ -52,7 +52,6 @@ extern "C" HRESULT EmbeddedRunBundle(
52
DWORD dwCurrentProcessId = ::GetCurrentProcessId();
53
HANDLE hCreatedPipesEvent = NULL;
54
LPWSTR sczCommand = NULL;
55
- STARTUPINFOW si = { };
55
PROCESS_INFORMATION pi = { };
56
BURN_PIPE_RESULT result = { };
57
@@ -79,10 +78,8 @@ extern "C" HRESULT EmbeddedRunBundle(
78
ExitOnFailure(hr, "Failed to append user args.");
79
}
80
82
- if (!::CreateProcessW(wzExecutablePath, sczCommand, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
83
- {
84
- ExitWithLastError(hr, "Failed to create embedded process at path: %ls", wzExecutablePath);
85
- }
81
+ hr = CoreCreateProcess(wzExecutablePath, sczCommand, TRUE, CREATE_NO_WINDOW, NULL, 0, &pi);
82
+ ExitOnFailure(hr, "Failed to create embedded process at path: %ls", wzExecutablePath);
83
84
connection.dwProcessId = ::GetProcessId(pi.hProcess);
85
connection.hProcess = pi.hProcess;
@@ -95,7 +92,7 @@ extern "C" HRESULT EmbeddedRunBundle(
92
ExitOnFailure(hr, "Failed to process messages from embedded message.");
93
94
// Get the return code from the embedded process.
98
- hr = ProcWaitForCompletion(connection.hProcess, INFINITE, pdwExitCode);
95
+ hr = CoreWaitForProcCompletion(connection.hProcess, INFINITE, pdwExitCode);
96
ExitOnFailure(hr, "Failed to wait for embedded executable: %ls", wzExecutablePath);
97
98
LExit:
src/burn/engine/engine.cpp
+2
-7
@@ -445,7 +445,6 @@ static HRESULT RunUntrusted(
445
LPWSTR sczCachedCleanRoomBundlePath = NULL;
446
LPWSTR sczParameters = NULL;
447
LPWSTR sczFullCommandLine = NULL;
448
- STARTUPINFOW si = { };
448
PROCESS_INFORMATION pi = { };
449
HANDLE hFileAttached = NULL;
450
HANDLE hFileSelf = NULL;
@@ -484,12 +483,8 @@ static HRESULT RunUntrusted(
483
hr = StrAllocFormattedSecure(&sczFullCommandLine, L"\"%ls\" %ls", wzCleanRoomBundlePath, sczParameters);
484
ExitOnFailure(hr, "Failed to allocate full command-line.");
485
487
- si.cb = sizeof(si);
488
- si.wShowWindow = static_cast<WORD>(pEngineState->command.nCmdShow);
489
- if (!::CreateProcessW(wzCleanRoomBundlePath, sczFullCommandLine, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
490
- {
491
- ExitWithLastError(hr, "Failed to launch clean room process: %ls", sczFullCommandLine);
492
- }
486
+ hr = CoreCreateProcess(wzCleanRoomBundlePath, sczFullCommandLine, TRUE, 0, NULL, static_cast<WORD>(pEngineState->command.nCmdShow), &pi);
487
+ ExitOnFailure(hr, "Failed to launch clean room process: %ls", sczFullCommandLine);
488
489
hProcess = pi.hProcess;
490
pi.hProcess = NULL;
src/burn/engine/exeengine.cpp
+2
-13
@@ -687,7 +687,6 @@ extern "C" HRESULT ExeEngineRunProcess(
687
{
688
HRESULT hr = S_OK;
689
LPWSTR sczCommand = NULL;
690
- STARTUPINFOW si = { };
690
PROCESS_INFORMATION pi = { };
691
GENERIC_EXECUTE_MESSAGE message = { };
692
int nResult = IDNOACTION;
@@ -695,7 +694,6 @@ extern "C" HRESULT ExeEngineRunProcess(
694
BOOL fDelayedCancel = FALSE;
695
BOOL fFireAndForget = BURN_PACKAGE_TYPE_EXE == pPackage->type && pPackage->Exe.fFireAndForget;
696
BOOL fInheritHandles = BURN_PACKAGE_TYPE_BUNDLE == pPackage->type;
698
- size_t cchCachedDirectory = 0;
697
698
// Always add user supplied arguments last.
699
if (wzUserArgs)
@@ -704,19 +702,10 @@ extern "C" HRESULT ExeEngineRunProcess(
702
ExitOnFailure(hr, "Failed to append user args.");
703
}
704
707
- // CreateProcessW has undocumented MAX_PATH restriction for lpCurrentDirectory even when long path support is enabled.
708
- if (wzCachedDirectory && FAILED(::StringCchLengthW(wzCachedDirectory, MAX_PATH - 1, &cchCachedDirectory)))
709
- {
710
- wzCachedDirectory = NULL;
711
- }
712
-
705
// Make the cache location of the executable the current directory to help those executables
706
// that expect stuff to be relative to them.
715
- si.cb = sizeof(si);
716
- if (!::CreateProcessW(wzExecutablePath, sczCommand ? sczCommand : sczBaseCommand, NULL, NULL, fInheritHandles, CREATE_NO_WINDOW, NULL, wzCachedDirectory, &si, &pi))
717
- {
718
- ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", wzExecutablePath);
719
- }
707
+ hr = CoreCreateProcess(wzExecutablePath, sczCommand ? sczCommand : sczBaseCommand, fInheritHandles, CREATE_NO_WINDOW, wzCachedDirectory, 0, &pi);
708
+ ExitOnFailure(hr, "Failed to CreateProcess on path: %ls", wzExecutablePath);
709
710
message.type = GENERIC_EXECUTE_MESSAGE_PROCESS_STARTED;
711
message.dwUIHint = MB_OK;
src/burn/engine/netfxchainer.cpp
+2
-6
@@ -345,7 +345,6 @@ extern "C" HRESULT NetFxRunChainer(
345
LPWSTR sczSectionName = NULL;
346
LPWSTR sczCommand = NULL;
347
NetFxChainer* pNetfxChainer = NULL;
348
- STARTUPINFOW si = { };
348
PROCESS_INFORMATION pi = { };
349
HRESULT hrInternalError = 0;
350
@@ -372,11 +371,8 @@ extern "C" HRESULT NetFxRunChainer(
371
ExitOnFailure(hr, "Failed to append user args.");
372
}
373
375
- si.cb = sizeof(si);
376
- if (!::CreateProcessW(wzExecutablePath, sczCommand, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
377
- {
378
- ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", wzExecutablePath);
379
- }
374
+ hr = CoreCreateProcess(wzExecutablePath, sczCommand, FALSE, CREATE_NO_WINDOW, NULL, 0, &pi);
375
+ ExitOnFailure(hr, "Failed to CreateProcess on path: %ls", wzExecutablePath);
376
377
HANDLE handles[2] = { pi.hProcess, pNetfxChainer->hEventChaineeSend };
378
src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj
+1
@@ -47,6 +47,7 @@
47
<ClCompile Include="AssemblyInfo.cpp" />
48
<ClCompile Include="CacheTest.cpp" />
49
<ClCompile Include="ElevationTest.cpp" />
50
+ <ClCompile Include="EmbeddedTest.cpp" />
51
<ClCompile Include="ManifestHelpers.cpp" />
52
<ClCompile Include="ManifestTest.cpp" />
53
<ClCompile Include="PlanTest.cpp" />
src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj.filters
+3
@@ -24,6 +24,9 @@
24
<ClCompile Include="ElevationTest.cpp">
25
<Filter>Source Files</Filter>
26
</ClCompile>
27
+ <ClCompile Include="EmbeddedTest.cpp">
28
+ <Filter>Source Files</Filter>
29
+ </ClCompile>
30
<ClCompile Include="ManifestHelpers.cpp">
31
<Filter>Source Files</Filter>
32
</ClCompile>
src/burn/test/BurnUnitTest/ElevationTest.cpp
+1
-1
@@ -61,7 +61,7 @@ namespace Bootstrapper
61
try
62
{
63
ShelFunctionOverride(ElevateTest_ShellExecuteExW);
64
- CoreFunctionOverride(ThrdWaitForCompletion);
64
+ CoreFunctionOverride(NULL, ThrdWaitForCompletion);
65
66
PipeConnectionInitialize(pConnection);
67
src/burn/test/BurnUnitTest/EmbeddedTest.cpp
new
+175
@@ -0,0 +1,175 @@
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
+const HRESULT S_TEST_SUCCEEDED = 0x3133;
7
+const DWORD TEST_EXIT_CODE = 666;
8
+
9
+struct BUNDLE_RUNNER_CONTEXT
10
+{
11
+ DWORD dwResult;
12
+};
13
+
14
+
15
+static BOOL STDAPICALLTYPE EmbeddedTest_CreateProcessW(
16
+ __in_opt LPCWSTR lpApplicationName,
17
+ __inout_opt LPWSTR lpCommandLine,
18
+ __in_opt LPSECURITY_ATTRIBUTES lpProcessAttributes,
19
+ __in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes,
20
+ __in BOOL bInheritHandles,
21
+ __in DWORD dwCreationFlags,
22
+ __in_opt LPVOID lpEnvironment,
23
+ __in_opt LPCWSTR lpCurrentDirectory,
24
+ __in LPSTARTUPINFOW lpStartupInfo,
25
+ __out LPPROCESS_INFORMATION lpProcessInformation
26
+ );
27
+static DWORD CALLBACK EmbeddedTest_ThreadProc(
28
+ __in LPVOID lpThreadParameter
29
+ );
30
+static int EmbeddedTest_GenericMessageHandler(
31
+ __in GENERIC_EXECUTE_MESSAGE* pMessage,
32
+ __in LPVOID pvContext
33
+ );
34
+
35
+namespace Microsoft
36
+{
37
+namespace Tools
38
+{
39
+namespace WindowsInstallerXml
40
+{
41
+namespace Test
42
+{
43
+namespace Bootstrapper
44
+{
45
+ using namespace System;
46
+ using namespace System::IO;
47
+ using namespace System::Threading;
48
+ using namespace Xunit;
49
+
50
+ public ref class EmbeddedTest : BurnUnitTest
51
+ {
52
+ public:
53
+ EmbeddedTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture)
54
+ {
55
+ }
56
+
57
+ [Fact]
58
+ void EmbeddedProtocolTest()
59
+ {
60
+ HRESULT hr = S_OK;
61
+ BUNDLE_RUNNER_CONTEXT bundleRunnerContext = { };
62
+ DWORD dwExitCode = 0;
63
+
64
+ try
65
+ {
66
+ CoreFunctionOverride(EmbeddedTest_CreateProcessW, ThrdWaitForCompletion);
67
+
68
+ //
69
+ // bundle runner setup
70
+ //
71
+ hr = EmbeddedRunBundle(L"C:\\ignored\\target.exe", L"\"C:\\ignored\\target.exe\"", NULL, EmbeddedTest_GenericMessageHandler, &bundleRunnerContext, &dwExitCode);
72
+ TestThrowOnFailure(hr, L"Failed to run embedded bundle.");
73
+
74
+ // check results
75
+ Assert::Equal<HRESULT>(S_TEST_SUCCEEDED, (HRESULT)bundleRunnerContext.dwResult);
76
+ Assert::Equal<DWORD>(TEST_EXIT_CODE, dwExitCode);
77
+ }
78
+ finally
79
+ {
80
+ }
81
+ }
82
+ };
83
+}
84
+}
85
+}
86
+}
87
+}
88
+
89
+
90
+static BOOL STDAPICALLTYPE EmbeddedTest_CreateProcessW(
91
+ __in_opt LPCWSTR /*lpApplicationName*/,
92
+ __inout_opt LPWSTR lpCommandLine,
93
+ __in_opt LPSECURITY_ATTRIBUTES /*lpProcessAttributes*/,
94
+ __in_opt LPSECURITY_ATTRIBUTES /*lpThreadAttributes*/,
95
+ __in BOOL /*bInheritHandles*/,
96
+ __in DWORD /*dwCreationFlags*/,
97
+ __in_opt LPVOID /*lpEnvironment*/,
98
+ __in_opt LPCWSTR /*lpCurrentDirectory*/,
99
+ __in LPSTARTUPINFOW /*lpStartupInfo*/,
100
+ __out LPPROCESS_INFORMATION lpProcessInformation
101
+ )
102
+{
103
+ HRESULT hr = S_OK;
104
+ LPWSTR scz = NULL;
105
+ LPCWSTR wzArgs = lpCommandLine + 24; //skip '"C:\ignored\target.exe" '
106
+
107
+ hr = StrAllocString(&scz, wzArgs, 0);
108
+ ExitOnFailure(hr, "Failed to copy arguments.");
109
+
110
+ // Pretend this thread is the embedded process.
111
+ lpProcessInformation->hProcess = ::CreateThread(NULL, 0, EmbeddedTest_ThreadProc, scz, 0, NULL);
112
+ ExitOnNullWithLastError(lpProcessInformation->hProcess, hr, "Failed to create thread.");
113
+
114
+ scz = NULL;
115
+
116
+LExit:
117
+ ReleaseStr(scz);
118
+
119
+ return SUCCEEDED(hr);
120
+}
121
+
122
+static DWORD CALLBACK EmbeddedTest_ThreadProc(
123
+ __in LPVOID lpThreadParameter
124
+ )
125
+{
126
+ HRESULT hr = S_OK;
127
+ LPWSTR sczArguments = (LPWSTR)lpThreadParameter;
128
+ BURN_ENGINE_STATE engineState = { };
129
+ BURN_PIPE_CONNECTION* pConnection = &engineState.embeddedConnection;
130
+ DWORD dwResult = 0;
131
+
132
+ engineState.internalCommand.mode = BURN_MODE_EMBEDDED;
133
+
134
+ PipeConnectionInitialize(pConnection);
135
+
136
+ StrAlloc(&pConnection->sczName, MAX_PATH);
137
+ StrAlloc(&pConnection->sczSecret, MAX_PATH);
138
+
139
+ // parse command line arguments
140
+ if (3 != swscanf_s(sczArguments, L"-burn.embedded %s %s %u", pConnection->sczName, MAX_PATH, pConnection->sczSecret, MAX_PATH, &pConnection->dwProcessId))
141
+ {
142
+ ExitWithRootFailure(hr, E_INVALIDARG, "Failed to parse argument string.");
143
+ }
144
+
145
+ // set up connection with parent bundle runner
146
+ hr = PipeChildConnect(pConnection, FALSE);
147
+ ExitOnFailure(hr, "Failed to connect to parent bundle runner.");
148
+
149
+ // post known message
150
+ hr = ExternalEngineSendEmbeddedError(&engineState, S_TEST_SUCCEEDED, NULL, 0, reinterpret_cast<int*>(&dwResult));
151
+ ExitOnFailure(hr, "Failed to post known message to parent bundle runner.");
152
+
153
+LExit:
154
+ PipeConnectionUninitialize(pConnection);
155
+ ReleaseStr(sczArguments);
156
+
157
+ return FAILED(hr) ? (DWORD)hr : dwResult;
158
+}
159
+
160
+static int EmbeddedTest_GenericMessageHandler(
161
+ __in GENERIC_EXECUTE_MESSAGE* pMessage,
162
+ __in LPVOID pvContext
163
+ )
164
+{
165
+ BUNDLE_RUNNER_CONTEXT* pContext = reinterpret_cast<BUNDLE_RUNNER_CONTEXT*>(pvContext);
166
+ DWORD dwResult = 0;
167
+
168
+ if (GENERIC_EXECUTE_MESSAGE_ERROR == pMessage->type)
169
+ {
170
+ pContext->dwResult = pMessage->error.dwErrorCode;
171
+ dwResult = TEST_EXIT_CODE;
172
+ }
173
+
174
+ return dwResult;
175
+}
src/burn/test/BurnUnitTest/precomp.h
+1
@@ -73,6 +73,7 @@
73
#include "manifest.h"
74
#include "splashscreen.h"
75
#include "detect.h"
76
+#include "externalengine.h"
77
78
#include "engine.version.h"
79