@joebigelow / wix / commits / 61847ddd

Import code from old v4 repo

Sean Hall committed Dec 29, 2018 at 22:12 UTC 61847dddd4fd497057c780658e383c4627de19ec
90 files changed +41905
src/Samples/ManagedBundleRunner/BundleErrorEventArgs.cs new
+33
@@ -0,0 +1,33 @@
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 +namespace Wix.Samples
4 +{
5 + using System;
6 +
7 + /// <summary>
8 + /// Arguments provided when bundle encounters an error.
9 + /// </summary>
10 + [Serializable]
11 + public class BundleErrorEventArgs : EventArgs
12 + {
13 + /// <summary>
14 + /// Gets the error code.
15 + /// </summary>
16 + public int Code { get; set; }
17 +
18 + /// <summary>
19 + /// Gets the error message.
20 + /// </summary>
21 + public string Message { get; set; }
22 +
23 + /// <summary>
24 + /// Gets the recommended display flags for an error dialog.
25 + /// </summary>
26 + public int UIHint { get; set; }
27 +
28 + /// <summary>
29 + /// Gets or sets the <see cref="Result"/> of the operation. This is passed back to the bundle.
30 + /// </summary>
31 + public BundleResult Result { get; set; }
32 + }
33 +}
src/Samples/ManagedBundleRunner/BundleProgressEventArgs.cs new
+23
@@ -0,0 +1,23 @@
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 +namespace Wix.Samples
4 +{
5 + using System;
6 +
7 + /// <summary>
8 + /// Arguments provided when bundle progress is updated.
9 + /// </summary>
10 + [Serializable]
11 + public class BundleProgressEventArgs : EventArgs
12 + {
13 + /// <summary>
14 + /// Gets the percentage from 0 to 100 completed for a bundle.
15 + /// </summary>
16 + public int Progress { get; set; }
17 +
18 + /// <summary>
19 + /// Gets or sets the <see cref="Result"/> of the operation. This is passed back to the bundle.
20 + /// </summary>
21 + public BundleResult Result { get; set; }
22 + }
23 +}
src/Samples/ManagedBundleRunner/BundleResult.cs new
+24
@@ -0,0 +1,24 @@
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 +namespace Wix.Samples
4 +{
5 + /// <summary>
6 + /// Result codes.
7 + /// </summary>
8 + public enum BundleResult
9 + {
10 + Error = -1,
11 + None,
12 + Ok,
13 + Cancel,
14 + Abort,
15 + Retry,
16 + Ignore,
17 + Yes,
18 + No,
19 + Close,
20 + Help,
21 + TryAgain,
22 + Continue,
23 + }
24 +}
src/Samples/ManagedBundleRunner/BundleRunner.cs new
+212
@@ -0,0 +1,212 @@
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 +namespace Wix.Samples
4 +{
5 + using System;
6 + using System.Diagnostics;
7 + using System.IO.Pipes;
8 + using System.Text;
9 + using System.Threading;
10 +
11 + /// <summary>
12 + /// Runs a bundle with provided command-line.
13 + /// </summary>
14 + public class BundleRunner
15 + {
16 + /// <summary>
17 + /// Creates a runner for the provided bundle.
18 + /// </summary>
19 + /// <param name="bundle">Path to the bundle to run.</param>
20 + public BundleRunner(string bundle)
21 + {
22 + this.Path = bundle;
23 + }
24 +
25 + /// <summary>
26 + /// Fired when the bundle encounters an error.
27 + /// </summary>
28 + public event EventHandler<BundleErrorEventArgs> Error;
29 +
30 + /// <summary>
31 + /// Fired when the bundle progress is udpated.
32 + /// </summary>
33 + public event EventHandler<BundleProgressEventArgs> Progress;
34 +
35 + /// <summary>
36 + /// Gets the path to the bundle to run.
37 + /// </summary>
38 + public string Path { get; private set; }
39 +
40 + /// <summary>
41 + /// Runs the bundle with the provided command-line.
42 + /// </summary>
43 + /// <param name="commandLine">Optional command-line to pass to the bundle.</param>
44 + /// <returns>Exit code from the bundle.</returns>
45 + public int Run(string commandLine = null)
46 + {
47 + WaitHandle[] waits = new WaitHandle[] { new ManualResetEvent(false), new ManualResetEvent(false) };
48 + int returnCode = 0;
49 + int pid = Process.GetCurrentProcess().Id;
50 + string pipeName = String.Concat("bpe_", pid);
51 + string pipeSecret = Guid.NewGuid().ToString("N");
52 +
53 + using (NamedPipeServerStream pipe = new NamedPipeServerStream(pipeName, PipeDirection.InOut, 1))
54 + {
55 + using (Process bundleProcess = new Process())
56 + {
57 + bundleProcess.StartInfo.FileName = this.Path;
58 + bundleProcess.StartInfo.Arguments = String.Format("{0} -burn.embedded {1} {2} {3}", commandLine ?? String.Empty, pipeName, pipeSecret, pid);
59 + bundleProcess.StartInfo.UseShellExecute = false;
60 + bundleProcess.StartInfo.CreateNoWindow = true;
61 + bundleProcess.Start();
62 +
63 + Connect(pipe, pipeSecret, pid, bundleProcess.Id);
64 +
65 + PumpMessages(pipe);
66 +
67 + bundleProcess.WaitForExit();
68 + returnCode = bundleProcess.ExitCode;
69 + }
70 + }
71 +
72 + return returnCode;
73 + }
74 +
75 + /// <summary>
76 + /// Called when bundle encounters an error.
77 + /// </summary>
78 + /// <param name="e">Additional arguments for this event.</param>
79 + protected virtual void OnError(BundleErrorEventArgs e)
80 + {
81 + EventHandler<BundleErrorEventArgs> handler = this.Error;
82 + if (handler != null)
83 + {
84 + handler(this, e);
85 + }
86 + }
87 +
88 + /// <summary>
89 + /// Called when bundle progress is updated.
90 + /// </summary>
91 + /// <param name="e">Additional arguments for this event.</param>
92 + protected virtual void OnProgress(BundleProgressEventArgs e)
93 + {
94 + EventHandler<BundleProgressEventArgs> handler = this.Progress;
95 + if (handler != null)
96 + {
97 + handler(this, e);
98 + }
99 + }
100 +
101 + private void Connect(NamedPipeServerStream pipe, string pipeSecret, int pid, int childPid)
102 + {
103 + pipe.WaitForConnection();
104 +
105 + WriteSecretToPipe(pipe, pipeSecret);
106 +
107 + WriteNumberToPipe(pipe, (uint)pid);
108 +
109 + uint ack = ReadNumberFromPipe(pipe);
110 + // This is not true when bundle is run under a debugger
111 + //if (ack != childPid)
112 + //{
113 + // throw new ApplicationException("Incorrect child process.");
114 + //}
115 + }
116 +
117 + private void PumpMessages(NamedPipeServerStream pipe)
118 + {
119 + uint messageId;
120 + while (TryReadNumberFromPipe(pipe, out messageId))
121 + {
122 + uint messageSize = ReadNumberFromPipe(pipe);
123 +
124 + BundleResult result = BundleResult.None;
125 + switch (messageId)
126 + {
127 + case 1: //error
128 + result = ProcessErrorMessage(pipe);
129 + break;
130 +
131 + case 2: // progress
132 + result = ProcessProgressMessage(pipe);
133 + break;
134 +
135 + default: // unknown message, do nothing.
136 + break;
137 + }
138 +
139 + CompleteMessage(pipe, result);
140 + }
141 + }
142 +
143 + private BundleResult ProcessErrorMessage(NamedPipeServerStream pipe)
144 + {
145 + BundleErrorEventArgs e = new BundleErrorEventArgs();
146 + e.Code = (int)ReadNumberFromPipe(pipe);
147 + e.Message = ReadStringFromPipe(pipe);
148 + e.UIHint = (int)ReadNumberFromPipe(pipe);
149 +
150 + this.OnError(e);
151 +
152 + return e.Result;
153 + }
154 +
155 + private BundleResult ProcessProgressMessage(NamedPipeServerStream pipe)
156 + {
157 + ReadNumberFromPipe(pipe); // eat the first progress number because it is always zero.
158 +
159 + BundleProgressEventArgs e = new BundleProgressEventArgs();
160 + e.Progress = (int)ReadNumberFromPipe(pipe);
161 +
162 + this.OnProgress(e);
163 +
164 + return e.Result;
165 + }
166 +
167 + private void CompleteMessage(NamedPipeServerStream pipe, BundleResult result)
168 + {
169 + uint complete = 0xF0000002;
170 + WriteNumberToPipe(pipe, complete);
171 + WriteNumberToPipe(pipe, 4); // size of message data
172 + WriteNumberToPipe(pipe, (uint)result);
173 + }
174 +
175 + private uint ReadNumberFromPipe(NamedPipeServerStream pipe)
176 + {
177 + byte[] buffer = new byte[4];
178 + pipe.Read(buffer, 0, buffer.Length);
179 + return BitConverter.ToUInt32(buffer, 0);
180 + }
181 +
182 + private string ReadStringFromPipe(NamedPipeServerStream pipe)
183 + {
184 + uint length = ReadNumberFromPipe(pipe);
185 +
186 + byte[] buffer = new byte[length * 2];
187 + pipe.Read(buffer, 0, buffer.Length);
188 +
189 + return Encoding.Unicode.GetString(buffer);
190 + }
191 +
192 + private bool TryReadNumberFromPipe(NamedPipeServerStream pipe, out uint value)
193 + {
194 + value = ReadNumberFromPipe(pipe); // reading will not block and return zero if pipe is not connected.
195 + return pipe.IsConnected;
196 + }
197 +
198 + private void WriteNumberToPipe(NamedPipeServerStream pipe, uint value)
199 + {
200 + byte[] buffer = BitConverter.GetBytes(value);
201 + pipe.Write(buffer, 0, buffer.Length);
202 + }
203 +
204 + private void WriteSecretToPipe(NamedPipeServerStream pipe, string secret)
205 + {
206 + byte[] buffer = Encoding.Unicode.GetBytes(secret);
207 +
208 + WriteNumberToPipe(pipe, (uint)buffer.Length);
209 + pipe.Write(buffer, 0, buffer.Length);
210 + }
211 + }
212 +}
src/Samples/runbundle/AssemblyInfo.cs new
+12
@@ -0,0 +1,12 @@
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 +using System;
4 +using System.Reflection;
5 +using System.Runtime.CompilerServices;
6 +using System.Runtime.InteropServices;
7 +
8 +[assembly: AssemblyTitle("Executable to demonstrate Bundle Runner Sample")]
9 +[assembly: AssemblyDescription("")]
10 +[assembly: AssemblyCulture("")]
11 +[assembly: CLSCompliant(true)]
12 +[assembly: ComVisible(false)]
src/Samples/runbundle/Program.cs new
+47
@@ -0,0 +1,47 @@
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 +namespace Wix.Samples
4 +{
5 + using System;
6 + using System.Linq;
7 + using Wix.Samples;
8 +
9 + /// <summary>
10 + /// Example executable that installs then immediately uninstalls a bundle showing progress.
11 + /// </summary>
12 + class Program
13 + {
14 + static int Main(string[] args)
15 + {
16 + if (args.Length == 0)
17 + {
18 + Console.WriteLine("Must provide the path to the bundle to install then uninstall.");
19 + return -1;
20 + }
21 +
22 + BundleRunner runner = new BundleRunner(args[0]);
23 + runner.Error += Program.OnError;
24 + runner.Progress += Program.OnProgress;
25 +
26 + Console.WriteLine("Installing: {0}", runner.Path);
27 + int exitCode = runner.Run(String.Join(" ", args.Skip(1).ToArray()));
28 + if (0 == exitCode)
29 + {
30 + Console.WriteLine("\r\nUninstalling: {0}", runner.Path);
31 + exitCode = runner.Run("-uninstall");
32 + }
33 +
34 + return exitCode;
35 + }
36 +
37 + static void OnError(object sender, BundleErrorEventArgs e)
38 + {
39 + Console.WriteLine("error: {0}, uiHint: {1}, message: {2}", e.Code, e.UIHint, e.Message);
40 + }
41 +
42 + static void OnProgress(object sender, BundleProgressEventArgs e)
43 + {
44 + Console.WriteLine("progresss: {0}%", e.Progress);
45 + }
46 + }
47 +}
src/engine/EngineForApplication.cpp new
+894
@@ -0,0 +1,894 @@
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 +static HRESULT BAEngineGetPackageCount(
6 + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
7 + __in BAENGINE_GETPACKAGECOUNT_ARGS* /*pArgs*/,
8 + __in BAENGINE_GETPACKAGECOUNT_RESULTS* pResults
9 + )
10 +{
11 + HRESULT hr = S_OK;
12 + DWORD* pcPackages = &pResults->cPackages;
13 +
14 + *pcPackages = pContext->pEngineState->packages.cPackages;
15 +
16 + return hr;
17 +}
18 +
19 +static HRESULT BAEngineGetVariableNumeric(
20 + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
21 + __in BAENGINE_GETVARIABLENUMERIC_ARGS* pArgs,
22 + __in BAENGINE_GETVARIABLENUMERIC_RESULTS* pResults
23 + )
24 +{
25 + HRESULT hr = S_OK;
26 + LPCWSTR wzVariable = pArgs->wzVariable;
27 + LONGLONG* pllValue = &pResults->llValue;
28 +
29 + if (wzVariable && *wzVariable)
30 + {
31 + hr = VariableGetNumeric(&pContext->pEngineState->variables, wzVariable, pllValue);
32 + }
33 + else
34 + {
35 + hr = E_INVALIDARG;
36 + }
37 +
38 + return hr;
39 +}
40 +
41 +static HRESULT BAEngineGetVariableString(
42 + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
43 + __in BAENGINE_GETVARIABLESTRING_ARGS* pArgs,
44 + __in BAENGINE_GETVARIABLESTRING_RESULTS* pResults
45 + )
46 +{
47 + HRESULT hr = S_OK;
48 + LPWSTR sczValue = NULL;
49 + size_t cchRemaining = 0;
50 + LPCWSTR wzVariable = pArgs->wzVariable;
51 + LPWSTR wzValue = pResults->wzValue;
52 + DWORD* pcchValue = &pResults->cchValue;
53 +
54 + if (wzVariable && *wzVariable)
55 + {
56 + hr = VariableGetString(&pContext->pEngineState->variables, wzVariable, &sczValue);
57 + if (SUCCEEDED(hr))
58 + {
59 + if (wzValue)
60 + {
61 + hr = ::StringCchCopyExW(wzValue, *pcchValue, sczValue, NULL, &cchRemaining, STRSAFE_FILL_BEHIND_NULL);
62 + if (STRSAFE_E_INSUFFICIENT_BUFFER == hr)
63 + {
64 + hr = E_MOREDATA;
65 +
66 + ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, &cchRemaining);
67 + *pcchValue = cchRemaining + 1;
68 + }
69 + }
70 + else
71 + {
72 + hr = E_MOREDATA;
73 +
74 + ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, &cchRemaining);
75 + *pcchValue = cchRemaining + 1;
76 + }
77 + }
78 + }
79 + else
80 + {
81 + hr = E_INVALIDARG;
82 + }
83 +
84 + StrSecureZeroFreeString(sczValue);
85 + return hr;
86 +}
87 +
88 +static HRESULT BAEngineGetVariableVersion(
89 + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
90 + __in BAENGINE_GETVARIABLEVERSION_ARGS* pArgs,
91 + __in BAENGINE_GETVARIABLEVERSION_RESULTS* pResults
92 + )
93 +{
94 + HRESULT hr = S_OK;
95 + LPCWSTR wzVariable = pArgs->wzVariable;
96 + DWORD64* pqwValue = &pResults->qwValue;
97 +
98 + if (wzVariable && *wzVariable)
99 + {
100 + hr = VariableGetVersion(&pContext->pEngineState->variables, wzVariable, pqwValue);
101 + }
102 + else
103 + {
104 + hr = E_INVALIDARG;
105 + }
106 +
107 + return hr;
108 +}
109 +
110 +static HRESULT BAEngineFormatString(
111 + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
112 + __in BAENGINE_FORMATSTRING_ARGS* pArgs,
113 + __in BAENGINE_FORMATSTRING_RESULTS* pResults
114 + )
115 +{
116 + HRESULT hr = S_OK;
117 + LPWSTR sczValue = NULL;
118 + DWORD cchValue = 0;
119 + LPCWSTR wzIn = pArgs->wzIn;
120 + LPWSTR wzOut = pResults->wzOut;
121 + DWORD* pcchOut = &pResults->cchOut;
122 +
123 + if (wzIn && *wzIn)
124 + {
125 + hr = VariableFormatString(&pContext->pEngineState->variables, wzIn, &sczValue, &cchValue);
126 + if (SUCCEEDED(hr))
127 + {
128 + if (wzOut)
129 + {
130 + hr = ::StringCchCopyExW(wzOut, *pcchOut, sczValue, NULL, NULL, STRSAFE_FILL_BEHIND_NULL);
131 + if (FAILED(hr))
132 + {
133 + *pcchOut = cchValue;
134 + if (STRSAFE_E_INSUFFICIENT_BUFFER == hr)
135 + {
136 + hr = E_MOREDATA;
137 + }
138 + }
139 + }
140 + else
141 + {
142 + hr = E_MOREDATA;
143 + *pcchOut = cchValue;
144 + }
145 + }
146 + }
147 + else
148 + {
149 + hr = E_INVALIDARG;
150 + }
151 +
152 + StrSecureZeroFreeString(sczValue);
153 + return hr;
154 +}
155 +
156 +static HRESULT BAEngineEscapeString(
157 + __in BOOTSTRAPPER_ENGINE_CONTEXT* /*pContext*/,
158 + __in BAENGINE_ESCAPESTRING_ARGS* pArgs,
159 + __in BAENGINE_ESCAPESTRING_RESULTS* pResults
160 + )
161 +{
162 + HRESULT hr = S_OK;
163 + LPWSTR sczValue = NULL;
164 + size_t cchRemaining = 0;
165 + LPCWSTR wzIn = pArgs->wzIn;
166 + LPWSTR wzOut = pResults->wzOut;
167 + DWORD* pcchOut = &pResults->cchOut;
168 +
169 + if (wzIn && *wzIn)
170 + {
171 + hr = VariableEscapeString(wzIn, &sczValue);
172 + if (SUCCEEDED(hr))
173 + {
174 + if (wzOut)
175 + {
176 + hr = ::StringCchCopyExW(wzOut, *pcchOut, sczValue, NULL, &cchRemaining, STRSAFE_FILL_BEHIND_NULL);
177 + if (STRSAFE_E_INSUFFICIENT_BUFFER == hr)
178 + {
179 + hr = E_MOREDATA;
180 + ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, &cchRemaining);
181 + *pcchOut = cchRemaining;
182 + }
183 + }
184 + else
185 + {
186 + ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, &cchRemaining);
187 + *pcchOut = cchRemaining;
188 + }
189 + }
190 + }
191 + else
192 + {
193 + hr = E_INVALIDARG;
194 + }
195 +
196 + StrSecureZeroFreeString(sczValue);
197 + return hr;
198 +}
199 +
200 +static HRESULT BAEngineEvaluateCondition(
201 + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
202 + __in BAENGINE_EVALUATECONDITION_ARGS* pArgs,
203 + __in BAENGINE_EVALUATECONDITION_RESULTS* pResults
204 + )
205 +{
206 + HRESULT hr = S_OK;
207 + LPCWSTR wzCondition = pArgs->wzCondition;
208 + BOOL* pf = &pResults->f;
209 +
210 + if (wzCondition && *wzCondition)
211 + {
212 + hr = ConditionEvaluate(&pContext->pEngineState->variables, wzCondition, pf);
213 + }
214 + else
215 + {
216 + hr = E_INVALIDARG;
217 + }
218 +
219 + return hr;
220 +}
221 +
222 +static HRESULT BAEngineLog(
223 + __in BOOTSTRAPPER_ENGINE_CONTEXT* /*pContext*/,
224 + __in BAENGINE_LOG_ARGS* pArgs,
225 + __in BAENGINE_LOG_RESULTS* /*pResults*/
226 + )
227 +{
228 + HRESULT hr = S_OK;
229 + REPORT_LEVEL rl = REPORT_NONE;
230 + BOOTSTRAPPER_LOG_LEVEL level = pArgs->level;
231 + LPCWSTR wzMessage = pArgs->wzMessage;
232 +
233 + switch (level)
234 + {
235 + case BOOTSTRAPPER_LOG_LEVEL_STANDARD:
236 + rl = REPORT_STANDARD;
237 + break;
238 +
239 + case BOOTSTRAPPER_LOG_LEVEL_VERBOSE:
240 + rl = REPORT_VERBOSE;
241 + break;
242 +
243 + case BOOTSTRAPPER_LOG_LEVEL_DEBUG:
244 + rl = REPORT_DEBUG;
245 + break;
246 +
247 + case BOOTSTRAPPER_LOG_LEVEL_ERROR:
248 + rl = REPORT_ERROR;
249 + break;
250 +
251 + default:
252 + ExitFunction1(hr = E_INVALIDARG);
253 + }
254 +
255 + hr = LogStringLine(rl, "%ls", wzMessage);
256 + ExitOnFailure(hr, "Failed to log BA message.");
257 +
258 +LExit:
259 + return hr;
260 +}
261 +
262 +static HRESULT BAEngineSendEmbeddedError(
263 + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
264 + __in BAENGINE_SENDEMBEDDEDERROR_ARGS* pArgs,
265 + __in BAENGINE_SENDEMBEDDEDERROR_RESULTS* pResults
266 + )
267 +{
268 + HRESULT hr = S_OK;
269 + BYTE* pbData = NULL;
270 + DWORD cbData = 0;
271 + DWORD dwResult = 0;
272 + DWORD dwErrorCode = pArgs->dwErrorCode;
273 + LPCWSTR wzMessage = pArgs->wzMessage;
274 + DWORD dwUIHint = pArgs->dwUIHint;
275 + int* pnResult = &pResults->nResult;
276 +
277 + if (BURN_MODE_EMBEDDED != pContext->pEngineState->mode)
278 + {
279 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_STATE);
280 + ExitOnRootFailure(hr, "BA requested to send embedded message when not in embedded mode.");
281 + }
282 +
283 + hr = BuffWriteNumber(&pbData, &cbData, dwErrorCode);
284 + ExitOnFailure(hr, "Failed to write error code to message buffer.");
285 +
286 + hr = BuffWriteString(&pbData, &cbData, wzMessage ? wzMessage : L"");
287 + ExitOnFailure(hr, "Failed to write message string to message buffer.");
288 +
289 + hr = BuffWriteNumber(&pbData, &cbData, dwUIHint);
290 + ExitOnFailure(hr, "Failed to write UI hint to message buffer.");
291 +
292 + hr = PipeSendMessage(pContext->pEngineState->embeddedConnection.hPipe, BURN_EMBEDDED_MESSAGE_TYPE_ERROR, pbData, cbData, NULL, NULL, &dwResult);
293 + ExitOnFailure(hr, "Failed to send embedded message over pipe.");
294 +
295 + *pnResult = static_cast<int>(dwResult);
296 +
297 +LExit:
298 + ReleaseBuffer(pbData);
299 + return hr;
300 +}
301 +
302 +static HRESULT BAEngineSendEmbeddedProgress(
303 + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
304 + __in BAENGINE_SENDEMBEDDEDPROGRESS_ARGS* pArgs,
305 + __in BAENGINE_SENDEMBEDDEDPROGRESS_RESULTS* pResults
306 + )
307 +{
308 + HRESULT hr = S_OK;
309 + BYTE* pbData = NULL;
310 + DWORD cbData = 0;
311 + DWORD dwResult = 0;
312 + DWORD dwProgressPercentage = pArgs->dwProgressPercentage;
313 + DWORD dwOverallProgressPercentage = pArgs->dwOverallProgressPercentage;
314 + int* pnResult = &pResults->nResult;
315 +
316 + if (BURN_MODE_EMBEDDED != pContext->pEngineState->mode)
317 + {
318 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_STATE);
319 + ExitOnRootFailure(hr, "BA requested to send embedded progress message when not in embedded mode.");
320 + }
321 +
322 + hr = BuffWriteNumber(&pbData, &cbData, dwProgressPercentage);
323 + ExitOnFailure(hr, "Failed to write progress percentage to message buffer.");
324 +
325 + hr = BuffWriteNumber(&pbData, &cbData, dwOverallProgressPercentage);
326 + ExitOnFailure(hr, "Failed to write overall progress percentage to message buffer.");
327 +
328 + hr = PipeSendMessage(pContext->pEngineState->embeddedConnection.hPipe, BURN_EMBEDDED_MESSAGE_TYPE_PROGRESS, pbData, cbData, NULL, NULL, &dwResult);
329 + ExitOnFailure(hr, "Failed to send embedded progress message over pipe.");
330 +
331 + *pnResult = static_cast<int>(dwResult);
332 +
333 +LExit:
334 + ReleaseBuffer(pbData);
335 + return hr;
336 +}
337 +
338 +static HRESULT BAEngineSetUpdate(
339 + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
340 + __in const BAENGINE_SETUPDATE_ARGS* pArgs,
341 + __in BAENGINE_SETUPDATE_RESULTS* /*pResults*/
342 + )
343 +{
344 + HRESULT hr = S_OK;
345 + LPCWSTR sczId = NULL;
346 + LPWSTR sczLocalSource = NULL;
347 + LPWSTR sczCommandline = NULL;
348 + UUID guid = { };
349 + WCHAR wzGuid[39];
350 + RPC_STATUS rs = RPC_S_OK;
351 + LPCWSTR wzLocalSource = pArgs->wzLocalSource;
352 + LPCWSTR wzDownloadSource = pArgs->wzDownloadSource;
353 + DWORD64 qwSize = pArgs->qwSize;
354 + BOOTSTRAPPER_UPDATE_HASH_TYPE hashType = pArgs->hashType;
355 + BYTE* rgbHash = pArgs->rgbHash;
356 + DWORD cbHash = pArgs->cbHash;
357 +
358 + ::EnterCriticalSection(&pContext->pEngineState->csActive);
359 +
360 + if ((!wzLocalSource || !*wzLocalSource) && (!wzDownloadSource || !*wzDownloadSource))
361 + {
362 + UpdateUninitialize(&pContext->pEngineState->update);
363 + }
364 + else if (BOOTSTRAPPER_UPDATE_HASH_TYPE_NONE == hashType && (0 != cbHash || rgbHash))
365 + {
366 + hr = E_INVALIDARG;
367 + }
368 + else if (BOOTSTRAPPER_UPDATE_HASH_TYPE_SHA1 == hashType && (SHA1_HASH_LEN != cbHash || !rgbHash))
369 + {
370 + hr = E_INVALIDARG;
371 + }
372 + else
373 + {
374 + UpdateUninitialize(&pContext->pEngineState->update);
375 +
376 + if (!wzLocalSource || !*wzLocalSource)
377 + {
378 + hr = StrAllocFormatted(&sczLocalSource, L"update\\%ls", pContext->pEngineState->registration.sczExecutableName);
379 + ExitOnFailure(hr, "Failed to default local update source");
380 + }
381 +
382 + hr = CoreRecreateCommandLine(&sczCommandline, BOOTSTRAPPER_ACTION_INSTALL, pContext->pEngineState->command.display, pContext->pEngineState->command.restart, BOOTSTRAPPER_RELATION_NONE, FALSE, pContext->pEngineState->registration.sczActiveParent, pContext->pEngineState->registration.sczAncestors, NULL, pContext->pEngineState->command.wzCommandLine);
383 + ExitOnFailure(hr, "Failed to recreate command-line for update bundle.");
384 +
385 + // Per-user bundles would fail to use the downloaded update bundle, as the existing install would already be cached
386 + // at the registration id's location. Here I am generating a random guid, but in the future it would be nice if the
387 + // feed would provide the ID of the update.
388 + if (!pContext->pEngineState->registration.fPerMachine)
389 + {
390 + rs = ::UuidCreate(&guid);
391 + hr = HRESULT_FROM_RPC(rs);
392 + ExitOnFailure(hr, "Failed to create bundle update guid.");
393 +
394 + if (!::StringFromGUID2(guid, wzGuid, countof(wzGuid)))
395 + {
396 + hr = E_OUTOFMEMORY;
397 + ExitOnRootFailure(hr, "Failed to convert bundle update guid into string.");
398 + }
399 +
400 + sczId = wzGuid;
401 + }
402 + else
403 + {
404 + sczId = pContext->pEngineState->registration.sczId;
405 + }
406 +
407 + hr = PseudoBundleInitialize(FILEMAKEVERSION(rmj, rmm, rup, 0), &pContext->pEngineState->update.package, FALSE, sczId, BOOTSTRAPPER_RELATION_UPDATE, BOOTSTRAPPER_PACKAGE_STATE_ABSENT, pContext->pEngineState->registration.sczExecutableName, sczLocalSource ? sczLocalSource : wzLocalSource, wzDownloadSource, qwSize, TRUE, sczCommandline, NULL, NULL, NULL, rgbHash, cbHash);
408 + ExitOnFailure(hr, "Failed to set update bundle.");
409 +
410 + pContext->pEngineState->update.fUpdateAvailable = TRUE;
411 + }
412 +
413 +LExit:
414 + ::LeaveCriticalSection(&pContext->pEngineState->csActive);
415 +
416 + ReleaseStr(sczCommandline);
417 + ReleaseStr(sczLocalSource);
418 + return hr;
419 +}
420 +
421 +static HRESULT BAEngineSetLocalSource(
422 + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
423 + __in BAENGINE_SETLOCALSOURCE_ARGS* pArgs,
424 + __in BAENGINE_SETLOCALSOURCE_RESULTS* /*pResults*/
425 + )
426 +{
427 + HRESULT hr = S_OK;
428 + BURN_CONTAINER* pContainer = NULL;
429 + BURN_PAYLOAD* pPayload = NULL;
430 + LPCWSTR wzPackageOrContainerId = pArgs->wzPackageOrContainerId;
431 + LPCWSTR wzPayloadId = pArgs->wzPayloadId;
432 + LPCWSTR wzPath = pArgs->wzPath;
433 +
434 + ::EnterCriticalSection(&pContext->pEngineState->csActive);
435 + hr = UserExperienceEnsureEngineInactive(&pContext->pEngineState->userExperience);
436 + ExitOnFailure(hr, "Engine is active, cannot change engine state.");
437 +
438 + if (!wzPath || !*wzPath)
439 + {
440 + hr = E_INVALIDARG;
441 + }
442 + else if (wzPayloadId && * wzPayloadId)
443 + {
444 + hr = PayloadFindById(&pContext->pEngineState->payloads, wzPayloadId, &pPayload);
445 + ExitOnFailure(hr, "BA requested unknown payload with id: %ls", wzPayloadId);
446 +
447 + if (BURN_PAYLOAD_PACKAGING_EMBEDDED == pPayload->packaging)
448 + {
449 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_OPERATION);
450 + ExitOnFailure(hr, "BA denied while trying to set source on embedded payload: %ls", wzPayloadId);
451 + }
452 +
453 + hr = StrAllocString(&pPayload->sczSourcePath, wzPath, 0);
454 + ExitOnFailure(hr, "Failed to set source path for payload.");
455 + }
456 + else if (wzPackageOrContainerId && *wzPackageOrContainerId)
457 + {
458 + hr = ContainerFindById(&pContext->pEngineState->containers, wzPackageOrContainerId, &pContainer);
459 + ExitOnFailure(hr, "BA requested unknown container with id: %ls", wzPackageOrContainerId);
460 +
461 + hr = StrAllocString(&pContainer->sczSourcePath, wzPath, 0);
462 + ExitOnFailure(hr, "Failed to set source path for container.");
463 + }
464 + else
465 + {
466 + hr = E_INVALIDARG;
467 + }
468 +
469 +LExit:
470 + ::LeaveCriticalSection(&pContext->pEngineState->csActive);
471 + return hr;
472 +}
473 +
474 +static HRESULT BAEngineSetDownloadSource(
475 + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
476 + __in BAENGINE_SETDOWNLOADSOURCE_ARGS* pArgs,
477 + __in BAENGINE_SETDOWNLOADSOURCE_RESULTS* /*pResults*/
478 + )
479 +{
480 + HRESULT hr = S_OK;
481 + BURN_CONTAINER* pContainer = NULL;
482 + BURN_PAYLOAD* pPayload = NULL;
483 + DOWNLOAD_SOURCE* pDownloadSource = NULL;
484 + LPCWSTR wzPackageOrContainerId = pArgs->wzPackageOrContainerId;
485 + LPCWSTR wzPayloadId = pArgs->wzPayloadId;
486 + LPCWSTR wzUrl = pArgs->wzUrl;
487 + LPCWSTR wzUser = pArgs->wzUser;
488 + LPCWSTR wzPassword = pArgs->wzPassword;
489 +
490 + ::EnterCriticalSection(&pContext->pEngineState->csActive);
491 + hr = UserExperienceEnsureEngineInactive(&pContext->pEngineState->userExperience);
492 + ExitOnFailure(hr, "Engine is active, cannot change engine state.");
493 +
494 + if (wzPayloadId && *wzPayloadId)
495 + {
496 + hr = PayloadFindById(&pContext->pEngineState->payloads, wzPayloadId, &pPayload);
497 + ExitOnFailure(hr, "BA requested unknown payload with id: %ls", wzPayloadId);
498 +
499 + if (BURN_PAYLOAD_PACKAGING_EMBEDDED == pPayload->packaging)
500 + {
501 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_OPERATION);
502 + ExitOnFailure(hr, "BA denied while trying to set download URL on embedded payload: %ls", wzPayloadId);
503 + }
504 +
505 + pDownloadSource = &pPayload->downloadSource;
506 + }
507 + else if (wzPackageOrContainerId && *wzPackageOrContainerId)
508 + {
509 + hr = ContainerFindById(&pContext->pEngineState->containers, wzPackageOrContainerId, &pContainer);
510 + ExitOnFailure(hr, "BA requested unknown container with id: %ls", wzPackageOrContainerId);
511 +
512 + pDownloadSource = &pContainer->downloadSource;
513 + }
514 + else
515 + {
516 + hr = E_INVALIDARG;
517 + ExitOnFailure(hr, "BA did not provide container or payload id.");
518 + }
519 +
520 + if (wzUrl && *wzUrl)
521 + {
522 + hr = StrAllocString(&pDownloadSource->sczUrl, wzUrl, 0);
523 + ExitOnFailure(hr, "Failed to set download URL.");
524 +
525 + if (wzUser && *wzUser)
526 + {
527 + hr = StrAllocString(&pDownloadSource->sczUser, wzUser, 0);
528 + ExitOnFailure(hr, "Failed to set download user.");
529 +
530 + if (wzPassword && *wzPassword)
531 + {
532 + hr = StrAllocString(&pDownloadSource->sczPassword, wzPassword, 0);
533 + ExitOnFailure(hr, "Failed to set download password.");
534 + }
535 + else // no password.
536 + {
537 + ReleaseNullStr(pDownloadSource->sczPassword);
538 + }
539 + }
540 + else // no user means no password either.
541 + {
542 + ReleaseNullStr(pDownloadSource->sczUser);
543 + ReleaseNullStr(pDownloadSource->sczPassword);
544 + }
545 + }
546 + else // no URL provided means clear out the whole download source.
547 + {
548 + ReleaseNullStr(pDownloadSource->sczUrl);
549 + ReleaseNullStr(pDownloadSource->sczUser);
550 + ReleaseNullStr(pDownloadSource->sczPassword);
551 + }
552 +
553 +LExit:
554 + ::LeaveCriticalSection(&pContext->pEngineState->csActive);
555 + return hr;
556 +}
557 +
558 +static HRESULT BAEngineSetVariableNumeric(
559 + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
560 + __in const BAENGINE_SETVARIABLENUMERIC_ARGS* pArgs,
561 + __in BAENGINE_SETVARIABLENUMERIC_RESULTS* /*pResults*/
562 + )
563 +{
564 + HRESULT hr = S_OK;
565 + LPCWSTR wzVariable = pArgs->wzVariable;
566 + LONGLONG llValue = pArgs->llValue;
567 +
568 + if (wzVariable && *wzVariable)
569 + {
570 + hr = VariableSetNumeric(&pContext->pEngineState->variables, wzVariable, llValue, FALSE);
571 + ExitOnFailure(hr, "Failed to set numeric variable.");
572 + }
573 + else
574 + {
575 + hr = E_INVALIDARG;
576 + ExitOnFailure(hr, "BA did not provide variable name.");
577 + }
578 +
579 +LExit:
580 + return hr;
581 +}
582 +
583 +static HRESULT BAEngineSetVariableString(
584 + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
585 + __in const BAENGINE_SETVARIABLESTRING_ARGS* pArgs,
586 + __in BAENGINE_SETVARIABLESTRING_RESULTS* /*pResults*/
587 + )
588 +{
589 + HRESULT hr = S_OK;
590 + LPCWSTR wzVariable = pArgs->wzVariable;
591 + LPCWSTR wzValue = pArgs->wzValue;
592 +
593 + if (wzVariable && *wzVariable)
594 + {
595 + hr = VariableSetString(&pContext->pEngineState->variables, wzVariable, wzValue, FALSE);
596 + ExitOnFailure(hr, "Failed to set numeric variable.");
597 + }
598 + else
599 + {
600 + hr = E_INVALIDARG;
601 + ExitOnFailure(hr, "BA did not provide variable name.");
602 + }
603 +
604 +LExit:
605 + return hr;
606 +}
607 +
608 +static HRESULT BAEngineSetVariableVersion(
609 + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
610 + __in const BAENGINE_SETVARIABLEVERSION_ARGS* pArgs,
611 + __in BAENGINE_SETVARIABLEVERSION_RESULTS* /*pResults*/
612 + )
613 +{
614 + HRESULT hr = S_OK;
615 + LPCWSTR wzVariable = pArgs->wzVariable;
616 + DWORD64 qwValue = pArgs->qwValue;
617 +
618 + if (wzVariable && *wzVariable)
619 + {
620 + hr = VariableSetVersion(&pContext->pEngineState->variables, wzVariable, qwValue, FALSE);
621 + ExitOnFailure(hr, "Failed to set version variable.");
622 + }
623 + else
624 + {
625 + hr = E_INVALIDARG;
626 + ExitOnFailure(hr, "BA did not provide variable name.");
627 + }
628 +
629 +LExit:
630 + return hr;
631 +}
632 +
633 +static HRESULT BAEngineCloseSplashScreen(
634 + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
635 + __in const BAENGINE_CLOSESPLASHSCREEN_ARGS* /*pArgs*/,
636 + __in BAENGINE_CLOSESPLASHSCREEN_RESULTS* /*pResults*/
637 + )
638 +{
639 + // If the splash screen is still around, close it.
640 + if (::IsWindow(pContext->pEngineState->command.hwndSplashScreen))
641 + {
642 + ::PostMessageW(pContext->pEngineState->command.hwndSplashScreen, WM_CLOSE, 0, 0);
643 + }
644 +
645 + return S_OK;
646 +}
647 +
648 +static HRESULT BAEngineDetect(
649 + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
650 + __in BAENGINE_DETECT_ARGS* pArgs,
651 + __in BAENGINE_DETECT_RESULTS* /*pResults*/
652 + )
653 +{
654 + HRESULT hr = S_OK;
655 +
656 + if (!::PostThreadMessageW(pContext->dwThreadId, WM_BURN_DETECT, 0, reinterpret_cast<LPARAM>(pArgs->hwndParent)))
657 + {
658 + ExitWithLastError(hr, "Failed to post detect message.");
659 + }
660 +
661 +LExit:
662 + return hr;
663 +}
664 +
665 +static HRESULT BAEnginePlan(
666 + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
667 + __in const BAENGINE_PLAN_ARGS* pArgs,
668 + __in BAENGINE_PLAN_RESULTS* /*pResults*/
669 + )
670 +{
671 + HRESULT hr = S_OK;
672 + BOOTSTRAPPER_ACTION action = pArgs->action;
673 +
674 + if (!::PostThreadMessageW(pContext->dwThreadId, WM_BURN_PLAN, 0, action))
675 + {
676 + ExitWithLastError(hr, "Failed to post plan message.");
677 + }
678 +
679 +LExit:
680 + return hr;
681 +}
682 +
683 +static HRESULT BAEngineElevate(
684 + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
685 + __in const BAENGINE_ELEVATE_ARGS* pArgs,
686 + __in BAENGINE_ELEVATE_RESULTS* /*pResults*/
687 + )
688 +{
689 + HRESULT hr = S_OK;
690 +
691 + if (INVALID_HANDLE_VALUE != pContext->pEngineState->companionConnection.hPipe)
692 + {
693 + hr = HRESULT_FROM_WIN32(ERROR_ALREADY_INITIALIZED);
694 + }
695 + else if (!::PostThreadMessageW(pContext->dwThreadId, WM_BURN_ELEVATE, 0, reinterpret_cast<LPARAM>(pArgs->hwndParent)))
696 + {
697 + ExitWithLastError(hr, "Failed to post elevate message.");
698 + }
699 +
700 +LExit:
701 + return hr;
702 +}
703 +
704 +static HRESULT BAEngineApply(
705 + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
706 + __in const BAENGINE_APPLY_ARGS* pArgs,
707 + __in BAENGINE_APPLY_RESULTS* /*pResults*/
708 + )
709 +{
710 + HRESULT hr = S_OK;
711 +
712 + if (!::PostThreadMessageW(pContext->dwThreadId, WM_BURN_APPLY, 0, reinterpret_cast<LPARAM>(pArgs->hwndParent)))
713 + {
714 + ExitWithLastError(hr, "Failed to post apply message.");
715 + }
716 +
717 +LExit:
718 + return hr;
719 +}
720 +
721 +static HRESULT BAEngineQuit(
722 + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
723 + __in const BAENGINE_QUIT_ARGS* pArgs,
724 + __in BAENGINE_QUIT_RESULTS* /*pResults*/
725 + )
726 +{
727 + HRESULT hr = S_OK;
728 +
729 + if (!::PostThreadMessageW(pContext->dwThreadId, WM_BURN_QUIT, static_cast<WPARAM>(pArgs->dwExitCode), 0))
730 + {
731 + ExitWithLastError(hr, "Failed to post shutdown message.");
732 + }
733 +
734 +LExit:
735 + return hr;
736 +}
737 +
738 +static HRESULT BAEngineLaunchApprovedExe(
739 + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
740 + __in const BAENGINE_LAUNCHAPPROVEDEXE_ARGS* pArgs,
741 + __in BAENGINE_LAUNCHAPPROVEDEXE_RESULTS* /*pResults*/
742 + )
743 +{
744 + HRESULT hr = S_OK;
745 + BURN_APPROVED_EXE* pApprovedExe = NULL;
746 + BOOL fLeaveCriticalSection = FALSE;
747 + BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe = (BURN_LAUNCH_APPROVED_EXE*)MemAlloc(sizeof(BURN_LAUNCH_APPROVED_EXE), TRUE);
748 + HWND hwndParent = pArgs->hwndParent;
749 + LPCWSTR wzApprovedExeForElevationId = pArgs->wzApprovedExeForElevationId;
750 + LPCWSTR wzArguments = pArgs->wzArguments;
751 + DWORD dwWaitForInputIdleTimeout = pArgs->dwWaitForInputIdleTimeout;
752 +
753 + ::EnterCriticalSection(&pContext->pEngineState->csActive);
754 + fLeaveCriticalSection = TRUE;
755 + hr = UserExperienceEnsureEngineInactive(&pContext->pEngineState->userExperience);
756 + ExitOnFailure(hr, "Engine is active, cannot change engine state.");
757 +
758 + if (!wzApprovedExeForElevationId || !*wzApprovedExeForElevationId)
759 + {
760 + ExitFunction1(hr = E_INVALIDARG);
761 + }
762 +
763 + hr = ApprovedExesFindById(&pContext->pEngineState->approvedExes, wzApprovedExeForElevationId, &pApprovedExe);
764 + ExitOnFailure(hr, "BA requested unknown approved exe with id: %ls", wzApprovedExeForElevationId);
765 +
766 + ::LeaveCriticalSection(&pContext->pEngineState->csActive);
767 + fLeaveCriticalSection = FALSE;
768 +
769 + hr = StrAllocString(&pLaunchApprovedExe->sczId, wzApprovedExeForElevationId, NULL);
770 + ExitOnFailure(hr, "Failed to copy the id.");
771 +
772 + if (wzArguments)
773 + {
774 + hr = StrAllocString(&pLaunchApprovedExe->sczArguments, wzArguments, NULL);
775 + ExitOnFailure(hr, "Failed to copy the arguments.");
776 + }
777 +
778 + pLaunchApprovedExe->dwWaitForInputIdleTimeout = dwWaitForInputIdleTimeout;
779 +
780 + pLaunchApprovedExe->hwndParent = hwndParent;
781 +
782 + if (!::PostThreadMessageW(pContext->dwThreadId, WM_BURN_LAUNCH_APPROVED_EXE, 0, reinterpret_cast<LPARAM>(pLaunchApprovedExe)))
783 + {
784 + ExitWithLastError(hr, "Failed to post launch approved exe message.");
785 + }
786 +
787 +LExit:
788 + if (fLeaveCriticalSection)
789 + {
790 + ::LeaveCriticalSection(&pContext->pEngineState->csActive);
791 + }
792 +
793 + if (FAILED(hr))
794 + {
795 + ApprovedExesUninitializeLaunch(pLaunchApprovedExe);
796 + }
797 +
798 + return hr;
799 +}
800 +
801 +HRESULT WINAPI EngineForApplicationProc(
802 + __in BOOTSTRAPPER_ENGINE_MESSAGE message,
803 + __in const LPVOID pvArgs,
804 + __inout LPVOID pvResults,
805 + __in_opt LPVOID pvContext
806 + )
807 +{
808 + HRESULT hr = S_OK;
809 + BOOTSTRAPPER_ENGINE_CONTEXT* pContext = reinterpret_cast<BOOTSTRAPPER_ENGINE_CONTEXT*>(pvContext);
810 +
811 + if (!pContext || !pvArgs || !pvResults)
812 + {
813 + ExitFunction1(hr = E_INVALIDARG);
814 + }
815 +
816 + switch (message)
817 + {
818 + case BOOTSTRAPPER_ENGINE_MESSAGE_GETPACKAGECOUNT:
819 + hr = BAEngineGetPackageCount(pContext, reinterpret_cast<BAENGINE_GETPACKAGECOUNT_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_GETPACKAGECOUNT_RESULTS*>(pvResults));
820 + break;
821 + case BOOTSTRAPPER_ENGINE_MESSAGE_GETVARIABLENUMERIC:
822 + hr = BAEngineGetVariableNumeric(pContext, reinterpret_cast<BAENGINE_GETVARIABLENUMERIC_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_GETVARIABLENUMERIC_RESULTS*>(pvResults));
823 + break;
824 + case BOOTSTRAPPER_ENGINE_MESSAGE_GETVARIABLESTRING:
825 + hr = BAEngineGetVariableString(pContext, reinterpret_cast<BAENGINE_GETVARIABLESTRING_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_GETVARIABLESTRING_RESULTS*>(pvResults));
826 + break;
827 + case BOOTSTRAPPER_ENGINE_MESSAGE_GETVARIABLEVERSION:
828 + hr = BAEngineGetVariableVersion(pContext, reinterpret_cast<BAENGINE_GETVARIABLEVERSION_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_GETVARIABLEVERSION_RESULTS*>(pvResults));
829 + break;
830 + case BOOTSTRAPPER_ENGINE_MESSAGE_FORMATSTRING:
831 + hr = BAEngineFormatString(pContext, reinterpret_cast<BAENGINE_FORMATSTRING_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_FORMATSTRING_RESULTS*>(pvResults));
832 + break;
833 + case BOOTSTRAPPER_ENGINE_MESSAGE_ESCAPESTRING:
834 + hr = BAEngineEscapeString(pContext, reinterpret_cast<BAENGINE_ESCAPESTRING_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_ESCAPESTRING_RESULTS*>(pvResults));
835 + break;
836 + case BOOTSTRAPPER_ENGINE_MESSAGE_EVALUATECONDITION:
837 + hr = BAEngineEvaluateCondition(pContext, reinterpret_cast<BAENGINE_EVALUATECONDITION_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_EVALUATECONDITION_RESULTS*>(pvResults));
838 + break;
839 + case BOOTSTRAPPER_ENGINE_MESSAGE_LOG:
840 + hr = BAEngineLog(pContext, reinterpret_cast<BAENGINE_LOG_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_LOG_RESULTS*>(pvResults));
841 + break;
842 + case BOOTSTRAPPER_ENGINE_MESSAGE_SENDEMBEDDEDERROR:
843 + hr = BAEngineSendEmbeddedError(pContext, reinterpret_cast<BAENGINE_SENDEMBEDDEDERROR_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_SENDEMBEDDEDERROR_RESULTS*>(pvResults));
844 + break;
845 + case BOOTSTRAPPER_ENGINE_MESSAGE_SENDEMBEDDEDPROGRESS:
846 + hr = BAEngineSendEmbeddedProgress(pContext, reinterpret_cast<BAENGINE_SENDEMBEDDEDPROGRESS_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_SENDEMBEDDEDPROGRESS_RESULTS*>(pvResults));
847 + break;
848 + case BOOTSTRAPPER_ENGINE_MESSAGE_SETUPDATE:
849 + hr = BAEngineSetUpdate(pContext, reinterpret_cast<BAENGINE_SETUPDATE_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_SETUPDATE_RESULTS*>(pvResults));
850 + break;
851 + case BOOTSTRAPPER_ENGINE_MESSAGE_SETLOCALSOURCE:
852 + hr = BAEngineSetLocalSource(pContext, reinterpret_cast<BAENGINE_SETLOCALSOURCE_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_SETLOCALSOURCE_RESULTS*>(pvResults));
853 + break;
854 + case BOOTSTRAPPER_ENGINE_MESSAGE_SETDOWNLOADSOURCE:
855 + hr = BAEngineSetDownloadSource(pContext, reinterpret_cast<BAENGINE_SETDOWNLOADSOURCE_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_SETDOWNLOADSOURCE_RESULTS*>(pvResults));
856 + break;
857 + case BOOTSTRAPPER_ENGINE_MESSAGE_SETVARIABLENUMERIC:
858 + hr = BAEngineSetVariableNumeric(pContext, reinterpret_cast<BAENGINE_SETVARIABLENUMERIC_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_SETVARIABLENUMERIC_RESULTS*>(pvResults));
859 + break;
860 + case BOOTSTRAPPER_ENGINE_MESSAGE_SETVARIABLESTRING:
861 + hr = BAEngineSetVariableString(pContext, reinterpret_cast<BAENGINE_SETVARIABLESTRING_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_SETVARIABLESTRING_RESULTS*>(pvResults));
862 + break;
863 + case BOOTSTRAPPER_ENGINE_MESSAGE_SETVARIABLEVERSION:
864 + hr = BAEngineSetVariableVersion(pContext, reinterpret_cast<BAENGINE_SETVARIABLEVERSION_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_SETVARIABLEVERSION_RESULTS*>(pvResults));
865 + break;
866 + case BOOTSTRAPPER_ENGINE_MESSAGE_CLOSESPLASHSCREEN:
867 + hr = BAEngineCloseSplashScreen(pContext, reinterpret_cast<BAENGINE_CLOSESPLASHSCREEN_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_CLOSESPLASHSCREEN_RESULTS*>(pvResults));
868 + break;
869 + case BOOTSTRAPPER_ENGINE_MESSAGE_DETECT:
870 + hr = BAEngineDetect(pContext, reinterpret_cast<BAENGINE_DETECT_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_DETECT_RESULTS*>(pvResults));
871 + break;
872 + case BOOTSTRAPPER_ENGINE_MESSAGE_PLAN:
873 + hr = BAEnginePlan(pContext, reinterpret_cast<BAENGINE_PLAN_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_PLAN_RESULTS*>(pvResults));
874 + break;
875 + case BOOTSTRAPPER_ENGINE_MESSAGE_ELEVATE:
876 + hr = BAEngineElevate(pContext, reinterpret_cast<BAENGINE_ELEVATE_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_ELEVATE_RESULTS*>(pvResults));
877 + break;
878 + case BOOTSTRAPPER_ENGINE_MESSAGE_APPLY:
879 + hr = BAEngineApply(pContext, reinterpret_cast<BAENGINE_APPLY_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_APPLY_RESULTS*>(pvResults));
880 + break;
881 + case BOOTSTRAPPER_ENGINE_MESSAGE_QUIT:
882 + hr = BAEngineQuit(pContext, reinterpret_cast<BAENGINE_QUIT_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_QUIT_RESULTS*>(pvResults));
883 + break;
884 + case BOOTSTRAPPER_ENGINE_MESSAGE_LAUNCHAPPROVEDEXE:
885 + hr = BAEngineLaunchApprovedExe(pContext, reinterpret_cast<BAENGINE_LAUNCHAPPROVEDEXE_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_LAUNCHAPPROVEDEXE_RESULTS*>(pvResults));
886 + break;
887 + default:
888 + hr = E_NOTIMPL;
889 + break;
890 + }
891 +
892 +LExit:
893 + return hr;
894 +}
src/engine/EngineForApplication.h new
+44
@@ -0,0 +1,44 @@
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 +#if defined(__cplusplus)
6 +extern "C" {
7 +#endif
8 +
9 +// constants
10 +
11 +enum WM_BURN
12 +{
13 + WM_BURN_FIRST = WM_APP + 0xFFF, // this enum value must always be first.
14 +
15 + WM_BURN_DETECT,
16 + WM_BURN_PLAN,
17 + WM_BURN_ELEVATE,
18 + WM_BURN_APPLY,
19 + WM_BURN_LAUNCH_APPROVED_EXE,
20 + WM_BURN_QUIT,
21 +
22 + WM_BURN_LAST, // this enum value must always be last.
23 +};
24 +
25 +// structs
26 +
27 +struct BOOTSTRAPPER_ENGINE_CONTEXT
28 +{
29 + BURN_ENGINE_STATE* pEngineState;
30 + DWORD dwThreadId;
31 +};
32 +
33 +// function declarations
34 +
35 +HRESULT WINAPI EngineForApplicationProc(
36 + __in BOOTSTRAPPER_ENGINE_MESSAGE message,
37 + __in const LPVOID pvArgs,
38 + __inout LPVOID pvResults,
39 + __in_opt LPVOID pvContext
40 + );
41 +
42 +#if defined(__cplusplus)
43 +}
44 +#endif
src/engine/apply.cpp new
+2516
@@ -0,0 +1,2516 @@
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 DWORD BURN_CACHE_MAX_RECOMMENDED_VERIFY_TRYAGAIN_ATTEMPTS = 2;
7 +
8 +// structs
9 +
10 +struct BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT
11 +{
12 + BURN_USER_EXPERIENCE* pUX;
13 + BURN_CONTAINER* pContainer;
14 + BURN_PACKAGE* pPackage;
15 + BURN_PAYLOAD* pPayload;
16 + DWORD64 qwCacheProgress;
17 + DWORD64 qwTotalCacheSize;
18 +
19 + BOOL fCancel;
20 + BOOL fError;
21 +};
22 +
23 +typedef struct _BURN_EXECUTE_CONTEXT
24 +{
25 + BURN_USER_EXPERIENCE* pUX;
26 + BOOL fRollback;
27 + BURN_PACKAGE* pExecutingPackage;
28 + DWORD cExecutedPackages;
29 + DWORD cExecutePackagesTotal;
30 + DWORD* pcOverallProgressTicks;
31 +} BURN_EXECUTE_CONTEXT;
32 +
33 +
34 +// internal function declarations
35 +static HRESULT WINAPI AuthenticationRequired(
36 + __in LPVOID pData,
37 + __in HINTERNET hUrl,
38 + __in long lHttpCode,
39 + __out BOOL* pfRetrySend,
40 + __out BOOL* pfRetry
41 + );
42 +
43 +static HRESULT ExecuteDependentRegistrationActions(
44 + __in HANDLE hPipe,
45 + __in const BURN_REGISTRATION* pRegistration,
46 + __in_ecount(cActions) const BURN_DEPENDENT_REGISTRATION_ACTION* rgActions,
47 + __in DWORD cActions
48 + );
49 +static HRESULT ExtractContainer(
50 + __in HANDLE hSourceEngineFile,
51 + __in BURN_CONTAINER* pContainer,
52 + __in_z LPCWSTR wzContainerPath,
53 + __in_ecount(cExtractPayloads) BURN_EXTRACT_PAYLOAD* rgExtractPayloads,
54 + __in DWORD cExtractPayloads
55 + );
56 +static void UpdateCacheSuccessProgress(
57 + __in BURN_PLAN* pPlan,
58 + __in BURN_CACHE_ACTION* pCacheAction,
59 + __inout DWORD64* pqwSuccessfulCachedProgress
60 + );
61 +static HRESULT LayoutBundle(
62 + __in BURN_USER_EXPERIENCE* pUX,
63 + __in BURN_VARIABLES* pVariables,
64 + __in HANDLE hPipe,
65 + __in_z LPCWSTR wzExecutableName,
66 + __in_z LPCWSTR wzLayoutDirectory,
67 + __in_z LPCWSTR wzUnverifiedPath,
68 + __in DWORD64 qwSuccessfulCacheProgress,
69 + __in DWORD64 qwTotalCacheSize
70 + );
71 +static HRESULT AcquireContainerOrPayload(
72 + __in BURN_USER_EXPERIENCE* pUX,
73 + __in BURN_VARIABLES* pVariables,
74 + __in_opt BURN_CONTAINER* pContainer,
75 + __in_opt BURN_PACKAGE* pPackage,
76 + __in_opt BURN_PAYLOAD* pPayload,
77 + __in LPCWSTR wzDestinationPath,
78 + __in DWORD64 qwSuccessfulCacheProgress,
79 + __in DWORD64 qwTotalCacheSize
80 + );
81 +static HRESULT LayoutOrCacheContainerOrPayload(
82 + __in BURN_USER_EXPERIENCE* pUX,
83 + __in HANDLE hPipe,
84 + __in_opt BURN_CONTAINER* pContainer,
85 + __in_opt BURN_PACKAGE* pPackage,
86 + __in_opt BURN_PAYLOAD* pPayload,
87 + __in BOOL fAlreadyProvidedProgress,
88 + __in DWORD64 qwSuccessfullyCacheProgress,
89 + __in DWORD64 qwTotalCacheSize,
90 + __in_z_opt LPCWSTR wzLayoutDirectory,
91 + __in_z LPCWSTR wzUnverifiedPath,
92 + __in BOOL fMove,
93 + __in DWORD cTryAgainAttempts,
94 + __out BOOL* pfRetry
95 + );
96 +static HRESULT PromptForSource(
97 + __in BURN_USER_EXPERIENCE* pUX,
98 + __in_z LPCWSTR wzPackageOrContainerId,
99 + __in_z_opt LPCWSTR wzPayloadId,
100 + __in_z LPCWSTR wzLocalSource,
101 + __in_z_opt LPCWSTR wzDownloadSource,
102 + __out BOOL* pfRetry,
103 + __out BOOL* pfDownload
104 + );
105 +static HRESULT CopyPayload(
106 + __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress,
107 + __in_z LPCWSTR wzSourcePath,
108 + __in_z LPCWSTR wzDestinationPath
109 + );
110 +static HRESULT DownloadPayload(
111 + __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress,
112 + __in_z LPCWSTR wzDestinationPath
113 + );
114 +static DWORD CALLBACK CacheProgressRoutine(
115 + __in LARGE_INTEGER TotalFileSize,
116 + __in LARGE_INTEGER TotalBytesTransferred,
117 + __in LARGE_INTEGER StreamSize,
118 + __in LARGE_INTEGER StreamBytesTransferred,
119 + __in DWORD dwStreamNumber,
120 + __in DWORD dwCallbackReason,
121 + __in HANDLE hSourceFile,
122 + __in HANDLE hDestinationFile,
123 + __in_opt LPVOID lpData
124 + );
125 +static void DoRollbackCache(
126 + __in BURN_USER_EXPERIENCE* pUX,
127 + __in BURN_PLAN* pPlan,
128 + __in HANDLE hPipe,
129 + __in DWORD dwCheckpoint
130 + );
131 +static HRESULT DoExecuteAction(
132 + __in BURN_ENGINE_STATE* pEngineState,
133 + __in BURN_EXECUTE_ACTION* pExecuteAction,
134 + __in_opt HANDLE hCacheThread,
135 + __in BURN_EXECUTE_CONTEXT* pContext,
136 + __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary,
137 + __out DWORD* pdwCheckpoint,
138 + __out BOOL* pfKeepRegistration,
139 + __out BOOL* pfSuspend,
140 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart
141 + );
142 +static HRESULT DoRollbackActions(
143 + __in BURN_ENGINE_STATE* pEngineState,
144 + __in BURN_EXECUTE_CONTEXT* pContext,
145 + __in DWORD dwCheckpoint,
146 + __in BOOL fInTransaction,
147 + __out BOOL* pfKeepRegistration,
148 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart
149 + );
150 +static HRESULT ExecuteExePackage(
151 + __in BURN_ENGINE_STATE* pEngineState,
152 + __in BURN_EXECUTE_ACTION* pExecuteAction,
153 + __in BURN_EXECUTE_CONTEXT* pContext,
154 + __in BOOL fRollback,
155 + __out BOOL* pfRetry,
156 + __out BOOL* pfSuspend,
157 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart
158 + );
159 +static HRESULT ExecuteMsiPackage(
160 + __in BURN_ENGINE_STATE* pEngineState,
161 + __in BURN_EXECUTE_ACTION* pExecuteAction,
162 + __in BURN_EXECUTE_CONTEXT* pContext,
163 + __in BOOL fRollback,
164 + __out BOOL* pfRetry,
165 + __out BOOL* pfSuspend,
166 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart
167 + );
168 +static HRESULT ExecuteMspPackage(
169 + __in BURN_ENGINE_STATE* pEngineState,
170 + __in BURN_EXECUTE_ACTION* pExecuteAction,
171 + __in BURN_EXECUTE_CONTEXT* pContext,
172 + __in BOOL fRollback,
173 + __out BOOL* pfRetry,
174 + __out BOOL* pfSuspend,
175 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart
176 + );
177 +static HRESULT ExecuteMsuPackage(
178 + __in BURN_ENGINE_STATE* pEngineState,
179 + __in BURN_EXECUTE_ACTION* pExecuteAction,
180 + __in BURN_EXECUTE_CONTEXT* pContext,
181 + __in BOOL fRollback,
182 + __in BOOL fStopWusaService,
183 + __out BOOL* pfRetry,
184 + __out BOOL* pfSuspend,
185 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart
186 + );
187 +static HRESULT ExecutePackageProviderAction(
188 + __in BURN_ENGINE_STATE* pEngineState,
189 + __in BURN_EXECUTE_ACTION* pAction,
190 + __in BURN_EXECUTE_CONTEXT* pContext
191 + );
192 +static HRESULT ExecuteDependencyAction(
193 + __in BURN_ENGINE_STATE* pEngineState,
194 + __in BURN_EXECUTE_ACTION* pAction,
195 + __in BURN_EXECUTE_CONTEXT* pContext
196 + );
197 +static HRESULT ExecuteCompatiblePackageAction(
198 + __in BURN_ENGINE_STATE* pEngineState,
199 + __in BURN_EXECUTE_ACTION* pAction
200 + );
201 +static HRESULT CleanPackage(
202 + __in HANDLE hElevatedPipe,
203 + __in BURN_PACKAGE* pPackage
204 + );
205 +static int GenericExecuteMessageHandler(
206 + __in GENERIC_EXECUTE_MESSAGE* pMessage,
207 + __in LPVOID pvContext
208 + );
209 +static int MsiExecuteMessageHandler(
210 + __in WIU_MSI_EXECUTE_MESSAGE* pMessage,
211 + __in_opt LPVOID pvContext
212 + );
213 +static HRESULT ReportOverallProgressTicks(
214 + __in BURN_USER_EXPERIENCE* pUX,
215 + __in BOOL fRollback,
216 + __in DWORD cOverallProgressTicksTotal,
217 + __in DWORD cOverallProgressTicks
218 + );
219 +static HRESULT ExecutePackageComplete(
220 + __in BURN_USER_EXPERIENCE* pUX,
221 + __in BURN_VARIABLES* pVariables,
222 + __in BURN_PACKAGE* pPackage,
223 + __in HRESULT hrOverall,
224 + __in HRESULT hrExecute,
225 + __in BOOL fRollback,
226 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart,
227 + __out BOOL* pfRetry,
228 + __out BOOL* pfSuspend
229 + );
230 +
231 +static HRESULT DoMsiBeginTransaction(
232 + __in BURN_EXECUTE_CONTEXT *context
233 + , __in BURN_ENGINE_STATE* pEngineState
234 +);
235 +static HRESULT DoMsiCommitTransaction(
236 + __in BURN_EXECUTE_CONTEXT *context
237 + , __in BURN_ENGINE_STATE* pEngineState
238 +);
239 +static HRESULT DoMsiRollbackTransaction(
240 + __in BURN_EXECUTE_CONTEXT *context
241 + , __in BURN_ENGINE_STATE* pEngineState
242 +);
243 +static HRESULT ExecuteMsiBeginTransaction(
244 + __in BURN_EXECUTE_CONTEXT* pContext
245 + , __in BURN_ENGINE_STATE* pEngineState
246 +);
247 +static HRESULT ExecuteMsiCommitTransaction(
248 + __in BURN_EXECUTE_CONTEXT* pContext
249 + , __in BURN_ENGINE_STATE* pEngineState
250 +);
251 +static HRESULT ExecuteMsiRollbackTransaction(
252 + __in BURN_EXECUTE_CONTEXT* pContext
253 + , __in BURN_ENGINE_STATE* pEngineState
254 +);
255 +
256 +// function definitions
257 +
258 +extern "C" void ApplyInitialize()
259 +{
260 + // Prevent the system from sleeping.
261 + ::SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED);
262 +}
263 +
264 +extern "C" void ApplyUninitialize()
265 +{
266 + ::SetThreadExecutionState(ES_CONTINUOUS);
267 +}
268 +
269 +extern "C" HRESULT ApplySetVariables(
270 + __in BURN_VARIABLES* pVariables
271 + )
272 +{
273 + HRESULT hr = S_OK;
274 +
275 + hr = VariableSetString(pVariables, BURN_BUNDLE_FORCED_RESTART_PACKAGE, NULL, TRUE);
276 + ExitOnFailure(hr, "Failed to set the bundle forced restart package built-in variable.");
277 +
278 +LExit:
279 + return hr;
280 +}
281 +
282 +extern "C" void ApplyReset(
283 + __in BURN_USER_EXPERIENCE* pUX,
284 + __in BURN_PACKAGES* pPackages
285 + )
286 +{
287 + UserExperienceExecuteReset(pUX);
288 +
289 + for (DWORD i = 0; i < pPackages->cPackages; ++i)
290 + {
291 + BURN_PACKAGE* pPackage = pPackages->rgPackages + i;
292 + pPackage->hrCacheResult = S_OK;
293 + }
294 +}
295 +
296 +extern "C" HRESULT ApplyLock(
297 + __in BOOL /*fPerMachine*/,
298 + __out HANDLE* /*phLock*/
299 + )
300 +{
301 + HRESULT hr = S_OK;
302 +#if 0 // eventually figure out the correct way to support this. In its current form, embedded bundles (including related bundles) are hosed.
303 + DWORD er = ERROR_SUCCESS;
304 + HANDLE hLock = NULL;
305 +
306 + hLock = ::CreateMutexW(NULL, TRUE, fPerMachine ? L"Global\\WixBurnExecutionLock" : L"Local\\WixBurnExecutionLock");
307 + ExitOnNullWithLastError(hLock, hr, "Failed to create lock.");
308 +
309 + er = ::GetLastError();
310 + if (ERROR_ALREADY_EXISTS == er)
311 + {
312 + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_INSTALL_ALREADY_RUNNING));
313 + }
314 +
315 + *phLock = hLock;
316 + hLock = NULL;
317 +
318 +LExit:
319 + ReleaseHandle(hLock);
320 +#endif
321 + return hr;
322 +}
323 +
324 +extern "C" HRESULT ApplyRegister(
325 + __in BURN_ENGINE_STATE* pEngineState
326 + )
327 +{
328 + HRESULT hr = S_OK;
329 + LPWSTR sczEngineWorkingPath = NULL;
330 +
331 + hr = UserExperienceOnRegisterBegin(&pEngineState->userExperience);
332 + ExitOnRootFailure(hr, "BA aborted register begin.");
333 +
334 + // If we have a resume mode that suggests the bundle is on the machine.
335 + if (BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING < pEngineState->command.resumeType)
336 + {
337 + // resume previous session
338 + if (pEngineState->registration.fPerMachine)
339 + {
340 + hr = ElevationSessionResume(pEngineState->companionConnection.hPipe, pEngineState->registration.sczResumeCommandLine, pEngineState->registration.fDisableResume, &pEngineState->variables);
341 + ExitOnFailure(hr, "Failed to resume registration session in per-machine process.");
342 + }
343 + else
344 + {
345 + hr = RegistrationSessionResume(&pEngineState->registration, &pEngineState->variables);
346 + ExitOnFailure(hr, "Failed to resume registration session.");
347 + }
348 + }
349 + else // need to complete registration on the machine.
350 + {
351 + hr = CacheCalculateBundleWorkingPath(pEngineState->registration.sczId, pEngineState->registration.sczExecutableName, &sczEngineWorkingPath);
352 + ExitOnFailure(hr, "Failed to calculate working path for engine.");
353 +
354 + // begin new session
355 + if (pEngineState->registration.fPerMachine)
356 + {
357 + hr = ElevationSessionBegin(pEngineState->companionConnection.hPipe, sczEngineWorkingPath, pEngineState->registration.sczResumeCommandLine, pEngineState->registration.fDisableResume, &pEngineState->variables, pEngineState->plan.dwRegistrationOperations, pEngineState->plan.dependencyRegistrationAction, pEngineState->plan.qwEstimatedSize);
358 + ExitOnFailure(hr, "Failed to begin registration session in per-machine process.");
359 + }
360 + else
361 + {
362 + hr = RegistrationSessionBegin(sczEngineWorkingPath, &pEngineState->registration, &pEngineState->variables, &pEngineState->userExperience, pEngineState->plan.dwRegistrationOperations, pEngineState->plan.dependencyRegistrationAction, pEngineState->plan.qwEstimatedSize);
363 + ExitOnFailure(hr, "Failed to begin registration session.");
364 + }
365 + }
366 +
367 + // Apply any registration actions.
368 + HRESULT hrExecuteRegistration = ExecuteDependentRegistrationActions(pEngineState->companionConnection.hPipe, &pEngineState->registration, pEngineState->plan.rgRegistrationActions, pEngineState->plan.cRegistrationActions);
369 + UNREFERENCED_PARAMETER(hrExecuteRegistration);
370 +
371 + // Try to save engine state.
372 + hr = CoreSaveEngineState(pEngineState);
373 + if (FAILED(hr))
374 + {
375 + LogErrorId(hr, MSG_STATE_NOT_SAVED, NULL, NULL, NULL);
376 + hr = S_OK;
377 + }
378 +
379 +LExit:
380 + UserExperienceOnRegisterComplete(&pEngineState->userExperience, hr);
381 + ReleaseStr(sczEngineWorkingPath);
382 +
383 + return hr;
384 +}
385 +
386 +extern "C" HRESULT ApplyUnregister(
387 + __in BURN_ENGINE_STATE* pEngineState,
388 + __in BOOL fFailedOrRollback,
389 + __in BOOL fKeepRegistration,
390 + __in BOOL fSuspend,
391 + __in BOOTSTRAPPER_APPLY_RESTART restart
392 + )
393 +{
394 + HRESULT hr = S_OK;
395 + BURN_RESUME_MODE resumeMode = BURN_RESUME_MODE_NONE;
396 +
397 + hr = UserExperienceOnUnregisterBegin(&pEngineState->userExperience);
398 + ExitOnRootFailure(hr, "BA aborted unregister begin.");
399 +
400 + // Calculate the correct resume mode. If a restart has been initiated, that trumps all other
401 + // modes. If the user chose to suspend the install then we'll use that as the resume mode.
402 + // Barring those special cases, if it was determined that we should keep the registration then
403 + // do that, otherwise the resume mode was initialized to none and registration will be removed.
404 + if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart)
405 + {
406 + resumeMode = BURN_RESUME_MODE_REBOOT_PENDING;
407 + }
408 + else if (fSuspend)
409 + {
410 + resumeMode = BURN_RESUME_MODE_SUSPEND;
411 + }
412 + else if (fKeepRegistration)
413 + {
414 + resumeMode = BURN_RESUME_MODE_ARP;
415 + }
416 +
417 + // If apply failed in any way and we're going to be keeping the bundle registered then
418 + // execute any rollback dependency registration actions.
419 + if (fFailedOrRollback && fKeepRegistration)
420 + {
421 + // Execute any rollback registration actions.
422 + HRESULT hrRegistrationRollback = ExecuteDependentRegistrationActions(pEngineState->companionConnection.hPipe, &pEngineState->registration, pEngineState->plan.rgRollbackRegistrationActions, pEngineState->plan.cRollbackRegistrationActions);
423 + UNREFERENCED_PARAMETER(hrRegistrationRollback);
424 + }
425 +
426 + if (pEngineState->registration.fPerMachine)
427 + {
428 + hr = ElevationSessionEnd(pEngineState->companionConnection.hPipe, resumeMode, restart, pEngineState->plan.dependencyRegistrationAction);
429 + ExitOnFailure(hr, "Failed to end session in per-machine process.");
430 + }
431 + else
432 + {
433 + hr = RegistrationSessionEnd(&pEngineState->registration, resumeMode, restart, pEngineState->plan.dependencyRegistrationAction);
434 + ExitOnFailure(hr, "Failed to end session in per-user process.");
435 + }
436 +
437 + pEngineState->resumeMode = resumeMode;
438 +
439 +LExit:
440 + UserExperienceOnUnregisterComplete(&pEngineState->userExperience, hr);
441 +
442 + return hr;
443 +}
444 +
445 +extern "C" HRESULT ApplyCache(
446 + __in HANDLE hSourceEngineFile,
447 + __in BURN_USER_EXPERIENCE* pUX,
448 + __in BURN_VARIABLES* pVariables,
449 + __in BURN_PLAN* pPlan,
450 + __in HANDLE hPipe,
451 + __inout DWORD* pcOverallProgressTicks,
452 + __out BOOL* pfRollback
453 + )
454 +{
455 + HRESULT hr = S_OK;
456 + DWORD dwCheckpoint = 0;
457 + BOOL fRetry = FALSE;
458 + DWORD iRetryAction = BURN_PLAN_INVALID_ACTION_INDEX;
459 + BURN_PACKAGE* pStartedPackage = NULL;
460 + DWORD64 qwSuccessfulCachedProgress = 0;
461 +
462 + // Allow us to retry and skip packages.
463 + DWORD iPackageStartAction = BURN_PLAN_INVALID_ACTION_INDEX;
464 + DWORD iPackageCompleteAction = BURN_PLAN_INVALID_ACTION_INDEX;
465 +
466 + hr = UserExperienceOnCacheBegin(pUX);
467 + ExitOnRootFailure(hr, "BA aborted cache.");
468 +
469 + do
470 + {
471 + hr = S_OK;
472 + fRetry = FALSE;
473 +
474 + // Allow us to retry just a container or payload.
475 + LPCWSTR wzRetryId = NULL;
476 + DWORD iRetryContainerOrPayloadAction = BURN_PLAN_INVALID_ACTION_INDEX;
477 + BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION cachePackageCompleteAction = BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE;
478 +
479 + // cache actions
480 + for (DWORD i = (BURN_PLAN_INVALID_ACTION_INDEX == iRetryAction) ? 0 : iRetryAction; SUCCEEDED(hr) && i < pPlan->cCacheActions; ++i)
481 + {
482 + BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + i;
483 + BOOL fRetryContainerOrPayload = FALSE;
484 + cachePackageCompleteAction = BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE;
485 +
486 + if (pCacheAction->fSkipUntilRetried)
487 + {
488 + // If this action was retried, let's make sure it will not be skipped any longer.
489 + if (iRetryAction == i)
490 + {
491 + pCacheAction->fSkipUntilRetried = FALSE;
492 + }
493 + else // skip the action.
494 + {
495 + continue;
496 + }
497 + }
498 +
499 + switch (pCacheAction->type)
500 + {
501 + case BURN_CACHE_ACTION_TYPE_CHECKPOINT:
502 + dwCheckpoint = pCacheAction->checkpoint.dwId;
503 + break;
504 +
505 + case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE:
506 + hr = LayoutBundle(pUX, pVariables, hPipe, pCacheAction->bundleLayout.sczExecutableName, pCacheAction->bundleLayout.sczLayoutDirectory, pCacheAction->bundleLayout.sczUnverifiedPath, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal);
507 + if (SUCCEEDED(hr))
508 + {
509 + UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress);
510 + ++(*pcOverallProgressTicks);
511 +
512 + hr = ReportOverallProgressTicks(pUX, FALSE, pPlan->cOverallProgressTicksTotal, *pcOverallProgressTicks);
513 + if (FAILED(hr))
514 + {
515 + LogErrorId(hr, MSG_USER_CANCELED, L"layout bundle", NULL, NULL);
516 + }
517 + }
518 + break;
519 +
520 + case BURN_CACHE_ACTION_TYPE_PACKAGE_START:
521 + iPackageStartAction = i; // if we retry this package, we'll start here in the plan.
522 + iPackageCompleteAction = pCacheAction->packageStart.iPackageCompleteAction; // if we ignore this package, we'll start after the complete action in the plan.
523 + pStartedPackage = pCacheAction->packageStart.pPackage;
524 +
525 + hr = UserExperienceOnCachePackageBegin(pUX, pStartedPackage->sczId, pCacheAction->packageStart.cCachePayloads, pCacheAction->packageStart.qwCachePayloadSizeTotal);
526 + if (FAILED(hr))
527 + {
528 + LogErrorId(hr, MSG_USER_CANCELED, L"begin cache package", pStartedPackage->sczId, NULL);
529 + }
530 + break;
531 +
532 + case BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER:
533 + hr = AcquireContainerOrPayload(pUX, pVariables, pCacheAction->resolveContainer.pContainer, NULL, NULL, pCacheAction->resolveContainer.sczUnverifiedPath, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal);
534 + if (SUCCEEDED(hr))
535 + {
536 + UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress);
537 + }
538 + else
539 + {
540 + LogErrorId(hr, MSG_FAILED_ACQUIRE_CONTAINER, pCacheAction->resolveContainer.pContainer->sczId, pCacheAction->resolveContainer.sczUnverifiedPath, NULL);
541 + }
542 + break;
543 +
544 + case BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER:
545 + // If this action is to be skipped until the acquire action is not skipped and the other
546 + // action is still being skipped then skip this action.
547 + if (BURN_PLAN_INVALID_ACTION_INDEX != pCacheAction->extractContainer.iSkipUntilAcquiredByAction && pPlan->rgCacheActions[pCacheAction->extractContainer.iSkipUntilAcquiredByAction].fSkipUntilRetried)
548 + {
549 + break;
550 + }
551 +
552 + hr = ExtractContainer(hSourceEngineFile, pCacheAction->extractContainer.pContainer, pCacheAction->extractContainer.sczContainerUnverifiedPath, pCacheAction->extractContainer.rgPayloads, pCacheAction->extractContainer.cPayloads);
553 + if (FAILED(hr))
554 + {
555 + LogErrorId(hr, MSG_FAILED_EXTRACT_CONTAINER, pCacheAction->extractContainer.pContainer->sczId, pCacheAction->extractContainer.sczContainerUnverifiedPath, NULL);
556 + }
557 + break;
558 +
559 + case BURN_CACHE_ACTION_TYPE_LAYOUT_CONTAINER:
560 + hr = LayoutOrCacheContainerOrPayload(pUX, hPipe, pCacheAction->layoutContainer.pContainer, pCacheAction->layoutContainer.pPackage, NULL, pPlan->rgContainerProgress[pCacheAction->layoutContainer.iProgress].fCachedDuringApply, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal, pCacheAction->layoutContainer.sczLayoutDirectory, pCacheAction->layoutContainer.sczUnverifiedPath, pCacheAction->layoutContainer.fMove, pCacheAction->layoutContainer.cTryAgainAttempts, &fRetryContainerOrPayload);
561 + if (SUCCEEDED(hr))
562 + {
563 + UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress);
564 + }
565 + else
566 + {
567 + LogErrorId(hr, MSG_FAILED_LAYOUT_CONTAINER, pCacheAction->layoutContainer.pContainer->sczId, pCacheAction->layoutContainer.sczLayoutDirectory, pCacheAction->layoutContainer.sczUnverifiedPath);
568 +
569 + if (fRetryContainerOrPayload)
570 + {
571 + wzRetryId = pCacheAction->layoutContainer.pContainer->sczId;
572 + iRetryContainerOrPayloadAction = pCacheAction->layoutContainer.iTryAgainAction;
573 +
574 + ++pCacheAction->layoutContainer.cTryAgainAttempts;
575 + }
576 + }
577 + break;
578 +
579 + case BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD:
580 + hr = AcquireContainerOrPayload(pUX, pVariables, NULL, pCacheAction->resolvePayload.pPackage, pCacheAction->resolvePayload.pPayload, pCacheAction->resolvePayload.sczUnverifiedPath, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal);
581 + if (SUCCEEDED(hr))
582 + {
583 + UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress);
584 + }
585 + else
586 + {
587 + LogErrorId(hr, MSG_FAILED_ACQUIRE_PAYLOAD, pCacheAction->resolvePayload.pPayload->sczKey, pCacheAction->resolvePayload.sczUnverifiedPath, NULL);
588 + }
589 + break;
590 +
591 + case BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD:
592 + hr = LayoutOrCacheContainerOrPayload(pUX, pCacheAction->cachePayload.pPackage->fPerMachine ? hPipe : INVALID_HANDLE_VALUE, NULL, pCacheAction->cachePayload.pPackage, pCacheAction->cachePayload.pPayload, pPlan->rgPayloadProgress[pCacheAction->cachePayload.iProgress].fCachedDuringApply, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal, NULL, pCacheAction->cachePayload.sczUnverifiedPath, pCacheAction->cachePayload.fMove, pCacheAction->cachePayload.cTryAgainAttempts, &fRetryContainerOrPayload);
593 + if (SUCCEEDED(hr))
594 + {
595 + UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress);
596 + }
597 + else
598 + {
599 + LogErrorId(hr, MSG_FAILED_CACHE_PAYLOAD, pCacheAction->cachePayload.pPayload->sczKey, pCacheAction->cachePayload.sczUnverifiedPath, NULL);
600 +
601 + if (fRetryContainerOrPayload)
602 + {
603 + wzRetryId = pCacheAction->cachePayload.pPayload->sczKey;
604 + iRetryContainerOrPayloadAction = pCacheAction->cachePayload.iTryAgainAction;
605 +
606 + ++pCacheAction->cachePayload.cTryAgainAttempts;
607 + }
608 + }
609 + break;
610 +
611 + case BURN_CACHE_ACTION_TYPE_LAYOUT_PAYLOAD:
612 + hr = LayoutOrCacheContainerOrPayload(pUX, hPipe, NULL, pCacheAction->layoutPayload.pPackage, pCacheAction->layoutPayload.pPayload, pPlan->rgPayloadProgress[pCacheAction->layoutPayload.iProgress].fCachedDuringApply, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal, pCacheAction->layoutPayload.sczLayoutDirectory, pCacheAction->layoutPayload.sczUnverifiedPath, pCacheAction->layoutPayload.fMove, pCacheAction->layoutPayload.cTryAgainAttempts, &fRetryContainerOrPayload);
613 + if (SUCCEEDED(hr))
614 + {
615 + UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress);
616 + }
617 + else
618 + {
619 + LogErrorId(hr, MSG_FAILED_LAYOUT_PAYLOAD, pCacheAction->layoutPayload.pPayload->sczKey, pCacheAction->layoutPayload.sczLayoutDirectory, pCacheAction->layoutPayload.sczUnverifiedPath);
620 +
621 + if (fRetryContainerOrPayload)
622 + {
623 + wzRetryId = pCacheAction->layoutPayload.pPayload->sczKey;
624 + iRetryContainerOrPayloadAction = pCacheAction->layoutPayload.iTryAgainAction;
625 +
626 + ++pCacheAction->layoutPayload.cTryAgainAttempts;
627 + }
628 + }
629 + break;
630 +
631 + case BURN_CACHE_ACTION_TYPE_PACKAGE_STOP:
632 + AssertSz(pStartedPackage == pCacheAction->packageStop.pPackage, "Expected package started cached to be the same as the package checkpointed.");
633 +
634 + hr = ReportOverallProgressTicks(pUX, FALSE, pPlan->cOverallProgressTicksTotal, *pcOverallProgressTicks + 1);
635 + if (FAILED(hr))
636 + {
637 + LogErrorId(hr, MSG_USER_CANCELED, L"end cache package", NULL, NULL);
638 + }
639 + else
640 + {
641 + ++(*pcOverallProgressTicks);
642 +
643 + UserExperienceOnCachePackageComplete(pUX, pStartedPackage->sczId, hr, &cachePackageCompleteAction);
644 +
645 + pStartedPackage->hrCacheResult = hr;
646 +
647 + iPackageStartAction = BURN_PLAN_INVALID_ACTION_INDEX;
648 + iPackageCompleteAction = BURN_PLAN_INVALID_ACTION_INDEX;
649 + pStartedPackage = NULL;
650 + }
651 + break;
652 +
653 + case BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT:
654 + if (!::SetEvent(pCacheAction->syncpoint.hEvent))
655 + {
656 + ExitWithLastError(hr, "Failed to set syncpoint event.");
657 + }
658 + break;
659 +
660 + default:
661 + AssertSz(FALSE, "Unknown cache action.");
662 + break;
663 + }
664 + }
665 +
666 + if (BURN_PLAN_INVALID_ACTION_INDEX != iRetryContainerOrPayloadAction)
667 + {
668 + Assert(wzRetryId);
669 +
670 + LogErrorId(hr, MSG_APPLY_RETRYING_PAYLOAD, wzRetryId, NULL, NULL);
671 +
672 + iRetryAction = iRetryContainerOrPayloadAction;
673 + fRetry = TRUE;
674 + }
675 + else if (pStartedPackage)
676 + {
677 + Assert(BURN_PLAN_INVALID_ACTION_INDEX != iPackageStartAction);
678 + Assert(BURN_PLAN_INVALID_ACTION_INDEX != iPackageCompleteAction);
679 +
680 + cachePackageCompleteAction = SUCCEEDED(hr) || pStartedPackage->fVital ? BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE : BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_IGNORE;
681 + UserExperienceOnCachePackageComplete(pUX, pStartedPackage->sczId, hr, &cachePackageCompleteAction);
682 + if (BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_RETRY == cachePackageCompleteAction)
683 + {
684 + LogErrorId(hr, MSG_APPLY_RETRYING_PACKAGE, pStartedPackage->sczId, NULL, NULL);
685 +
686 + iRetryAction = iPackageStartAction;
687 + fRetry = TRUE;
688 + }
689 + else if (BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_IGNORE == cachePackageCompleteAction && !pStartedPackage->fVital) // ignore non-vital download failures.
690 + {
691 + LogId(REPORT_STANDARD, MSG_APPLY_CONTINUING_NONVITAL_PACKAGE, pStartedPackage->sczId, hr);
692 +
693 + ++(*pcOverallProgressTicks); // add progress even though we didn't fully cache the package.
694 +
695 + iRetryAction = iPackageCompleteAction + 1;
696 + fRetry = TRUE;
697 + }
698 +
699 + pStartedPackage->hrCacheResult = hr;
700 +
701 + iPackageStartAction = BURN_PLAN_INVALID_ACTION_INDEX;
702 + iPackageCompleteAction = BURN_PLAN_INVALID_ACTION_INDEX;
703 + pStartedPackage = NULL;
704 + }
705 + else
706 + {
707 + Assert(BURN_PLAN_INVALID_ACTION_INDEX == iPackageStartAction);
708 + Assert(BURN_PLAN_INVALID_ACTION_INDEX == iPackageCompleteAction);
709 + }
710 + } while (fRetry);
711 +
712 +LExit:
713 + Assert(NULL == pStartedPackage);
714 + Assert(BURN_PLAN_INVALID_ACTION_INDEX == iPackageStartAction);
715 + Assert(BURN_PLAN_INVALID_ACTION_INDEX == iPackageCompleteAction);
716 +
717 + if (FAILED(hr))
718 + {
719 + DoRollbackCache(pUX, pPlan, hPipe, dwCheckpoint);
720 + *pfRollback = TRUE;
721 + }
722 +
723 + // Clean up any remanents in the cache.
724 + if (INVALID_HANDLE_VALUE != hPipe)
725 + {
726 + ElevationCacheCleanup(hPipe);
727 + }
728 +
729 + CacheCleanup(FALSE, pPlan->wzBundleId);
730 +
731 + UserExperienceOnCacheComplete(pUX, hr);
732 + return hr;
733 +}
734 +
735 +extern "C" HRESULT ApplyExecute(
736 + __in BURN_ENGINE_STATE* pEngineState,
737 + __in_opt HANDLE hCacheThread,
738 + __inout DWORD* pcOverallProgressTicks,
739 + __out BOOL* pfKeepRegistration,
740 + __out BOOL* pfRollback,
741 + __out BOOL* pfSuspend,
742 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart
743 + )
744 +{
745 + HRESULT hr = S_OK;
746 + DWORD dwCheckpoint = 0;
747 + BURN_EXECUTE_CONTEXT context = { };
748 + BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL;
749 + BOOL fSeekNextRollbackBoundary = FALSE;
750 + BOOL fInTransaction = FALSE;
751 +
752 + context.pUX = &pEngineState->userExperience;
753 + context.cExecutePackagesTotal = pEngineState->plan.cExecutePackagesTotal;
754 + context.pcOverallProgressTicks = pcOverallProgressTicks;
755 +
756 + // Send execute begin to BA.
757 + hr = UserExperienceOnExecuteBegin(&pEngineState->userExperience, pEngineState->plan.cExecutePackagesTotal);
758 + ExitOnRootFailure(hr, "BA aborted execute begin.");
759 +
760 + // Do execute actions.
761 + for (DWORD i = 0; i < pEngineState->plan.cExecuteActions; ++i)
762 + {
763 + BURN_EXECUTE_ACTION* pExecuteAction = &pEngineState->plan.rgExecuteActions[i];
764 + if (pExecuteAction->fDeleted)
765 + {
766 + continue;
767 + }
768 +
769 + // Transaction end/start
770 + if (BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY == pExecuteAction->type)
771 + {
772 + // End previous transaction
773 + if (fInTransaction)
774 + {
775 + LogString(REPORT_STANDARD, "Committing MSI transaction\n");
776 + hr = DoMsiCommitTransaction(&context, pEngineState);
777 + ExitOnFailure(hr, "Failed committing an MSI transaction");
778 + fInTransaction = FALSE;
779 + }
780 +
781 + // Start New transaction
782 + if (!fInTransaction && pExecuteAction->rollbackBoundary.pRollbackBoundary && pExecuteAction->rollbackBoundary.pRollbackBoundary->fTransaction)
783 + {
784 + // Transactions don't go together with DisableRollback.
785 + if (pEngineState->fDisableRollback)
786 + {
787 + LogString(REPORT_STANDARD, "Ignoring Transaction flag due to DisableRollback flag\n");
788 + }
789 + else
790 + {
791 + LogString(REPORT_STANDARD, "Starting a new MSI transaction\n");
792 + hr = DoMsiBeginTransaction(&context, pEngineState);
793 + ExitOnFailure(hr, "Failed beginning an MSI transaction");
794 + fInTransaction = TRUE;
795 + }
796 + }
797 + }
798 +
799 + // If we are seeking the next rollback boundary, skip if this action wasn't it.
800 + if (fSeekNextRollbackBoundary)
801 + {
802 + if (BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY == pExecuteAction->type)
803 + {
804 + continue;
805 + }
806 + else
807 + {
808 + fSeekNextRollbackBoundary = FALSE;
809 + }
810 + }
811 +
812 + // Execute the action.
813 + hr = DoExecuteAction(pEngineState, pExecuteAction, hCacheThread, &context, &pRollbackBoundary, &dwCheckpoint, pfKeepRegistration, pfSuspend, pRestart);
814 +
815 + if (*pfSuspend || BOOTSTRAPPER_APPLY_RESTART_INITIATED == *pRestart)
816 + {
817 + if (fInTransaction)
818 + {
819 + hr = E_INVALIDSTATE;
820 + LogString(REPORT_ERROR, "Ilegal state: Reboot requested within an MSI transaction. Transaction will rollback.");
821 + }
822 + else
823 + {
824 + ExitFunction();
825 + }
826 + }
827 +
828 + if (FAILED(hr))
829 + {
830 + // If we failed, but rollback is disabled just bail with our error code.
831 + if (pEngineState->fDisableRollback)
832 + {
833 + *pfRollback = TRUE;
834 + break;
835 + }
836 + else // the action failed, roll back to previous rollback boundary.
837 + {
838 + HRESULT hrRollback = DoRollbackActions(pEngineState, &context, dwCheckpoint, fInTransaction, pfKeepRegistration, pRestart);
839 + UNREFERENCED_PARAMETER(hrRollback);
840 + fInTransaction = FALSE;
841 +
842 + // If the rollback boundary is vital, end execution here.
843 + if (pRollbackBoundary && pRollbackBoundary->fVital)
844 + {
845 + *pfRollback = TRUE;
846 + break;
847 + }
848 +
849 + // Move forward to next rollback boundary.
850 + fSeekNextRollbackBoundary = TRUE;
851 + }
852 + }
853 + }
854 +
855 + if (fInTransaction)
856 + {
857 + LogString(REPORT_STANDARD, "Committing an MSI transaction\n");
858 + hr = DoMsiCommitTransaction(&context, pEngineState);
859 + ExitOnFailure(hr, "Failed committing an MSI transaction");
860 + fInTransaction = FALSE;
861 + }
862 +
863 +LExit:
864 + // Send execute complete to BA.
865 + UserExperienceOnExecuteComplete(&pEngineState->userExperience, hr);
866 +
867 + return hr;
868 +}
869 +
870 +extern "C" void ApplyClean(
871 + __in BURN_USER_EXPERIENCE* /*pUX*/,
872 + __in BURN_PLAN* pPlan,
873 + __in HANDLE hPipe
874 + )
875 +{
876 + HRESULT hr = S_OK;
877 +
878 + for (DWORD i = 0; i < pPlan->cCleanActions; ++i)
879 + {
880 + BURN_CLEAN_ACTION* pCleanAction = pPlan->rgCleanActions + i;
881 +
882 + hr = CleanPackage(hPipe, pCleanAction->pPackage);
883 + }
884 +}
885 +
886 +
887 +// internal helper functions
888 +
889 +static HRESULT ExecuteDependentRegistrationActions(
890 + __in HANDLE hPipe,
891 + __in const BURN_REGISTRATION* pRegistration,
892 + __in_ecount(cActions) const BURN_DEPENDENT_REGISTRATION_ACTION* rgActions,
893 + __in DWORD cActions
894 + )
895 +{
896 + HRESULT hr = S_OK;
897 +
898 + for (DWORD iAction = 0; iAction < cActions; ++iAction)
899 + {
900 + const BURN_DEPENDENT_REGISTRATION_ACTION* pAction = rgActions + iAction;
901 +
902 + if (pRegistration->fPerMachine)
903 + {
904 + hr = ElevationProcessDependentRegistration(hPipe, pAction);
905 + ExitOnFailure(hr, "Failed to execute dependent registration action.");
906 + }
907 + else
908 + {
909 + hr = DependencyProcessDependentRegistration(pRegistration, pAction);
910 + ExitOnFailure(hr, "Failed to process dependency registration action.");
911 + }
912 + }
913 +
914 +LExit:
915 + return hr;
916 +}
917 +
918 +static HRESULT ExtractContainer(
919 + __in HANDLE hSourceEngineFile,
920 + __in BURN_CONTAINER* pContainer,
921 + __in_z LPCWSTR wzContainerPath,
922 + __in_ecount(cExtractPayloads) BURN_EXTRACT_PAYLOAD* rgExtractPayloads,
923 + __in DWORD cExtractPayloads
924 + )
925 +{
926 + HRESULT hr = S_OK;
927 + BURN_CONTAINER_CONTEXT context = { };
928 + HANDLE hContainerHandle = INVALID_HANDLE_VALUE;
929 + LPWSTR sczExtractPayloadId = NULL;
930 +
931 + // If the container is actually attached, then it was planned to be acquired through hSourceEngineFile.
932 + if (pContainer->fActuallyAttached)
933 + {
934 + hContainerHandle = hSourceEngineFile;
935 + }
936 +
937 + hr = ContainerOpen(&context, pContainer, hContainerHandle, wzContainerPath);
938 + ExitOnFailure(hr, "Failed to open container: %ls.", pContainer->sczId);
939 +
940 + while (S_OK == (hr = ContainerNextStream(&context, &sczExtractPayloadId)))
941 + {
942 + BOOL fExtracted = FALSE;
943 +
944 + for (DWORD iExtract = 0; iExtract < cExtractPayloads; ++iExtract)
945 + {
946 + BURN_EXTRACT_PAYLOAD* pExtract = rgExtractPayloads + iExtract;
947 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczExtractPayloadId, -1, pExtract->pPayload->sczSourcePath, -1))
948 + {
949 + // TODO: Send progress when extracting stream to file.
950 + hr = ContainerStreamToFile(&context, pExtract->sczUnverifiedPath);
951 + ExitOnFailure(hr, "Failed to extract payload: %ls from container: %ls", sczExtractPayloadId, pContainer->sczId);
952 +
953 + fExtracted = TRUE;
954 + break;
955 + }
956 + }
957 +
958 + if (!fExtracted)
959 + {
960 + hr = ContainerSkipStream(&context);
961 + ExitOnFailure(hr, "Failed to skip the extraction of payload: %ls from container: %ls", sczExtractPayloadId, pContainer->sczId);
962 + }
963 + }
964 +
965 + if (E_NOMOREITEMS == hr)
966 + {
967 + hr = S_OK;
968 + }
969 + ExitOnFailure(hr, "Failed to extract all payloads from container: %ls", pContainer->sczId);
970 +
971 +LExit:
972 + ReleaseStr(sczExtractPayloadId);
973 + ContainerClose(&context);
974 +
975 + return hr;
976 +}
977 +
978 +static void UpdateCacheSuccessProgress(
979 + __in BURN_PLAN* pPlan,
980 + __in BURN_CACHE_ACTION* pCacheAction,
981 + __inout DWORD64* pqwSuccessfulCachedProgress
982 + )
983 +{
984 + switch (pCacheAction->type)
985 + {
986 + case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE:
987 + *pqwSuccessfulCachedProgress += pCacheAction->bundleLayout.qwBundleSize;
988 + break;
989 +
990 + case BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER:
991 + if (!pPlan->rgContainerProgress[pCacheAction->resolveContainer.iProgress].fCachedDuringApply)
992 + {
993 + pPlan->rgContainerProgress[pCacheAction->resolveContainer.iProgress].fCachedDuringApply = TRUE;
994 + *pqwSuccessfulCachedProgress += pCacheAction->resolveContainer.pContainer->qwFileSize;
995 + }
996 + break;
997 +
998 + case BURN_CACHE_ACTION_TYPE_LAYOUT_CONTAINER:
999 + if (!pPlan->rgContainerProgress[pCacheAction->layoutContainer.iProgress].fCachedDuringApply)
1000 + {
1001 + pPlan->rgContainerProgress[pCacheAction->layoutContainer.iProgress].fCachedDuringApply = TRUE;
1002 + *pqwSuccessfulCachedProgress += pCacheAction->layoutContainer.pContainer->qwFileSize;
1003 + }
1004 + break;
1005 +
1006 + case BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD:
1007 + if (!pPlan->rgPayloadProgress[pCacheAction->resolvePayload.iProgress].fCachedDuringApply)
1008 + {
1009 + pPlan->rgPayloadProgress[pCacheAction->resolvePayload.iProgress].fCachedDuringApply = TRUE;
1010 + *pqwSuccessfulCachedProgress += pCacheAction->resolvePayload.pPayload->qwFileSize;
1011 + }
1012 + break;
1013 +
1014 + case BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD:
1015 + if (!pPlan->rgPayloadProgress[pCacheAction->cachePayload.iProgress].fCachedDuringApply)
1016 + {
1017 + pPlan->rgPayloadProgress[pCacheAction->cachePayload.iProgress].fCachedDuringApply = TRUE;
1018 + *pqwSuccessfulCachedProgress += pCacheAction->cachePayload.pPayload->qwFileSize;
1019 + }
1020 + break;
1021 +
1022 + case BURN_CACHE_ACTION_TYPE_LAYOUT_PAYLOAD:
1023 + if (!pPlan->rgPayloadProgress[pCacheAction->layoutPayload.iProgress].fCachedDuringApply)
1024 + {
1025 + pPlan->rgPayloadProgress[pCacheAction->layoutPayload.iProgress].fCachedDuringApply = TRUE;
1026 + *pqwSuccessfulCachedProgress += pCacheAction->layoutPayload.pPayload->qwFileSize;
1027 + }
1028 + break;
1029 +
1030 + default:
1031 + AssertSz(FALSE, "Unexpected cache action type.");
1032 + break;
1033 + }
1034 +}
1035 +
1036 +static HRESULT LayoutBundle(
1037 + __in BURN_USER_EXPERIENCE* pUX,
1038 + __in BURN_VARIABLES* pVariables,
1039 + __in HANDLE hPipe,
1040 + __in_z LPCWSTR wzExecutableName,
1041 + __in_z LPCWSTR wzLayoutDirectory,
1042 + __in_z LPCWSTR wzUnverifiedPath,
1043 + __in DWORD64 qwSuccessfulCacheProgress,
1044 + __in DWORD64 qwTotalCacheSize
1045 + )
1046 +{
1047 + HRESULT hr = S_OK;
1048 + LPWSTR sczBundlePath = NULL;
1049 + LPWSTR sczDestinationPath = NULL;
1050 + int nEquivalentPaths = 0;
1051 + BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT progress = { };
1052 + BOOL fRetry = FALSE;
1053 + BOOL fRetryAcquire = FALSE;
1054 +
1055 + hr = VariableGetString(pVariables, BURN_BUNDLE_SOURCE_PROCESS_PATH, &sczBundlePath);
1056 + if (FAILED(hr))
1057 + {
1058 + if (E_NOTFOUND != hr)
1059 + {
1060 + ExitOnFailure(hr, "Failed to get path to bundle source process path to layout.");
1061 + }
1062 +
1063 + hr = PathForCurrentProcess(&sczBundlePath, NULL);
1064 + ExitOnFailure(hr, "Failed to get path to bundle to layout.");
1065 + }
1066 +
1067 + hr = PathConcat(wzLayoutDirectory, wzExecutableName, &sczDestinationPath);
1068 + ExitOnFailure(hr, "Failed to concat layout path for bundle.");
1069 +
1070 + // If the destination path is the currently running bundle, bail.
1071 + hr = PathCompare(sczBundlePath, sczDestinationPath, &nEquivalentPaths);
1072 + ExitOnFailure(hr, "Failed to determine if layout bundle path was equivalent with current process path.");
1073 +
1074 + if (CSTR_EQUAL == nEquivalentPaths)
1075 + {
1076 + ExitFunction1(hr = S_OK);
1077 + }
1078 +
1079 + progress.pUX = pUX;
1080 + progress.qwCacheProgress = qwSuccessfulCacheProgress;
1081 + progress.qwTotalCacheSize = qwTotalCacheSize;
1082 +
1083 + do
1084 + {
1085 + hr = S_OK;
1086 + fRetry = FALSE;
1087 +
1088 + for (;;)
1089 + {
1090 + fRetryAcquire = FALSE;
1091 + progress.fCancel = FALSE;
1092 +
1093 + hr = UserExperienceOnCacheAcquireBegin(pUX, NULL, NULL, BOOTSTRAPPER_CACHE_OPERATION_COPY, sczBundlePath);
1094 + ExitOnRootFailure(hr, "BA aborted cache acquire begin.");
1095 +
1096 + hr = CopyPayload(&progress, sczBundlePath, wzUnverifiedPath);
1097 + // Error handling happens after sending complete message to BA.
1098 +
1099 + UserExperienceOnCacheAcquireComplete(pUX, NULL, NULL, hr, &fRetryAcquire);
1100 + if (fRetryAcquire)
1101 + {
1102 + continue;
1103 + }
1104 +
1105 + ExitOnFailure(hr, "Failed to copy bundle from: '%ls' to: '%ls'", sczBundlePath, wzUnverifiedPath);
1106 + break;
1107 + }
1108 +
1109 + do
1110 + {
1111 + hr = UserExperienceOnCacheVerifyBegin(pUX, NULL, NULL);
1112 + ExitOnRootFailure(hr, "BA aborted cache verify begin.");
1113 +
1114 + if (INVALID_HANDLE_VALUE != hPipe)
1115 + {
1116 + hr = ElevationLayoutBundle(hPipe, wzLayoutDirectory, wzUnverifiedPath);
1117 + }
1118 + else
1119 + {
1120 + hr = CacheLayoutBundle(wzExecutableName, wzLayoutDirectory, wzUnverifiedPath);
1121 + }
1122 +
1123 + BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION action = BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_NONE;
1124 + UserExperienceOnCacheVerifyComplete(pUX, NULL, NULL, hr, &action);
1125 + if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYVERIFICATION == action)
1126 + {
1127 + hr = S_FALSE; // retry verify.
1128 + }
1129 + else if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYACQUISITION == action)
1130 + {
1131 + fRetry = TRUE; // go back and retry acquire.
1132 + }
1133 + } while (S_FALSE == hr);
1134 + } while (fRetry);
1135 + LogExitOnFailure(hr, MSG_FAILED_LAYOUT_BUNDLE, "Failed to layout bundle: %ls to layout directory: %ls", sczBundlePath, wzLayoutDirectory);
1136 +
1137 +LExit:
1138 + ReleaseStr(sczDestinationPath);
1139 + ReleaseStr(sczBundlePath);
1140 +
1141 + return hr;
1142 +}
1143 +
1144 +static HRESULT AcquireContainerOrPayload(
1145 + __in BURN_USER_EXPERIENCE* pUX,
1146 + __in BURN_VARIABLES* pVariables,
1147 + __in_opt BURN_CONTAINER* pContainer,
1148 + __in_opt BURN_PACKAGE* pPackage,
1149 + __in_opt BURN_PAYLOAD* pPayload,
1150 + __in LPCWSTR wzDestinationPath,
1151 + __in DWORD64 qwSuccessfulCacheProgress,
1152 + __in DWORD64 qwTotalCacheSize
1153 + )
1154 +{
1155 + AssertSz(pContainer || pPayload, "Must provide a container or a payload.");
1156 +
1157 + HRESULT hr = S_OK;
1158 + int nEquivalentPaths = 0;
1159 + LPCWSTR wzPackageOrContainerId = pContainer ? pContainer->sczId : pPackage ? pPackage->sczId : NULL;
1160 + LPCWSTR wzPayloadId = pPayload ? pPayload->sczKey : NULL;
1161 + LPCWSTR wzRelativePath = pContainer ? pContainer->sczFilePath : pPayload->sczFilePath;
1162 + LPWSTR sczSourceFullPath = NULL;
1163 + BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT progress = { };
1164 + BOOL fRetry = FALSE;
1165 +
1166 + progress.pContainer = pContainer;
1167 + progress.pPackage = pPackage;
1168 + progress.pPayload = pPayload;
1169 + progress.pUX = pUX;
1170 + progress.qwCacheProgress = qwSuccessfulCacheProgress;
1171 + progress.qwTotalCacheSize = qwTotalCacheSize;
1172 +
1173 + do
1174 + {
1175 + LPCWSTR wzDownloadUrl = pContainer ? pContainer->downloadSource.sczUrl : pPayload->downloadSource.sczUrl;
1176 + LPCWSTR wzSourcePath = pContainer ? pContainer->sczSourcePath : pPayload->sczSourcePath;
1177 +
1178 + BOOL fFoundLocal = FALSE;
1179 + BOOL fCopy = FALSE;
1180 + BOOL fDownload = FALSE;
1181 +
1182 + fRetry = FALSE;
1183 + progress.fCancel = FALSE;
1184 +
1185 + hr = CacheFindLocalSource(wzSourcePath, pVariables, &fFoundLocal, &sczSourceFullPath);
1186 + ExitOnFailure(hr, "Failed to search local source.");
1187 +
1188 + if (fFoundLocal) // the file exists locally, so copy it.
1189 + {
1190 + // If the source path and destination path are different, do the copy (otherwise there's no point).
1191 + hr = PathCompare(sczSourceFullPath, wzDestinationPath, &nEquivalentPaths);
1192 + ExitOnFailure(hr, "Failed to determine if payload source path was equivalent to the destination path.");
1193 +
1194 + fCopy = (CSTR_EQUAL != nEquivalentPaths);
1195 + }
1196 + else // can't find the file locally, so prompt for source.
1197 + {
1198 + DWORD dwLogId = pContainer ? (wzPayloadId ? MSG_PROMPT_CONTAINER_PAYLOAD_SOURCE : MSG_PROMPT_CONTAINER_SOURCE) : pPackage ? MSG_PROMPT_PACKAGE_PAYLOAD_SOURCE : MSG_PROMPT_BUNDLE_PAYLOAD_SOURCE;
1199 + LogId(REPORT_STANDARD, dwLogId, wzPackageOrContainerId ? wzPackageOrContainerId : L"", wzPayloadId ? wzPayloadId : L"", sczSourceFullPath);
1200 +
1201 + hr = PromptForSource(pUX, wzPackageOrContainerId, wzPayloadId, sczSourceFullPath, wzDownloadUrl, &fRetry, &fDownload);
1202 +
1203 + // If the BA requested download then ensure a download url is available (it may have been set
1204 + // during PromptForSource so we need to check again).
1205 + if (fDownload)
1206 + {
1207 + wzDownloadUrl = pContainer ? pContainer->downloadSource.sczUrl : pPayload->downloadSource.sczUrl;
1208 + if (!wzDownloadUrl || !*wzDownloadUrl)
1209 + {
1210 + hr = E_INVALIDARG;
1211 + }
1212 + }
1213 +
1214 + // Log the error
1215 + LogExitOnFailure(hr, MSG_PAYLOAD_FILE_NOT_PRESENT, "Failed while prompting for source (original path '%ls').", sczSourceFullPath);
1216 + }
1217 +
1218 + if (fCopy)
1219 + {
1220 + hr = UserExperienceOnCacheAcquireBegin(pUX, wzPackageOrContainerId, wzPayloadId, BOOTSTRAPPER_CACHE_OPERATION_COPY, sczSourceFullPath);
1221 + ExitOnRootFailure(hr, "BA aborted cache acquire begin.");
1222 +
1223 + hr = CopyPayload(&progress, sczSourceFullPath, wzDestinationPath);
1224 + // Error handling happens after sending complete message to BA.
1225 +
1226 + // We successfully copied from a source location, set that as the last used source.
1227 + if (SUCCEEDED(hr))
1228 + {
1229 + CacheSetLastUsedSource(pVariables, sczSourceFullPath, wzRelativePath);
1230 + }
1231 + }
1232 + else if (fDownload)
1233 + {
1234 + hr = UserExperienceOnCacheAcquireBegin(pUX, wzPackageOrContainerId, wzPayloadId, BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD, wzDownloadUrl);
1235 + ExitOnRootFailure(hr, "BA aborted cache download payload begin.");
1236 +
1237 + hr = DownloadPayload(&progress, wzDestinationPath);
1238 + // Error handling happens after sending complete message to BA.
1239 + }
1240 +
1241 + if (fCopy || fDownload)
1242 + {
1243 + UserExperienceOnCacheAcquireComplete(pUX, wzPackageOrContainerId, wzPayloadId, hr, &fRetry);
1244 + if (fRetry)
1245 + {
1246 + hr = S_OK;
1247 + }
1248 + }
1249 + ExitOnFailure(hr, "Failed to acquire payload from: '%ls' to working path: '%ls'", fCopy ? sczSourceFullPath : wzDownloadUrl, wzDestinationPath);
1250 + } while (fRetry);
1251 + ExitOnFailure(hr, "Failed to find external payload to cache.");
1252 +
1253 +LExit:
1254 + ReleaseStr(sczSourceFullPath);
1255 +
1256 + return hr;
1257 +}
1258 +
1259 +static HRESULT LayoutOrCacheContainerOrPayload(
1260 + __in BURN_USER_EXPERIENCE* pUX,
1261 + __in HANDLE hPipe,
1262 + __in_opt BURN_CONTAINER* pContainer,
1263 + __in_opt BURN_PACKAGE* pPackage,
1264 + __in_opt BURN_PAYLOAD* pPayload,
1265 + __in BOOL fAlreadyProvidedProgress,
1266 + __in DWORD64 qwSuccessfulCachedProgress,
1267 + __in DWORD64 qwTotalCacheSize,
1268 + __in_z_opt LPCWSTR wzLayoutDirectory,
1269 + __in_z LPCWSTR wzUnverifiedPath,
1270 + __in BOOL fMove,
1271 + __in DWORD cTryAgainAttempts,
1272 + __out BOOL* pfRetry
1273 + )
1274 +{
1275 + HRESULT hr = S_OK;
1276 + LPCWSTR wzPackageOrContainerId = pContainer ? pContainer->sczId : pPackage ? pPackage->sczId : L"";
1277 + LPCWSTR wzPayloadId = pPayload ? pPayload->sczKey : L"";
1278 + LARGE_INTEGER liContainerOrPayloadSize = { };
1279 + LARGE_INTEGER liZero = { };
1280 + BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT progress = { };
1281 +
1282 + liContainerOrPayloadSize.QuadPart = pContainer ? pContainer->qwFileSize : pPayload->qwFileSize;
1283 +
1284 + progress.pContainer = pContainer;
1285 + progress.pPackage = pPackage;
1286 + progress.pPayload = pPayload;
1287 + progress.pUX = pUX;
1288 + progress.qwTotalCacheSize = qwTotalCacheSize;
1289 + if (fAlreadyProvidedProgress)
1290 + {
1291 + Assert(qwSuccessfulCachedProgress >= static_cast<DWORD64>(liContainerOrPayloadSize.QuadPart));
1292 + progress.qwCacheProgress = qwSuccessfulCachedProgress - liContainerOrPayloadSize.QuadPart; // remove the payload size, since it was marked successful thus included in the successful size already.
1293 + }
1294 + else
1295 + {
1296 + progress.qwCacheProgress = qwSuccessfulCachedProgress;
1297 + }
1298 +
1299 + *pfRetry = FALSE;
1300 +
1301 + do
1302 + {
1303 + hr = UserExperienceOnCacheVerifyBegin(pUX, wzPackageOrContainerId, wzPayloadId);
1304 + ExitOnRootFailure(hr, "BA aborted cache verify begin.");
1305 +
1306 + if (INVALID_HANDLE_VALUE != hPipe) // pass the decision off to the elevated process.
1307 + {
1308 + hr = ElevationCacheOrLayoutContainerOrPayload(hPipe, pContainer, pPackage, pPayload, wzLayoutDirectory, wzUnverifiedPath, fMove);
1309 + }
1310 + else if (wzLayoutDirectory) // layout the container or payload.
1311 + {
1312 + if (pContainer)
1313 + {
1314 + hr = CacheLayoutContainer(pContainer, wzLayoutDirectory, wzUnverifiedPath, fMove);
1315 + }
1316 + else
1317 + {
1318 + hr = CacheLayoutPayload(pPayload, wzLayoutDirectory, wzUnverifiedPath, fMove);
1319 + }
1320 + }
1321 + else // complete the payload.
1322 + {
1323 + Assert(!pContainer);
1324 + Assert(pPackage);
1325 +
1326 + hr = CacheCompletePayload(pPackage->fPerMachine, pPayload, pPackage->sczCacheId, wzUnverifiedPath, fMove);
1327 + }
1328 +
1329 + // If succeeded, send 100% complete here. If the payload was already cached this is the first progress the BA
1330 + // will get.
1331 + if (SUCCEEDED(hr))
1332 + {
1333 + CacheProgressRoutine(liContainerOrPayloadSize, liContainerOrPayloadSize, liZero, liZero, 0, 0, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, &progress);
1334 + if (progress.fCancel)
1335 + {
1336 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1337 + }
1338 + else if (progress.fError)
1339 + {
1340 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE);
1341 + }
1342 + ExitOnRootFailure(hr, "BA aborted verify of %hs: %ls", pContainer ? "container" : "payload", pContainer ? wzPackageOrContainerId : wzPayloadId);
1343 + }
1344 +
1345 + BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION action = FAILED(hr) && cTryAgainAttempts < BURN_CACHE_MAX_RECOMMENDED_VERIFY_TRYAGAIN_ATTEMPTS ? BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYACQUISITION : BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_NONE;
1346 + UserExperienceOnCacheVerifyComplete(pUX, wzPackageOrContainerId, wzPayloadId, hr, &action);
1347 + if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYVERIFICATION == action)
1348 + {
1349 + hr = S_FALSE; // retry verify.
1350 + }
1351 + else if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYACQUISITION == action)
1352 + {
1353 + *pfRetry = TRUE; // go back and retry acquire.
1354 + }
1355 + } while (S_FALSE == hr);
1356 +
1357 +LExit:
1358 + return hr;
1359 +}
1360 +
1361 +static HRESULT PromptForSource(
1362 + __in BURN_USER_EXPERIENCE* pUX,
1363 + __in_z LPCWSTR wzPackageOrContainerId,
1364 + __in_z_opt LPCWSTR wzPayloadId,
1365 + __in_z LPCWSTR wzLocalSource,
1366 + __in_z_opt LPCWSTR wzDownloadSource,
1367 + __inout BOOL* pfRetry,
1368 + __inout BOOL* pfDownload
1369 + )
1370 +{
1371 + HRESULT hr = S_OK;
1372 + BOOTSTRAPPER_RESOLVESOURCE_ACTION action = BOOTSTRAPPER_RESOLVESOURCE_ACTION_NONE;
1373 +
1374 + UserExperienceDeactivateEngine(pUX);
1375 +
1376 + hr = UserExperienceOnResolveSource(pUX, wzPackageOrContainerId, wzPayloadId, wzLocalSource, wzDownloadSource, &action);
1377 + if (FAILED(hr))
1378 + {
1379 + ExitFunction();
1380 + }
1381 +
1382 + switch (action)
1383 + {
1384 + case BOOTSTRAPPER_RESOLVESOURCE_ACTION_NONE:
1385 + hr = E_FILENOTFOUND;
1386 + break;
1387 +
1388 + case BOOTSTRAPPER_RESOLVESOURCE_ACTION_RETRY:
1389 + *pfRetry = TRUE;
1390 + break;
1391 +
1392 + case BOOTSTRAPPER_RESOLVESOURCE_ACTION_DOWNLOAD:
1393 + *pfDownload = TRUE;
1394 + break;
1395 +
1396 + default:
1397 + hr = E_INVALIDARG;
1398 + break;
1399 + }
1400 +
1401 +LExit:
1402 + UserExperienceActivateEngine(pUX, NULL);
1403 + return hr;
1404 +}
1405 +
1406 +static HRESULT CopyPayload(
1407 + __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress,
1408 + __in_z LPCWSTR wzSourcePath,
1409 + __in_z LPCWSTR wzDestinationPath
1410 + )
1411 +{
1412 + HRESULT hr = S_OK;
1413 + DWORD dwFileAttributes = 0;
1414 + LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : L"";
1415 + LPCWSTR wzPayloadId = pProgress->pPayload ? pProgress->pPayload->sczKey : L"";
1416 +
1417 + DWORD dwLogId = pProgress->pContainer ? (pProgress->pPayload ? MSG_ACQUIRE_CONTAINER_PAYLOAD : MSG_ACQUIRE_CONTAINER) : pProgress->pPackage ? MSG_ACQUIRE_PACKAGE_PAYLOAD : MSG_ACQUIRE_BUNDLE_PAYLOAD;
1418 + LogId(REPORT_STANDARD, dwLogId, wzPackageOrContainerId, wzPayloadId, "copy", wzSourcePath);
1419 +
1420 + // If the destination file already exists, clear the readonly bit to avoid E_ACCESSDENIED.
1421 + if (FileExistsEx(wzDestinationPath, &dwFileAttributes))
1422 + {
1423 + if (FILE_ATTRIBUTE_READONLY & dwFileAttributes)
1424 + {
1425 + dwFileAttributes &= ~FILE_ATTRIBUTE_READONLY;
1426 + if (!::SetFileAttributes(wzDestinationPath, dwFileAttributes))
1427 + {
1428 + ExitWithLastError(hr, "Failed to clear readonly bit on payload destination path: %ls", wzDestinationPath);
1429 + }
1430 + }
1431 + }
1432 +
1433 + if (!::CopyFileExW(wzSourcePath, wzDestinationPath, CacheProgressRoutine, pProgress, &pProgress->fCancel, 0))
1434 + {
1435 + if (pProgress->fCancel)
1436 + {
1437 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1438 + ExitOnRootFailure(hr, "BA aborted copy of payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath);
1439 + }
1440 + else
1441 + {
1442 + ExitWithLastError(hr, "Failed attempt to copy payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath);
1443 + }
1444 + }
1445 +
1446 +LExit:
1447 + return hr;
1448 +}
1449 +
1450 +static HRESULT DownloadPayload(
1451 + __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress,
1452 + __in_z LPCWSTR wzDestinationPath
1453 + )
1454 +{
1455 + HRESULT hr = S_OK;
1456 + DWORD dwFileAttributes = 0;
1457 + LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : L"";
1458 + LPCWSTR wzPayloadId = pProgress->pPayload ? pProgress->pPayload->sczKey : L"";
1459 + DOWNLOAD_SOURCE* pDownloadSource = pProgress->pContainer ? &pProgress->pContainer->downloadSource : &pProgress->pPayload->downloadSource;
1460 + DWORD64 qwDownloadSize = pProgress->pContainer ? pProgress->pContainer->qwFileSize : pProgress->pPayload->qwFileSize;
1461 + DOWNLOAD_CACHE_CALLBACK cacheCallback = { };
1462 + DOWNLOAD_AUTHENTICATION_CALLBACK authenticationCallback = { };
1463 + APPLY_AUTHENTICATION_REQUIRED_DATA authenticationData = { };
1464 +
1465 + DWORD dwLogId = pProgress->pContainer ? (pProgress->pPayload ? MSG_ACQUIRE_CONTAINER_PAYLOAD : MSG_ACQUIRE_CONTAINER) : pProgress->pPackage ? MSG_ACQUIRE_PACKAGE_PAYLOAD : MSG_ACQUIRE_BUNDLE_PAYLOAD;
1466 + LogId(REPORT_STANDARD, dwLogId, wzPackageOrContainerId, wzPayloadId, "download", pDownloadSource->sczUrl);
1467 +
1468 + // If the destination file already exists, clear the readonly bit to avoid E_ACCESSDENIED.
1469 + if (FileExistsEx(wzDestinationPath, &dwFileAttributes))
1470 + {
1471 + if (FILE_ATTRIBUTE_READONLY & dwFileAttributes)
1472 + {
1473 + dwFileAttributes &= ~FILE_ATTRIBUTE_READONLY;
1474 + if (!::SetFileAttributes(wzDestinationPath, dwFileAttributes))
1475 + {
1476 + ExitWithLastError(hr, "Failed to clear readonly bit on payload destination path: %ls", wzDestinationPath);
1477 + }
1478 + }
1479 + }
1480 +
1481 + cacheCallback.pfnProgress = CacheProgressRoutine;
1482 + cacheCallback.pfnCancel = NULL; // TODO: set this
1483 + cacheCallback.pv = pProgress;
1484 +
1485 + // If the protocol is specially marked, "bits" let's use that.
1486 + if (L'b' == pDownloadSource->sczUrl[0] &&
1487 + L'i' == pDownloadSource->sczUrl[1] &&
1488 + L't' == pDownloadSource->sczUrl[2] &&
1489 + L's' == pDownloadSource->sczUrl[3] &&
1490 + (L':' == pDownloadSource->sczUrl[4] || (L's' == pDownloadSource->sczUrl[4] && L':' == pDownloadSource->sczUrl[5]))
1491 + )
1492 + {
1493 + hr = BitsDownloadUrl(&cacheCallback, pDownloadSource, wzDestinationPath);
1494 + }
1495 + else // wininet handles everything else.
1496 + {
1497 + authenticationData.pUX = pProgress->pUX;
1498 + authenticationData.wzPackageOrContainerId = wzPackageOrContainerId;
1499 + authenticationData.wzPayloadId = wzPayloadId;
1500 + authenticationCallback.pv = static_cast<LPVOID>(&authenticationData);
1501 + authenticationCallback.pfnAuthenticate = &AuthenticationRequired;
1502 +
1503 + hr = DownloadUrl(pDownloadSource, qwDownloadSize, wzDestinationPath, &cacheCallback, &authenticationCallback);
1504 + }
1505 + ExitOnFailure(hr, "Failed attempt to download URL: '%ls' to: '%ls'", pDownloadSource->sczUrl, wzDestinationPath);
1506 +
1507 +LExit:
1508 + return hr;
1509 +}
1510 +
1511 +static HRESULT WINAPI AuthenticationRequired(
1512 + __in LPVOID pData,
1513 + __in HINTERNET hUrl,
1514 + __in long lHttpCode,
1515 + __out BOOL* pfRetrySend,
1516 + __out BOOL* pfRetry
1517 + )
1518 +{
1519 + Assert(401 == lHttpCode || 407 == lHttpCode);
1520 +
1521 + HRESULT hr = S_OK;
1522 + DWORD er = ERROR_SUCCESS;
1523 + BOOTSTRAPPER_ERROR_TYPE errorType = (401 == lHttpCode) ? BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_SERVER : BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_PROXY;
1524 + LPWSTR sczError = NULL;
1525 + int nResult = IDNOACTION;
1526 +
1527 + *pfRetrySend = FALSE;
1528 + *pfRetry = FALSE;
1529 +
1530 + hr = StrAllocFromError(&sczError, HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED), NULL);
1531 + ExitOnFailure(hr, "Failed to allocation error string.");
1532 +
1533 + APPLY_AUTHENTICATION_REQUIRED_DATA* authenticationData = reinterpret_cast<APPLY_AUTHENTICATION_REQUIRED_DATA*>(pData);
1534 +
1535 + UserExperienceOnError(authenticationData->pUX, errorType, authenticationData->wzPackageOrContainerId, ERROR_ACCESS_DENIED, sczError, MB_RETRYTRYAGAIN, 0, NULL, &nResult); // ignore return value;
1536 + nResult = UserExperienceCheckExecuteResult(authenticationData->pUX, FALSE, MB_RETRYTRYAGAIN, nResult);
1537 + if (IDTRYAGAIN == nResult && authenticationData->pUX->hwndApply)
1538 + {
1539 + er = ::InternetErrorDlg(authenticationData->pUX->hwndApply, hUrl, ERROR_INTERNET_INCORRECT_PASSWORD, FLAGS_ERROR_UI_FILTER_FOR_ERRORS | FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS | FLAGS_ERROR_UI_FLAGS_GENERATE_DATA, NULL);
1540 + if (ERROR_SUCCESS == er || ERROR_CANCELLED == er)
1541 + {
1542 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1543 + }
1544 + else if (ERROR_INTERNET_FORCE_RETRY == er)
1545 + {
1546 + *pfRetrySend = TRUE;
1547 + hr = S_OK;
1548 + }
1549 + else
1550 + {
1551 + hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
1552 + }
1553 + }
1554 + else if (IDRETRY == nResult)
1555 + {
1556 + *pfRetry = TRUE;
1557 + hr = S_OK;
1558 + }
1559 + else
1560 + {
1561 + hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
1562 + }
1563 +
1564 +LExit:
1565 + ReleaseStr(sczError);
1566 +
1567 + return hr;
1568 +}
1569 +
1570 +static DWORD CALLBACK CacheProgressRoutine(
1571 + __in LARGE_INTEGER TotalFileSize,
1572 + __in LARGE_INTEGER TotalBytesTransferred,
1573 + __in LARGE_INTEGER /*StreamSize*/,
1574 + __in LARGE_INTEGER /*StreamBytesTransferred*/,
1575 + __in DWORD /*dwStreamNumber*/,
1576 + __in DWORD /*dwCallbackReason*/,
1577 + __in HANDLE /*hSourceFile*/,
1578 + __in HANDLE /*hDestinationFile*/,
1579 + __in_opt LPVOID lpData
1580 + )
1581 +{
1582 + HRESULT hr = S_OK;
1583 + DWORD dwResult = PROGRESS_CONTINUE;
1584 + BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress = static_cast<BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT*>(lpData);
1585 + LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : NULL;
1586 + LPCWSTR wzPayloadId = pProgress->pPayload ? pProgress->pPayload->sczKey : NULL;
1587 + DWORD64 qwCacheProgress = pProgress->qwCacheProgress + TotalBytesTransferred.QuadPart;
1588 + if (qwCacheProgress > pProgress->qwTotalCacheSize)
1589 + {
1590 + AssertSz(FALSE, "Apply has cached more than Plan envisioned.");
1591 + qwCacheProgress = pProgress->qwTotalCacheSize;
1592 + }
1593 + DWORD dwOverallPercentage = pProgress->qwTotalCacheSize ? static_cast<DWORD>(qwCacheProgress * 100 / pProgress->qwTotalCacheSize) : 0;
1594 +
1595 + hr = UserExperienceOnCacheAcquireProgress(pProgress->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage);
1596 + if (HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) == hr)
1597 + {
1598 + dwResult = PROGRESS_CANCEL;
1599 + pProgress->fCancel = TRUE;
1600 + }
1601 + else if (FAILED(hr))
1602 + {
1603 + dwResult = PROGRESS_CANCEL;
1604 + pProgress->fError = TRUE;
1605 + }
1606 + else
1607 + {
1608 + dwResult = PROGRESS_CONTINUE;
1609 + }
1610 +
1611 + return dwResult;
1612 +}
1613 +
1614 +static void DoRollbackCache(
1615 + __in BURN_USER_EXPERIENCE* /*pUX*/,
1616 + __in BURN_PLAN* pPlan,
1617 + __in HANDLE hPipe,
1618 + __in DWORD dwCheckpoint
1619 + )
1620 +{
1621 + HRESULT hr = S_OK;
1622 + DWORD iCheckpoint = 0;
1623 +
1624 + // Scan to last checkpoint.
1625 + for (DWORD i = 0; i < pPlan->cRollbackCacheActions; ++i)
1626 + {
1627 + BURN_CACHE_ACTION* pRollbackCacheAction = &pPlan->rgRollbackCacheActions[i];
1628 +
1629 + if (BURN_CACHE_ACTION_TYPE_CHECKPOINT == pRollbackCacheAction->type && pRollbackCacheAction->checkpoint.dwId == dwCheckpoint)
1630 + {
1631 + iCheckpoint = i;
1632 + break;
1633 + }
1634 + }
1635 +
1636 + // Rollback cache actions.
1637 + if (iCheckpoint)
1638 + {
1639 + // i has to be a signed integer so it doesn't get decremented to 0xFFFFFFFF.
1640 + for (int i = iCheckpoint - 1; i >= 0; --i)
1641 + {
1642 + BURN_CACHE_ACTION* pRollbackCacheAction = &pPlan->rgRollbackCacheActions[i];
1643 +
1644 + switch (pRollbackCacheAction->type)
1645 + {
1646 + case BURN_CACHE_ACTION_TYPE_CHECKPOINT:
1647 + break;
1648 +
1649 + case BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE:
1650 + hr = CleanPackage(hPipe, pRollbackCacheAction->rollbackPackage.pPackage);
1651 + break;
1652 +
1653 + default:
1654 + AssertSz(FALSE, "Invalid rollback cache action.");
1655 + break;
1656 + }
1657 + }
1658 + }
1659 +}
1660 +
1661 +/* MSI Transactions:
1662 + * All MSI/MSP/MSU packages wrapped in MsiBeginTranasaction-MsiEndTransaction pair are installed or uninstalled together.
1663 +*/
1664 +static HRESULT ExecuteMsiBeginTransaction(
1665 + __in BURN_EXECUTE_CONTEXT* pContext
1666 + , __in BURN_ENGINE_STATE* pEngineState
1667 +)
1668 +{
1669 + HRESULT hr = S_OK;
1670 + UINT uResult = ERROR_SUCCESS;
1671 +
1672 + // Per user/machine context
1673 + if (pEngineState->plan.fPerMachine)
1674 + {
1675 + hr = ElevationMsiBeginTransaction(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pContext);
1676 + ExitOnFailure(hr, "Failed to begin an MSI transaction.");
1677 + }
1678 + else
1679 + {
1680 + MSIHANDLE hMsiTrns = NULL;
1681 + HANDLE hMsiTrnsEvent = NULL;
1682 + uResult = MsiBeginTransaction(L"WiX", 0, &hMsiTrns, &hMsiTrnsEvent);
1683 + ExitOnWin32Error(uResult, hr, "Failed beginning an MSI transaction");
1684 + }
1685 +
1686 +LExit:
1687 + return hr;
1688 +}
1689 +
1690 +static HRESULT ExecuteMsiCommitTransaction(
1691 + __in BURN_EXECUTE_CONTEXT* pContext
1692 + , __in BURN_ENGINE_STATE* pEngineState
1693 +)
1694 +{
1695 + HRESULT hr = S_OK;
1696 + UINT uResult = ERROR_SUCCESS;
1697 +
1698 + // Per user/machine context
1699 + if (pEngineState->plan.fPerMachine)
1700 + {
1701 + hr = ElevationMsiCommitTransaction(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pContext);
1702 + ExitOnFailure(hr, "Failed to commit an MSI transaction.");
1703 + }
1704 + else
1705 + {
1706 + uResult = MsiEndTransaction(MSITRANSACTIONSTATE_COMMIT);
1707 + ExitOnWin32Error(uResult, hr, "Failed beginning an MSI transaction");
1708 + }
1709 +
1710 +LExit:
1711 + return hr;
1712 +}
1713 +
1714 +static HRESULT ExecuteMsiRollbackTransaction(
1715 + __in BURN_EXECUTE_CONTEXT* pContext
1716 + , __in BURN_ENGINE_STATE* pEngineState
1717 +)
1718 +{
1719 + HRESULT hr = S_OK;
1720 + UINT uResult = ERROR_SUCCESS;
1721 +
1722 + // Per user/machine context
1723 + if (pEngineState->plan.fPerMachine)
1724 + {
1725 + hr = ElevationMsiRollbackTransaction(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pContext);
1726 + ExitOnFailure(hr, "Failed to rollback an MSI transaction.");
1727 + }
1728 + else
1729 + {
1730 + uResult = MsiEndTransaction(MSITRANSACTIONSTATE_ROLLBACK);
1731 + ExitOnWin32Error(uResult, hr, "Failed beginning an MSI transaction");
1732 + }
1733 +
1734 +LExit:
1735 + return hr;
1736 +}
1737 +
1738 +// Currently, supporting only elevated transactions.
1739 +static HRESULT DoMsiBeginTransaction(
1740 + __in BURN_EXECUTE_CONTEXT *pContext
1741 + , __in BURN_ENGINE_STATE* pEngineState
1742 +)
1743 +{
1744 + HRESULT hr = S_OK;
1745 +
1746 + hr = ExecuteMsiBeginTransaction(pContext, pEngineState);
1747 + ExitOnFailure(hr, "Failed to execute EXE package.");
1748 +
1749 +LExit:
1750 + return hr;
1751 +}
1752 +
1753 +static HRESULT DoMsiCommitTransaction(
1754 + __in BURN_EXECUTE_CONTEXT *pContext
1755 + , __in BURN_ENGINE_STATE* pEngineState
1756 +)
1757 +{
1758 + HRESULT hr = S_OK;
1759 +
1760 + hr = ExecuteMsiCommitTransaction(pContext, pEngineState);
1761 + ExitOnFailure(hr, "Failed to execute EXE package.");
1762 +
1763 +LExit:
1764 + return hr;
1765 +}
1766 +
1767 +static HRESULT DoMsiRollbackTransaction(
1768 + __in BURN_EXECUTE_CONTEXT *pContext
1769 + , __in BURN_ENGINE_STATE* pEngineState
1770 +)
1771 +{
1772 + HRESULT hr = S_OK;
1773 +
1774 + hr = ExecuteMsiRollbackTransaction(pContext, pEngineState);
1775 + ExitOnFailure(hr, "Failed to execute EXE package.");
1776 +
1777 +LExit:
1778 + return hr;
1779 +}
1780 +
1781 +
1782 +static HRESULT DoExecuteAction(
1783 + __in BURN_ENGINE_STATE* pEngineState,
1784 + __in BURN_EXECUTE_ACTION* pExecuteAction,
1785 + __in_opt HANDLE hCacheThread,
1786 + __in BURN_EXECUTE_CONTEXT* pContext,
1787 + __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary,
1788 + __out DWORD* pdwCheckpoint,
1789 + __out BOOL* pfKeepRegistration,
1790 + __out BOOL* pfSuspend,
1791 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart
1792 + )
1793 +{
1794 + Assert(!pExecuteAction->fDeleted);
1795 +
1796 + HRESULT hr = S_OK;
1797 + HANDLE rghWait[2] = { };
1798 + BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE;
1799 + BOOL fRetry = FALSE;
1800 + BOOL fStopWusaService = FALSE;
1801 +
1802 + pContext->fRollback = FALSE;
1803 +
1804 + do
1805 + {
1806 + switch (pExecuteAction->type)
1807 + {
1808 + case BURN_EXECUTE_ACTION_TYPE_CHECKPOINT:
1809 + *pdwCheckpoint = pExecuteAction->checkpoint.dwId;
1810 + break;
1811 +
1812 + case BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT:
1813 + // wait for cache sync-point
1814 + rghWait[0] = pExecuteAction->syncpoint.hEvent;
1815 + rghWait[1] = hCacheThread;
1816 + switch (::WaitForMultipleObjects(rghWait[1] ? 2 : 1, rghWait, FALSE, INFINITE))
1817 + {
1818 + case WAIT_OBJECT_0:
1819 + break;
1820 +
1821 + case WAIT_OBJECT_0 + 1:
1822 + if (!::GetExitCodeThread(hCacheThread, (DWORD*)&hr))
1823 + {
1824 + ExitWithLastError(hr, "Failed to get cache thread exit code.");
1825 + }
1826 +
1827 + if (SUCCEEDED(hr))
1828 + {
1829 + hr = E_UNEXPECTED;
1830 + }
1831 + ExitOnFailure(hr, "Cache thread exited unexpectedly.");
1832 +
1833 + case WAIT_FAILED: __fallthrough;
1834 + default:
1835 + ExitWithLastError(hr, "Failed to wait for cache check-point.");
1836 + }
1837 + break;
1838 +
1839 + case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE:
1840 + hr = ExecuteExePackage(pEngineState, pExecuteAction, pContext, FALSE, &fRetry, pfSuspend, &restart);
1841 + ExitOnFailure(hr, "Failed to execute EXE package.");
1842 + break;
1843 +
1844 + case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE:
1845 + hr = ExecuteMsiPackage(pEngineState, pExecuteAction, pContext, FALSE, &fRetry, pfSuspend, &restart);
1846 + ExitOnFailure(hr, "Failed to execute MSI package.");
1847 + break;
1848 +
1849 + case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET:
1850 + hr = ExecuteMspPackage(pEngineState, pExecuteAction, pContext, FALSE, &fRetry, pfSuspend, &restart);
1851 + ExitOnFailure(hr, "Failed to execute MSP package.");
1852 + break;
1853 +
1854 + case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE:
1855 + hr = ExecuteMsuPackage(pEngineState, pExecuteAction, pContext, FALSE, fStopWusaService, &fRetry, pfSuspend, &restart);
1856 + fStopWusaService = fRetry;
1857 + ExitOnFailure(hr, "Failed to execute MSU package.");
1858 + break;
1859 +
1860 + case BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER:
1861 + hr = ExecutePackageProviderAction(pEngineState, pExecuteAction, pContext);
1862 + ExitOnFailure(hr, "Failed to execute package provider registration action.");
1863 + break;
1864 +
1865 + case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY:
1866 + hr = ExecuteDependencyAction(pEngineState, pExecuteAction, pContext);
1867 + ExitOnFailure(hr, "Failed to execute dependency action.");
1868 + break;
1869 +
1870 + case BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE:
1871 + hr = ExecuteCompatiblePackageAction(pEngineState, pExecuteAction);
1872 + ExitOnFailure(hr, "Failed to execute compatible package action.");
1873 + break;
1874 +
1875 + case BURN_EXECUTE_ACTION_TYPE_REGISTRATION:
1876 + *pfKeepRegistration = pExecuteAction->registration.fKeep;
1877 + break;
1878 +
1879 + case BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY:
1880 + *ppRollbackBoundary = pExecuteAction->rollbackBoundary.pRollbackBoundary;
1881 + break;
1882 +
1883 + case BURN_EXECUTE_ACTION_TYPE_SERVICE_STOP: __fallthrough;
1884 + case BURN_EXECUTE_ACTION_TYPE_SERVICE_START: __fallthrough;
1885 + default:
1886 + hr = E_UNEXPECTED;
1887 + ExitOnFailure(hr, "Invalid execute action.");
1888 + }
1889 +
1890 + if (*pRestart < restart)
1891 + {
1892 + *pRestart = restart;
1893 + }
1894 + } while (fRetry && *pRestart < BOOTSTRAPPER_APPLY_RESTART_INITIATED);
1895 +
1896 +LExit:
1897 + return hr;
1898 +}
1899 +
1900 +static HRESULT DoRollbackActions(
1901 + __in BURN_ENGINE_STATE* pEngineState,
1902 + __in BURN_EXECUTE_CONTEXT* pContext,
1903 + __in DWORD dwCheckpoint,
1904 + __in BOOL fInTransaction,
1905 + __out BOOL* pfKeepRegistration,
1906 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart
1907 +)
1908 +{
1909 + HRESULT hr = S_OK;
1910 + DWORD iCheckpoint = 0;
1911 + BOOL fRetryIgnored = FALSE;
1912 + BOOL fSuspendIgnored = FALSE;
1913 +
1914 + pContext->fRollback = TRUE;
1915 +
1916 + // Rollback MSI transaction
1917 + if (fInTransaction)
1918 + {
1919 + hr = DoMsiRollbackTransaction(pContext, pEngineState);
1920 + ExitOnFailure(hr, "Failed rolling back transaction");
1921 + }
1922 +
1923 + // scan to last checkpoint
1924 + for (DWORD i = 0; i < pEngineState->plan.cRollbackActions; ++i)
1925 + {
1926 + BURN_EXECUTE_ACTION* pRollbackAction = &pEngineState->plan.rgRollbackActions[i];
1927 + if (pRollbackAction->fDeleted)
1928 + {
1929 + continue;
1930 + }
1931 +
1932 + if (BURN_EXECUTE_ACTION_TYPE_CHECKPOINT == pRollbackAction->type)
1933 + {
1934 + if (pRollbackAction->checkpoint.dwId == dwCheckpoint)
1935 + {
1936 + iCheckpoint = i;
1937 + break;
1938 + }
1939 + }
1940 + }
1941 +
1942 + // execute rollback actions
1943 + if (iCheckpoint)
1944 + {
1945 + // i has to be a signed integer so it doesn't get decremented to 0xFFFFFFFF.
1946 + for (int i = iCheckpoint - 1; i >= 0; --i)
1947 + {
1948 + BURN_EXECUTE_ACTION* pRollbackAction = &pEngineState->plan.rgRollbackActions[i];
1949 + if (pRollbackAction->fDeleted)
1950 + {
1951 + continue;
1952 + }
1953 +
1954 + BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE;
1955 + switch (pRollbackAction->type)
1956 + {
1957 + case BURN_EXECUTE_ACTION_TYPE_CHECKPOINT:
1958 + break;
1959 +
1960 + case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE:
1961 + hr = ExecuteExePackage(pEngineState, pRollbackAction, pContext, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart);
1962 + TraceError(hr, "Failed to rollback EXE package.");
1963 + hr = S_OK;
1964 + break;
1965 +
1966 + case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE:
1967 + if (fInTransaction)
1968 + {
1969 + LogString(REPORT_STANDARD, "Skipping rolling back an MSI package- already done in transaction rollback\n");
1970 + break;
1971 + }
1972 + hr = ExecuteMsiPackage(pEngineState, pRollbackAction, pContext, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart);
1973 + TraceError(hr, "Failed to rollback MSI package.");
1974 + hr = S_OK;
1975 + break;
1976 +
1977 + case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET:
1978 + if (fInTransaction)
1979 + {
1980 + LogString(REPORT_STANDARD, "Skipping rolling back an MSP package- already done in transaction rollback\n");
1981 + break;
1982 + }
1983 + hr = ExecuteMspPackage(pEngineState, pRollbackAction, pContext, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart);
1984 + TraceError(hr, "Failed to rollback MSP package.");
1985 + hr = S_OK;
1986 + break;
1987 +
1988 + case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE:
1989 + if (fInTransaction)
1990 + {
1991 + LogString(REPORT_STANDARD, "Skipping rolling back an MSU package- already done in transaction rollback\n");
1992 + break;
1993 + }
1994 + hr = ExecuteMsuPackage(pEngineState, pRollbackAction, pContext, TRUE, FALSE, &fRetryIgnored, &fSuspendIgnored, &restart);
1995 + TraceError(hr, "Failed to rollback MSU package.");
1996 + hr = S_OK;
1997 + break;
1998 +
1999 + case BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER:
2000 + hr = ExecutePackageProviderAction(pEngineState, pRollbackAction, pContext);
2001 + TraceError(hr, "Failed to rollback package provider action.");
2002 + hr = S_OK;
2003 + break;
2004 +
2005 + case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY:
2006 + hr = ExecuteDependencyAction(pEngineState, pRollbackAction, pContext);
2007 + TraceError(hr, "Failed to rollback dependency action.");
2008 + hr = S_OK;
2009 + break;
2010 +
2011 + case BURN_EXECUTE_ACTION_TYPE_REGISTRATION:
2012 + *pfKeepRegistration = pRollbackAction->registration.fKeep;
2013 + break;
2014 +
2015 + case BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY:
2016 + ExitFunction1(hr = S_OK);
2017 +
2018 + case BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE:
2019 + hr = CleanPackage(pEngineState->companionConnection.hPipe, pRollbackAction->uncachePackage.pPackage);
2020 + break;
2021 +
2022 + case BURN_EXECUTE_ACTION_TYPE_SERVICE_STOP: __fallthrough;
2023 + case BURN_EXECUTE_ACTION_TYPE_SERVICE_START: __fallthrough;
2024 + default:
2025 + hr = E_UNEXPECTED;
2026 + ExitOnFailure(hr, "Invalid rollback action: %d.", pRollbackAction->type);
2027 + }
2028 +
2029 + if (*pRestart < restart)
2030 + {
2031 + *pRestart = restart;
2032 + }
2033 + }
2034 + }
2035 +
2036 +LExit:
2037 + return hr;
2038 +}
2039 +
2040 +static HRESULT ExecuteExePackage(
2041 + __in BURN_ENGINE_STATE* pEngineState,
2042 + __in BURN_EXECUTE_ACTION* pExecuteAction,
2043 + __in BURN_EXECUTE_CONTEXT* pContext,
2044 + __in BOOL fRollback,
2045 + __out BOOL* pfRetry,
2046 + __out BOOL* pfSuspend,
2047 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart
2048 + )
2049 +{
2050 + HRESULT hr = S_OK;
2051 + HRESULT hrExecute = S_OK;
2052 + GENERIC_EXECUTE_MESSAGE message = { };
2053 + int nResult = 0;
2054 + BOOL fBeginCalled = FALSE;
2055 +
2056 + if (FAILED(pExecuteAction->exePackage.pPackage->hrCacheResult))
2057 + {
2058 + LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pExecuteAction->exePackage.pPackage->sczId, pExecuteAction->exePackage.pPackage->hrCacheResult);
2059 + ExitFunction1(hr = S_OK);
2060 + }
2061 +
2062 + Assert(pContext->fRollback == fRollback);
2063 + pContext->pExecutingPackage = pExecuteAction->exePackage.pPackage;
2064 + fBeginCalled = TRUE;
2065 +
2066 + // Send package execute begin to BA.
2067 + hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->exePackage.pPackage->sczId, !fRollback);
2068 + ExitOnRootFailure(hr, "BA aborted execute EXE package begin.");
2069 +
2070 + message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
2071 + message.dwAllowedResults = MB_OKCANCEL;
2072 + message.progress.dwPercentage = fRollback ? 100 : 0;
2073 + nResult = GenericExecuteMessageHandler(&message, pContext);
2074 + hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult);
2075 + ExitOnRootFailure(hr, "UX aborted EXE progress.");
2076 +
2077 + // Execute package.
2078 + if (pExecuteAction->exePackage.pPackage->fPerMachine)
2079 + {
2080 + hrExecute = ElevationExecuteExePackage(pEngineState->companionConnection.hPipe, pExecuteAction, &pEngineState->variables, fRollback, GenericExecuteMessageHandler, pContext, pRestart);
2081 + ExitOnFailure(hrExecute, "Failed to configure per-machine EXE package.");
2082 + }
2083 + else
2084 + {
2085 + hrExecute = ExeEngineExecutePackage(pExecuteAction, &pEngineState->variables, fRollback, GenericExecuteMessageHandler, pContext, pRestart);
2086 + ExitOnFailure(hrExecute, "Failed to configure per-user EXE package.");
2087 + }
2088 +
2089 + message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
2090 + message.dwAllowedResults = MB_OKCANCEL;
2091 + message.progress.dwPercentage = fRollback ? 0 : 100;
2092 + nResult = GenericExecuteMessageHandler(&message, pContext);
2093 + hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult);
2094 + ExitOnRootFailure(hr, "UX aborted EXE progress.");
2095 +
2096 + pContext->cExecutedPackages += fRollback ? -1 : 1;
2097 + (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1;
2098 +
2099 + hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks);
2100 + ExitOnRootFailure(hr, "UX aborted EXE package execute progress.");
2101 +
2102 +LExit:
2103 + if (fBeginCalled)
2104 + {
2105 + hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pExecuteAction->exePackage.pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
2106 + }
2107 +
2108 + return hr;
2109 +}
2110 +
2111 +static HRESULT ExecuteMsiPackage(
2112 + __in BURN_ENGINE_STATE* pEngineState,
2113 + __in BURN_EXECUTE_ACTION* pExecuteAction,
2114 + __in BURN_EXECUTE_CONTEXT* pContext,
2115 + __in BOOL fRollback,
2116 + __out BOOL* pfRetry,
2117 + __out BOOL* pfSuspend,
2118 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart
2119 + )
2120 +{
2121 + HRESULT hr = S_OK;
2122 + HRESULT hrExecute = S_OK;
2123 + BOOL fBeginCalled = FALSE;
2124 +
2125 + if (FAILED(pExecuteAction->msiPackage.pPackage->hrCacheResult))
2126 + {
2127 + LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pExecuteAction->msiPackage.pPackage->sczId, pExecuteAction->msiPackage.pPackage->hrCacheResult);
2128 + ExitFunction1(hr = S_OK);
2129 + }
2130 +
2131 + Assert(pContext->fRollback == fRollback);
2132 + pContext->pExecutingPackage = pExecuteAction->msiPackage.pPackage;
2133 + fBeginCalled = TRUE;
2134 +
2135 + // Send package execute begin to BA.
2136 + hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->msiPackage.pPackage->sczId, !fRollback);
2137 + ExitOnRootFailure(hr, "BA aborted execute MSI package begin.");
2138 +
2139 + // execute package
2140 + if (pExecuteAction->msiPackage.pPackage->fPerMachine)
2141 + {
2142 + hrExecute = ElevationExecuteMsiPackage(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart);
2143 + ExitOnFailure(hrExecute, "Failed to configure per-machine MSI package.");
2144 + }
2145 + else
2146 + {
2147 + hrExecute = MsiEngineExecutePackage(pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart);
2148 + ExitOnFailure(hrExecute, "Failed to configure per-user MSI package.");
2149 + }
2150 +
2151 + pContext->cExecutedPackages += fRollback ? -1 : 1;
2152 + (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1;
2153 +
2154 + hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks);
2155 + ExitOnRootFailure(hr, "UX aborted MSI package execute progress.");
2156 +
2157 +LExit:
2158 + if (fBeginCalled)
2159 + {
2160 + hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pExecuteAction->msiPackage.pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
2161 + }
2162 +
2163 + return hr;
2164 +}
2165 +
2166 +static HRESULT ExecuteMspPackage(
2167 + __in BURN_ENGINE_STATE* pEngineState,
2168 + __in BURN_EXECUTE_ACTION* pExecuteAction,
2169 + __in BURN_EXECUTE_CONTEXT* pContext,
2170 + __in BOOL fRollback,
2171 + __out BOOL* pfRetry,
2172 + __out BOOL* pfSuspend,
2173 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart
2174 + )
2175 +{
2176 + HRESULT hr = S_OK;
2177 + HRESULT hrExecute = S_OK;
2178 + BOOL fBeginCalled = FALSE;
2179 +
2180 + if (FAILED(pExecuteAction->mspTarget.pPackage->hrCacheResult))
2181 + {
2182 + LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pExecuteAction->mspTarget.pPackage->sczId, pExecuteAction->mspTarget.pPackage->hrCacheResult);
2183 + ExitFunction1(hr = S_OK);
2184 + }
2185 +
2186 + Assert(pContext->fRollback == fRollback);
2187 + pContext->pExecutingPackage = pExecuteAction->mspTarget.pPackage;
2188 + fBeginCalled = TRUE;
2189 +
2190 + // Send package execute begin to BA.
2191 + hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->mspTarget.pPackage->sczId, !fRollback);
2192 + ExitOnRootFailure(hr, "BA aborted execute MSP package begin.");
2193 +
2194 + // Now send all the patches that target this product code.
2195 + for (DWORD i = 0; i < pExecuteAction->mspTarget.cOrderedPatches; ++i)
2196 + {
2197 + BURN_PACKAGE* pMspPackage = pExecuteAction->mspTarget.rgOrderedPatches[i].pPackage;
2198 +
2199 + hr = UserExperienceOnExecutePatchTarget(&pEngineState->userExperience, pMspPackage->sczId, pExecuteAction->mspTarget.sczTargetProductCode);
2200 + ExitOnRootFailure(hr, "BA aborted execute MSP target.");
2201 + }
2202 +
2203 + // execute package
2204 + if (pExecuteAction->mspTarget.fPerMachineTarget)
2205 + {
2206 + hrExecute = ElevationExecuteMspPackage(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart);
2207 + ExitOnFailure(hrExecute, "Failed to configure per-machine MSP package.");
2208 + }
2209 + else
2210 + {
2211 + hrExecute = MspEngineExecutePackage(pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart);
2212 + ExitOnFailure(hrExecute, "Failed to configure per-user MSP package.");
2213 + }
2214 +
2215 + pContext->cExecutedPackages += fRollback ? -1 : 1;
2216 + (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1;
2217 +
2218 + hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks);
2219 + ExitOnRootFailure(hr, "UX aborted MSP package execute progress.");
2220 +
2221 +LExit:
2222 + if (fBeginCalled)
2223 + {
2224 + hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pExecuteAction->mspTarget.pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
2225 + }
2226 +
2227 + return hr;
2228 +}
2229 +
2230 +static HRESULT ExecuteMsuPackage(
2231 + __in BURN_ENGINE_STATE* pEngineState,
2232 + __in BURN_EXECUTE_ACTION* pExecuteAction,
2233 + __in BURN_EXECUTE_CONTEXT* pContext,
2234 + __in BOOL fRollback,
2235 + __in BOOL fStopWusaService,
2236 + __out BOOL* pfRetry,
2237 + __out BOOL* pfSuspend,
2238 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart
2239 + )
2240 +{
2241 + HRESULT hr = S_OK;
2242 + HRESULT hrExecute = S_OK;
2243 + GENERIC_EXECUTE_MESSAGE message = { };
2244 + int nResult = 0;
2245 + BOOL fBeginCalled = FALSE;
2246 +
2247 + if (FAILED(pExecuteAction->msuPackage.pPackage->hrCacheResult))
2248 + {
2249 + LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pExecuteAction->msuPackage.pPackage->sczId, pExecuteAction->msuPackage.pPackage->hrCacheResult);
2250 + ExitFunction1(hr = S_OK);
2251 + }
2252 +
2253 + Assert(pContext->fRollback == fRollback);
2254 + pContext->pExecutingPackage = pExecuteAction->msuPackage.pPackage;
2255 + fBeginCalled = TRUE;
2256 +
2257 + // Send package execute begin to BA.
2258 + hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->msuPackage.pPackage->sczId, !fRollback);
2259 + ExitOnRootFailure(hr, "BA aborted execute MSU package begin.");
2260 +
2261 + message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
2262 + message.dwAllowedResults = MB_OKCANCEL;
2263 + message.progress.dwPercentage = fRollback ? 100 : 0;
2264 + nResult = GenericExecuteMessageHandler(&message, pContext);
2265 + hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult);
2266 + ExitOnRootFailure(hr, "UX aborted MSU progress.");
2267 +
2268 + // execute package
2269 + if (pExecuteAction->msuPackage.pPackage->fPerMachine)
2270 + {
2271 + hrExecute = ElevationExecuteMsuPackage(pEngineState->companionConnection.hPipe, pExecuteAction, fRollback, fStopWusaService, GenericExecuteMessageHandler, pContext, pRestart);
2272 + ExitOnFailure(hrExecute, "Failed to configure per-machine MSU package.");
2273 + }
2274 + else
2275 + {
2276 + hrExecute = E_UNEXPECTED;
2277 + ExitOnFailure(hr, "MSU packages cannot be per-user.");
2278 + }
2279 +
2280 + message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
2281 + message.dwAllowedResults = MB_OKCANCEL;
2282 + message.progress.dwPercentage = fRollback ? 0 : 100;
2283 + nResult = GenericExecuteMessageHandler(&message, pContext);
2284 + hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult);
2285 + ExitOnRootFailure(hr, "UX aborted MSU progress.");
2286 +
2287 + pContext->cExecutedPackages += fRollback ? -1 : 1;
2288 + (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1;
2289 +
2290 + hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks);
2291 + ExitOnRootFailure(hr, "UX aborted MSU package execute progress.");
2292 +
2293 +LExit:
2294 + if (fBeginCalled)
2295 + {
2296 + hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pExecuteAction->msuPackage.pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
2297 + }
2298 +
2299 + return hr;
2300 +}
2301 +
2302 +static HRESULT ExecutePackageProviderAction(
2303 + __in BURN_ENGINE_STATE* pEngineState,
2304 + __in BURN_EXECUTE_ACTION* pAction,
2305 + __in BURN_EXECUTE_CONTEXT* /*pContext*/
2306 + )
2307 +{
2308 + HRESULT hr = S_OK;
2309 +
2310 + if (pAction->packageProvider.pPackage->fPerMachine)
2311 + {
2312 + hr = ElevationExecutePackageProviderAction(pEngineState->companionConnection.hPipe, pAction);
2313 + ExitOnFailure(hr, "Failed to register the package provider on per-machine package.");
2314 + }
2315 + else
2316 + {
2317 + hr = DependencyExecutePackageProviderAction(pAction);
2318 + ExitOnFailure(hr, "Failed to register the package provider on per-user package.");
2319 + }
2320 +
2321 +LExit:
2322 + return hr;
2323 +}
2324 +
2325 +static HRESULT ExecuteDependencyAction(
2326 + __in BURN_ENGINE_STATE* pEngineState,
2327 + __in BURN_EXECUTE_ACTION* pAction,
2328 + __in BURN_EXECUTE_CONTEXT* /*pContext*/
2329 + )
2330 +{
2331 + HRESULT hr = S_OK;
2332 +
2333 + if (pAction->packageDependency.pPackage->fPerMachine)
2334 + {
2335 + hr = ElevationExecutePackageDependencyAction(pEngineState->companionConnection.hPipe, pAction);
2336 + ExitOnFailure(hr, "Failed to register the dependency on per-machine package.");
2337 + }
2338 + else
2339 + {
2340 + hr = DependencyExecutePackageDependencyAction(FALSE, pAction);
2341 + ExitOnFailure(hr, "Failed to register the dependency on per-user package.");
2342 + }
2343 +
2344 +LExit:
2345 + return hr;
2346 +}
2347 +
2348 +static HRESULT ExecuteCompatiblePackageAction(
2349 + __in BURN_ENGINE_STATE* pEngineState,
2350 + __in BURN_EXECUTE_ACTION* pAction
2351 + )
2352 +{
2353 + HRESULT hr = S_OK;
2354 +
2355 + if (pAction->compatiblePackage.pReferencePackage->fPerMachine)
2356 + {
2357 + hr = ElevationLoadCompatiblePackageAction(pEngineState->companionConnection.hPipe, pAction);
2358 + ExitOnFailure(hr, "Failed to load compatible package on per-machine package.");
2359 + }
2360 +
2361 + // Compatible package already loaded in this process.
2362 +
2363 +LExit:
2364 + return hr;
2365 +}
2366 +
2367 +static HRESULT CleanPackage(
2368 + __in HANDLE hElevatedPipe,
2369 + __in BURN_PACKAGE* pPackage
2370 + )
2371 +{
2372 + HRESULT hr = S_OK;
2373 +
2374 + if (pPackage->fPerMachine)
2375 + {
2376 + hr = ElevationCleanPackage(hElevatedPipe, pPackage);
2377 + }
2378 + else
2379 + {
2380 + hr = CacheRemovePackage(FALSE, pPackage->sczId, pPackage->sczCacheId);
2381 + }
2382 +
2383 + return hr;
2384 +}
2385 +
2386 +static int GenericExecuteMessageHandler(
2387 + __in GENERIC_EXECUTE_MESSAGE* pMessage,
2388 + __in LPVOID pvContext
2389 + )
2390 +{
2391 + BURN_EXECUTE_CONTEXT* pContext = (BURN_EXECUTE_CONTEXT*)pvContext;
2392 + int nResult = IDNOACTION;
2393 +
2394 + switch (pMessage->type)
2395 + {
2396 + case GENERIC_EXECUTE_MESSAGE_PROGRESS:
2397 + {
2398 + DWORD dwOverallProgress = pContext->cExecutePackagesTotal ? (pContext->cExecutedPackages * 100 + pMessage->progress.dwPercentage) / (pContext->cExecutePackagesTotal) : 0;
2399 + UserExperienceOnExecuteProgress(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->progress.dwPercentage, dwOverallProgress, &nResult); // ignore return value.
2400 + }
2401 + break;
2402 +
2403 + case GENERIC_EXECUTE_MESSAGE_ERROR:
2404 + UserExperienceOnError(pContext->pUX, BOOTSTRAPPER_ERROR_TYPE_EXE_PACKAGE, pContext->pExecutingPackage->sczId, pMessage->error.dwErrorCode, pMessage->error.wzMessage, pMessage->dwAllowedResults, 0, NULL, &nResult); // ignore return value.
2405 + break;
2406 +
2407 + case GENERIC_EXECUTE_MESSAGE_FILES_IN_USE:
2408 + UserExperienceOnExecuteFilesInUse(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->filesInUse.cFiles, pMessage->filesInUse.rgwzFiles, &nResult); // ignore return value.
2409 + break;
2410 + }
2411 +
2412 + nResult = UserExperienceCheckExecuteResult(pContext->pUX, pContext->fRollback, pMessage->dwAllowedResults, nResult);
2413 + return nResult;
2414 +}
2415 +
2416 +static int MsiExecuteMessageHandler(
2417 + __in WIU_MSI_EXECUTE_MESSAGE* pMessage,
2418 + __in_opt LPVOID pvContext
2419 + )
2420 +{
2421 + BURN_EXECUTE_CONTEXT* pContext = (BURN_EXECUTE_CONTEXT*)pvContext;
2422 + int nResult = IDNOACTION;
2423 +
2424 + switch (pMessage->type)
2425 + {
2426 + case WIU_MSI_EXECUTE_MESSAGE_PROGRESS:
2427 + {
2428 + DWORD dwOverallProgress = pContext->cExecutePackagesTotal ? (pContext->cExecutedPackages * 100 + pMessage->progress.dwPercentage) / (pContext->cExecutePackagesTotal) : 0;
2429 + UserExperienceOnExecuteProgress(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->progress.dwPercentage, dwOverallProgress, &nResult); // ignore return value.
2430 + }
2431 + break;
2432 +
2433 + case WIU_MSI_EXECUTE_MESSAGE_ERROR:
2434 + nResult = pMessage->nResultRecommendation;
2435 + UserExperienceOnError(pContext->pUX, BOOTSTRAPPER_ERROR_TYPE_WINDOWS_INSTALLER, pContext->pExecutingPackage->sczId, pMessage->error.dwErrorCode, pMessage->error.wzMessage, pMessage->dwAllowedResults, pMessage->cData, pMessage->rgwzData, &nResult); // ignore return value.
2436 + break;
2437 +
2438 + case WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE:
2439 + nResult = pMessage->nResultRecommendation;
2440 + UserExperienceOnExecuteMsiMessage(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->msiMessage.mt, pMessage->dwAllowedResults, pMessage->msiMessage.wzMessage, pMessage->cData, pMessage->rgwzData, &nResult); // ignore return value.
2441 + break;
2442 +
2443 + case WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE:
2444 + UserExperienceOnExecuteFilesInUse(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->msiFilesInUse.cFiles, pMessage->msiFilesInUse.rgwzFiles, &nResult); // ignore return value.
2445 + break;
2446 + }
2447 +
2448 + nResult = UserExperienceCheckExecuteResult(pContext->pUX, pContext->fRollback, pMessage->dwAllowedResults, nResult);
2449 + return nResult;
2450 +}
2451 +
2452 +static HRESULT ReportOverallProgressTicks(
2453 + __in BURN_USER_EXPERIENCE* pUX,
2454 + __in BOOL fRollback,
2455 + __in DWORD cOverallProgressTicksTotal,
2456 + __in DWORD cOverallProgressTicks
2457 + )
2458 +{
2459 + HRESULT hr = S_OK;
2460 + DWORD dwProgress = cOverallProgressTicksTotal ? (cOverallProgressTicks * 100 / cOverallProgressTicksTotal) : 0;
2461 +
2462 + // TODO: consider sending different progress numbers in the future.
2463 + hr = UserExperienceOnProgress(pUX, fRollback, dwProgress, dwProgress);
2464 +
2465 + return hr;
2466 +}
2467 +
2468 +static HRESULT ExecutePackageComplete(
2469 + __in BURN_USER_EXPERIENCE* pUX,
2470 + __in BURN_VARIABLES* pVariables,
2471 + __in BURN_PACKAGE* pPackage,
2472 + __in HRESULT hrOverall,
2473 + __in HRESULT hrExecute,
2474 + __in BOOL fRollback,
2475 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart,
2476 + __out BOOL* pfRetry,
2477 + __out BOOL* pfSuspend
2478 + )
2479 +{
2480 + HRESULT hr = FAILED(hrOverall) ? hrOverall : hrExecute; // if the overall function failed use that otherwise use the execution result.
2481 + BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION executePackageCompleteAction = FAILED(hrOverall) || SUCCEEDED(hrExecute) || pPackage->fVital ? BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_NONE : BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_IGNORE;
2482 +
2483 + // Send package execute complete to BA.
2484 + UserExperienceOnExecutePackageComplete(pUX, pPackage->sczId, hr, *pRestart, &executePackageCompleteAction);
2485 + if (BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_RESTART == executePackageCompleteAction)
2486 + {
2487 + *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED;
2488 + }
2489 + *pfRetry = (FAILED(hrExecute) && BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_RETRY == executePackageCompleteAction); // allow retry only on failures.
2490 + *pfSuspend = (BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_SUSPEND == executePackageCompleteAction);
2491 +
2492 + // Remember this package as the package that initiated the forced restart.
2493 + if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == *pRestart)
2494 + {
2495 + // Best effort to set the forced restart package variable.
2496 + VariableSetString(pVariables, BURN_BUNDLE_FORCED_RESTART_PACKAGE, pPackage->sczId, TRUE);
2497 + }
2498 +
2499 + // If we're retrying, leave a message in the log file and say everything is okay.
2500 + if (*pfRetry)
2501 + {
2502 + LogId(REPORT_STANDARD, MSG_APPLY_RETRYING_PACKAGE, pPackage->sczId, hrExecute);
2503 + hr = S_OK;
2504 + }
2505 + else if (SUCCEEDED(hrOverall) && FAILED(hrExecute) && BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_IGNORE == executePackageCompleteAction && !pPackage->fVital) // If we *only* failed to execute and the BA ignored this *not-vital* package, say everything is okay.
2506 + {
2507 + LogId(REPORT_STANDARD, MSG_APPLY_CONTINUING_NONVITAL_PACKAGE, pPackage->sczId, hrExecute);
2508 + hr = S_OK;
2509 + }
2510 + else
2511 + {
2512 + LogId(REPORT_STANDARD, MSG_APPLY_COMPLETED_PACKAGE, LoggingRollbackOrExecute(fRollback), pPackage->sczId, hr, LoggingRestartToString(*pRestart));
2513 + }
2514 +
2515 + return hr;
2516 +}
src/engine/apply.h new
+106
@@ -0,0 +1,106 @@
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 +enum GENERIC_EXECUTE_MESSAGE_TYPE
11 +{
12 + GENERIC_EXECUTE_MESSAGE_NONE,
13 + GENERIC_EXECUTE_MESSAGE_ERROR,
14 + GENERIC_EXECUTE_MESSAGE_PROGRESS,
15 + GENERIC_EXECUTE_MESSAGE_FILES_IN_USE,
16 +};
17 +
18 +typedef struct _APPLY_AUTHENTICATION_REQUIRED_DATA
19 +{
20 + BURN_USER_EXPERIENCE* pUX;
21 + LPCWSTR wzPackageOrContainerId;
22 + LPCWSTR wzPayloadId;
23 +} APPLY_AUTHENTICATION_REQUIRED_DATA;
24 +
25 +typedef struct _GENERIC_EXECUTE_MESSAGE
26 +{
27 + GENERIC_EXECUTE_MESSAGE_TYPE type;
28 + DWORD dwAllowedResults;
29 +
30 + union
31 + {
32 + struct
33 + {
34 + DWORD dwErrorCode;
35 + LPCWSTR wzMessage;
36 + } error;
37 + struct
38 + {
39 + DWORD dwPercentage;
40 + } progress;
41 + struct
42 + {
43 + DWORD cFiles;
44 + LPCWSTR* rgwzFiles;
45 + } filesInUse;
46 + };
47 +} GENERIC_EXECUTE_MESSAGE;
48 +
49 +
50 +typedef int (*PFN_GENERICMESSAGEHANDLER)(
51 + __in GENERIC_EXECUTE_MESSAGE* pMessage,
52 + __in LPVOID pvContext
53 + );
54 +
55 +
56 +void ApplyInitialize();
57 +void ApplyUninitialize();
58 +HRESULT ApplySetVariables(
59 + __in BURN_VARIABLES* pVariables
60 + );
61 +void ApplyReset(
62 + __in BURN_USER_EXPERIENCE* pUX,
63 + __in BURN_PACKAGES* pPackages
64 + );
65 +HRESULT ApplyLock(
66 + __in BOOL fPerMachine,
67 + __out HANDLE* phLock
68 + );
69 +HRESULT ApplyRegister(
70 + __in BURN_ENGINE_STATE* pEngineState
71 + );
72 +HRESULT ApplyUnregister(
73 + __in BURN_ENGINE_STATE* pEngineState,
74 + __in BOOL fFailedOrRollback,
75 + __in BOOL fRollback,
76 + __in BOOL fSuspend,
77 + __in BOOTSTRAPPER_APPLY_RESTART restart
78 + );
79 +HRESULT ApplyCache(
80 + __in HANDLE hSourceEngineFile,
81 + __in BURN_USER_EXPERIENCE* pUX,
82 + __in BURN_VARIABLES* pVariables,
83 + __in BURN_PLAN* pPlan,
84 + __in HANDLE hPipe,
85 + __inout DWORD* pcOverallProgressTicks,
86 + __out BOOL* pfRollback
87 + );
88 +HRESULT ApplyExecute(
89 + __in BURN_ENGINE_STATE* pEngineState,
90 + __in_opt HANDLE hCacheThread,
91 + __inout DWORD* pcOverallProgressTicks,
92 + __out BOOL* pfKeepRegistration,
93 + __out BOOL* pfRollback,
94 + __out BOOL* pfSuspend,
95 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart
96 + );
97 +void ApplyClean(
98 + __in BURN_USER_EXPERIENCE* pUX,
99 + __in BURN_PLAN* pPlan,
100 + __in HANDLE hPipe
101 + );
102 +
103 +
104 +#ifdef __cplusplus
105 +}
106 +#endif
src/engine/approvedexe.cpp new
+262
@@ -0,0 +1,262 @@
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 +// function definitions
7 +
8 +extern "C" HRESULT ApprovedExesParseFromXml(
9 + __in BURN_APPROVED_EXES* pApprovedExes,
10 + __in IXMLDOMNode* pixnBundle
11 + )
12 +{
13 + HRESULT hr = S_OK;
14 + IXMLDOMNodeList* pixnNodes = NULL;
15 + IXMLDOMNode* pixnNode = NULL;
16 + DWORD cNodes = 0;
17 + LPWSTR scz = NULL;
18 +
19 + // select approved exe nodes
20 + hr = XmlSelectNodes(pixnBundle, L"ApprovedExeForElevation", &pixnNodes);
21 + ExitOnFailure(hr, "Failed to select approved exe nodes.");
22 +
23 + // get approved exe node count
24 + hr = pixnNodes->get_length((long*)&cNodes);
25 + ExitOnFailure(hr, "Failed to get approved exe node count.");
26 +
27 + if (!cNodes)
28 + {
29 + ExitFunction();
30 + }
31 +
32 + // allocate memory for approved exes
33 + pApprovedExes->rgApprovedExes = (BURN_APPROVED_EXE*)MemAlloc(sizeof(BURN_APPROVED_EXE) * cNodes, TRUE);
34 + ExitOnNull(pApprovedExes->rgApprovedExes, hr, E_OUTOFMEMORY, "Failed to allocate memory for approved exe structs.");
35 +
36 + pApprovedExes->cApprovedExes = cNodes;
37 +
38 + // parse approved exe elements
39 + for (DWORD i = 0; i < cNodes; ++i)
40 + {
41 + BURN_APPROVED_EXE* pApprovedExe = &pApprovedExes->rgApprovedExes[i];
42 +
43 + hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
44 + ExitOnFailure(hr, "Failed to get next node.");
45 +
46 + // @Id
47 + hr = XmlGetAttributeEx(pixnNode, L"Id", &pApprovedExe->sczId);
48 + ExitOnFailure(hr, "Failed to get @Id.");
49 +
50 + // @Key
51 + hr = XmlGetAttributeEx(pixnNode, L"Key", &pApprovedExe->sczKey);
52 + ExitOnFailure(hr, "Failed to get @Key.");
53 +
54 + // @ValueName
55 + hr = XmlGetAttributeEx(pixnNode, L"ValueName", &pApprovedExe->sczValueName);
56 + if (E_NOTFOUND != hr)
57 + {
58 + ExitOnFailure(hr, "Failed to get @ValueName.");
59 + }
60 +
61 + // @Win64
62 + hr = XmlGetYesNoAttribute(pixnNode, L"Win64", &pApprovedExe->fWin64);
63 + if (E_NOTFOUND != hr)
64 + {
65 + ExitOnFailure(hr, "Failed to get @Win64.");
66 + }
67 +
68 + // prepare next iteration
69 + ReleaseNullObject(pixnNode);
70 + ReleaseNullStr(scz);
71 + }
72 +
73 + hr = S_OK;
74 +
75 +LExit:
76 + ReleaseObject(pixnNodes);
77 + ReleaseObject(pixnNode);
78 + ReleaseStr(scz);
79 + return hr;
80 +}
81 +
82 +extern "C" void ApprovedExesUninitialize(
83 + __in BURN_APPROVED_EXES* pApprovedExes
84 + )
85 +{
86 + if (pApprovedExes->rgApprovedExes)
87 + {
88 + for (DWORD i = 0; i < pApprovedExes->cApprovedExes; ++i)
89 + {
90 + BURN_APPROVED_EXE* pApprovedExe = &pApprovedExes->rgApprovedExes[i];
91 +
92 + ReleaseStr(pApprovedExe->sczId);
93 + ReleaseStr(pApprovedExe->sczKey);
94 + ReleaseStr(pApprovedExe->sczValueName);
95 + }
96 + MemFree(pApprovedExes->rgApprovedExes);
97 + }
98 +}
99 +
100 +extern "C" void ApprovedExesUninitializeLaunch(
101 + __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe
102 + )
103 +{
104 + if (pLaunchApprovedExe)
105 + {
106 + ReleaseStr(pLaunchApprovedExe->sczArguments);
107 + ReleaseStr(pLaunchApprovedExe->sczExecutablePath);
108 + ReleaseStr(pLaunchApprovedExe->sczId);
109 + MemFree(pLaunchApprovedExe);
110 + }
111 +}
112 +
113 +extern "C" HRESULT ApprovedExesFindById(
114 + __in BURN_APPROVED_EXES* pApprovedExes,
115 + __in_z LPCWSTR wzId,
116 + __out BURN_APPROVED_EXE** ppApprovedExe
117 + )
118 +{
119 + HRESULT hr = S_OK;
120 + BURN_APPROVED_EXE* pApprovedExe = NULL;
121 +
122 + for (DWORD i = 0; i < pApprovedExes->cApprovedExes; ++i)
123 + {
124 + pApprovedExe = &pApprovedExes->rgApprovedExes[i];
125 +
126 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pApprovedExe->sczId, -1, wzId, -1))
127 + {
128 + *ppApprovedExe = pApprovedExe;
129 + ExitFunction1(hr = S_OK);
130 + }
131 + }
132 +
133 + hr = E_NOTFOUND;
134 +
135 +LExit:
136 + return hr;
137 +}
138 +
139 +extern "C" HRESULT ApprovedExesLaunch(
140 + __in BURN_VARIABLES* pVariables,
141 + __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe,
142 + __out DWORD* pdwProcessId
143 + )
144 +{
145 + HRESULT hr = S_OK;
146 + LPWSTR sczArgumentsFormatted = NULL;
147 + LPWSTR sczArgumentsObfuscated = NULL;
148 + LPWSTR sczCommand = NULL;
149 + LPWSTR sczCommandObfuscated = NULL;
150 + LPWSTR sczExecutableDirectory = NULL;
151 + STARTUPINFOW si = { };
152 + PROCESS_INFORMATION pi = { };
153 +
154 + // build command
155 + if (pLaunchApprovedExe->sczArguments && *pLaunchApprovedExe->sczArguments)
156 + {
157 + hr = VariableFormatString(pVariables, pLaunchApprovedExe->sczArguments, &sczArgumentsFormatted, NULL);
158 + ExitOnFailure(hr, "Failed to format argument string.");
159 +
160 + hr = StrAllocFormattedSecure(&sczCommand, L"\"%ls\" %s", pLaunchApprovedExe->sczExecutablePath, sczArgumentsFormatted);
161 + ExitOnFailure(hr, "Failed to create executable command.");
162 +
163 + hr = VariableFormatStringObfuscated(pVariables, pLaunchApprovedExe->sczArguments, &sczArgumentsObfuscated, NULL);
164 + ExitOnFailure(hr, "Failed to format obfuscated argument string.");
165 +
166 + hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\" %s", pLaunchApprovedExe->sczExecutablePath, sczArgumentsObfuscated);
167 + }
168 + else
169 + {
170 + hr = StrAllocFormatted(&sczCommand, L"\"%ls\"", pLaunchApprovedExe->sczExecutablePath);
171 + ExitOnFailure(hr, "Failed to create executable command.");
172 +
173 + hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\"", pLaunchApprovedExe->sczExecutablePath);
174 + }
175 + ExitOnFailure(hr, "Failed to create obfuscated executable command.");
176 +
177 + // Try to get the directory of the executable so we can set the current directory of the process to help those executables
178 + // that expect stuff to be relative to them. Best effort only.
179 + hr = PathGetDirectory(pLaunchApprovedExe->sczExecutablePath, &sczExecutableDirectory);
180 + if (FAILED(hr))
181 + {
182 + ReleaseNullStr(sczExecutableDirectory);
183 + }
184 +
185 + LogId(REPORT_STANDARD, MSG_LAUNCHING_APPROVED_EXE, pLaunchApprovedExe->sczExecutablePath, sczCommandObfuscated);
186 +
187 + si.cb = sizeof(si);
188 + if (!::CreateProcessW(pLaunchApprovedExe->sczExecutablePath, sczCommand, NULL, NULL, FALSE, CREATE_NEW_PROCESS_GROUP, NULL, sczExecutableDirectory, &si, &pi))
189 + {
190 + ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", pLaunchApprovedExe->sczExecutablePath);
191 + }
192 +
193 + *pdwProcessId = pi.dwProcessId;
194 +
195 + if (pLaunchApprovedExe->dwWaitForInputIdleTimeout)
196 + {
197 + ::WaitForInputIdle(pi.hProcess, pLaunchApprovedExe->dwWaitForInputIdleTimeout);
198 + }
199 +
200 +LExit:
201 + StrSecureZeroFreeString(sczArgumentsFormatted);
202 + ReleaseStr(sczArgumentsObfuscated);
203 + StrSecureZeroFreeString(sczCommand);
204 + ReleaseStr(sczCommandObfuscated);
205 + ReleaseStr(sczExecutableDirectory);
206 +
207 + ReleaseHandle(pi.hThread);
208 + ReleaseHandle(pi.hProcess);
209 +
210 + return hr;
211 +}
212 +
213 +extern "C" HRESULT ApprovedExesVerifySecureLocation(
214 + __in BURN_VARIABLES* pVariables,
215 + __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe
216 + )
217 +{
218 + HRESULT hr = S_OK;
219 + LPWSTR scz = NULL;
220 +
221 + const LPCWSTR vrgSecureFolderVariables[] = {
222 + L"ProgramFiles64Folder",
223 + L"ProgramFilesFolder",
224 + };
225 +
226 + for (DWORD i = 0; i < countof(vrgSecureFolderVariables); ++i)
227 + {
228 + LPCWSTR wzSecureFolderVariable = vrgSecureFolderVariables[i];
229 +
230 + hr = VariableGetString(pVariables, wzSecureFolderVariable, &scz);
231 + if (SUCCEEDED(hr))
232 + {
233 + hr = PathDirectoryContainsPath(scz, pLaunchApprovedExe->sczExecutablePath);
234 + if (S_OK == hr)
235 + {
236 + ExitFunction();
237 + }
238 + }
239 + else if (E_NOTFOUND != hr)
240 + {
241 + ExitOnFailure(hr, "Failed to get the variable: %ls", wzSecureFolderVariable);
242 + }
243 + }
244 +
245 + // The problem with using a Variable for the root package cache folder is that it might not have been secured yet.
246 + // Getting it through CacheGetRootCompletedPath makes sure it has been secured.
247 + hr = CacheGetRootCompletedPath(TRUE, TRUE, &scz);
248 + ExitOnFailure(hr, "Failed to get the root package cache folder.");
249 +
250 + hr = PathDirectoryContainsPath(scz, pLaunchApprovedExe->sczExecutablePath);
251 + if (S_OK == hr)
252 + {
253 + ExitFunction();
254 + }
255 +
256 + hr = S_FALSE;
257 +
258 +LExit:
259 + ReleaseStr(scz);
260 +
261 + return hr;
262 +}
src/engine/approvedexe.h new
+67
@@ -0,0 +1,67 @@
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 +#if defined(__cplusplus)
6 +extern "C" {
7 +#endif
8 +
9 +
10 +// structs
11 +
12 +typedef struct _BURN_APPROVED_EXE
13 +{
14 + LPWSTR sczId;
15 + LPWSTR sczKey;
16 + LPWSTR sczValueName;
17 + BOOL fWin64;
18 +} BURN_APPROVED_EXE;
19 +
20 +typedef struct _BURN_APPROVED_EXES
21 +{
22 + BURN_APPROVED_EXE* rgApprovedExes;
23 + DWORD cApprovedExes;
24 +} BURN_APPROVED_EXES;
25 +
26 +typedef struct _BURN_LAUNCH_APPROVED_EXE
27 +{
28 + HWND hwndParent;
29 + LPWSTR sczId;
30 + LPWSTR sczExecutablePath;
31 + LPWSTR sczArguments;
32 + DWORD dwWaitForInputIdleTimeout;
33 +} BURN_LAUNCH_APPROVED_EXE;
34 +
35 +
36 +// function declarations
37 +
38 +HRESULT ApprovedExesParseFromXml(
39 + __in BURN_APPROVED_EXES* pApprovedExes,
40 + __in IXMLDOMNode* pixnBundle
41 + );
42 +
43 +void ApprovedExesUninitialize(
44 + __in BURN_APPROVED_EXES* pApprovedExes
45 + );
46 +void ApprovedExesUninitializeLaunch(
47 + __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe
48 + );
49 +HRESULT ApprovedExesFindById(
50 + __in BURN_APPROVED_EXES* pApprovedExes,
51 + __in_z LPCWSTR wzId,
52 + __out BURN_APPROVED_EXE** ppApprovedExe
53 + );
54 +HRESULT ApprovedExesLaunch(
55 + __in BURN_VARIABLES* pVariables,
56 + __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe,
57 + __out DWORD* pdwProcessId
58 + );
59 +HRESULT ApprovedExesVerifySecureLocation(
60 + __in BURN_VARIABLES* pVariables,
61 + __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe
62 + );
63 +
64 +
65 +#if defined(__cplusplus)
66 +}
67 +#endif
src/engine/bitsengine.cpp new
+505
@@ -0,0 +1,505 @@
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 +// const
6 +
7 +const DWORD BITSENGINE_NO_PROGRESS_TIMEOUT = 2 * 60;
8 +const DWORD BITSENGINE_MSG_WAIT_TIMEOUT = 1;
9 +
10 +// functions
11 +
12 +static HRESULT CreateJob(
13 + __out IBackgroundCopyJob** ppJob
14 + );
15 +static HRESULT SetCredentials(
16 + __in IBackgroundCopyJob* pJob,
17 + __in_z_opt LPCWSTR wzUser,
18 + __in_z_opt LPCWSTR wzPassword
19 + );
20 +static void SendError(
21 + __in DOWNLOAD_CACHE_CALLBACK* pCacheCallback,
22 + __in IBackgroundCopyJob* pJob,
23 + __in HRESULT hrError,
24 + __in BG_ERROR_CONTEXT context,
25 + __out_opt BOOL* pfRetry
26 + );
27 +
28 +
29 +// class
30 +
31 +class CBurnBitsCallback : public IBackgroundCopyCallback
32 +{
33 +public: // IUnknown
34 + virtual STDMETHODIMP QueryInterface(
35 + __in const IID& riid,
36 + __out void** ppvObject
37 + )
38 + {
39 + HRESULT hr = S_OK;
40 +
41 + ExitOnNull(ppvObject, hr, E_INVALIDARG, "Invalid argument ppvObject");
42 + *ppvObject = NULL;
43 +
44 + if (::IsEqualIID(__uuidof(IBackgroundCopyCallback), riid))
45 + {
46 + *ppvObject = static_cast<IBackgroundCopyCallback*>(this);
47 + }
48 + else if (::IsEqualIID(IID_IUnknown, riid))
49 + {
50 + *ppvObject = reinterpret_cast<IUnknown*>(this);
51 + }
52 + else // no interface for requested iid
53 + {
54 + ExitFunction1(hr = E_NOINTERFACE);
55 + }
56 +
57 + AddRef();
58 +
59 + LExit:
60 + return hr;
61 + }
62 +
63 + virtual STDMETHODIMP_(ULONG) AddRef()
64 + {
65 + return ::InterlockedIncrement(&this->m_cReferences);
66 + }
67 +
68 + virtual STDMETHODIMP_(ULONG) Release()
69 + {
70 + long l = ::InterlockedDecrement(&this->m_cReferences);
71 + if (0 < l)
72 + {
73 + return l;
74 + }
75 +
76 + delete this;
77 + return 0;
78 + }
79 +
80 +public: // IBackgroundCopyCallback
81 + virtual STDMETHODIMP JobTransferred(
82 + __in IBackgroundCopyJob* pJob
83 + )
84 + {
85 + HRESULT hr = S_OK;
86 +
87 + hr = SendProgress(pJob);
88 + ExitOnFailure(hr, "Failure while sending progress during BITS job transferred.");
89 +
90 + LExit:
91 + if (FAILED(hr))
92 + {
93 + ProcessResult(BG_ERROR_CONTEXT_NONE, hr);
94 + }
95 + else
96 + {
97 + ::SetEvent(m_hComplete);
98 + }
99 +
100 + return S_OK; // must return S_OK otherwise BITS just keeps calling back.
101 + }
102 +
103 + virtual STDMETHODIMP JobError(
104 + __in IBackgroundCopyJob* /*pJob*/,
105 + __in IBackgroundCopyError* pError
106 + )
107 + {
108 + HRESULT hr = S_OK;
109 + BG_ERROR_CONTEXT context = BG_ERROR_CONTEXT_NONE;
110 + HRESULT hrError = S_OK;
111 +
112 + hr = pError->GetError(&context, &hrError);
113 + ExitOnFailure(hr, "Failed to get error context.");
114 +
115 + if (SUCCEEDED(hrError))
116 + {
117 + hr = E_UNEXPECTED;
118 + }
119 +
120 + LExit:
121 + ProcessResult(context, FAILED(hrError) ? hrError : hr);
122 +
123 + return S_OK; // must return S_OK otherwise BITS just keeps calling back.
124 + }
125 +
126 + virtual STDMETHODIMP JobModification(
127 + __in IBackgroundCopyJob* pJob,
128 + __in DWORD /*dwReserved*/
129 + )
130 + {
131 + HRESULT hr = S_OK;
132 + BG_JOB_STATE state = BG_JOB_STATE_ERROR;
133 +
134 + ::EnterCriticalSection(&m_cs);
135 +
136 + hr = pJob->GetState(&state);
137 + ExitOnFailure(hr, "Failed to get state during job modification.");
138 +
139 + // If we're actually downloading stuff, let's send progress.
140 + if (BG_JOB_STATE_TRANSFERRING == state)
141 + {
142 + hr = SendProgress(pJob);
143 + ExitOnFailure(hr, "Failure while sending progress during BITS job modification.");
144 + }
145 +
146 + LExit:
147 + ::LeaveCriticalSection(&m_cs);
148 +
149 + ProcessResult(BG_ERROR_CONTEXT_NONE, hr);
150 +
151 + return S_OK; // documentation says to always return S_OK
152 + }
153 +
154 +public:
155 + void Reset()
156 + {
157 + m_hrError = S_OK;
158 + m_contextError = BG_ERROR_CONTEXT_NONE;
159 +
160 + ::ResetEvent(m_hComplete);
161 + }
162 +
163 + HRESULT WaitForCompletion(
164 + __in IBackgroundCopyJob* pJob
165 + )
166 + {
167 + HRESULT hr = S_OK;
168 + HANDLE rghEvents[1] = { m_hComplete };
169 + MSG msg = { };
170 + BOOL fMessageProcessed = FALSE;
171 +
172 + do
173 + {
174 + fMessageProcessed = FALSE;
175 +
176 + switch (::MsgWaitForMultipleObjects(countof(rghEvents), rghEvents, FALSE, BITSENGINE_MSG_WAIT_TIMEOUT * 1000, QS_ALLINPUT))
177 + {
178 + case WAIT_OBJECT_0:
179 + break;
180 +
181 + case WAIT_OBJECT_0 + 1:
182 + ::PeekMessageW(&msg, NULL, 0, 0, PM_NOREMOVE);
183 + fMessageProcessed = TRUE;
184 + break;
185 +
186 + case WAIT_TIMEOUT:
187 + // Call the progress callback periodically if we are not transferring to ensure that cancelling is responsive
188 + // (progress callback is also handles cancelling). Note that if we are transferring, IBackgroundCopyCallback
189 + // methods handle progress/cancelling. If we are not transferring, the IBackgroundCopyCallback methods may
190 + // not be called until the job times out (minutes for a foreground job, weeks for a background job).
191 + SendProgressIfNotTransferring(pJob);
192 + fMessageProcessed = TRUE;
193 + break;
194 +
195 + default:
196 + ExitWithLastError(hr, "Failed while waiting for download.");
197 + }
198 + } while (fMessageProcessed);
199 +
200 + LExit:
201 + return hr;
202 + }
203 +
204 + void GetError(
205 + __out HRESULT* pHR,
206 + __out BG_ERROR_CONTEXT* pContext
207 + )
208 + {
209 + *pHR = m_hrError;
210 + *pContext = m_contextError;
211 + }
212 +
213 +private:
214 + HRESULT SendProgress(
215 + __in IBackgroundCopyJob* pJob
216 + )
217 + {
218 + HRESULT hr = S_OK;
219 + BG_JOB_PROGRESS progress = { };
220 +
221 + if (m_pCallback && m_pCallback->pfnProgress)
222 + {
223 + hr = pJob->GetProgress(&progress);
224 + ExitOnFailure(hr, "Failed to get progress when BITS job was transferred.");
225 +
226 + hr = CacheSendProgressCallback(m_pCallback, progress.BytesTransferred, progress.BytesTotal, INVALID_HANDLE_VALUE);
227 + ExitOnFailure(hr, "Failed to send progress from BITS job.");
228 + }
229 +
230 + LExit:
231 + return hr;
232 + }
233 +
234 + void SendProgressIfNotTransferring(
235 + __in IBackgroundCopyJob* pJob
236 + )
237 + {
238 + HRESULT hr = S_OK;
239 + BG_JOB_STATE state = BG_JOB_STATE_ERROR;
240 +
241 + ::EnterCriticalSection(&m_cs);
242 +
243 + hr = pJob->GetState(&state);
244 + ExitOnFailure(hr, "Failed to get BITS job state.");
245 +
246 + if (BG_JOB_STATE_TRANSFERRING != state)
247 + {
248 + hr = SendProgress(pJob);
249 + ExitOnFailure(hr, "Failure while sending progress.");
250 + }
251 +
252 + LExit:
253 + ::LeaveCriticalSection(&m_cs);
254 +
255 + ProcessResult(BG_ERROR_CONTEXT_NONE, hr);
256 + }
257 +
258 + void ProcessResult(
259 + __in BG_ERROR_CONTEXT context,
260 + __in HRESULT hr
261 + )
262 + {
263 + if (FAILED(hr))
264 + {
265 + m_contextError = context;
266 + m_hrError = hr;
267 +
268 + ::SetEvent(m_hComplete);
269 + }
270 + }
271 +
272 +public:
273 + CBurnBitsCallback(
274 + __in_opt DOWNLOAD_CACHE_CALLBACK* pCallback,
275 + __out HRESULT* pHR
276 + )
277 + {
278 + HRESULT hr = S_OK;
279 +
280 + m_cReferences = 1;
281 + ::InitializeCriticalSection(&m_cs);
282 +
283 + m_hComplete = ::CreateEventW(NULL, TRUE, FALSE, NULL);
284 + ExitOnNullWithLastError(m_hComplete, hr, "Failed to create BITS job complete event.");
285 +
286 + m_contextError = BG_ERROR_CONTEXT_NONE;
287 + m_hrError = S_OK;
288 +
289 + m_pCallback = pCallback;
290 +
291 + LExit:
292 + *pHR = hr;
293 + }
294 +
295 + ~CBurnBitsCallback()
296 + {
297 + m_pCallback = NULL;
298 + ReleaseHandle(m_hComplete);
299 + ::DeleteCriticalSection(&m_cs);
300 + }
301 +
302 +private:
303 + long m_cReferences;
304 + CRITICAL_SECTION m_cs;
305 + BG_ERROR_CONTEXT m_contextError;
306 + HRESULT m_hrError;
307 +
308 + HANDLE m_hComplete;
309 + DOWNLOAD_CACHE_CALLBACK* m_pCallback;
310 +};
311 +
312 +
313 +extern "C" HRESULT BitsDownloadUrl(
314 + __in DOWNLOAD_CACHE_CALLBACK* pCallback,
315 + __in DOWNLOAD_SOURCE* pDownloadSource,
316 + __in_z LPCWSTR wzDestinationPath
317 + )
318 +{
319 + HRESULT hr = S_OK;
320 + LPWSTR sczDownloadUrl = NULL;
321 + CBurnBitsCallback* pBitsCallback = NULL;
322 + IBackgroundCopyJob* pJob = NULL;
323 + BOOL fRetry = FALSE;
324 + BG_ERROR_CONTEXT contextError = BG_ERROR_CONTEXT_NONE;
325 +
326 + // If the URL isn't at least 8 characters long (e.g.: "bits://X") then it
327 + // isn't going to do us any good.
328 + if (8 > lstrlenW(pDownloadSource->sczUrl))
329 + {
330 + hr = E_INVALIDARG;
331 + ExitOnRootFailure(hr, "Invalid BITS engine URL: %ls", pDownloadSource->sczUrl);
332 + }
333 +
334 + // Fix the URL to be "http" instead of "bits".
335 + hr = StrAllocString(&sczDownloadUrl, pDownloadSource->sczUrl, 0);
336 + ExitOnFailure(hr, "Failed to copy download URL.");
337 +
338 + sczDownloadUrl[0] = L'h';
339 + sczDownloadUrl[1] = L't';
340 + sczDownloadUrl[2] = L't';
341 + sczDownloadUrl[3] = L'p';
342 +
343 + // Create and configure the BITS job.
344 + hr = CreateJob(&pJob);
345 + ExitOnFailure(hr, "Failed to create BITS job.");
346 +
347 + hr = SetCredentials(pJob, pDownloadSource->sczUser, pDownloadSource->sczPassword);
348 + ExitOnFailure(hr, "Failed to set credentials for BITS job.");
349 +
350 + hr = pJob->AddFile(sczDownloadUrl, wzDestinationPath);
351 + ExitOnFailure(hr, "Failed to add file to BITS job.");
352 +
353 + // Set the callback into the BITs job.
354 + pBitsCallback = new CBurnBitsCallback(pCallback, &hr);
355 + ExitOnNull(pBitsCallback, hr, E_OUTOFMEMORY, "Failed to create BITS job callback.");
356 + ExitOnFailure(hr, "Failed to initialize BITS job callback.");
357 +
358 + hr = pJob->SetNotifyInterface(pBitsCallback);
359 + ExitOnFailure(hr, "Failed to set callback interface for BITS job.");
360 +
361 + // Go into our retry download loop.
362 + do
363 + {
364 + fRetry = FALSE;
365 +
366 + pBitsCallback->Reset(); // ensure we are ready for the download to start (again?).
367 +
368 + hr = pJob->Resume();
369 + ExitOnFailure(hr, "Falied to start BITS job.");
370 +
371 + hr = pBitsCallback->WaitForCompletion(pJob);
372 + ExitOnFailure(hr, "Failed while waiting for BITS download.");
373 +
374 + // See if there are any errors.
375 + pBitsCallback->GetError(&hr, &contextError);
376 + if (HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) == hr)
377 + {
378 + ExitFunction();
379 + }
380 + else if (FAILED(hr))
381 + {
382 + SendError(pCallback, pJob, hr, contextError, &fRetry);
383 + }
384 + } while (fRetry);
385 + ExitOnFailure(hr, "Failed to download BITS job.");
386 +
387 + // After all that, we should have the file downloaded so complete the job to get
388 + // the file copied to the destination.
389 + hr = pJob->Complete();
390 + ExitOnFailure(hr, "Failed to complete BITS job.");
391 +
392 +LExit:
393 + if (pJob)
394 + {
395 + pJob->SetNotifyInterface(NULL);
396 +
397 + // If we failed, kill the job.
398 + if (FAILED(hr))
399 + {
400 + pJob->Cancel(); // TODO: should we cancel if we're going to retry the package? Probably the right thing to do.
401 + }
402 + }
403 +
404 + ReleaseObject(pBitsCallback);
405 + ReleaseObject(pJob);
406 + ReleaseStr(sczDownloadUrl);
407 +
408 + return hr;
409 +}
410 +
411 +static HRESULT CreateJob(
412 + __out IBackgroundCopyJob** ppJob
413 + )
414 +{
415 + HRESULT hr = S_OK;
416 + IBackgroundCopyManager* pBitsManager = NULL;
417 + IBackgroundCopyJob* pJob = NULL;
418 + GUID guidJob = { };
419 +
420 + hr = ::CoCreateInstance(__uuidof(BackgroundCopyManager), NULL, CLSCTX_ALL, __uuidof(IBackgroundCopyManager), reinterpret_cast<LPVOID*>(&pBitsManager));
421 + ExitOnFailure(hr, "Failed to create IBackgroundCopyManager.");
422 +
423 + hr = pBitsManager->CreateJob(L"WixBurn", BG_JOB_TYPE_DOWNLOAD, &guidJob, &pJob);
424 + ExitOnFailure(hr, "Failed to create BITS job.");
425 +
426 + hr = pJob->SetNotifyFlags(BG_NOTIFY_JOB_TRANSFERRED | BG_NOTIFY_JOB_ERROR | BG_NOTIFY_JOB_MODIFICATION);
427 + ExitOnFailure(hr, "Failed to set notification flags for BITS job.");
428 +
429 + hr = pJob->SetNoProgressTimeout(BITSENGINE_NO_PROGRESS_TIMEOUT); // use 2 minutes since default is 14 days.
430 + ExitOnFailure(hr, "Failed to set progress timeout.");
431 +
432 + hr = pJob->SetPriority(BG_JOB_PRIORITY_FOREGROUND);
433 + ExitOnFailure(hr, "Failed to set BITS job to foreground.");
434 +
435 + *ppJob = pJob;
436 + pJob = NULL;
437 +
438 +LExit:
439 + ReleaseObject(pJob);
440 + ReleaseObject(pBitsManager);
441 +
442 + return hr;
443 +}
444 +
445 +static HRESULT SetCredentials(
446 + __in IBackgroundCopyJob* pJob,
447 + __in_z_opt LPCWSTR wzUser,
448 + __in_z_opt LPCWSTR wzPassword
449 + )
450 +{
451 + HRESULT hr = S_OK;
452 + IBackgroundCopyJob2* pJob2 = NULL;
453 + BG_AUTH_CREDENTIALS ac = { };
454 +
455 + // If IBackgroundCopyJob2::SetCredentials() is supported, set the username/password.
456 + hr = pJob->QueryInterface(IID_PPV_ARGS(&pJob2));
457 + if (SUCCEEDED(hr))
458 + {
459 + ac.Target = BG_AUTH_TARGET_PROXY;
460 + ac.Credentials.Basic.UserName = const_cast<LPWSTR>(wzUser);
461 + ac.Credentials.Basic.Password = const_cast<LPWSTR>(wzPassword);
462 +
463 + ac.Scheme = BG_AUTH_SCHEME_NTLM;
464 + hr = pJob2->SetCredentials(&ac);
465 + ExitOnFailure(hr, "Failed to set background copy NTLM credentials");
466 +
467 + ac.Scheme = BG_AUTH_SCHEME_NEGOTIATE;
468 + hr = pJob2->SetCredentials(&ac);
469 + ExitOnFailure(hr, "Failed to set background copy negotiate credentials");
470 + }
471 +
472 + hr = S_OK;
473 +
474 +LExit:
475 + ReleaseObject(pJob2);
476 +
477 + return hr;
478 +}
479 +
480 +static void SendError(
481 + __in DOWNLOAD_CACHE_CALLBACK* pCacheCallback,
482 + __in IBackgroundCopyJob* pJob,
483 + __in HRESULT hrError,
484 + __in BG_ERROR_CONTEXT /*context*/,
485 + __out_opt BOOL* pfRetry
486 + )
487 +{
488 + HRESULT hr = S_OK;
489 + IBackgroundCopyError* pError = NULL;
490 + LPWSTR pszErrorDescription = NULL;
491 +
492 + hr = pJob->GetError(&pError);
493 + if (SUCCEEDED(hr))
494 + {
495 + pError->GetErrorDescription(LANGIDFROMLCID(::GetThreadLocale()), &pszErrorDescription);
496 + }
497 +
498 + CacheSendErrorCallback(pCacheCallback, hrError, pszErrorDescription, pfRetry);
499 +
500 + if (pszErrorDescription)
501 + {
502 + ::CoTaskMemFree(pszErrorDescription);
503 + }
504 + ReleaseObject(pError);
505 +}
src/engine/bitsengine.h new
+23
@@ -0,0 +1,23 @@
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 +// structs
10 +
11 +
12 +// functions
13 +
14 +HRESULT BitsDownloadUrl(
15 + __in DOWNLOAD_CACHE_CALLBACK* pCallback,
16 + __in DOWNLOAD_SOURCE* pDownloadSource,
17 + __in LPCWSTR wzDestinationPath
18 + );
19 +
20 +
21 +#ifdef __cplusplus
22 +}
23 +#endif
src/engine/cabextract.cpp new
+974
@@ -0,0 +1,974 @@
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 +#include <fdi.h>
6 +
7 +#define ARRAY_GROWTH_SIZE 2
8 +
9 +const LPSTR INVALID_CAB_NAME = "<the>.cab";
10 +
11 +// structs
12 +
13 +typedef struct _BURN_CAB_CONTEXT
14 +{
15 + HANDLE hFile;
16 + DWORD64 qwOffset;
17 + DWORD64 qwSize;
18 +
19 + HANDLE hThread;
20 + HANDLE hBeginOperationEvent;
21 + HANDLE hOperationCompleteEvent;
22 +
23 + BURN_CAB_OPERATION operation;
24 + HRESULT hrError;
25 +
26 + LPWSTR* psczStreamName;
27 + LPCWSTR wzTargetFile;
28 + HANDLE hTargetFile;
29 + BYTE* pbTargetBuffer;
30 + DWORD cbTargetBuffer;
31 + DWORD iTargetBuffer;
32 +} BURN_CAB_CONTEXT;
33 +
34 +
35 +// internal function declarations
36 +
37 +static HRESULT BeginAndWaitForOperation(
38 + __in BURN_CONTAINER_CONTEXT* pContext
39 + );
40 +static HRESULT WaitForOperation(
41 + __in BURN_CONTAINER_CONTEXT* pContext
42 + );
43 +static DWORD WINAPI ExtractThreadProc(
44 + __in LPVOID lpThreadParameter
45 + );
46 +static INT_PTR DIAMONDAPI CabNotifyCallback(
47 + __in FDINOTIFICATIONTYPE iNotification,
48 + __inout FDINOTIFICATION *pFDINotify
49 + );
50 +static INT_PTR CopyFileCallback(
51 + __in BURN_CONTAINER_CONTEXT* pContext,
52 + __inout FDINOTIFICATION *pFDINotify
53 + );
54 +static INT_PTR CloseFileInfoCallback(
55 + __in BURN_CONTAINER_CONTEXT* pContext,
56 + __inout FDINOTIFICATION *pFDINotify
57 + );
58 +static LPVOID DIAMONDAPI CabAlloc(
59 + __in DWORD dwSize
60 + );
61 +static void DIAMONDAPI CabFree(
62 + __in LPVOID pvData
63 + );
64 +static INT_PTR FAR DIAMONDAPI CabOpen(
65 + __in char FAR *pszFile,
66 + __in int /* oflag */,
67 + __in int /* pmode */
68 + );
69 +static UINT FAR DIAMONDAPI CabRead(
70 + __in INT_PTR hf,
71 + __out void FAR *pv,
72 + __in UINT cb
73 + );
74 +static UINT FAR DIAMONDAPI CabWrite(
75 + __in INT_PTR hf,
76 + __in void FAR *pv,
77 + __in UINT cb
78 + );
79 +static long FAR DIAMONDAPI CabSeek(
80 + __in INT_PTR hf,
81 + __in long dist,
82 + __in int seektype
83 + );
84 +static int FAR DIAMONDAPI CabClose(
85 + __in INT_PTR hf
86 + );
87 +static HRESULT AddVirtualFilePointer(
88 + __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
89 + __in HANDLE hFile,
90 + __in LONGLONG llInitialFilePointer
91 + );
92 +static HRESULT ReadIfVirtualFilePointer(
93 + __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
94 + __in HANDLE hFile,
95 + __in DWORD cbRead
96 + );
97 +static BOOL SetIfVirtualFilePointer(
98 + __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
99 + __in HANDLE hFile,
100 + __in LONGLONG llDistance,
101 + __out LONGLONG* pllNewPostion,
102 + __in DWORD dwSeekType
103 + );
104 +static HRESULT CloseIfVirturalFilePointer(
105 + __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
106 + __in HANDLE hFile
107 + );
108 +static BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* GetVirtualFilePointer(
109 + __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
110 + __in HANDLE hFile
111 + );
112 +
113 +
114 +// internal variables
115 +
116 +__declspec(thread) static BURN_CONTAINER_CONTEXT* vpContext;
117 +
118 +
119 +// function definitions
120 +
121 +extern "C" void CabExtractInitialize()
122 +{
123 +}
124 +
125 +extern "C" HRESULT CabExtractOpen(
126 + __in BURN_CONTAINER_CONTEXT* pContext,
127 + __in LPCWSTR wzFilePath
128 + )
129 +{
130 + HRESULT hr = S_OK;
131 +
132 + // initialize context
133 + pContext->Cabinet.hTargetFile = INVALID_HANDLE_VALUE;
134 +
135 + hr = StrAllocString(&pContext->Cabinet.sczFile, wzFilePath, 0);
136 + ExitOnFailure(hr, "Failed to copy file name.");
137 +
138 + // create events
139 + pContext->Cabinet.hBeginOperationEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL);
140 + ExitOnNullWithLastError(pContext->Cabinet.hBeginOperationEvent, hr, "Failed to create begin operation event.");
141 +
142 + pContext->Cabinet.hOperationCompleteEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL);
143 + ExitOnNullWithLastError(pContext->Cabinet.hOperationCompleteEvent, hr, "Failed to create operation complete event.");
144 +
145 + // create extraction thread
146 + pContext->Cabinet.hThread = ::CreateThread(NULL, 0, ExtractThreadProc, pContext, 0, NULL);
147 + ExitOnNullWithLastError(pContext->Cabinet.hThread, hr, "Failed to create extraction thread.");
148 +
149 + // wait for operation to complete
150 + hr = WaitForOperation(pContext);
151 + ExitOnFailure(hr, "Failed to wait for operation complete.");
152 +
153 +LExit:
154 + return hr;
155 +}
156 +
157 +extern "C" HRESULT CabExtractNextStream(
158 + __in BURN_CONTAINER_CONTEXT* pContext,
159 + __inout_z LPWSTR* psczStreamName
160 + )
161 +{
162 + HRESULT hr = S_OK;
163 +
164 + // set operation to move to next stream
165 + pContext->Cabinet.operation = BURN_CAB_OPERATION_NEXT_STREAM;
166 + pContext->Cabinet.psczStreamName = psczStreamName;
167 +
168 + // begin operation and wait
169 + hr = BeginAndWaitForOperation(pContext);
170 + if (E_ABORT != hr && E_NOMOREITEMS != hr)
171 + {
172 + ExitOnFailure(hr, "Failed to begin and wait for operation.");
173 + }
174 +
175 +LExit:
176 + return hr;
177 +}
178 +
179 +extern "C" HRESULT CabExtractStreamToFile(
180 + __in BURN_CONTAINER_CONTEXT* pContext,
181 + __in_z LPCWSTR wzFileName
182 + )
183 +{
184 + HRESULT hr = S_OK;
185 +
186 + // set operation to move to next stream
187 + pContext->Cabinet.operation = BURN_CAB_OPERATION_STREAM_TO_FILE;
188 + pContext->Cabinet.wzTargetFile = wzFileName;
189 +
190 + // begin operation and wait
191 + hr = BeginAndWaitForOperation(pContext);
192 + ExitOnFailure(hr, "Failed to begin and wait for operation.");
193 +
194 + // clear file name
195 + pContext->Cabinet.wzTargetFile = NULL;
196 +
197 +LExit:
198 + return hr;
199 +}
200 +
201 +extern "C" HRESULT CabExtractStreamToBuffer(
202 + __in BURN_CONTAINER_CONTEXT* pContext,
203 + __out BYTE** ppbBuffer,
204 + __out SIZE_T* pcbBuffer
205 + )
206 +{
207 + HRESULT hr = S_OK;
208 +
209 + // set operation to move to next stream
210 + pContext->Cabinet.operation = BURN_CAB_OPERATION_STREAM_TO_BUFFER;
211 +
212 + // begin operation and wait
213 + hr = BeginAndWaitForOperation(pContext);
214 + ExitOnFailure(hr, "Failed to begin and wait for operation.");
215 +
216 + // return values
217 + *ppbBuffer = pContext->Cabinet.pbTargetBuffer;
218 + *pcbBuffer = pContext->Cabinet.cbTargetBuffer;
219 +
220 + // clear buffer variables
221 + pContext->Cabinet.pbTargetBuffer = NULL;
222 + pContext->Cabinet.cbTargetBuffer = 0;
223 + pContext->Cabinet.iTargetBuffer = 0;
224 +
225 +LExit:
226 + return hr;
227 +}
228 +
229 +extern "C" HRESULT CabExtractSkipStream(
230 + __in BURN_CONTAINER_CONTEXT* pContext
231 + )
232 +{
233 + HRESULT hr = S_OK;
234 +
235 + // set operation to move to next stream
236 + pContext->Cabinet.operation = BURN_CAB_OPERATION_SKIP_STREAM;
237 +
238 + // begin operation and wait
239 + hr = BeginAndWaitForOperation(pContext);
240 + ExitOnFailure(hr, "Failed to begin and wait for operation.");
241 +
242 +LExit:
243 + return hr;
244 +}
245 +
246 +extern "C" HRESULT CabExtractClose(
247 + __in BURN_CONTAINER_CONTEXT* pContext
248 + )
249 +{
250 + HRESULT hr = S_OK;
251 +
252 + // terminate worker thread
253 + if (pContext->Cabinet.hThread)
254 + {
255 + // set operation to move to close
256 + pContext->Cabinet.operation = BURN_CAB_OPERATION_CLOSE;
257 +
258 + // set begin operation event
259 + if (!::SetEvent(pContext->Cabinet.hBeginOperationEvent))
260 + {
261 + ExitWithLastError(hr, "Failed to set begin operation event.");
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 + }
269 + }
270 +
271 +LExit:
272 + ReleaseHandle(pContext->Cabinet.hThread);
273 + ReleaseHandle(pContext->Cabinet.hBeginOperationEvent);
274 + ReleaseHandle(pContext->Cabinet.hOperationCompleteEvent);
275 + ReleaseMem(pContext->Cabinet.rgVirtualFilePointers);
276 + ReleaseStr(pContext->Cabinet.sczFile);
277 +
278 + return hr;
279 +}
280 +
281 +
282 +// internal helper functions
283 +
284 +static HRESULT BeginAndWaitForOperation(
285 + __in BURN_CONTAINER_CONTEXT* pContext
286 + )
287 +{
288 + HRESULT hr = S_OK;
289 +
290 + // set begin operation event
291 + if (!::SetEvent(pContext->Cabinet.hBeginOperationEvent))
292 + {
293 + ExitWithLastError(hr, "Failed to set begin operation event.");
294 + }
295 +
296 + // wait for operation to complete
297 + hr = WaitForOperation(pContext);
298 +
299 +LExit:
300 + return hr;
301 +}
302 +
303 +static HRESULT WaitForOperation(
304 + __in BURN_CONTAINER_CONTEXT* pContext
305 + )
306 +{
307 + HRESULT hr = S_OK;
308 + HANDLE rghWait[2] = { };
309 +
310 + // wait for operation complete event
311 + rghWait[0] = pContext->Cabinet.hOperationCompleteEvent;
312 + rghWait[1] = pContext->Cabinet.hThread;
313 + switch (::WaitForMultipleObjects(countof(rghWait), rghWait, FALSE, INFINITE))
314 + {
315 + case WAIT_OBJECT_0:
316 + if (!::ResetEvent(pContext->Cabinet.hOperationCompleteEvent))
317 + {
318 + ExitWithLastError(hr, "Failed to reset operation complete event.");
319 + }
320 + break;
321 +
322 + case WAIT_OBJECT_0 + 1:
323 + if (!::GetExitCodeThread(pContext->Cabinet.hThread, (DWORD*)&hr))
324 + {
325 + ExitWithLastError(hr, "Failed to get extraction thread exit code.");
326 + }
327 + ExitFunction();
328 +
329 + case WAIT_FAILED: __fallthrough;
330 + default:
331 + ExitWithLastError(hr, "Failed to wait for operation complete event.");
332 + }
333 +
334 + // clear operation
335 + pContext->Cabinet.operation = BURN_CAB_OPERATION_NONE;
336 +
337 +LExit:
338 + return hr;
339 +}
340 +
341 +static DWORD WINAPI ExtractThreadProc(
342 + __in LPVOID lpThreadParameter
343 + )
344 +{
345 + HRESULT hr = S_OK;
346 + BURN_CONTAINER_CONTEXT* pContext = (BURN_CONTAINER_CONTEXT*)lpThreadParameter;
347 + BOOL fComInitialized = FALSE;
348 + HFDI hfdi = NULL;
349 + ERF erf = { };
350 +
351 + // initialize COM
352 + hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
353 + ExitOnFailure(hr, "Failed to initialize COM.");
354 + fComInitialized = TRUE;
355 +
356 + // save context in TLS storage
357 + vpContext = pContext;
358 +
359 + // create FDI context
360 + hfdi = ::FDICreate(CabAlloc, CabFree, CabOpen, CabRead, CabWrite, CabClose, CabSeek, cpuUNKNOWN, &erf);
361 + ExitOnNull(hfdi, hr, E_FAIL, "Failed to initialize cabinet.dll.");
362 +
363 + // begin CAB extraction
364 + if (!::FDICopy(hfdi, INVALID_CAB_NAME, "", 0, CabNotifyCallback, NULL, NULL))
365 + {
366 + hr = pContext->Cabinet.hrError;
367 + if (E_ABORT == hr || E_NOMOREITEMS == hr)
368 + {
369 + ExitFunction();
370 + }
371 + else if (SUCCEEDED(hr))
372 + {
373 + if (ERROR_SUCCESS != erf.erfType)
374 + {
375 + hr = HRESULT_FROM_WIN32(erf.erfType);
376 + }
377 + else
378 + {
379 + switch (erf.erfOper)
380 + {
381 + case FDIERROR_NONE:
382 + hr = E_UNEXPECTED;
383 + break;
384 + case FDIERROR_CABINET_NOT_FOUND:
385 + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
386 + break;
387 + case FDIERROR_NOT_A_CABINET:
388 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION);
389 + break;
390 + case FDIERROR_UNKNOWN_CABINET_VERSION:
391 + hr = HRESULT_FROM_WIN32(ERROR_VERSION_PARSE_ERROR);
392 + break;
393 + case FDIERROR_CORRUPT_CABINET:
394 + hr = HRESULT_FROM_WIN32(ERROR_FILE_CORRUPT);
395 + break;
396 + case FDIERROR_ALLOC_FAIL:
397 + hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
398 + break;
399 + case FDIERROR_BAD_COMPR_TYPE:
400 + hr = HRESULT_FROM_WIN32(ERROR_UNSUPPORTED_COMPRESSION);
401 + break;
402 + case FDIERROR_MDI_FAIL:
403 + hr = HRESULT_FROM_WIN32(ERROR_BAD_COMPRESSION_BUFFER);
404 + break;
405 + case FDIERROR_TARGET_FILE:
406 + hr = HRESULT_FROM_WIN32(ERROR_WRITE_FAULT);
407 + break;
408 + case FDIERROR_RESERVE_MISMATCH:
409 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
410 + break;
411 + case FDIERROR_WRONG_CABINET:
412 + hr = HRESULT_FROM_WIN32(ERROR_DATATYPE_MISMATCH);
413 + break;
414 + case FDIERROR_USER_ABORT:
415 + hr = E_ABORT;
416 + break;
417 + default:
418 + hr = E_FAIL;
419 + break;
420 + }
421 + }
422 + }
423 + ExitOnFailure(hr, "Failed to extract all files from container, erf: %d:%X:%d", erf.fError, erf.erfOper, erf.erfType);
424 + }
425 +
426 + // set operation complete event
427 + if (!::SetEvent(pContext->Cabinet.hOperationCompleteEvent))
428 + {
429 + ExitWithLastError(hr, "Failed to set operation complete event.");
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 + }
437 +
438 + if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent))
439 + {
440 + ExitWithLastError(hr, "Failed to reset begin operation event.");
441 + }
442 +
443 + // read operation
444 + switch (pContext->Cabinet.operation)
445 + {
446 + case BURN_CAB_OPERATION_NEXT_STREAM:
447 + ExitFunction1(hr = E_NOMOREITEMS);
448 + break;
449 +
450 + case BURN_CAB_OPERATION_CLOSE:
451 + ExitFunction1(hr = S_OK);
452 +
453 + default:
454 + hr = E_INVALIDSTATE;
455 + ExitOnRootFailure(hr, "Invalid operation for this state.");
456 + }
457 +
458 +LExit:
459 + if (hfdi)
460 + {
461 + ::FDIDestroy(hfdi);
462 + }
463 + if (fComInitialized)
464 + {
465 + ::CoUninitialize();
466 + }
467 +
468 + return (DWORD)hr;
469 +}
470 +
471 +static INT_PTR DIAMONDAPI CabNotifyCallback(
472 + __in FDINOTIFICATIONTYPE iNotification,
473 + __inout FDINOTIFICATION *pFDINotify
474 + )
475 +{
476 + BURN_CONTAINER_CONTEXT* pContext = vpContext;
477 + INT_PTR ipResult = 0; // result to return on success
478 +
479 + switch (iNotification)
480 + {
481 + case fdintCOPY_FILE:
482 + ipResult = CopyFileCallback(pContext, pFDINotify);
483 + break;
484 +
485 + case fdintCLOSE_FILE_INFO: // resource extraction complete
486 + ipResult = CloseFileInfoCallback(pContext, pFDINotify);
487 + break;
488 +
489 + case fdintPARTIAL_FILE: __fallthrough; // no action needed for these messages
490 + case fdintNEXT_CABINET: __fallthrough;
491 + case fdintENUMERATE: __fallthrough;
492 + case fdintCABINET_INFO:
493 + break;
494 +
495 + default:
496 + AssertSz(FALSE, "CabExtractCallback() - unknown FDI notification command");
497 + };
498 +
499 +//LExit:
500 + return ipResult;
501 +}
502 +
503 +static INT_PTR CopyFileCallback(
504 + __in BURN_CONTAINER_CONTEXT* pContext,
505 + __inout FDINOTIFICATION* pFDINotify
506 + )
507 +{
508 + HRESULT hr = S_OK;
509 + INT_PTR ipResult = 1; // result to return on success
510 + LPWSTR pwzPath = NULL;
511 + LARGE_INTEGER li = { };
512 +
513 + // set operation complete event
514 + if (!::SetEvent(pContext->Cabinet.hOperationCompleteEvent))
515 + {
516 + ExitWithLastError(hr, "Failed to set operation complete event.");
517 + }
518 +
519 + // 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 + }
524 +
525 + if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent))
526 + {
527 + ExitWithLastError(hr, "Failed to reset begin operation event.");
528 + }
529 +
530 + // read operation
531 + switch (pContext->Cabinet.operation)
532 + {
533 + case BURN_CAB_OPERATION_NEXT_STREAM:
534 + break;
535 +
536 + case BURN_CAB_OPERATION_CLOSE:
537 + ExitFunction1(hr = E_ABORT);
538 +
539 + default:
540 + hr = E_INVALIDSTATE;
541 + ExitOnRootFailure(hr, "Invalid operation for this state.");
542 + }
543 +
544 + // copy stream name
545 + hr = StrAllocStringAnsi(pContext->Cabinet.psczStreamName, pFDINotify->psz1, 0, CP_UTF8);
546 + ExitOnFailure(hr, "Failed to copy stream name: %ls", pFDINotify->psz1);
547 +
548 + // set operation complete event
549 + if (!::SetEvent(pContext->Cabinet.hOperationCompleteEvent))
550 + {
551 + ExitWithLastError(hr, "Failed to set operation complete event.");
552 + }
553 +
554 + // 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 + }
559 +
560 + if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent))
561 + {
562 + ExitWithLastError(hr, "Failed to reset begin operation event.");
563 + }
564 +
565 + // read operation
566 + switch (pContext->Cabinet.operation)
567 + {
568 + case BURN_CAB_OPERATION_STREAM_TO_FILE:
569 + // create file
570 + pContext->Cabinet.hTargetFile = ::CreateFileW(pContext->Cabinet.wzTargetFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
571 + if (INVALID_HANDLE_VALUE == pContext->Cabinet.hTargetFile)
572 + {
573 + ExitWithLastError(hr, "Failed to create file: %ls", pContext->Cabinet.wzTargetFile);
574 + }
575 +
576 + // set file size
577 + li.QuadPart = pFDINotify->cb;
578 + if (!::SetFilePointerEx(pContext->Cabinet.hTargetFile, li, NULL, FILE_BEGIN))
579 + {
580 + ExitWithLastError(hr, "Failed to set file pointer to end of file.");
581 + }
582 +
583 + if (!::SetEndOfFile(pContext->Cabinet.hTargetFile))
584 + {
585 + ExitWithLastError(hr, "Failed to set end of file.");
586 + }
587 +
588 + li.QuadPart = 0;
589 + if (!::SetFilePointerEx(pContext->Cabinet.hTargetFile, li, NULL, FILE_BEGIN))
590 + {
591 + ExitWithLastError(hr, "Failed to set file pointer to beginning of file.");
592 + }
593 +
594 + break;
595 +
596 + case BURN_CAB_OPERATION_STREAM_TO_BUFFER:
597 + // allocate buffer for stream
598 + pContext->Cabinet.pbTargetBuffer = (BYTE*)MemAlloc(pFDINotify->cb, TRUE);
599 + ExitOnNull(pContext->Cabinet.pbTargetBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer for stream.");
600 +
601 + // set buffer size and write position
602 + pContext->Cabinet.cbTargetBuffer = pFDINotify->cb;
603 + pContext->Cabinet.iTargetBuffer = 0;
604 +
605 + break;
606 +
607 + case BURN_CAB_OPERATION_SKIP_STREAM:
608 + ipResult = 0;
609 + break;
610 +
611 + case BURN_CAB_OPERATION_CLOSE:
612 + ExitFunction1(hr = E_ABORT);
613 +
614 + default:
615 + hr = E_INVALIDSTATE;
616 + ExitOnRootFailure(hr, "Invalid operation for this state.");
617 + }
618 +
619 +LExit:
620 + ReleaseStr(pwzPath);
621 +
622 + pContext->Cabinet.hrError = hr;
623 + return SUCCEEDED(hr) ? ipResult : -1;
624 +}
625 +
626 +static INT_PTR CloseFileInfoCallback(
627 + __in BURN_CONTAINER_CONTEXT* pContext,
628 + __inout FDINOTIFICATION *pFDINotify
629 + )
630 +{
631 + HRESULT hr = S_OK;
632 + INT_PTR ipResult = 1; // result to return on success
633 + FILETIME ftLocal = { };
634 + FILETIME ft = { };
635 +
636 + // read operation
637 + switch (pContext->Cabinet.operation)
638 + {
639 + case BURN_CAB_OPERATION_STREAM_TO_FILE:
640 + // Make a best effort to set the time on the new file before
641 + // we close it.
642 + if (::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ftLocal))
643 + {
644 + if (::LocalFileTimeToFileTime(&ftLocal, &ft))
645 + {
646 + ::SetFileTime(pContext->Cabinet.hTargetFile, &ft, &ft, &ft);
647 + }
648 + }
649 +
650 + // close file
651 + ReleaseFile(pContext->Cabinet.hTargetFile);
652 + break;
653 +
654 + case BURN_CAB_OPERATION_STREAM_TO_BUFFER:
655 + break;
656 +
657 + case BURN_CAB_OPERATION_CLOSE:
658 + ExitFunction1(hr = E_ABORT);
659 +
660 + default:
661 + hr = E_INVALIDSTATE;
662 + ExitOnRootFailure(hr, "Invalid operation for this state.");
663 + }
664 +
665 + //if (pContext->pfnProgress)
666 + //{
667 + // hr = StrAllocFormatted(&pwzPath, L"%s%ls", pContext->wzRootPath, pFDINotify->psz1);
668 + // ExitOnFailure(hr, "Failed to calculate file path from: %ls and %s", pContext->wzRootPath, pFDINotify->psz1);
669 + // if (SUCCEEDED(hr))
670 + // {
671 + // hr = pContext->pfnProgress(BOX_PROGRESS_DECOMPRESSION_END, pwzPath, 0, pContext->pvContext);
672 + // if (S_OK != hr)
673 + // {
674 + // pContext->hrError = hr;
675 + // ExitFunction();
676 + // }
677 + // }
678 + //}
679 +
680 +LExit:
681 + pContext->Cabinet.hrError = hr;
682 + return SUCCEEDED(hr) ? ipResult : -1;
683 +}
684 +
685 +static LPVOID DIAMONDAPI CabAlloc(
686 + __in DWORD dwSize
687 + )
688 +{
689 + return MemAlloc(dwSize, FALSE);
690 +}
691 +
692 +static void DIAMONDAPI CabFree(
693 + __in LPVOID pvData
694 + )
695 +{
696 + MemFree(pvData);
697 +}
698 +
699 +static INT_PTR FAR DIAMONDAPI CabOpen(
700 + __in char FAR * pszFile,
701 + __in int /* oflag */,
702 + __in int /* pmode */
703 + )
704 +{
705 + HRESULT hr = S_OK;
706 + BURN_CONTAINER_CONTEXT* pContext = vpContext;
707 + HANDLE hFile = INVALID_HANDLE_VALUE;
708 +
709 + // If this is the invalid cab name, use our file handle.
710 + if (CSTR_EQUAL == ::CompareStringA(LOCALE_NEUTRAL, 0, INVALID_CAB_NAME, -1, pszFile, -1))
711 + {
712 + if (!::DuplicateHandle(::GetCurrentProcess(), pContext->hFile, ::GetCurrentProcess(), &hFile, 0, FALSE, DUPLICATE_SAME_ACCESS))
713 + {
714 + ExitWithLastError(hr, "Failed to duplicate handle to cab container.");
715 + }
716 +
717 + // Use a virtual file pointer since duplicated file handles share their file pointer. Seek to container offset
718 + // to start.
719 + hr = AddVirtualFilePointer(&pContext->Cabinet, hFile, pContext->qwOffset);
720 + ExitOnFailure(hr, "Failed to add virtual file pointer for cab container.");
721 + }
722 + else // open file requested. This is used in the rare cases where the CAB API wants to create a temp file.
723 + {
724 + hFile = ::CreateFileA(pszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
725 + ExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open cabinet file: %hs", pszFile);
726 + }
727 +
728 +LExit:
729 + pContext->Cabinet.hrError = hr;
730 + return FAILED(hr) ? -1 : (INT_PTR)hFile;
731 +}
732 +
733 +static UINT FAR DIAMONDAPI CabRead(
734 + __in INT_PTR hf,
735 + __out void FAR *pv,
736 + __in UINT cb
737 + )
738 +{
739 + HRESULT hr = S_OK;
740 + BURN_CONTAINER_CONTEXT* pContext = vpContext;
741 + HANDLE hFile = (HANDLE)hf;
742 + DWORD cbRead = 0;
743 +
744 + ReadIfVirtualFilePointer(&pContext->Cabinet, hFile, cb);
745 +
746 + if (!::ReadFile(hFile, pv, cb, &cbRead, NULL))
747 + {
748 + ExitWithLastError(hr, "Failed to read during cabinet extraction.");
749 + }
750 +
751 +LExit:
752 + pContext->Cabinet.hrError = hr;
753 + return FAILED(hr) ? -1 : cbRead;
754 +}
755 +
756 +static UINT FAR DIAMONDAPI CabWrite(
757 + __in INT_PTR /* hf */,
758 + __in void FAR *pv,
759 + __in UINT cb
760 + )
761 +{
762 + HRESULT hr = S_OK;
763 + BURN_CONTAINER_CONTEXT* pContext = vpContext;
764 + DWORD cbWrite = 0;
765 +
766 + switch (pContext->Cabinet.operation)
767 + {
768 + case BURN_CAB_OPERATION_STREAM_TO_FILE:
769 + // write file
770 + if (!::WriteFile(pContext->Cabinet.hTargetFile, pv, cb, &cbWrite, NULL))
771 + {
772 + ExitWithLastError(hr, "Failed to write during cabinet extraction.");
773 + }
774 + break;
775 +
776 + case BURN_CAB_OPERATION_STREAM_TO_BUFFER:
777 + // copy to target buffer
778 + memcpy_s(pContext->Cabinet.pbTargetBuffer + pContext->Cabinet.iTargetBuffer, pContext->Cabinet.cbTargetBuffer - pContext->Cabinet.iTargetBuffer, pv, cb);
779 + pContext->Cabinet.iTargetBuffer += cb;
780 +
781 + cbWrite = cb;
782 + break;
783 +
784 + default:
785 + hr = E_INVALIDSTATE;
786 + ExitOnFailure(hr, "Unexpected call to CabWrite().");
787 + }
788 +
789 +LExit:
790 + pContext->Cabinet.hrError = hr;
791 + return FAILED(hr) ? -1 : cbWrite;
792 +}
793 +
794 +static long FAR DIAMONDAPI CabSeek(
795 + __in INT_PTR hf,
796 + __in long dist,
797 + __in int seektype
798 + )
799 +{
800 + HRESULT hr = S_OK;
801 + BURN_CONTAINER_CONTEXT* pContext = vpContext;
802 + HANDLE hFile = (HANDLE)hf;
803 + LARGE_INTEGER liDistance = { };
804 + LARGE_INTEGER liNewPointer = { };
805 + DWORD dwSeekType = 0;
806 +
807 + // We assume that CabSeek() will only be called to seek the
808 + // cabinet itself so we have to offset the seek operations to
809 + // where the internal cabinet starts.
810 + switch (seektype)
811 + {
812 + case FILE_BEGIN:
813 + liDistance.QuadPart = pContext->qwOffset + dist;
814 + dwSeekType = FILE_BEGIN;
815 + break;
816 +
817 + case FILE_CURRENT:
818 + liDistance.QuadPart = dist;
819 + dwSeekType = FILE_CURRENT;
820 + break;
821 +
822 + case FILE_END:
823 + liDistance.QuadPart = pContext->qwOffset + pContext->qwSize + dist;
824 + dwSeekType = FILE_BEGIN;
825 + break;
826 +
827 + default:
828 + hr = E_INVALIDARG;
829 + ExitOnFailure(hr, "Invalid seek type.");;
830 + }
831 +
832 + if (SetIfVirtualFilePointer(&pContext->Cabinet, hFile, liDistance.QuadPart, &liNewPointer.QuadPart, seektype))
833 + {
834 + // set file pointer
835 + if (!::SetFilePointerEx(hFile, liDistance, &liNewPointer, seektype))
836 + {
837 + ExitWithLastError(hr, "Failed to move file pointer 0x%x bytes.", dist);
838 + }
839 + }
840 +
841 + liNewPointer.QuadPart -= pContext->qwOffset;
842 +
843 +LExit:
844 + pContext->Cabinet.hrError = hr;
845 + return FAILED(hr) ? -1 : liNewPointer.LowPart;
846 +}
847 +
848 +static int FAR DIAMONDAPI CabClose(
849 + __in INT_PTR hf
850 + )
851 +{
852 + BURN_CONTAINER_CONTEXT* pContext = vpContext;
853 + HANDLE hFile = (HANDLE)hf;
854 +
855 + CloseIfVirturalFilePointer(&pContext->Cabinet, hFile);
856 + ReleaseFileHandle(hFile);
857 +
858 + return 0;
859 +}
860 +
861 +static HRESULT AddVirtualFilePointer(
862 + __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
863 + __in HANDLE hFile,
864 + __in LONGLONG llInitialFilePointer
865 + )
866 +{
867 + HRESULT hr = S_OK;
868 +
869 + hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pCabinetContext->rgVirtualFilePointers), pCabinetContext->cVirtualFilePointers, sizeof(BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER), ARRAY_GROWTH_SIZE);
870 + ExitOnFailure(hr, "Failed to allocate memory for the virtual file pointer array.");
871 +
872 + pCabinetContext->rgVirtualFilePointers[pCabinetContext->cVirtualFilePointers].hFile = hFile;
873 + pCabinetContext->rgVirtualFilePointers[pCabinetContext->cVirtualFilePointers].liPosition.QuadPart = llInitialFilePointer;
874 + ++pCabinetContext->cVirtualFilePointers;
875 +
876 +LExit:
877 + return hr;
878 +}
879 +
880 +static HRESULT ReadIfVirtualFilePointer(
881 + __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
882 + __in HANDLE hFile,
883 + __in DWORD cbRead
884 + )
885 +{
886 + HRESULT hr = E_NOTFOUND;
887 +
888 + BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = GetVirtualFilePointer(pCabinetContext, hFile);
889 + if (pVfp)
890 + {
891 + // Set the file handle to the virtual file pointer.
892 + if (!::SetFilePointerEx(hFile, pVfp->liPosition, NULL, FILE_BEGIN))
893 + {
894 + ExitWithLastError(hr, "Failed to move to virtual file pointer.");
895 + }
896 +
897 + pVfp->liPosition.QuadPart += cbRead; // add the amount that will be read to advance the pointer.
898 + hr = S_OK;
899 + }
900 +
901 +LExit:
902 + return hr;
903 +}
904 +
905 +static BOOL SetIfVirtualFilePointer(
906 + __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
907 + __in HANDLE hFile,
908 + __in LONGLONG llDistance,
909 + __out LONGLONG* pllNewPostion,
910 + __in DWORD dwSeekType
911 + )
912 +{
913 + BOOL fFound = FALSE;
914 +
915 + BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = GetVirtualFilePointer(pCabinetContext, hFile);
916 + if (pVfp)
917 + {
918 + switch (dwSeekType)
919 + {
920 + case FILE_BEGIN:
921 + pVfp->liPosition.QuadPart = llDistance;
922 + break;
923 +
924 + case FILE_CURRENT:
925 + pVfp->liPosition.QuadPart += llDistance;
926 + break;
927 +
928 + case FILE_END: __fallthrough;
929 + default:
930 + AssertSz(FALSE, "Unsupported seek type.");
931 + break;
932 + }
933 +
934 + *pllNewPostion = pVfp->liPosition.QuadPart;
935 + fFound = TRUE;
936 + }
937 +
938 + return fFound;
939 +}
940 +
941 +static HRESULT CloseIfVirturalFilePointer(
942 + __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
943 + __in HANDLE hFile
944 + )
945 +{
946 + HRESULT hr = E_NOTFOUND;
947 +
948 + BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = GetVirtualFilePointer(pCabinetContext, hFile);
949 + if (pVfp)
950 + {
951 + pVfp->hFile = INVALID_HANDLE_VALUE;
952 + pVfp->liPosition.QuadPart = 0;
953 + hr = S_OK;
954 + }
955 +
956 + return hr;
957 +}
958 +
959 +static BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* GetVirtualFilePointer(
960 + __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
961 + __in HANDLE hFile
962 + )
963 +{
964 + for (DWORD i = 0; i < pCabinetContext->cVirtualFilePointers; ++i)
965 + {
966 + BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = pCabinetContext->rgVirtualFilePointers + i;
967 + if (pVfp->hFile == hFile)
968 + {
969 + return pVfp;
970 + }
971 + }
972 +
973 + return NULL;
974 +}
src/engine/cabextract.h new
+40
@@ -0,0 +1,40 @@
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 +#if defined(__cplusplus)
6 +extern "C" {
7 +#endif
8 +
9 +
10 +// function declarations
11 +
12 +void CabExtractInitialize();
13 +HRESULT CabExtractOpen(
14 + __in BURN_CONTAINER_CONTEXT* pContext,
15 + __in LPCWSTR wzFilePath
16 + );
17 +HRESULT CabExtractNextStream(
18 + __in BURN_CONTAINER_CONTEXT* pContext,
19 + __inout_z LPWSTR* psczStreamName
20 + );
21 +HRESULT CabExtractStreamToFile(
22 + __in BURN_CONTAINER_CONTEXT* pContext,
23 + __in_z LPCWSTR wzFileName
24 + );
25 +HRESULT CabExtractStreamToBuffer(
26 + __in BURN_CONTAINER_CONTEXT* pContext,
27 + __out BYTE** ppbBuffer,
28 + __out SIZE_T* pcbBuffer
29 + );
30 +HRESULT CabExtractSkipStream(
31 + __in BURN_CONTAINER_CONTEXT* pContext
32 + );
33 +HRESULT CabExtractClose(
34 + __in BURN_CONTAINER_CONTEXT* pContext
35 + );
36 +
37 +
38 +#if defined(__cplusplus)
39 +}
40 +#endif
src/engine/cache.cpp new
+2026
@@ -0,0 +1,2026 @@
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 +static const LPCWSTR BUNDLE_CLEAN_ROOM_WORKING_FOLDER_NAME = L".cr";
6 +static const LPCWSTR BUNDLE_WORKING_FOLDER_NAME = L".be";
7 +static const LPCWSTR UNVERIFIED_CACHE_FOLDER_NAME = L".unverified";
8 +static const LPCWSTR PACKAGE_CACHE_FOLDER_NAME = L"Package Cache";
9 +static const DWORD FILE_OPERATION_RETRY_COUNT = 3;
10 +static const DWORD FILE_OPERATION_RETRY_WAIT = 2000;
11 +
12 +static BOOL vfInitializedCache = FALSE;
13 +static BOOL vfRunningFromCache = FALSE;
14 +static LPWSTR vsczSourceProcessPath = NULL;
15 +static LPWSTR vsczWorkingFolder = NULL;
16 +static LPWSTR vsczDefaultUserPackageCache = NULL;
17 +static LPWSTR vsczDefaultMachinePackageCache = NULL;
18 +static LPWSTR vsczCurrentMachinePackageCache = NULL;
19 +
20 +static HRESULT CalculateWorkingFolder(
21 + __in_z LPCWSTR wzBundleId,
22 + __deref_out_z LPWSTR* psczWorkingFolder
23 + );
24 +static HRESULT GetLastUsedSourceFolder(
25 + __in BURN_VARIABLES* pVariables,
26 + __out_z LPWSTR* psczLastSource
27 + );
28 +static HRESULT CreateCompletedPath(
29 + __in BOOL fPerMachine,
30 + __in LPCWSTR wzCacheId,
31 + __out LPWSTR* psczCacheDirectory
32 + );
33 +static HRESULT CreateUnverifiedPath(
34 + __in BOOL fPerMachine,
35 + __in_z LPCWSTR wzPayloadId,
36 + __out_z LPWSTR* psczUnverifiedPayloadPath
37 + );
38 +static HRESULT GetRootPath(
39 + __in BOOL fPerMachine,
40 + __in BOOL fAllowRedirect,
41 + __deref_out_z LPWSTR* psczRootPath
42 + );
43 +static HRESULT VerifyThenTransferContainer(
44 + __in BURN_CONTAINER* pContainer,
45 + __in_z LPCWSTR wzCachedPath,
46 + __in_z LPCWSTR wzUnverifiedContainerPath,
47 + __in BOOL fMove
48 + );
49 +static HRESULT VerifyThenTransferPayload(
50 + __in BURN_PAYLOAD* pPayload,
51 + __in_z LPCWSTR wzCachedPath,
52 + __in_z LPCWSTR wzUnverifiedPayloadPath,
53 + __in BOOL fMove
54 + );
55 +static HRESULT TransferWorkingPathToUnverifiedPath(
56 + __in_z LPCWSTR wzWorkingPath,
57 + __in_z LPCWSTR wzUnverifiedPayloadPath,
58 + __in BOOL fMove
59 + );
60 +static HRESULT VerifyFileAgainstPayload(
61 + __in BURN_PAYLOAD* pPayload,
62 + __in_z LPCWSTR wzVerifyPath
63 + );
64 +static HRESULT ResetPathPermissions(
65 + __in BOOL fPerMachine,
66 + __in_z LPCWSTR wzPath
67 + );
68 +static HRESULT SecurePath(
69 + __in LPCWSTR wzPath
70 + );
71 +static HRESULT CopyEngineToWorkingFolder(
72 + __in_z LPCWSTR wzSourcePath,
73 + __in_z LPCWSTR wzWorkingFolderName,
74 + __in_z LPCWSTR wzExecutableName,
75 + __in BURN_PAYLOADS* pUxPayloads,
76 + __in BURN_SECTION* pSection,
77 + __deref_out_z_opt LPWSTR* psczEngineWorkingPath
78 + );
79 +static HRESULT CopyEngineWithSignatureFixup(
80 + __in HANDLE hEngineFile,
81 + __in_z LPCWSTR wzEnginePath,
82 + __in_z LPCWSTR wzTargetPath,
83 + __in BURN_SECTION* pSection
84 + );
85 +static HRESULT RemoveBundleOrPackage(
86 + __in BOOL fBundle,
87 + __in BOOL fPerMachine,
88 + __in_z LPCWSTR wzBundleOrPackageId,
89 + __in_z LPCWSTR wzCacheId
90 + );
91 +static HRESULT VerifyHash(
92 + __in BYTE* pbHash,
93 + __in DWORD cbHash,
94 + __in_z LPCWSTR wzUnverifiedPayloadPath,
95 + __in HANDLE hFile
96 + );
97 +static HRESULT VerifyPayloadWithCatalog(
98 + __in BURN_PAYLOAD* pPayload,
99 + __in_z LPCWSTR wzUnverifiedPayloadPath,
100 + __in HANDLE hFile
101 + );
102 +static HRESULT VerifyPayloadAgainstChain(
103 + __in BURN_PAYLOAD* pPayload,
104 + __in PCCERT_CHAIN_CONTEXT pChainContext
105 + );
106 +
107 +
108 +extern "C" HRESULT CacheInitialize(
109 + __in BURN_REGISTRATION* pRegistration,
110 + __in BURN_VARIABLES* pVariables,
111 + __in_z_opt LPCWSTR wzSourceProcessPath
112 + )
113 +{
114 + HRESULT hr = S_OK;
115 + LPWSTR sczCurrentPath = NULL;
116 + LPWSTR sczCompletedFolder = NULL;
117 + LPWSTR sczCompletedPath = NULL;
118 + LPWSTR sczOriginalSource = NULL;
119 + LPWSTR sczOriginalSourceFolder = NULL;
120 + int nCompare = 0;
121 +
122 + if (!vfInitializedCache)
123 + {
124 + hr = PathForCurrentProcess(&sczCurrentPath, NULL);
125 + ExitOnFailure(hr, "Failed to get current process path.");
126 +
127 + // Determine if we are running from the package cache or not.
128 + hr = CacheGetCompletedPath(pRegistration->fPerMachine, pRegistration->sczId, &sczCompletedFolder);
129 + ExitOnFailure(hr, "Failed to get completed path for bundle.");
130 +
131 + hr = PathConcat(sczCompletedFolder, pRegistration->sczExecutableName, &sczCompletedPath);
132 + ExitOnFailure(hr, "Failed to combine working path with engine file name.");
133 +
134 + hr = PathCompare(sczCurrentPath, sczCompletedPath, &nCompare);
135 + ExitOnFailure(hr, "Failed to compare current path for bundle: %ls", sczCurrentPath);
136 +
137 + vfRunningFromCache = (CSTR_EQUAL == nCompare);
138 +
139 + // If a source process path was not provided (e.g. we are not being
140 + // run in a clean room) then use the current process path as the
141 + // source process path.
142 + if (!wzSourceProcessPath)
143 + {
144 + wzSourceProcessPath = sczCurrentPath;
145 + }
146 +
147 + hr = StrAllocString(&vsczSourceProcessPath, wzSourceProcessPath, 0);
148 + ExitOnFailure(hr, "Failed to initialize cache source path.");
149 +
150 + // If we're not running from the cache, ensure the original source is set.
151 + if (!vfRunningFromCache)
152 + {
153 + // If the original source has not been set already then set it where the bundle is
154 + // running from right now. This value will be persisted and we'll use it when launched
155 + // from the clean room or package cache since none of our packages will be relative to
156 + // those locations.
157 + hr = VariableGetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE, &sczOriginalSource);
158 + if (E_NOTFOUND == hr)
159 + {
160 + hr = VariableSetLiteralString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE, wzSourceProcessPath, FALSE);
161 + ExitOnFailure(hr, "Failed to set original source variable.");
162 +
163 + hr = StrAllocString(&sczOriginalSource, wzSourceProcessPath, 0);
164 + ExitOnFailure(hr, "Failed to copy current path to original source.");
165 + }
166 +
167 + hr = VariableGetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER, &sczOriginalSourceFolder);
168 + if (E_NOTFOUND == hr)
169 + {
170 + hr = PathGetDirectory(sczOriginalSource, &sczOriginalSourceFolder);
171 + ExitOnFailure(hr, "Failed to get directory from original source path.");
172 +
173 + hr = VariableSetLiteralString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER, sczOriginalSourceFolder, FALSE);
174 + ExitOnFailure(hr, "Failed to set original source directory variable.");
175 + }
176 + }
177 +
178 + vfInitializedCache = TRUE;
179 + }
180 +
181 +LExit:
182 + ReleaseStr(sczCurrentPath);
183 + ReleaseStr(sczCompletedFolder);
184 + ReleaseStr(sczCompletedPath);
185 + ReleaseStr(sczOriginalSource);
186 + ReleaseStr(sczOriginalSourceFolder);
187 +
188 + return hr;
189 +}
190 +
191 +extern "C" HRESULT CacheEnsureWorkingFolder(
192 + __in_z LPCWSTR wzBundleId,
193 + __deref_out_z_opt LPWSTR* psczWorkingFolder
194 + )
195 +{
196 + HRESULT hr = S_OK;
197 + LPWSTR sczWorkingFolder = NULL;
198 +
199 + hr = CalculateWorkingFolder(wzBundleId, &sczWorkingFolder);
200 + ExitOnFailure(hr, "Failed to calculate working folder to ensure it exists.");
201 +
202 + hr = DirEnsureExists(sczWorkingFolder, NULL);
203 + ExitOnFailure(hr, "Failed create working folder.");
204 +
205 + // Best effort to ensure our working folder is not encrypted.
206 + ::DecryptFileW(sczWorkingFolder, 0);
207 +
208 + if (psczWorkingFolder)
209 + {
210 + hr = StrAllocString(psczWorkingFolder, sczWorkingFolder, 0);
211 + ExitOnFailure(hr, "Failed to copy working folder.");
212 + }
213 +
214 +LExit:
215 + ReleaseStr(sczWorkingFolder);
216 +
217 + return hr;
218 +}
219 +
220 +extern "C" HRESULT CacheCalculateBundleWorkingPath(
221 + __in_z LPCWSTR wzBundleId,
222 + __in LPCWSTR wzExecutableName,
223 + __deref_out_z LPWSTR* psczWorkingPath
224 + )
225 +{
226 + Assert(vfInitializedCache);
227 +
228 + HRESULT hr = S_OK;
229 + LPWSTR sczWorkingFolder = NULL;
230 +
231 + // If the bundle is running out of the package cache then we use that as the
232 + // working folder since we feel safe in the package cache.
233 + if (vfRunningFromCache)
234 + {
235 + hr = PathForCurrentProcess(psczWorkingPath, NULL);
236 + ExitOnFailure(hr, "Failed to get current process path.");
237 + }
238 + else // Otherwise, use the real working folder.
239 + {
240 + hr = CalculateWorkingFolder(wzBundleId, &sczWorkingFolder);
241 + ExitOnFailure(hr, "Failed to get working folder for bundle.");
242 +
243 + hr = StrAllocFormatted(psczWorkingPath, L"%ls%ls\\%ls", sczWorkingFolder, BUNDLE_WORKING_FOLDER_NAME, wzExecutableName);
244 + ExitOnFailure(hr, "Failed to calculate the bundle working path.");
245 + }
246 +
247 +LExit:
248 + ReleaseStr(sczWorkingFolder);
249 +
250 + return hr;
251 +}
252 +
253 +extern "C" HRESULT CacheCalculateBundleLayoutWorkingPath(
254 + __in_z LPCWSTR wzBundleId,
255 + __deref_out_z LPWSTR* psczWorkingPath
256 + )
257 +{
258 + HRESULT hr = S_OK;
259 + LPWSTR sczWorkingFolder = NULL;
260 +
261 + hr = CalculateWorkingFolder(wzBundleId, psczWorkingPath);
262 + ExitOnFailure(hr, "Failed to get working folder for bundle layout.");
263 +
264 + hr = StrAllocConcat(psczWorkingPath, wzBundleId, 0);
265 + ExitOnFailure(hr, "Failed to append bundle id for bundle layout working path.");
266 +
267 +LExit:
268 + ReleaseStr(sczWorkingFolder);
269 +
270 + return hr;
271 +}
272 +
273 +extern "C" HRESULT CacheCalculatePayloadWorkingPath(
274 + __in_z LPCWSTR wzBundleId,
275 + __in BURN_PAYLOAD* pPayload,
276 + __deref_out_z LPWSTR* psczWorkingPath
277 + )
278 +{
279 + HRESULT hr = S_OK;
280 +
281 + hr = CalculateWorkingFolder(wzBundleId, psczWorkingPath);
282 + ExitOnFailure(hr, "Failed to get working folder for payload.");
283 +
284 + hr = StrAllocConcat(psczWorkingPath, pPayload->sczKey, 0);
285 + ExitOnFailure(hr, "Failed to append SHA1 hash as payload unverified path.");
286 +
287 +LExit:
288 + return hr;
289 +}
290 +
291 +extern "C" HRESULT CacheCalculateContainerWorkingPath(
292 + __in_z LPCWSTR wzBundleId,
293 + __in BURN_CONTAINER* pContainer,
294 + __deref_out_z LPWSTR* psczWorkingPath
295 + )
296 +{
297 + HRESULT hr = S_OK;
298 +
299 + hr = CalculateWorkingFolder(wzBundleId, psczWorkingPath);
300 + ExitOnFailure(hr, "Failed to get working folder for container.");
301 +
302 + hr = StrAllocConcat(psczWorkingPath, pContainer->sczHash, 0);
303 + ExitOnFailure(hr, "Failed to append SHA1 hash as container unverified path.");
304 +
305 +LExit:
306 + return hr;
307 +}
308 +
309 +extern "C" HRESULT CacheGetRootCompletedPath(
310 + __in BOOL fPerMachine,
311 + __in BOOL fForceInitialize,
312 + __deref_out_z LPWSTR* psczRootCompletedPath
313 + )
314 +{
315 + HRESULT hr = S_OK;
316 +
317 + if (fForceInitialize)
318 + {
319 + hr = CreateCompletedPath(fPerMachine, L"", psczRootCompletedPath);
320 + }
321 + else
322 + {
323 + hr = GetRootPath(fPerMachine, TRUE, psczRootCompletedPath);
324 + }
325 +
326 + return hr;
327 +}
328 +
329 +extern "C" HRESULT CacheGetCompletedPath(
330 + __in BOOL fPerMachine,
331 + __in_z LPCWSTR wzCacheId,
332 + __deref_out_z LPWSTR* psczCompletedPath
333 + )
334 +{
335 + HRESULT hr = S_OK;
336 + BOOL fRedirected = FALSE;
337 + LPWSTR sczRootPath = NULL;
338 + LPWSTR sczCurrentCompletedPath = NULL;
339 + LPWSTR sczDefaultCompletedPath = NULL;
340 +
341 + hr = GetRootPath(fPerMachine, TRUE, &sczRootPath);
342 + ExitOnFailure(hr, "Failed to get %hs package cache root directory.", fPerMachine ? "per-machine" : "per-user");
343 +
344 + // GetRootPath returns S_FALSE if the package cache is redirected elsewhere.
345 + fRedirected = S_FALSE == hr;
346 +
347 + hr = PathConcat(sczRootPath, wzCacheId, &sczCurrentCompletedPath);
348 + ExitOnFailure(hr, "Failed to construct cache path.");
349 +
350 + hr = PathBackslashTerminate(&sczCurrentCompletedPath);
351 + ExitOnFailure(hr, "Failed to ensure cache path was backslash terminated.");
352 +
353 + // Return the old package cache directory if the new directory does not exist but the old directory does.
354 + // If neither package cache directory exists return the (possibly) redirected package cache directory.
355 + if (fRedirected && !DirExists(sczCurrentCompletedPath, NULL))
356 + {
357 + hr = GetRootPath(fPerMachine, FALSE, &sczRootPath);
358 + ExitOnFailure(hr, "Failed to get old %hs package cache root directory.", fPerMachine ? "per-machine" : "per-user");
359 +
360 + hr = PathConcat(sczRootPath, wzCacheId, &sczDefaultCompletedPath);
361 + ExitOnFailure(hr, "Failed to construct cache path.");
362 +
363 + hr = PathBackslashTerminate(&sczDefaultCompletedPath);
364 + ExitOnFailure(hr, "Failed to ensure cache path was backslash terminated.");
365 +
366 + if (DirExists(sczDefaultCompletedPath, NULL))
367 + {
368 + *psczCompletedPath = sczDefaultCompletedPath;
369 + sczDefaultCompletedPath = NULL;
370 +
371 + ExitFunction();
372 + }
373 + }
374 +
375 + *psczCompletedPath = sczCurrentCompletedPath;
376 + sczCurrentCompletedPath = NULL;
377 +
378 +LExit:
379 + ReleaseNullStr(sczDefaultCompletedPath);
380 + ReleaseNullStr(sczCurrentCompletedPath);
381 + ReleaseNullStr(sczRootPath);
382 +
383 + return hr;
384 +}
385 +
386 +extern "C" HRESULT CacheGetResumePath(
387 + __in_z LPCWSTR wzPayloadWorkingPath,
388 + __deref_out_z LPWSTR* psczResumePath
389 + )
390 +{
391 + HRESULT hr = S_OK;
392 +
393 + hr = StrAllocFormatted(psczResumePath, L"%ls.R", wzPayloadWorkingPath);
394 + ExitOnFailure(hr, "Failed to create resume path.");
395 +
396 +LExit:
397 + return hr;
398 +}
399 +
400 +extern "C" HRESULT CacheFindLocalSource(
401 + __in_z LPCWSTR wzSourcePath,
402 + __in BURN_VARIABLES* pVariables,
403 + __out BOOL* pfFound,
404 + __out_z LPWSTR* psczSourceFullPath
405 + )
406 +{
407 + HRESULT hr = S_OK;
408 + LPWSTR sczSourceProcessFolder = NULL;
409 + LPWSTR sczCurrentPath = NULL;
410 + LPWSTR sczLastSourcePath = NULL;
411 + LPWSTR sczLastSourceFolder = NULL;
412 + LPWSTR sczLayoutPath = NULL;
413 + LPWSTR sczLayoutFolder = NULL;
414 + LPCWSTR rgwzSearchPaths[3] = { };
415 + DWORD cSearchPaths = 0;
416 +
417 + // If the source path provided is a full path, obviously that is where we should be looking.
418 + if (PathIsAbsolute(wzSourcePath))
419 + {
420 + rgwzSearchPaths[0] = wzSourcePath;
421 + cSearchPaths = 1;
422 + }
423 + else
424 + {
425 + // If we're not running from cache or we couldn't get the last source, use
426 + // the source path location first. In the case where we are in the bundle's
427 + // package cache and couldn't find a last used source we unfortunately will
428 + // be picking the package cache path which isn't likely to have what we are
429 + // looking for.
430 + hr = GetLastUsedSourceFolder(pVariables, &sczLastSourceFolder);
431 + if (!vfRunningFromCache || FAILED(hr))
432 + {
433 + hr = PathGetDirectory(vsczSourceProcessPath, &sczSourceProcessFolder);
434 + ExitOnFailure(hr, "Failed to get current process directory.");
435 +
436 + hr = PathConcat(sczSourceProcessFolder, wzSourcePath, &sczCurrentPath);
437 + ExitOnFailure(hr, "Failed to combine last source with source.");
438 +
439 + rgwzSearchPaths[0] = sczCurrentPath;
440 + cSearchPaths = 1;
441 + }
442 +
443 + // If we have a last used source and it does not duplicate the existing search path,
444 + // add the last used source to the search path second.
445 + if (sczLastSourceFolder && *sczLastSourceFolder)
446 + {
447 + hr = PathConcat(sczLastSourceFolder, wzSourcePath, &sczLastSourcePath);
448 + ExitOnFailure(hr, "Failed to combine last source with source.");
449 +
450 + if (0 == cSearchPaths || CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, rgwzSearchPaths[0], -1, sczLastSourcePath, -1))
451 + {
452 + rgwzSearchPaths[cSearchPaths] = sczLastSourcePath;
453 + ++cSearchPaths;
454 + }
455 + }
456 +
457 + // Also consider the layout directory if set on the command line or by the BA.
458 + hr = VariableGetString(pVariables, BURN_BUNDLE_LAYOUT_DIRECTORY, &sczLayoutFolder);
459 + if (E_NOTFOUND != hr)
460 + {
461 + ExitOnFailure(hr, "Failed to get bundle layout directory property.");
462 +
463 + hr = PathConcat(sczLayoutFolder, wzSourcePath, &sczLayoutPath);
464 + ExitOnFailure(hr, "Failed to combine layout source with source.");
465 +
466 + rgwzSearchPaths[cSearchPaths] = sczLayoutPath;
467 + ++cSearchPaths;
468 + }
469 + }
470 +
471 + *pfFound = FALSE; // assume we won't find the file locally.
472 +
473 + for (DWORD i = 0; i < cSearchPaths; ++i)
474 + {
475 + // If the file exists locally, copy its path.
476 + if (FileExistsEx(rgwzSearchPaths[i], NULL))
477 + {
478 + hr = StrAllocString(psczSourceFullPath, rgwzSearchPaths[i], 0);
479 + ExitOnFailure(hr, "Failed to copy source path.");
480 +
481 + *pfFound = TRUE;
482 + break;
483 + }
484 + }
485 +
486 + // If nothing was found, return the first thing in our search path as the
487 + // best path where we thought we should have found the file.
488 + if (!*pfFound)
489 + {
490 + hr = StrAllocString(psczSourceFullPath, rgwzSearchPaths[0], 0);
491 + ExitOnFailure(hr, "Failed to copy source path.");
492 + }
493 +
494 +LExit:
495 + ReleaseStr(sczCurrentPath);
496 + ReleaseStr(sczSourceProcessFolder);
497 + ReleaseStr(sczLastSourceFolder);
498 + ReleaseStr(sczLastSourcePath);
499 + ReleaseStr(sczLayoutFolder);
500 + ReleaseStr(sczLayoutPath);
501 +
502 + return hr;
503 +}
504 +
505 +extern "C" HRESULT CacheSetLastUsedSource(
506 + __in BURN_VARIABLES* pVariables,
507 + __in_z LPCWSTR wzSourcePath,
508 + __in_z LPCWSTR wzRelativePath
509 + )
510 +{
511 + HRESULT hr = S_OK;
512 + size_t cchSourcePath = 0;
513 + size_t cchRelativePath = 0;
514 + size_t iSourceRelativePath = 0;
515 + LPWSTR sczSourceFolder = NULL;
516 + LPWSTR sczLastSourceFolder = NULL;
517 + int nCompare = 0;
518 +
519 + hr = ::StringCchLengthW(wzSourcePath, STRSAFE_MAX_CCH, &cchSourcePath);
520 + ExitOnFailure(hr, "Failed to determine length of source path.");
521 +
522 + hr = ::StringCchLengthW(wzRelativePath, STRSAFE_MAX_CCH, &cchRelativePath);
523 + ExitOnFailure(hr, "Failed to determine length of relative path.");
524 +
525 + // If the source path is smaller than the relative path (plus space for "X:\") then we know they
526 + // are not relative to each other.
527 + if (cchSourcePath < cchRelativePath + 3)
528 + {
529 + ExitFunction();
530 + }
531 +
532 + // If the source path ends with the relative path then this source could be a new path.
533 + iSourceRelativePath = cchSourcePath - cchRelativePath;
534 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, wzSourcePath + iSourceRelativePath, -1, wzRelativePath, -1))
535 + {
536 + hr = StrAllocString(&sczSourceFolder, wzSourcePath, iSourceRelativePath);
537 + ExitOnFailure(hr, "Failed to trim source folder.");
538 +
539 + hr = VariableGetString(pVariables, BURN_BUNDLE_LAST_USED_SOURCE, &sczLastSourceFolder);
540 + if (SUCCEEDED(hr))
541 + {
542 + nCompare = ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczSourceFolder, -1, sczLastSourceFolder, -1);
543 + }
544 + else if (E_NOTFOUND == hr)
545 + {
546 + nCompare = CSTR_GREATER_THAN;
547 + hr = S_OK;
548 + }
549 +
550 + if (CSTR_EQUAL != nCompare)
551 + {
552 + hr = VariableSetLiteralString(pVariables, BURN_BUNDLE_LAST_USED_SOURCE, sczSourceFolder, FALSE);
553 + ExitOnFailure(hr, "Failed to set last source.");
554 + }
555 + }
556 +
557 +LExit:
558 + ReleaseStr(sczLastSourceFolder);
559 + ReleaseStr(sczSourceFolder);
560 +
561 + return hr;
562 +}
563 +
564 +extern "C" HRESULT CacheSendProgressCallback(
565 + __in DOWNLOAD_CACHE_CALLBACK* pCallback,
566 + __in DWORD64 dw64Progress,
567 + __in DWORD64 dw64Total,
568 + __in HANDLE hDestinationFile
569 + )
570 +{
571 + static LARGE_INTEGER LARGE_INTEGER_ZERO = { };
572 +
573 + HRESULT hr = S_OK;
574 + DWORD dwResult = PROGRESS_CONTINUE;
575 + LARGE_INTEGER liTotalSize = { };
576 + LARGE_INTEGER liTotalTransferred = { };
577 +
578 + if (pCallback->pfnProgress)
579 + {
580 + liTotalSize.QuadPart = dw64Total;
581 + liTotalTransferred.QuadPart = dw64Progress;
582 +
583 + dwResult = (*pCallback->pfnProgress)(liTotalSize, liTotalTransferred, LARGE_INTEGER_ZERO, LARGE_INTEGER_ZERO, 1, CALLBACK_CHUNK_FINISHED, INVALID_HANDLE_VALUE, hDestinationFile, pCallback->pv);
584 + switch (dwResult)
585 + {
586 + case PROGRESS_CONTINUE:
587 + hr = S_OK;
588 + break;
589 +
590 + case PROGRESS_CANCEL: __fallthrough; // TODO: should cancel and stop be treated differently?
591 + case PROGRESS_STOP:
592 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
593 + ExitOnRootFailure(hr, "UX aborted on download progress.");
594 +
595 + case PROGRESS_QUIET: // Not actually an error, just an indication to the caller to stop requesting progress.
596 + pCallback->pfnProgress = NULL;
597 + hr = S_OK;
598 + break;
599 +
600 + default:
601 + hr = E_UNEXPECTED;
602 + ExitOnRootFailure(hr, "Invalid return code from progress routine.");
603 + }
604 + }
605 +
606 +LExit:
607 + return hr;
608 +}
609 +
610 +extern "C" void CacheSendErrorCallback(
611 + __in DOWNLOAD_CACHE_CALLBACK* pCallback,
612 + __in HRESULT hrError,
613 + __in_z_opt LPCWSTR wzError,
614 + __out_opt BOOL* pfRetry
615 + )
616 +{
617 + if (pfRetry)
618 + {
619 + *pfRetry = FALSE;
620 + }
621 +
622 + if (pCallback->pfnCancel)
623 + {
624 + int nResult = (*pCallback->pfnCancel)(hrError, wzError, pfRetry != NULL, pCallback->pv);
625 + if (pfRetry && IDRETRY == nResult)
626 + {
627 + *pfRetry = TRUE;
628 + }
629 + }
630 +}
631 +
632 +extern "C" BOOL CacheBundleRunningFromCache()
633 +{
634 + return vfRunningFromCache;
635 +}
636 +
637 +extern "C" HRESULT CacheBundleToCleanRoom(
638 + __in BURN_PAYLOADS* pUxPayloads,
639 + __in BURN_SECTION* pSection,
640 + __deref_out_z_opt LPWSTR* psczCleanRoomBundlePath
641 + )
642 +{
643 + HRESULT hr = S_OK;
644 + LPWSTR sczSourcePath = NULL;
645 + LPWSTR wzExecutableName = NULL;
646 +
647 + hr = PathForCurrentProcess(&sczSourcePath, NULL);
648 + ExitOnFailure(hr, "Failed to get current path for process to cache to clean room.");
649 +
650 + wzExecutableName = PathFile(sczSourcePath);
651 +
652 + hr = CopyEngineToWorkingFolder(sczSourcePath, BUNDLE_CLEAN_ROOM_WORKING_FOLDER_NAME, wzExecutableName, pUxPayloads, pSection, psczCleanRoomBundlePath);
653 + ExitOnFailure(hr, "Failed to cache bundle to clean room.");
654 +
655 +LExit:
656 + ReleaseStr(sczSourcePath);
657 +
658 + return hr;
659 +}
660 +
661 +extern "C" HRESULT CacheBundleToWorkingDirectory(
662 + __in_z LPCWSTR /*wzBundleId*/,
663 + __in_z LPCWSTR wzExecutableName,
664 + __in BURN_PAYLOADS* pUxPayloads,
665 + __in BURN_SECTION* pSection,
666 + __deref_out_z_opt LPWSTR* psczEngineWorkingPath
667 + )
668 +{
669 + Assert(vfInitializedCache);
670 +
671 + HRESULT hr = S_OK;
672 + LPWSTR sczSourcePath = NULL;
673 +
674 + // Initialize the source.
675 + hr = PathForCurrentProcess(&sczSourcePath, NULL);
676 + ExitOnFailure(hr, "Failed to get current process path.");
677 +
678 + // If the bundle is running out of the package cache then we don't need to copy it to
679 + // the working folder since we feel safe in the package cache and will run from there.
680 + if (vfRunningFromCache)
681 + {
682 + hr = StrAllocString(psczEngineWorkingPath, sczSourcePath, 0);
683 + ExitOnFailure(hr, "Failed to use current process path as target path.");
684 + }
685 + else // otherwise, carry on putting the bundle in the working folder.
686 + {
687 + hr = CopyEngineToWorkingFolder(sczSourcePath, BUNDLE_WORKING_FOLDER_NAME, wzExecutableName, pUxPayloads, pSection, psczEngineWorkingPath);
688 + ExitOnFailure(hr, "Failed to copy engine to working folder.");
689 + }
690 +
691 +LExit:
692 + ReleaseStr(sczSourcePath);
693 +
694 + return hr;
695 +}
696 +
697 +extern "C" HRESULT CacheLayoutBundle(
698 + __in_z LPCWSTR wzExecutableName,
699 + __in_z LPCWSTR wzLayoutDirectory,
700 + __in_z LPCWSTR wzSourceBundlePath
701 + )
702 +{
703 + HRESULT hr = S_OK;
704 + LPWSTR sczTargetPath = NULL;
705 +
706 + hr = PathConcat(wzLayoutDirectory, wzExecutableName, &sczTargetPath);
707 + ExitOnFailure(hr, "Failed to combine completed path with engine file name for layout.");
708 +
709 + LogStringLine(REPORT_STANDARD, "Layout bundle from: '%ls' to: '%ls'", wzSourceBundlePath, sczTargetPath);
710 +
711 + hr = FileEnsureMoveWithRetry(wzSourceBundlePath, sczTargetPath, TRUE, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
712 + ExitOnFailure(hr, "Failed to layout bundle from: '%ls' to '%ls'", wzSourceBundlePath, sczTargetPath);
713 +
714 +LExit:
715 + ReleaseStr(sczTargetPath);
716 +
717 + return hr;
718 +}
719 +
720 +extern "C" HRESULT CacheCompleteBundle(
721 + __in BOOL fPerMachine,
722 + __in_z LPCWSTR wzExecutableName,
723 + __in_z LPCWSTR wzBundleId,
724 + __in BURN_PAYLOADS* pUxPayloads,
725 + __in_z LPCWSTR wzSourceBundlePath
726 +#ifdef DEBUG
727 + , __in_z LPCWSTR wzExecutablePath
728 +#endif
729 + )
730 +{
731 + HRESULT hr = S_OK;
732 + int nCompare = 0;
733 + LPWSTR sczTargetDirectory = NULL;
734 + LPWSTR sczTargetPath = NULL;
735 + LPWSTR sczSourceDirectory = NULL;
736 + LPWSTR sczPayloadSourcePath = NULL;
737 +
738 + hr = CreateCompletedPath(fPerMachine, wzBundleId, &sczTargetDirectory);
739 + ExitOnFailure(hr, "Failed to create completed cache path for bundle.");
740 +
741 + hr = PathConcat(sczTargetDirectory, wzExecutableName, &sczTargetPath);
742 + ExitOnFailure(hr, "Failed to combine completed path with engine file name.");
743 +
744 + Assert(CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, wzExecutablePath, -1, sczTargetPath, -1));
745 +
746 + // If the bundle is running out of the package cache then we don't need to copy it there
747 + // (and don't want to since it'll be in use) so bail.
748 + hr = PathCompare(wzSourceBundlePath, sczTargetPath, &nCompare);
749 + ExitOnFailure(hr, "Failed to compare completed cache path for bundle: %ls", wzSourceBundlePath);
750 +
751 + if (CSTR_EQUAL == nCompare)
752 + {
753 + ExitFunction();
754 + }
755 +
756 + // Otherwise, carry on putting the bundle in the cache.
757 + LogStringLine(REPORT_STANDARD, "Caching bundle from: '%ls' to: '%ls'", wzSourceBundlePath, sczTargetPath);
758 +
759 + FileRemoveFromPendingRename(sczTargetPath); // best effort to ensure bundle is not deleted from cache post restart.
760 +
761 + hr = FileEnsureCopyWithRetry(wzSourceBundlePath, sczTargetPath, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
762 + ExitOnFailure(hr, "Failed to cache bundle from: '%ls' to '%ls'", wzSourceBundlePath, sczTargetPath);
763 +
764 + // Reset the path permissions in the cache.
765 + hr = ResetPathPermissions(fPerMachine, sczTargetPath);
766 + ExitOnFailure(hr, "Failed to reset permissions on cached bundle: '%ls'", sczTargetPath);
767 +
768 + hr = PathGetDirectory(wzSourceBundlePath, &sczSourceDirectory);
769 + ExitOnFailure(hr, "Failed to get directory from engine working path: %ls", wzSourceBundlePath);
770 +
771 + // Cache external UX payloads to completed path.
772 + for (DWORD i = 0; i < pUxPayloads->cPayloads; ++i)
773 + {
774 + BURN_PAYLOAD* pPayload = &pUxPayloads->rgPayloads[i];
775 +
776 + if (BURN_PAYLOAD_PACKAGING_EXTERNAL == pPayload->packaging)
777 + {
778 + hr = PathConcat(sczSourceDirectory, pPayload->sczSourcePath, &sczPayloadSourcePath);
779 + ExitOnFailure(hr, "Failed to build payload source path.");
780 +
781 + hr = CacheCompletePayload(fPerMachine, pPayload, wzBundleId, sczPayloadSourcePath, FALSE);
782 + ExitOnFailure(hr, "Failed to complete the cache of payload: %ls", pPayload->sczKey);
783 + }
784 + }
785 +
786 +LExit:
787 + ReleaseStr(sczPayloadSourcePath);
788 + ReleaseStr(sczSourceDirectory);
789 + ReleaseStr(sczTargetPath);
790 + ReleaseStr(sczTargetDirectory);
791 +
792 + return hr;
793 +}
794 +
795 +extern "C" HRESULT CacheLayoutContainer(
796 + __in BURN_CONTAINER* pContainer,
797 + __in_z_opt LPCWSTR wzLayoutDirectory,
798 + __in_z LPCWSTR wzUnverifiedContainerPath,
799 + __in BOOL fMove
800 + )
801 +{
802 + HRESULT hr = S_OK;
803 + LPWSTR sczCachedPath = NULL;
804 +
805 + hr = PathConcat(wzLayoutDirectory, pContainer->sczFilePath, &sczCachedPath);
806 + ExitOnFailure(hr, "Failed to concat complete cached path.");
807 +
808 + hr = VerifyThenTransferContainer(pContainer, sczCachedPath, wzUnverifiedContainerPath, fMove);
809 + ExitOnFailure(hr, "Failed to layout container from cached path: %ls", sczCachedPath);
810 +
811 +LExit:
812 + ReleaseStr(sczCachedPath);
813 +
814 + return hr;
815 +}
816 +
817 +extern "C" HRESULT CacheLayoutPayload(
818 + __in BURN_PAYLOAD* pPayload,
819 + __in_z_opt LPCWSTR wzLayoutDirectory,
820 + __in_z LPCWSTR wzUnverifiedPayloadPath,
821 + __in BOOL fMove
822 + )
823 +{
824 + HRESULT hr = S_OK;
825 + LPWSTR sczCachedPath = NULL;
826 +
827 + hr = PathConcat(wzLayoutDirectory, pPayload->sczFilePath, &sczCachedPath);
828 + ExitOnFailure(hr, "Failed to concat complete cached path.");
829 +
830 + hr = VerifyThenTransferPayload(pPayload, sczCachedPath, wzUnverifiedPayloadPath, fMove);
831 + ExitOnFailure(hr, "Failed to layout payload from cached payload: %ls", sczCachedPath);
832 +
833 +LExit:
834 + ReleaseStr(sczCachedPath);
835 +
836 + return hr;
837 +}
838 +
839 +extern "C" HRESULT CacheCompletePayload(
840 + __in BOOL fPerMachine,
841 + __in BURN_PAYLOAD* pPayload,
842 + __in_z_opt LPCWSTR wzCacheId,
843 + __in_z LPCWSTR wzWorkingPayloadPath,
844 + __in BOOL fMove
845 + )
846 +{
847 + HRESULT hr = S_OK;
848 + LPWSTR sczCachedDirectory = NULL;
849 + LPWSTR sczCachedPath = NULL;
850 + LPWSTR sczUnverifiedPayloadPath = NULL;
851 +
852 + hr = CreateCompletedPath(fPerMachine, wzCacheId, &sczCachedDirectory);
853 + ExitOnFailure(hr, "Failed to get cached path for package with cache id: %ls", wzCacheId);
854 +
855 + hr = PathConcat(sczCachedDirectory, pPayload->sczFilePath, &sczCachedPath);
856 + ExitOnFailure(hr, "Failed to concat complete cached path.");
857 +
858 + // If the cached file matches what we expected, we're good.
859 + hr = VerifyFileAgainstPayload(pPayload, sczCachedPath);
860 + if (SUCCEEDED(hr))
861 + {
862 + ::DecryptFileW(sczCachedPath, 0); // Let's try to make sure it's not encrypted.
863 + LogId(REPORT_STANDARD, MSG_VERIFIED_EXISTING_PAYLOAD, pPayload->sczKey, sczCachedPath);
864 + ExitFunction();
865 + }
866 + else if (E_PATHNOTFOUND != hr && E_FILENOTFOUND != hr)
867 + {
868 + LogErrorId(hr, MSG_FAILED_VERIFY_PAYLOAD, pPayload->sczKey, sczCachedPath, NULL);
869 +
870 + FileEnsureDelete(sczCachedPath); // if the file existed but did not verify correctly, make it go away.
871 + }
872 +
873 + hr = CreateUnverifiedPath(fPerMachine, pPayload->sczKey, &sczUnverifiedPayloadPath);
874 + ExitOnFailure(hr, "Failed to create unverified path.");
875 +
876 + // If the working path exists, let's get it into the unverified path so we can reset the ACLs and verify the file.
877 + if (FileExistsEx(wzWorkingPayloadPath, NULL))
878 + {
879 + hr = TransferWorkingPathToUnverifiedPath(wzWorkingPayloadPath, sczUnverifiedPayloadPath, fMove);
880 + ExitOnFailure(hr, "Failed to transfer working path to unverified path for payload: %ls.", pPayload->sczKey);
881 + }
882 + else if (!FileExistsEx(sczUnverifiedPayloadPath, NULL)) // if the working path and unverified path do not exist, nothing we can do.
883 + {
884 + hr = E_FILENOTFOUND;
885 + ExitOnFailure(hr, "Failed to find payload: %ls in working path: %ls and unverified path: %ls", pPayload->sczKey, wzWorkingPayloadPath, sczUnverifiedPayloadPath);
886 + }
887 +
888 + hr = ResetPathPermissions(fPerMachine, sczUnverifiedPayloadPath);
889 + ExitOnFailure(hr, "Failed to reset permissions on unverified cached payload: %ls", pPayload->sczKey);
890 +
891 + hr = VerifyFileAgainstPayload(pPayload, sczUnverifiedPayloadPath);
892 + if (FAILED(hr))
893 + {
894 + LogErrorId(hr, MSG_FAILED_VERIFY_PAYLOAD, pPayload->sczKey, sczUnverifiedPayloadPath, NULL);
895 +
896 + FileEnsureDelete(sczUnverifiedPayloadPath); // if the file did not verify correctly, make it go away.
897 + ExitFunction();
898 + }
899 +
900 + LogId(REPORT_STANDARD, MSG_VERIFIED_ACQUIRED_PAYLOAD, pPayload->sczKey, sczUnverifiedPayloadPath, fMove ? "moving" : "copying", sczCachedPath);
901 +
902 + hr = FileEnsureMoveWithRetry(sczUnverifiedPayloadPath, sczCachedPath, TRUE, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
903 + ExitOnFailure(hr, "Failed to move verified file to complete payload path: %ls", sczCachedPath);
904 +
905 + ::DecryptFileW(sczCachedPath, 0); // Let's try to make sure it's not encrypted.
906 +
907 +LExit:
908 + ReleaseStr(sczUnverifiedPayloadPath);
909 + ReleaseStr(sczCachedPath);
910 + ReleaseStr(sczCachedDirectory);
911 +
912 + return hr;
913 +}
914 +
915 +extern "C" HRESULT CacheRemoveWorkingFolder(
916 + __in_z_opt LPCWSTR wzBundleId
917 + )
918 +{
919 + HRESULT hr = S_OK;
920 + LPWSTR sczWorkingFolder = NULL;
921 +
922 + if (vfInitializedCache)
923 + {
924 + hr = CalculateWorkingFolder(wzBundleId, &sczWorkingFolder);
925 + ExitOnFailure(hr, "Failed to calculate the working folder to remove it.");
926 +
927 + // Try to clean out everything in the working folder.
928 + hr = DirEnsureDeleteEx(sczWorkingFolder, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE);
929 + TraceError(hr, "Could not delete bundle engine working folder.");
930 + }
931 +
932 +LExit:
933 + ReleaseStr(sczWorkingFolder);
934 +
935 + return hr;
936 +}
937 +
938 +extern "C" HRESULT CacheRemoveBundle(
939 + __in BOOL fPerMachine,
940 + __in_z LPCWSTR wzBundleId
941 + )
942 +{
943 + HRESULT hr = S_OK;
944 +
945 + hr = RemoveBundleOrPackage(TRUE, fPerMachine, wzBundleId, wzBundleId);
946 + ExitOnFailure(hr, "Failed to remove bundle id: %ls.", wzBundleId);
947 +
948 +LExit:
949 + return hr;
950 +}
951 +
952 +extern "C" HRESULT CacheRemovePackage(
953 + __in BOOL fPerMachine,
954 + __in_z LPCWSTR wzPackageId,
955 + __in_z LPCWSTR wzCacheId
956 + )
957 +{
958 + HRESULT hr = S_OK;
959 +
960 + hr = RemoveBundleOrPackage(FALSE, fPerMachine, wzPackageId, wzCacheId);
961 + ExitOnFailure(hr, "Failed to remove package id: %ls.", wzPackageId);
962 +
963 +LExit:
964 + return hr;
965 +}
966 +
967 +extern "C" HRESULT CacheVerifyPayloadSignature(
968 + __in BURN_PAYLOAD* pPayload,
969 + __in_z LPCWSTR wzUnverifiedPayloadPath,
970 + __in HANDLE hFile
971 + )
972 +{
973 + HRESULT hr = S_OK;
974 + LONG er = ERROR_SUCCESS;
975 +
976 + GUID guidAuthenticode = WINTRUST_ACTION_GENERIC_VERIFY_V2;
977 + WINTRUST_FILE_INFO wfi = { };
978 + WINTRUST_DATA wtd = { };
979 + CRYPT_PROVIDER_DATA* pProviderData = NULL;
980 + CRYPT_PROVIDER_SGNR* pSigner = NULL;
981 +
982 + // Verify the payload assuming online.
983 + wfi.cbStruct = sizeof(wfi);
984 + wfi.pcwszFilePath = wzUnverifiedPayloadPath;
985 + wfi.hFile = hFile;
986 +
987 + wtd.cbStruct = sizeof(wtd);
988 + wtd.dwUnionChoice = WTD_CHOICE_FILE;
989 + wtd.pFile = &wfi;
990 + wtd.dwStateAction = WTD_STATEACTION_VERIFY;
991 + wtd.dwProvFlags = WTD_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
992 + wtd.dwUIChoice = WTD_UI_NONE;
993 +
994 + er = ::WinVerifyTrust(static_cast<HWND>(INVALID_HANDLE_VALUE), &guidAuthenticode, &wtd);
995 + if (er)
996 + {
997 + // Verify the payload assuming offline.
998 + wtd.dwProvFlags |= WTD_CACHE_ONLY_URL_RETRIEVAL;
999 +
1000 + er = ::WinVerifyTrust(static_cast<HWND>(INVALID_HANDLE_VALUE), &guidAuthenticode, &wtd);
1001 + ExitOnWin32Error(er, hr, "Failed authenticode verification of payload: %ls", wzUnverifiedPayloadPath);
1002 + }
1003 +
1004 + pProviderData = WTHelperProvDataFromStateData(wtd.hWVTStateData);
1005 + ExitOnNullWithLastError(pProviderData, hr, "Failed to get provider state from authenticode certificate.");
1006 +
1007 + pSigner = WTHelperGetProvSignerFromChain(pProviderData, 0, FALSE, 0);
1008 + ExitOnNullWithLastError(pSigner, hr, "Failed to get signer chain from authenticode certificate.");
1009 +
1010 + hr = VerifyPayloadAgainstChain(pPayload, pSigner->pChainContext);
1011 + ExitOnFailure(hr, "Failed to verify expected payload against actual certificate chain.");
1012 +
1013 +LExit:
1014 + return hr;
1015 +}
1016 +
1017 +extern "C" void CacheCleanup(
1018 + __in BOOL fPerMachine,
1019 + __in_z LPCWSTR wzBundleId
1020 + )
1021 +{
1022 + HRESULT hr = S_OK;
1023 + LPWSTR sczFolder = NULL;
1024 + LPWSTR sczFiles = NULL;
1025 + LPWSTR sczDelete = NULL;
1026 + HANDLE hFind = INVALID_HANDLE_VALUE;
1027 + WIN32_FIND_DATAW wfd = { };
1028 + DWORD cFileName = 0;
1029 +
1030 + hr = CacheGetCompletedPath(fPerMachine, UNVERIFIED_CACHE_FOLDER_NAME, &sczFolder);
1031 + if (SUCCEEDED(hr))
1032 + {
1033 + hr = DirEnsureDeleteEx(sczFolder, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE);
1034 + }
1035 +
1036 + if (!fPerMachine)
1037 + {
1038 + hr = CalculateWorkingFolder(wzBundleId, &sczFolder);
1039 + if (SUCCEEDED(hr))
1040 + {
1041 + hr = PathConcat(sczFolder, L"*.*", &sczFiles);
1042 + if (SUCCEEDED(hr))
1043 + {
1044 + hFind = ::FindFirstFileW(sczFiles, &wfd);
1045 + if (INVALID_HANDLE_VALUE != hFind)
1046 + {
1047 + do
1048 + {
1049 + // Skip directories.
1050 + if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1051 + {
1052 + continue;
1053 + }
1054 +
1055 + // For extra safety and to silence OACR.
1056 + wfd.cFileName[MAX_PATH - 1] = L'\0';
1057 +
1058 + // Skip resume files (they end with ".R").
1059 + cFileName = lstrlenW(wfd.cFileName);
1060 + if (2 < cFileName && L'.' == wfd.cFileName[cFileName - 2] && (L'R' == wfd.cFileName[cFileName - 1] || L'r' == wfd.cFileName[cFileName - 1]))
1061 + {
1062 + continue;
1063 + }
1064 +
1065 + hr = PathConcat(sczFolder, wfd.cFileName, &sczDelete);
1066 + if (SUCCEEDED(hr))
1067 + {
1068 + hr = FileEnsureDelete(sczDelete);
1069 + }
1070 + } while (::FindNextFileW(hFind, &wfd));
1071 + }
1072 + }
1073 + }
1074 + }
1075 +
1076 + if (INVALID_HANDLE_VALUE != hFind)
1077 + {
1078 + ::FindClose(hFind);
1079 + }
1080 +
1081 + ReleaseStr(sczDelete);
1082 + ReleaseStr(sczFiles);
1083 + ReleaseStr(sczFolder);
1084 +}
1085 +
1086 +extern "C" void CacheUninitialize()
1087 +{
1088 + ReleaseNullStr(vsczCurrentMachinePackageCache);
1089 + ReleaseNullStr(vsczDefaultMachinePackageCache);
1090 + ReleaseNullStr(vsczDefaultUserPackageCache);
1091 + ReleaseNullStr(vsczWorkingFolder);
1092 + ReleaseNullStr(vsczSourceProcessPath);
1093 +
1094 + vfRunningFromCache = FALSE;
1095 + vfInitializedCache = FALSE;
1096 +}
1097 +
1098 +// Internal functions.
1099 +
1100 +static HRESULT CalculateWorkingFolder(
1101 + __in_z LPCWSTR /*wzBundleId*/,
1102 + __deref_out_z LPWSTR* psczWorkingFolder
1103 + )
1104 +{
1105 + HRESULT hr = S_OK;
1106 + RPC_STATUS rs = RPC_S_OK;
1107 + BOOL fElevated = FALSE;
1108 + WCHAR wzTempPath[MAX_PATH] = { };
1109 + UUID guid = {};
1110 + WCHAR wzGuid[39];
1111 +
1112 + if (!vsczWorkingFolder)
1113 + {
1114 + ProcElevated(::GetCurrentProcess(), &fElevated);
1115 +
1116 + if (fElevated)
1117 + {
1118 + if (!::GetWindowsDirectoryW(wzTempPath, countof(wzTempPath)))
1119 + {
1120 + ExitWithLastError(hr, "Failed to get windows path for working folder.");
1121 + }
1122 +
1123 + hr = PathFixedBackslashTerminate(wzTempPath, countof(wzTempPath));
1124 + ExitOnFailure(hr, "Failed to ensure windows path for working folder ended in backslash.");
1125 +
1126 + hr = ::StringCchCatW(wzTempPath, countof(wzTempPath), L"Temp\\");
1127 + ExitOnFailure(hr, "Failed to concat Temp directory on windows path for working folder.");
1128 + }
1129 + else if (0 == ::GetTempPathW(countof(wzTempPath), wzTempPath))
1130 + {
1131 + ExitWithLastError(hr, "Failed to get temp path for working folder.");
1132 + }
1133 +
1134 + rs = ::UuidCreate(&guid);
1135 + hr = HRESULT_FROM_RPC(rs);
1136 + ExitOnFailure(hr, "Failed to create working folder guid.");
1137 +
1138 + if (!::StringFromGUID2(guid, wzGuid, countof(wzGuid)))
1139 + {
1140 + hr = E_OUTOFMEMORY;
1141 + ExitOnRootFailure(hr, "Failed to convert working folder guid into string.");
1142 + }
1143 +
1144 + hr = StrAllocFormatted(&vsczWorkingFolder, L"%ls%ls\\", wzTempPath, wzGuid);
1145 + ExitOnFailure(hr, "Failed to append bundle id on to temp path for working folder.");
1146 + }
1147 +
1148 + hr = StrAllocString(psczWorkingFolder, vsczWorkingFolder, 0);
1149 + ExitOnFailure(hr, "Failed to copy working folder path.");
1150 +
1151 +LExit:
1152 + return hr;
1153 +}
1154 +
1155 +static HRESULT GetRootPath(
1156 + __in BOOL fPerMachine,
1157 + __in BOOL fAllowRedirect,
1158 + __deref_out_z LPWSTR* psczRootPath
1159 + )
1160 +{
1161 + HRESULT hr = S_OK;
1162 + LPWSTR sczAppData = NULL;
1163 + int nCompare = 0;
1164 +
1165 + // Cache paths are initialized once so they cannot be changed while the engine is caching payloads.
1166 + if (fPerMachine)
1167 + {
1168 + // Always construct the default machine package cache path so we can determine if we're redirected.
1169 + if (!vsczDefaultMachinePackageCache)
1170 + {
1171 + hr = PathGetKnownFolder(CSIDL_COMMON_APPDATA, &sczAppData);
1172 + ExitOnFailure(hr, "Failed to find local %hs appdata directory.", "per-machine");
1173 +
1174 + hr = PathConcat(sczAppData, PACKAGE_CACHE_FOLDER_NAME, &vsczDefaultMachinePackageCache);
1175 + ExitOnFailure(hr, "Failed to construct %hs package cache directory name.", "per-machine");
1176 +
1177 + hr = PathBackslashTerminate(&vsczDefaultMachinePackageCache);
1178 + ExitOnFailure(hr, "Failed to backslash terminate default %hs package cache directory name.", "per-machine");
1179 + }
1180 +
1181 + if (!vsczCurrentMachinePackageCache)
1182 + {
1183 + hr = PolcReadString(POLICY_BURN_REGISTRY_PATH, L"PackageCache", NULL, &vsczCurrentMachinePackageCache);
1184 + ExitOnFailure(hr, "Failed to read PackageCache policy directory.");
1185 +
1186 + if (vsczCurrentMachinePackageCache)
1187 + {
1188 + hr = PathBackslashTerminate(&vsczCurrentMachinePackageCache);
1189 + ExitOnFailure(hr, "Failed to backslash terminate redirected per-machine package cache directory name.");
1190 + }
1191 + else
1192 + {
1193 + hr = StrAllocString(&vsczCurrentMachinePackageCache, vsczDefaultMachinePackageCache, 0);
1194 + ExitOnFailure(hr, "Failed to copy default package cache directory to current package cache directory.");
1195 + }
1196 + }
1197 +
1198 + hr = StrAllocString(psczRootPath, fAllowRedirect ? vsczCurrentMachinePackageCache : vsczDefaultMachinePackageCache, 0);
1199 + ExitOnFailure(hr, "Failed to copy %hs package cache root directory.", "per-machine");
1200 +
1201 + hr = PathCompare(vsczDefaultMachinePackageCache, *psczRootPath, &nCompare);
1202 + ExitOnFailure(hr, "Failed to compare default and current package cache directories.");
1203 +
1204 + // Return S_FALSE if the current location is not the default location (redirected).
1205 + hr = CSTR_EQUAL == nCompare ? S_OK : S_FALSE;
1206 + }
1207 + else
1208 + {
1209 + if (!vsczDefaultUserPackageCache)
1210 + {
1211 + hr = PathGetKnownFolder(CSIDL_LOCAL_APPDATA, &sczAppData);
1212 + ExitOnFailure(hr, "Failed to find local %hs appdata directory.", "per-user");
1213 +
1214 + hr = PathConcat(sczAppData, PACKAGE_CACHE_FOLDER_NAME, &vsczDefaultUserPackageCache);
1215 + ExitOnFailure(hr, "Failed to construct %hs package cache directory name.", "per-user");
1216 +
1217 + hr = PathBackslashTerminate(&vsczDefaultUserPackageCache);
1218 + ExitOnFailure(hr, "Failed to backslash terminate default %hs package cache directory name.", "per-user");
1219 + }
1220 +
1221 + hr = StrAllocString(psczRootPath, vsczDefaultUserPackageCache, 0);
1222 + ExitOnFailure(hr, "Failed to copy %hs package cache root directory.", "per-user");
1223 + }
1224 +
1225 +LExit:
1226 + ReleaseStr(sczAppData);
1227 +
1228 + return hr;
1229 +}
1230 +
1231 +static HRESULT GetLastUsedSourceFolder(
1232 + __in BURN_VARIABLES* pVariables,
1233 + __out_z LPWSTR* psczLastSource
1234 + )
1235 +{
1236 + HRESULT hr = S_OK;
1237 + LPWSTR sczOriginalSource = NULL;
1238 +
1239 + hr = VariableGetString(pVariables, BURN_BUNDLE_LAST_USED_SOURCE, psczLastSource);
1240 + if (E_NOTFOUND == hr)
1241 + {
1242 + // Try the original source folder.
1243 + hr = VariableGetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE, &sczOriginalSource);
1244 + if (SUCCEEDED(hr))
1245 + {
1246 + hr = PathGetDirectory(sczOriginalSource, psczLastSource);
1247 + }
1248 + }
1249 +
1250 + return hr;
1251 +}
1252 +
1253 +static HRESULT CreateCompletedPath(
1254 + __in BOOL fPerMachine,
1255 + __in LPCWSTR wzId,
1256 + __out LPWSTR* psczCacheDirectory
1257 + )
1258 +{
1259 + static BOOL fPerMachineCacheRootVerified = FALSE;
1260 +
1261 + HRESULT hr = S_OK;
1262 + LPWSTR sczCacheDirectory = NULL;
1263 +
1264 + // If we are doing a permachine install but have not yet verified that the root cache folder
1265 + // was created with the correct ACLs yet, do that now.
1266 + if (fPerMachine && !fPerMachineCacheRootVerified)
1267 + {
1268 + hr = GetRootPath(fPerMachine, TRUE, &sczCacheDirectory);
1269 + ExitOnFailure(hr, "Failed to get cache directory.");
1270 +
1271 + hr = DirEnsureExists(sczCacheDirectory, NULL);
1272 + ExitOnFailure(hr, "Failed to create cache directory: %ls", sczCacheDirectory);
1273 +
1274 + hr = SecurePath(sczCacheDirectory);
1275 + ExitOnFailure(hr, "Failed to secure cache directory: %ls", sczCacheDirectory);
1276 +
1277 + fPerMachineCacheRootVerified = TRUE;
1278 + }
1279 +
1280 + // Get the cache completed path, ensure it exists, and reset any permissions people
1281 + // might have tried to set on the directory so we inherit the (correct!) security
1282 + // permissions from the parent directory.
1283 + hr = CacheGetCompletedPath(fPerMachine, wzId, &sczCacheDirectory);
1284 + ExitOnFailure(hr, "Failed to get cache directory.");
1285 +
1286 + hr = DirEnsureExists(sczCacheDirectory, NULL);
1287 + ExitOnFailure(hr, "Failed to create cache directory: %ls", sczCacheDirectory);
1288 +
1289 + ResetPathPermissions(fPerMachine, sczCacheDirectory);
1290 +
1291 + *psczCacheDirectory = sczCacheDirectory;
1292 + sczCacheDirectory = NULL;
1293 +
1294 +LExit:
1295 + ReleaseStr(sczCacheDirectory);
1296 + return hr;
1297 +}
1298 +
1299 +static HRESULT CreateUnverifiedPath(
1300 + __in BOOL fPerMachine,
1301 + __in_z LPCWSTR wzPayloadId,
1302 + __out_z LPWSTR* psczUnverifiedPayloadPath
1303 + )
1304 +{
1305 + static BOOL fUnverifiedCacheFolderCreated = FALSE;
1306 +
1307 + HRESULT hr = S_OK;
1308 + LPWSTR sczUnverifiedCacheFolder = NULL;
1309 +
1310 + hr = CacheGetCompletedPath(fPerMachine, UNVERIFIED_CACHE_FOLDER_NAME, &sczUnverifiedCacheFolder);
1311 + ExitOnFailure(hr, "Failed to get cache directory.");
1312 +
1313 + if (!fUnverifiedCacheFolderCreated)
1314 + {
1315 + hr = DirEnsureExists(sczUnverifiedCacheFolder, NULL);
1316 + ExitOnFailure(hr, "Failed to create unverified cache directory: %ls", sczUnverifiedCacheFolder);
1317 +
1318 + ResetPathPermissions(fPerMachine, sczUnverifiedCacheFolder);
1319 + }
1320 +
1321 + hr = PathConcat(sczUnverifiedCacheFolder, wzPayloadId, psczUnverifiedPayloadPath);
1322 + ExitOnFailure(hr, "Failed to concat payload id to unverified folder path.");
1323 +
1324 +LExit:
1325 + ReleaseStr(sczUnverifiedCacheFolder);
1326 +
1327 + return hr;
1328 +}
1329 +
1330 +static HRESULT VerifyThenTransferContainer(
1331 + __in BURN_CONTAINER* pContainer,
1332 + __in_z LPCWSTR wzCachedPath,
1333 + __in_z LPCWSTR wzUnverifiedContainerPath,
1334 + __in BOOL fMove
1335 + )
1336 +{
1337 + HRESULT hr = S_OK;
1338 + HANDLE hFile = INVALID_HANDLE_VALUE;
1339 +
1340 + // Get the container on disk actual hash.
1341 + hFile = ::CreateFileW(wzUnverifiedContainerPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
1342 + if (INVALID_HANDLE_VALUE == hFile)
1343 + {
1344 + ExitWithLastError(hr, "Failed to open container in working path: %ls", wzUnverifiedContainerPath);
1345 + }
1346 +
1347 + // Container should have a hash we can use to verify with.
1348 + if (pContainer->pbHash)
1349 + {
1350 + hr = VerifyHash(pContainer->pbHash, pContainer->cbHash, wzUnverifiedContainerPath, hFile);
1351 + ExitOnFailure(hr, "Failed to verify container hash: %ls", wzCachedPath);
1352 + }
1353 +
1354 + LogStringLine(REPORT_STANDARD, "%ls container from working path '%ls' to path '%ls'", fMove ? L"Moving" : L"Copying", wzUnverifiedContainerPath, wzCachedPath);
1355 +
1356 + if (fMove)
1357 + {
1358 + hr = FileEnsureMoveWithRetry(wzUnverifiedContainerPath, wzCachedPath, TRUE, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
1359 + ExitOnFailure(hr, "Failed to move %ls to %ls", wzUnverifiedContainerPath, wzCachedPath);
1360 + }
1361 + else
1362 + {
1363 + hr = FileEnsureCopyWithRetry(wzUnverifiedContainerPath, wzCachedPath, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
1364 + ExitOnFailure(hr, "Failed to copy %ls to %ls", wzUnverifiedContainerPath, wzCachedPath);
1365 + }
1366 +
1367 +LExit:
1368 + ReleaseFileHandle(hFile);
1369 +
1370 + return hr;
1371 +}
1372 +
1373 +static HRESULT VerifyThenTransferPayload(
1374 + __in BURN_PAYLOAD* pPayload,
1375 + __in_z LPCWSTR wzCachedPath,
1376 + __in_z LPCWSTR wzUnverifiedPayloadPath,
1377 + __in BOOL fMove
1378 + )
1379 +{
1380 + HRESULT hr = S_OK;
1381 + HANDLE hFile = INVALID_HANDLE_VALUE;
1382 +
1383 + // Get the payload on disk actual hash.
1384 + hFile = ::CreateFileW(wzUnverifiedPayloadPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
1385 + if (INVALID_HANDLE_VALUE == hFile)
1386 + {
1387 + ExitWithLastError(hr, "Failed to open payload in working path: %ls", wzUnverifiedPayloadPath);
1388 + }
1389 +
1390 + // If the payload has a certificate root public key identifier provided, verify the certificate.
1391 + if (pPayload->pbCertificateRootPublicKeyIdentifier)
1392 + {
1393 + hr = CacheVerifyPayloadSignature(pPayload, wzUnverifiedPayloadPath, hFile);
1394 + ExitOnFailure(hr, "Failed to verify payload signature: %ls", wzCachedPath);
1395 + }
1396 + else if (pPayload->pCatalog) // If catalog files are specified, attempt to verify the file with a catalog file
1397 + {
1398 + hr = VerifyPayloadWithCatalog(pPayload, wzUnverifiedPayloadPath, hFile);
1399 + ExitOnFailure(hr, "Failed to verify payload signature: %ls", wzCachedPath);
1400 + }
1401 + else if (pPayload->pbHash) // the payload should have a hash we can use to verify it.
1402 + {
1403 + hr = VerifyHash(pPayload->pbHash, pPayload->cbHash, wzUnverifiedPayloadPath, hFile);
1404 + ExitOnFailure(hr, "Failed to verify payload hash: %ls", wzCachedPath);
1405 + }
1406 +
1407 + LogStringLine(REPORT_STANDARD, "%ls payload from working path '%ls' to path '%ls'", fMove ? L"Moving" : L"Copying", wzUnverifiedPayloadPath, wzCachedPath);
1408 +
1409 + if (fMove)
1410 + {
1411 + hr = FileEnsureMoveWithRetry(wzUnverifiedPayloadPath, wzCachedPath, TRUE, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
1412 + ExitOnFailure(hr, "Failed to move %ls to %ls", wzUnverifiedPayloadPath, wzCachedPath);
1413 + }
1414 + else
1415 + {
1416 + hr = FileEnsureCopyWithRetry(wzUnverifiedPayloadPath, wzCachedPath, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
1417 + ExitOnFailure(hr, "Failed to copy %ls to %ls", wzUnverifiedPayloadPath, wzCachedPath);
1418 + }
1419 +
1420 +LExit:
1421 + ReleaseFileHandle(hFile);
1422 +
1423 + return hr;
1424 +}
1425 +
1426 +static HRESULT TransferWorkingPathToUnverifiedPath(
1427 + __in_z LPCWSTR wzWorkingPath,
1428 + __in_z LPCWSTR wzUnverifiedPayloadPath,
1429 + __in BOOL fMove
1430 + )
1431 +{
1432 + HRESULT hr = S_OK;
1433 +
1434 + if (fMove)
1435 + {
1436 + hr = FileEnsureMoveWithRetry(wzWorkingPath, wzUnverifiedPayloadPath, TRUE, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
1437 + ExitOnFailure(hr, "Failed to move %ls to %ls", wzWorkingPath, wzUnverifiedPayloadPath);
1438 + }
1439 + else
1440 + {
1441 + hr = FileEnsureCopyWithRetry(wzWorkingPath, wzUnverifiedPayloadPath, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
1442 + ExitOnFailure(hr, "Failed to copy %ls to %ls", wzWorkingPath, wzUnverifiedPayloadPath);
1443 + }
1444 +
1445 +LExit:
1446 + return hr;
1447 +}
1448 +
1449 +static HRESULT VerifyFileAgainstPayload(
1450 + __in BURN_PAYLOAD* pPayload,
1451 + __in_z LPCWSTR wzVerifyPath
1452 + )
1453 +{
1454 + HRESULT hr = S_OK;
1455 + HANDLE hFile = INVALID_HANDLE_VALUE;
1456 +
1457 + // Get the payload on disk actual hash.
1458 + hFile = ::CreateFileW(wzVerifyPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
1459 + if (INVALID_HANDLE_VALUE == hFile)
1460 + {
1461 + hr = HRESULT_FROM_WIN32(::GetLastError());
1462 + if (E_PATHNOTFOUND == hr || E_FILENOTFOUND == hr)
1463 + {
1464 + ExitFunction(); // do not log error when the file was not found.
1465 + }
1466 + ExitOnRootFailure(hr, "Failed to open payload at path: %ls", wzVerifyPath);
1467 + }
1468 +
1469 + // If the payload has a certificate root public key identifier provided, verify the certificate.
1470 + if (pPayload->pbCertificateRootPublicKeyIdentifier)
1471 + {
1472 + hr = CacheVerifyPayloadSignature(pPayload, wzVerifyPath, hFile);
1473 + ExitOnFailure(hr, "Failed to verify signature of payload: %ls", pPayload->sczKey);
1474 + }
1475 + else if (pPayload->pCatalog) // If catalog files are specified, attempt to verify the file with a catalog file
1476 + {
1477 + hr = VerifyPayloadWithCatalog(pPayload, wzVerifyPath, hFile);
1478 + ExitOnFailure(hr, "Failed to verify catalog signature of payload: %ls", pPayload->sczKey);
1479 + }
1480 + else if (pPayload->pbHash) // the payload should have a hash we can use to verify it.
1481 + {
1482 + hr = VerifyHash(pPayload->pbHash, pPayload->cbHash, wzVerifyPath, hFile);
1483 + ExitOnFailure(hr, "Failed to verify hash of payload: %ls", pPayload->sczKey);
1484 + }
1485 +
1486 +LExit:
1487 + ReleaseFileHandle(hFile);
1488 +
1489 + return hr;
1490 +}
1491 +
1492 +static HRESULT AllocateSid(
1493 + __in WELL_KNOWN_SID_TYPE type,
1494 + __out PSID* ppSid
1495 + )
1496 +{
1497 + HRESULT hr = S_OK;
1498 + PSID pAllocSid = NULL;
1499 + DWORD cbSid = SECURITY_MAX_SID_SIZE;
1500 +
1501 + pAllocSid = static_cast<PSID>(MemAlloc(cbSid, TRUE));
1502 + ExitOnNull(pAllocSid, hr, E_OUTOFMEMORY, "Failed to allocate memory for well known SID.");
1503 +
1504 + if (!::CreateWellKnownSid(type, NULL, pAllocSid, &cbSid))
1505 + {
1506 + ExitWithLastError(hr, "Failed to create well known SID.");
1507 + }
1508 +
1509 + *ppSid = pAllocSid;
1510 + pAllocSid = NULL;
1511 +
1512 +LExit:
1513 + ReleaseMem(pAllocSid);
1514 + return hr;
1515 +}
1516 +
1517 +
1518 +static HRESULT ResetPathPermissions(
1519 + __in BOOL fPerMachine,
1520 + __in LPCWSTR wzPath
1521 + )
1522 +{
1523 + HRESULT hr = S_OK;
1524 + DWORD er = ERROR_SUCCESS;
1525 + DWORD dwSetSecurity = DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION;
1526 + ACL acl = { };
1527 + PSID pSid = NULL;
1528 +
1529 + if (fPerMachine)
1530 + {
1531 + hr = AllocateSid(WinBuiltinAdministratorsSid, &pSid);
1532 + ExitOnFailure(hr, "Failed to allocate administrator SID.");
1533 +
1534 + // Create an empty (not NULL!) ACL to reset the permissions on the file to purely inherit from parent.
1535 + if (!::InitializeAcl(&acl, sizeof(acl), ACL_REVISION))
1536 + {
1537 + ExitWithLastError(hr, "Failed to initialize ACL.");
1538 + }
1539 +
1540 + dwSetSecurity |= OWNER_SECURITY_INFORMATION;
1541 + }
1542 +
1543 + hr = AclSetSecurityWithRetry(wzPath, SE_FILE_OBJECT, dwSetSecurity, pSid, NULL, &acl, NULL, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
1544 + ExitOnWin32Error(er, hr, "Failed to reset the ACL on cached file: %ls", wzPath);
1545 +
1546 + ::SetFileAttributesW(wzPath, FILE_ATTRIBUTE_NORMAL); // Let's try to reset any possible read-only/system bits.
1547 +
1548 +LExit:
1549 + ReleaseMem(pSid);
1550 + return hr;
1551 +}
1552 +
1553 +
1554 +static HRESULT GrantAccessAndAllocateSid(
1555 + __in WELL_KNOWN_SID_TYPE type,
1556 + __in DWORD dwGrantAccess,
1557 + __in EXPLICIT_ACCESS* pAccess
1558 + )
1559 +{
1560 + HRESULT hr = S_OK;
1561 +
1562 + hr = AllocateSid(type, reinterpret_cast<PSID*>(&pAccess->Trustee.ptstrName));
1563 + ExitOnFailure(hr, "Failed to allocate SID to grate access.");
1564 +
1565 + pAccess->grfAccessMode = GRANT_ACCESS;
1566 + pAccess->grfAccessPermissions = dwGrantAccess;
1567 + pAccess->grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
1568 + pAccess->Trustee.TrusteeForm = TRUSTEE_IS_SID;
1569 + pAccess->Trustee.TrusteeType = TRUSTEE_IS_GROUP;
1570 +
1571 +LExit:
1572 + return hr;
1573 +}
1574 +
1575 +
1576 +static HRESULT SecurePath(
1577 + __in LPCWSTR wzPath
1578 + )
1579 +{
1580 + HRESULT hr = S_OK;
1581 + DWORD er = ERROR_SUCCESS;
1582 + EXPLICIT_ACCESSW access[4] = { };
1583 + PACL pAcl = NULL;
1584 +
1585 + // Administrators must be the first one in the array so we can reuse the allocated SID below.
1586 + hr = GrantAccessAndAllocateSid(WinBuiltinAdministratorsSid, FILE_ALL_ACCESS, &access[0]);
1587 + ExitOnFailure(hr, "Failed to allocate access for Administrators group to path: %ls", wzPath);
1588 +
1589 + hr = GrantAccessAndAllocateSid(WinLocalSystemSid, FILE_ALL_ACCESS, &access[1]);
1590 + ExitOnFailure(hr, "Failed to allocate access for SYSTEM group to path: %ls", wzPath);
1591 +
1592 + hr = GrantAccessAndAllocateSid(WinWorldSid, GENERIC_READ | GENERIC_EXECUTE, &access[2]);
1593 + ExitOnFailure(hr, "Failed to allocate access for Everyone group to path: %ls", wzPath);
1594 +
1595 + hr = GrantAccessAndAllocateSid(WinBuiltinUsersSid, GENERIC_READ | GENERIC_EXECUTE, &access[3]);
1596 + ExitOnFailure(hr, "Failed to allocate access for Users group to path: %ls", wzPath);
1597 +
1598 + er = ::SetEntriesInAclW(countof(access), access, NULL, &pAcl);
1599 + ExitOnWin32Error(er, hr, "Failed to create ACL to secure cache path: %ls", wzPath);
1600 +
1601 + // Set the ACL and ensure the Administrators group ends up the owner
1602 + hr = AclSetSecurityWithRetry(wzPath, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION,
1603 + reinterpret_cast<PSID>(access[0].Trustee.ptstrName), NULL, pAcl, NULL, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
1604 + ExitOnFailure(hr, "Failed to secure cache path: %ls", wzPath);
1605 +
1606 +LExit:
1607 + if (pAcl)
1608 + {
1609 + ::LocalFree(pAcl);
1610 + }
1611 +
1612 + for (DWORD i = 0; i < countof(access); ++i)
1613 + {
1614 + ReleaseMem(access[i].Trustee.ptstrName);
1615 + }
1616 +
1617 + return hr;
1618 +}
1619 +
1620 +
1621 +static HRESULT CopyEngineToWorkingFolder(
1622 + __in_z LPCWSTR wzSourcePath,
1623 + __in_z LPCWSTR wzWorkingFolderName,
1624 + __in_z LPCWSTR wzExecutableName,
1625 + __in BURN_PAYLOADS* pUxPayloads,
1626 + __in BURN_SECTION* pSection,
1627 + __deref_out_z_opt LPWSTR* psczEngineWorkingPath
1628 + )
1629 +{
1630 + HRESULT hr = S_OK;
1631 + LPWSTR sczWorkingFolder = NULL;
1632 + LPWSTR sczTargetDirectory = NULL;
1633 + LPWSTR sczTargetPath = NULL;
1634 + LPWSTR sczSourceDirectory = NULL;
1635 + LPWSTR sczPayloadSourcePath = NULL;
1636 + LPWSTR sczPayloadTargetPath = NULL;
1637 +
1638 + hr = CacheEnsureWorkingFolder(NULL, &sczWorkingFolder);
1639 + ExitOnFailure(hr, "Failed to create working path to copy engine.");
1640 +
1641 + hr = PathConcat(sczWorkingFolder, wzWorkingFolderName, &sczTargetDirectory);
1642 + ExitOnFailure(hr, "Failed to calculate the bundle working folder target name.");
1643 +
1644 + hr = DirEnsureExists(sczTargetDirectory, NULL);
1645 + ExitOnFailure(hr, "Failed create bundle working folder.");
1646 +
1647 + hr = PathConcat(sczTargetDirectory, wzExecutableName, &sczTargetPath);
1648 + ExitOnFailure(hr, "Failed to combine working path with engine file name.");
1649 +
1650 + // Copy the engine without any attached containers to the working path.
1651 + hr = CopyEngineWithSignatureFixup(pSection->hEngineFile, wzSourcePath, sczTargetPath, pSection);
1652 + ExitOnFailure(hr, "Failed to copy engine: '%ls' to working path: %ls", wzSourcePath, sczTargetPath);
1653 +
1654 + // Copy external UX payloads to working path.
1655 + for (DWORD i = 0; i < pUxPayloads->cPayloads; ++i)
1656 + {
1657 + BURN_PAYLOAD* pPayload = &pUxPayloads->rgPayloads[i];
1658 +
1659 + if (BURN_PAYLOAD_PACKAGING_EXTERNAL == pPayload->packaging)
1660 + {
1661 + if (!sczSourceDirectory)
1662 + {
1663 + hr = PathGetDirectory(wzSourcePath, &sczSourceDirectory);
1664 + ExitOnFailure(hr, "Failed to get directory from engine path: %ls", wzSourcePath);
1665 + }
1666 +
1667 + hr = PathConcat(sczSourceDirectory, pPayload->sczSourcePath, &sczPayloadSourcePath);
1668 + ExitOnFailure(hr, "Failed to build payload source path for working copy.");
1669 +
1670 + hr = PathConcat(sczTargetDirectory, pPayload->sczFilePath, &sczPayloadTargetPath);
1671 + ExitOnFailure(hr, "Failed to build payload target path for working copy.");
1672 +
1673 + hr = FileEnsureCopyWithRetry(sczPayloadSourcePath, sczPayloadTargetPath, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
1674 + ExitOnFailure(hr, "Failed to copy UX payload from: '%ls' to: '%ls'", sczPayloadSourcePath, sczPayloadTargetPath);
1675 + }
1676 + }
1677 +
1678 + if (psczEngineWorkingPath)
1679 + {
1680 + hr = StrAllocString(psczEngineWorkingPath, sczTargetPath, 0);
1681 + ExitOnFailure(hr, "Failed to copy target path for engine working path.");
1682 + }
1683 +
1684 +LExit:
1685 + ReleaseStr(sczPayloadTargetPath);
1686 + ReleaseStr(sczPayloadSourcePath);
1687 + ReleaseStr(sczSourceDirectory);
1688 + ReleaseStr(sczTargetPath);
1689 + ReleaseStr(sczTargetDirectory);
1690 + ReleaseStr(sczWorkingFolder);
1691 +
1692 + return hr;
1693 +}
1694 +
1695 +
1696 +static HRESULT CopyEngineWithSignatureFixup(
1697 + __in HANDLE hEngineFile,
1698 + __in_z LPCWSTR wzEnginePath,
1699 + __in_z LPCWSTR wzTargetPath,
1700 + __in BURN_SECTION* pSection
1701 + )
1702 +{
1703 + HRESULT hr = S_OK;
1704 + HANDLE hTarget = INVALID_HANDLE_VALUE;
1705 + LARGE_INTEGER li = { };
1706 + DWORD dwZeroOriginals[3] = { };
1707 +
1708 + hTarget = ::CreateFileW(wzTargetPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
1709 + if (INVALID_HANDLE_VALUE == hTarget)
1710 + {
1711 + ExitWithLastError(hr, "Failed to create engine file at path: %ls", wzTargetPath);
1712 + }
1713 +
1714 + hr = FileSetPointer(hEngineFile, 0, NULL, FILE_BEGIN);
1715 + ExitOnFailure(hr, "Failed to seek to beginning of engine file: %ls", wzEnginePath);
1716 +
1717 + hr = FileCopyUsingHandles(hEngineFile, hTarget, pSection->cbEngineSize, NULL);
1718 + ExitOnFailure(hr, "Failed to copy engine from: %ls to: %ls", wzEnginePath, wzTargetPath);
1719 +
1720 + // If the original executable was signed, let's put back the checksum and signature.
1721 + if (pSection->dwOriginalSignatureOffset)
1722 + {
1723 + // Fix up the checksum.
1724 + li.QuadPart = pSection->dwChecksumOffset;
1725 + if (!::SetFilePointerEx(hTarget, li, NULL, FILE_BEGIN))
1726 + {
1727 + ExitWithLastError(hr, "Failed to seek to checksum in exe header.");
1728 + }
1729 +
1730 + hr = FileWriteHandle(hTarget, reinterpret_cast<LPBYTE>(&pSection->dwOriginalChecksum), sizeof(pSection->dwOriginalChecksum));
1731 + ExitOnFailure(hr, "Failed to update signature offset.");
1732 +
1733 + // Fix up the signature information.
1734 + li.QuadPart = pSection->dwCertificateTableOffset;
1735 + if (!::SetFilePointerEx(hTarget, li, NULL, FILE_BEGIN))
1736 + {
1737 + ExitWithLastError(hr, "Failed to seek to signature table in exe header.");
1738 + }
1739 +
1740 + hr = FileWriteHandle(hTarget, reinterpret_cast<LPBYTE>(&pSection->dwOriginalSignatureOffset), sizeof(pSection->dwOriginalSignatureOffset));
1741 + ExitOnFailure(hr, "Failed to update signature offset.");
1742 +
1743 + hr = FileWriteHandle(hTarget, reinterpret_cast<LPBYTE>(&pSection->dwOriginalSignatureSize), sizeof(pSection->dwOriginalSignatureSize));
1744 + ExitOnFailure(hr, "Failed to update signature offset.");
1745 +
1746 + // Zero out the original information since that is how it was when the file was originally signed.
1747 + li.QuadPart = pSection->dwOriginalChecksumAndSignatureOffset;
1748 + if (!::SetFilePointerEx(hTarget, li, NULL, FILE_BEGIN))
1749 + {
1750 + ExitWithLastError(hr, "Failed to seek to original data in exe burn section header.");
1751 + }
1752 +
1753 + hr = FileWriteHandle(hTarget, reinterpret_cast<LPBYTE>(&dwZeroOriginals), sizeof(dwZeroOriginals));
1754 + ExitOnFailure(hr, "Failed to zero out original data offset.");
1755 + }
1756 +
1757 +LExit:
1758 + ReleaseFileHandle(hTarget);
1759 +
1760 + return hr;
1761 +}
1762 +
1763 +
1764 +static HRESULT RemoveBundleOrPackage(
1765 + __in BOOL fBundle,
1766 + __in BOOL fPerMachine,
1767 + __in_z LPCWSTR wzBundleOrPackageId,
1768 + __in_z LPCWSTR wzCacheId
1769 + )
1770 +{
1771 + HRESULT hr = S_OK;
1772 + LPWSTR sczRootCacheDirectory = NULL;
1773 + LPWSTR sczDirectory = NULL;
1774 +
1775 + hr = CacheGetCompletedPath(fPerMachine, wzCacheId, &sczDirectory);
1776 + ExitOnFailure(hr, "Failed to calculate cache path.");
1777 +
1778 + LogId(REPORT_STANDARD, fBundle ? MSG_UNCACHE_BUNDLE : MSG_UNCACHE_PACKAGE, wzBundleOrPackageId, sczDirectory);
1779 +
1780 + // Try really hard to remove the cache directory.
1781 + hr = E_FAIL;
1782 + for (DWORD iRetry = 0; FAILED(hr) && iRetry < FILE_OPERATION_RETRY_COUNT; ++iRetry)
1783 + {
1784 + if (0 < iRetry)
1785 + {
1786 + ::Sleep(FILE_OPERATION_RETRY_WAIT);
1787 + }
1788 +
1789 + hr = DirEnsureDeleteEx(sczDirectory, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE);
1790 + if (E_PATHNOTFOUND == hr)
1791 + {
1792 + break;
1793 + }
1794 + }
1795 +
1796 + if (FAILED(hr))
1797 + {
1798 + LogId(REPORT_STANDARD, fBundle ? MSG_UNABLE_UNCACHE_BUNDLE : MSG_UNABLE_UNCACHE_PACKAGE, wzBundleOrPackageId, sczDirectory, hr);
1799 + hr = S_OK;
1800 + }
1801 + else
1802 + {
1803 + // Try to remove root package cache in the off chance it is now empty.
1804 + hr = GetRootPath(fPerMachine, TRUE, &sczRootCacheDirectory);
1805 + ExitOnFailure(hr, "Failed to get %hs package cache root directory.", fPerMachine ? "per-machine" : "per-user");
1806 + DirEnsureDeleteEx(sczRootCacheDirectory, DIR_DELETE_SCHEDULE);
1807 +
1808 + // GetRootPath returns S_FALSE if the package cache is redirected elsewhere.
1809 + if (S_FALSE == hr)
1810 + {
1811 + hr = GetRootPath(fPerMachine, FALSE, &sczRootCacheDirectory);
1812 + ExitOnFailure(hr, "Failed to get old %hs package cache root directory.", fPerMachine ? "per-machine" : "per-user");
1813 + DirEnsureDeleteEx(sczRootCacheDirectory, DIR_DELETE_SCHEDULE);
1814 + }
1815 + }
1816 +
1817 +LExit:
1818 + ReleaseStr(sczDirectory);
1819 + ReleaseStr(sczRootCacheDirectory);
1820 +
1821 + return hr;
1822 +}
1823 +
1824 +static HRESULT VerifyHash(
1825 + __in BYTE* pbHash,
1826 + __in DWORD cbHash,
1827 + __in_z LPCWSTR wzUnverifiedPayloadPath,
1828 + __in HANDLE hFile
1829 + )
1830 +{
1831 + UNREFERENCED_PARAMETER(wzUnverifiedPayloadPath);
1832 +
1833 + HRESULT hr = S_OK;
1834 + BYTE rgbActualHash[SHA1_HASH_LEN] = { };
1835 + DWORD64 qwHashedBytes;
1836 + LPWSTR pszExpected = NULL;
1837 + LPWSTR pszActual = NULL;
1838 +
1839 + // TODO: create a cryp hash file that sends progress.
1840 + hr = CrypHashFileHandle(hFile, PROV_RSA_FULL, CALG_SHA1, rgbActualHash, sizeof(rgbActualHash), &qwHashedBytes);
1841 + ExitOnFailure(hr, "Failed to calculate hash for path: %ls", wzUnverifiedPayloadPath);
1842 +
1843 + // Compare hashes.
1844 + if (cbHash != sizeof(rgbActualHash) || 0 != memcmp(pbHash, rgbActualHash, SHA1_HASH_LEN))
1845 + {
1846 + hr = CRYPT_E_HASH_VALUE;
1847 +
1848 + // Best effort to log the expected and actual hash value strings.
1849 + if (SUCCEEDED(StrAllocHexEncode(pbHash, cbHash, &pszExpected)) &&
1850 + SUCCEEDED(StrAllocHexEncode(rgbActualHash, SHA1_HASH_LEN, &pszActual)))
1851 + {
1852 + ExitOnFailure(hr, "Hash mismatch for path: %ls, expected: %ls, actual: %ls", wzUnverifiedPayloadPath, pszExpected, pszActual);
1853 + }
1854 + else
1855 + {
1856 + ExitOnFailure(hr, "Hash mismatch for path: %ls", wzUnverifiedPayloadPath);
1857 + }
1858 + }
1859 +
1860 +LExit:
1861 + ReleaseStr(pszActual);
1862 + ReleaseStr(pszExpected);
1863 +
1864 + return hr;
1865 +}
1866 +
1867 +static HRESULT VerifyPayloadWithCatalog(
1868 + __in BURN_PAYLOAD* pPayload,
1869 + __in_z LPCWSTR wzUnverifiedPayloadPath,
1870 + __in HANDLE hFile
1871 + )
1872 +{
1873 + HRESULT hr = S_FALSE;
1874 + DWORD er = ERROR_SUCCESS;
1875 + WINTRUST_DATA WinTrustData = { };
1876 + WINTRUST_CATALOG_INFO WinTrustCatalogInfo = { };
1877 + GUID gSubSystemDriver = WINTRUST_ACTION_GENERIC_VERIFY_V2;
1878 + LPWSTR sczLowerCaseFile = NULL;
1879 + LPWSTR pCurrent = NULL;
1880 + LPWSTR sczName = NULL;
1881 + DWORD dwHashSize = 0;
1882 + DWORD dwTagSize;
1883 + LPBYTE pbHash = NULL;
1884 +
1885 + // Get lower case file name. Older operating systems need a lower case file
1886 + // to match in the catalog
1887 + hr = StrAllocString(&sczLowerCaseFile, wzUnverifiedPayloadPath, 0);
1888 + ExitOnFailure(hr, "Failed to allocate memory");
1889 +
1890 + // Go through each character doing the lower case of each letter
1891 + pCurrent = sczLowerCaseFile;
1892 + while ('\0' != *pCurrent)
1893 + {
1894 + *pCurrent = (WCHAR)_tolower(*pCurrent);
1895 + pCurrent++;
1896 + }
1897 +
1898 + // Get file hash
1899 + CryptCATAdminCalcHashFromFileHandle(hFile, &dwHashSize, pbHash, 0);
1900 + er = ::GetLastError();
1901 + if (ERROR_INSUFFICIENT_BUFFER == er)
1902 + {
1903 + pbHash = (LPBYTE)MemAlloc(dwHashSize, TRUE);
1904 + if (!CryptCATAdminCalcHashFromFileHandle(hFile, &dwHashSize, pbHash, 0))
1905 + {
1906 + ExitWithLastError(hr, "Failed to get file hash.");
1907 + }
1908 + }
1909 + else
1910 + {
1911 + ExitOnWin32Error(er, hr, "Failed to get file hash.");
1912 + }
1913 +
1914 + // Make the hash into a string. This is the member tag for the catalog
1915 + dwTagSize = (dwHashSize * 2) + 1;
1916 + hr = StrAlloc(&sczName, dwTagSize);
1917 + ExitOnFailure(hr, "Failed to allocate string.");
1918 + hr = StrHexEncode(pbHash, dwHashSize, sczName, dwTagSize);
1919 + ExitOnFailure(hr, "Failed to encode file hash.");
1920 +
1921 + // Set up the WinVerifyTrust structures assuming online.
1922 + WinTrustData.cbStruct = sizeof(WINTRUST_DATA);
1923 + WinTrustData.dwUIChoice = WTD_UI_NONE;
1924 + WinTrustData.dwUnionChoice = WTD_CHOICE_CATALOG;
1925 + WinTrustData.dwStateAction = WTD_STATEACTION_VERIFY;
1926 + WinTrustData.dwProvFlags = WTD_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
1927 + WinTrustData.pCatalog = &WinTrustCatalogInfo;
1928 +
1929 + WinTrustCatalogInfo.cbStruct = sizeof(WINTRUST_CATALOG_INFO);
1930 + WinTrustCatalogInfo.pbCalculatedFileHash = pbHash;
1931 + WinTrustCatalogInfo.cbCalculatedFileHash = dwHashSize;
1932 + WinTrustCatalogInfo.hMemberFile = hFile;
1933 + WinTrustCatalogInfo.pcwszMemberTag = sczName;
1934 + WinTrustCatalogInfo.pcwszMemberFilePath = sczLowerCaseFile;
1935 + WinTrustCatalogInfo.pcwszCatalogFilePath = pPayload->pCatalog->sczLocalFilePath;
1936 +
1937 + hr = ::WinVerifyTrust(static_cast<HWND>(INVALID_HANDLE_VALUE), &gSubSystemDriver, &WinTrustData);
1938 + if (hr)
1939 + {
1940 + // Set up the WinVerifyTrust structures assuming online.
1941 + WinTrustData.dwProvFlags |= WTD_CACHE_ONLY_URL_RETRIEVAL;
1942 +
1943 + er = ::WinVerifyTrust(static_cast<HWND>(INVALID_HANDLE_VALUE), &gSubSystemDriver, &WinTrustData);
1944 +
1945 + // WinVerifyTrust returns 0 for success, a few different Win32 error codes if it can't
1946 + // find the provider, and any other error code is provider specific, so may not
1947 + // be an actual Win32 error code
1948 + ExitOnWin32Error(er, hr, "Could not verify file %ls.", wzUnverifiedPayloadPath);
1949 + }
1950 +
1951 + // Need to close the WinVerifyTrust action
1952 + WinTrustData.dwStateAction = WTD_STATEACTION_CLOSE;
1953 + er = ::WinVerifyTrust(static_cast<HWND>(INVALID_HANDLE_VALUE), &gSubSystemDriver, &WinTrustData);
1954 + ExitOnWin32Error(er, hr, "Could not close verify handle.");
1955 +
1956 +LExit:
1957 + ReleaseStr(sczLowerCaseFile);
1958 + ReleaseStr(sczName);
1959 + ReleaseMem(pbHash);
1960 +
1961 + return hr;
1962 +}
1963 +
1964 +static HRESULT VerifyPayloadAgainstChain(
1965 + __in BURN_PAYLOAD* pPayload,
1966 + __in PCCERT_CHAIN_CONTEXT pChainContext
1967 + )
1968 +{
1969 + HRESULT hr = S_OK;
1970 + PCCERT_CONTEXT pChainElementCertContext = NULL;
1971 +
1972 + BYTE rgbPublicKeyIdentifier[SHA1_HASH_LEN] = { };
1973 + DWORD cbPublicKeyIdentifier = sizeof(rgbPublicKeyIdentifier);
1974 + BYTE* pbThumbprint = NULL;
1975 + DWORD cbThumbprint = 0;
1976 +
1977 + // Walk up the chain looking for a certificate in the chain that matches our expected public key identifier
1978 + // and thumbprint (if a thumbprint was provided).
1979 + HRESULT hrChainVerification = E_NOTFOUND; // assume we won't find a match.
1980 + for (DWORD i = 0; i < pChainContext->rgpChain[0]->cElement; ++i)
1981 + {
1982 + pChainElementCertContext = pChainContext->rgpChain[0]->rgpElement[i]->pCertContext;
1983 +
1984 + // Get the certificate's public key identifier.
1985 + if (!::CryptHashPublicKeyInfo(NULL, CALG_SHA1, 0, X509_ASN_ENCODING, &pChainElementCertContext->pCertInfo->SubjectPublicKeyInfo, rgbPublicKeyIdentifier, &cbPublicKeyIdentifier))
1986 + {
1987 + ExitWithLastError(hr, "Failed to get certificate public key identifier.");
1988 + }
1989 +
1990 + // Compare the certificate's public key identifier with the payload's public key identifier. If they
1991 + // match, we're one step closer to the a positive result.
1992 + if (pPayload->cbCertificateRootPublicKeyIdentifier == cbPublicKeyIdentifier &&
1993 + 0 == memcmp(pPayload->pbCertificateRootPublicKeyIdentifier, rgbPublicKeyIdentifier, cbPublicKeyIdentifier))
1994 + {
1995 + // If the payload specified a thumbprint for the certificate, verify it.
1996 + if (pPayload->pbCertificateRootThumbprint)
1997 + {
1998 + hr = CertReadProperty(pChainElementCertContext, CERT_SHA1_HASH_PROP_ID, &pbThumbprint, &cbThumbprint);
1999 + ExitOnFailure(hr, "Failed to read certificate thumbprint.");
2000 +
2001 + if (pPayload->cbCertificateRootThumbprint == cbThumbprint &&
2002 + 0 == memcmp(pPayload->pbCertificateRootThumbprint, pbThumbprint, cbThumbprint))
2003 + {
2004 + // If we got here, we found that our payload public key identifier and thumbprint
2005 + // matched an element in the certficate chain.
2006 + hrChainVerification = S_OK;
2007 + break;
2008 + }
2009 +
2010 + ReleaseNullMem(pbThumbprint);
2011 + }
2012 + else // no thumbprint match necessary so we're good to go.
2013 + {
2014 + hrChainVerification = S_OK;
2015 + break;
2016 + }
2017 + }
2018 + }
2019 + hr = hrChainVerification;
2020 + ExitOnFailure(hr, "Failed to find expected public key in certificate chain.");
2021 +
2022 +LExit:
2023 + ReleaseMem(pbThumbprint);
2024 +
2025 + return hr;
2026 +}
src/engine/cache.h new
+150
@@ -0,0 +1,150 @@
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 +// structs
10 +
11 +// functions
12 +
13 +HRESULT CacheInitialize(
14 + __in BURN_REGISTRATION* pRegistration,
15 + __in BURN_VARIABLES* pVariables,
16 + __in_z_opt LPCWSTR wzSourceProcessPath
17 + );
18 +HRESULT CacheEnsureWorkingFolder(
19 + __in LPCWSTR wzBundleId,
20 + __deref_out_z_opt LPWSTR* psczWorkingFolder
21 + );
22 +HRESULT CacheCalculateBundleWorkingPath(
23 + __in_z LPCWSTR wzBundleId,
24 + __in LPCWSTR wzExecutableName,
25 + __deref_out_z LPWSTR* psczWorkingPath
26 + );
27 +HRESULT CacheCalculateBundleLayoutWorkingPath(
28 + __in_z LPCWSTR wzBundleId,
29 + __deref_out_z LPWSTR* psczWorkingPath
30 + );
31 +HRESULT CacheCalculatePayloadWorkingPath(
32 + __in_z LPCWSTR wzBundleId,
33 + __in BURN_PAYLOAD* pPayload,
34 + __deref_out_z LPWSTR* psczWorkingPath
35 + );
36 +HRESULT CacheCalculateContainerWorkingPath(
37 + __in_z LPCWSTR wzBundleId,
38 + __in BURN_CONTAINER* pContainer,
39 + __deref_out_z LPWSTR* psczWorkingPath
40 + );
41 +HRESULT CacheGetRootCompletedPath(
42 + __in BOOL fPerMachine,
43 + __in BOOL fForceInitialize,
44 + __deref_out_z LPWSTR* psczRootCompletedPath
45 + );
46 +HRESULT CacheGetCompletedPath(
47 + __in BOOL fPerMachine,
48 + __in_z LPCWSTR wzCacheId,
49 + __deref_out_z LPWSTR* psczCompletedPath
50 + );
51 +HRESULT CacheGetResumePath(
52 + __in_z LPCWSTR wzPayloadWorkingPath,
53 + __deref_out_z LPWSTR* psczResumePath
54 + );
55 +HRESULT CacheFindLocalSource(
56 + __in_z LPCWSTR wzSourcePath,
57 + __in BURN_VARIABLES* pVariables,
58 + __out BOOL* pfFound,
59 + __out_z LPWSTR* psczSourceFullPath
60 + );
61 +HRESULT CacheSetLastUsedSource(
62 + __in BURN_VARIABLES* pVariables,
63 + __in_z LPCWSTR wzSourcePath,
64 + __in_z LPCWSTR wzRelativePath
65 + );
66 +HRESULT CacheSendProgressCallback(
67 + __in DOWNLOAD_CACHE_CALLBACK* pCallback,
68 + __in DWORD64 dw64Progress,
69 + __in DWORD64 dw64Total,
70 + __in HANDLE hDestinationFile
71 + );
72 +void CacheSendErrorCallback(
73 + __in DOWNLOAD_CACHE_CALLBACK* pCallback,
74 + __in HRESULT hrError,
75 + __in_z_opt LPCWSTR wzError,
76 + __out_opt BOOL* pfRetry
77 + );
78 +BOOL CacheBundleRunningFromCache();
79 +HRESULT CacheBundleToCleanRoom(
80 + __in BURN_PAYLOADS* pUxPayloads,
81 + __in BURN_SECTION* pSection,
82 + __deref_out_z_opt LPWSTR* psczCleanRoomBundlePath
83 + );
84 +HRESULT CacheBundleToWorkingDirectory(
85 + __in_z LPCWSTR wzBundleId,
86 + __in_z LPCWSTR wzExecutableName,
87 + __in BURN_PAYLOADS* pUxPayloads,
88 + __in BURN_SECTION* pSection,
89 + __deref_out_z_opt LPWSTR* psczEngineWorkingPath
90 + );
91 +HRESULT CacheLayoutBundle(
92 + __in_z LPCWSTR wzExecutableName,
93 + __in_z LPCWSTR wzLayoutDirectory,
94 + __in_z LPCWSTR wzSourceBundlePath
95 + );
96 +HRESULT CacheCompleteBundle(
97 + __in BOOL fPerMachine,
98 + __in_z LPCWSTR wzExecutableName,
99 + __in_z LPCWSTR wzBundleId,
100 + __in BURN_PAYLOADS* pUxPayloads,
101 + __in_z LPCWSTR wzSourceBundlePath
102 +#ifdef DEBUG
103 + , __in_z LPCWSTR wzExecutablePath
104 +#endif
105 + );
106 +HRESULT CacheLayoutContainer(
107 + __in BURN_CONTAINER* pContainer,
108 + __in_z_opt LPCWSTR wzLayoutDirectory,
109 + __in_z LPCWSTR wzUnverifiedContainerPath,
110 + __in BOOL fMove
111 + );
112 +HRESULT CacheLayoutPayload(
113 + __in BURN_PAYLOAD* pPayload,
114 + __in_z_opt LPCWSTR wzLayoutDirectory,
115 + __in_z LPCWSTR wzUnverifiedPayloadPath,
116 + __in BOOL fMove
117 + );
118 +HRESULT CacheCompletePayload(
119 + __in BOOL fPerMachine,
120 + __in BURN_PAYLOAD* pPayload,
121 + __in_z_opt LPCWSTR wzCacheId,
122 + __in_z LPCWSTR wzUnverifiedPayloadPath,
123 + __in BOOL fMove
124 + );
125 +HRESULT CacheRemoveWorkingFolder(
126 + __in_z_opt LPCWSTR wzBundleId
127 + );
128 +HRESULT CacheRemoveBundle(
129 + __in BOOL fPerMachine,
130 + __in_z LPCWSTR wzPackageId
131 + );
132 +HRESULT CacheRemovePackage(
133 + __in BOOL fPerMachine,
134 + __in_z LPCWSTR wzPackageId,
135 + __in_z LPCWSTR wzCacheId
136 + );
137 +HRESULT CacheVerifyPayloadSignature(
138 + __in BURN_PAYLOAD* pPayload,
139 + __in_z LPCWSTR wzUnverifiedPayloadPath,
140 + __in HANDLE hFile
141 + );
142 +void CacheCleanup(
143 + __in BOOL fPerMachine,
144 + __in_z LPCWSTR wzBundleId
145 + );
146 +void CacheUninitialize();
147 +
148 +#ifdef __cplusplus
149 +}
150 +#endif
src/engine/catalog.cpp new
+180
@@ -0,0 +1,180 @@
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 +// function definitions
7 +
8 +extern "C" HRESULT CatalogsParseFromXml(
9 + __in BURN_CATALOGS* pCatalogs,
10 + __in IXMLDOMNode* pixnBundle
11 + )
12 +{
13 + HRESULT hr = S_OK;
14 + IXMLDOMNodeList* pixnNodes = NULL;
15 + IXMLDOMNode* pixnNode = NULL;
16 + DWORD cNodes = 0;
17 + LPWSTR scz = NULL;
18 +
19 + // select catalog nodes
20 + hr = XmlSelectNodes(pixnBundle, L"Catalog", &pixnNodes);
21 + ExitOnFailure(hr, "Failed to select catalog nodes.");
22 +
23 + // get catalog node count
24 + hr = pixnNodes->get_length((long*)&cNodes);
25 + ExitOnFailure(hr, "Failed to get payload node count.");
26 + if (!cNodes)
27 + {
28 + ExitFunction();
29 + }
30 +
31 + // allocate memory for catalogs
32 + pCatalogs->rgCatalogs = (BURN_CATALOG*)MemAlloc(sizeof(BURN_CATALOG) * cNodes, TRUE);
33 + ExitOnNull(pCatalogs->rgCatalogs, hr, E_OUTOFMEMORY, "Failed to allocate memory for payload structs.");
34 +
35 + pCatalogs->cCatalogs = cNodes;
36 +
37 + // parse catalog elements
38 + for (DWORD i = 0; i < cNodes; ++i)
39 + {
40 + BURN_CATALOG* pCatalog = &pCatalogs->rgCatalogs[i];
41 + pCatalog->hFile = INVALID_HANDLE_VALUE;
42 +
43 + hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
44 + ExitOnFailure(hr, "Failed to get next node.");
45 +
46 + // @Id
47 + hr = XmlGetAttributeEx(pixnNode, L"Id", &pCatalog->sczKey);
48 + ExitOnFailure(hr, "Failed to get @Id.");
49 +
50 + // @Payload
51 + hr = XmlGetAttributeEx(pixnNode, L"Payload", &pCatalog->sczPayload);
52 + ExitOnFailure(hr, "Failed to get @Payload.");
53 +
54 + // prepare next iteration
55 + ReleaseNullObject(pixnNode);
56 + }
57 +
58 +LExit:
59 + ReleaseObject(pixnNodes);
60 + ReleaseObject(pixnNode);
61 + ReleaseStr(scz);
62 +
63 + return hr;
64 +}
65 +
66 +extern "C" HRESULT CatalogFindById(
67 + __in BURN_CATALOGS* pCatalogs,
68 + __in_z LPCWSTR wzId,
69 + __out BURN_CATALOG** ppCatalog
70 + )
71 +{
72 + HRESULT hr = S_OK;
73 + BURN_CATALOG* pCatalog = NULL;
74 +
75 + for (DWORD i = 0; i < pCatalogs->cCatalogs; ++i)
76 + {
77 + pCatalog = &pCatalogs->rgCatalogs[i];
78 +
79 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pCatalog->sczKey, -1, wzId, -1))
80 + {
81 + *ppCatalog = pCatalog;
82 + ExitFunction1(hr = S_OK);
83 + }
84 + }
85 +
86 + hr = E_NOTFOUND;
87 +
88 +LExit:
89 + return hr;
90 +}
91 +
92 +extern "C" HRESULT CatalogLoadFromPayload(
93 + __in BURN_CATALOGS* pCatalogs,
94 + __in BURN_PAYLOADS* pPayloads
95 + )
96 +{
97 + HRESULT hr = S_OK;
98 + BURN_CATALOG* pCatalog = NULL;
99 + BURN_PAYLOAD* pPayload = NULL;
100 +
101 + // go through each catalog file
102 + for (DWORD i = 0; i < pCatalogs->cCatalogs; i++)
103 + {
104 + pCatalog = &pCatalogs->rgCatalogs[i];
105 +
106 + // get the payload for this catalog file
107 + hr = PayloadFindById(pPayloads, pCatalog->sczPayload, &pPayload);
108 + ExitOnFailure(hr, "Failed to find payload for catalog file.");
109 +
110 + // Get the local file name
111 + hr = StrAllocString(&pCatalog->sczLocalFilePath, pPayload->sczLocalFilePath, 0);
112 + ExitOnFailure(hr, "Failed to get catalog local file path");
113 +
114 + // Get a handle to the file
115 + pCatalog->hFile = ::CreateFileW(pCatalog->sczLocalFilePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
116 + if (INVALID_HANDLE_VALUE == pCatalog->hFile)
117 + {
118 + ExitWithLastError(hr, "Failed to open catalog in working path: %ls", pCatalog->sczLocalFilePath);
119 + }
120 +
121 + // Verify the catalog file
122 + hr = CacheVerifyPayloadSignature(pPayload, pCatalog->sczLocalFilePath, pCatalog->hFile);
123 + ExitOnFailure(hr, "Failed to verify catalog signature: %ls", pCatalog->sczLocalFilePath);
124 + }
125 +
126 +LExit:
127 + return hr;
128 +}
129 +
130 +extern "C" HRESULT CatalogElevatedUpdateCatalogFile(
131 + __in BURN_CATALOGS* pCatalogs,
132 + __in_z LPCWSTR wzId,
133 + __in_z LPCWSTR wzPath
134 + )
135 +{
136 + HRESULT hr = S_OK;
137 + BURN_CATALOG* pCatalog = NULL;
138 +
139 + // Find the catalog
140 + hr = CatalogFindById(pCatalogs, wzId, &pCatalog);
141 + ExitOnFailure(hr, "Failed to locate catalog information.");
142 +
143 + if (NULL == pCatalog->sczLocalFilePath)
144 + {
145 + hr = StrAllocString(&pCatalog->sczLocalFilePath, wzPath, 0);
146 + ExitOnFailure(hr, "Failed to allocated catalog path.");
147 +
148 + // Get a handle to the file
149 + pCatalog->hFile = ::CreateFileW(pCatalog->sczLocalFilePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
150 + if (INVALID_HANDLE_VALUE == pCatalog->hFile)
151 + {
152 + ExitWithLastError(hr, "Failed to open catalog in working path: %ls", pCatalog->sczLocalFilePath);
153 + }
154 + }
155 +
156 +LExit:
157 + return hr;
158 +}
159 +
160 +extern "C" void CatalogUninitialize(
161 + __in BURN_CATALOGS* pCatalogs
162 + )
163 +{
164 + if (pCatalogs->rgCatalogs)
165 + {
166 + for (DWORD i = 0; i < pCatalogs->cCatalogs; ++i)
167 + {
168 + BURN_CATALOG* pCatalog = &pCatalogs->rgCatalogs[i];
169 +
170 + ReleaseHandle(pCatalog->hFile);
171 + ReleaseStr(pCatalog->sczKey);
172 + ReleaseStr(pCatalog->sczLocalFilePath);
173 + ReleaseStr(pCatalog->sczPayload);
174 + }
175 + MemFree(pCatalogs->rgCatalogs);
176 + }
177 +
178 + // clear struct
179 + memset(pCatalogs, 0, sizeof(BURN_CATALOGS));
180 +}
src/engine/catalog.h new
+56
@@ -0,0 +1,56 @@
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 +#if defined(__cplusplus)
6 +extern "C" {
7 +#endif
8 +
9 +// structs
10 +
11 +typedef struct _BURN_CATALOG
12 +{
13 + LPWSTR sczKey;
14 + LPWSTR sczPayload;
15 +
16 + // mutable members
17 + LPWSTR sczLocalFilePath; // location of extracted or downloaded copy
18 + HANDLE hFile;
19 +} BURN_CATALOG;
20 +
21 +typedef struct _BURN_CATALOGS
22 +{
23 + BURN_CATALOG* rgCatalogs;
24 + DWORD cCatalogs;
25 +} BURN_CATALOGS;
26 +
27 +typedef struct _BURN_PAYLOADS BURN_PAYLOADS;
28 +
29 +
30 +// functions
31 +
32 +HRESULT CatalogsParseFromXml(
33 + __in BURN_CATALOGS* pCatalogs,
34 + __in IXMLDOMNode* pixnBundle
35 + );
36 +HRESULT CatalogFindById(
37 + __in BURN_CATALOGS* pCatalogs,
38 + __in_z LPCWSTR wzId,
39 + __out BURN_CATALOG** ppCatalog
40 + );
41 +HRESULT CatalogLoadFromPayload(
42 + __in BURN_CATALOGS* pCatalogs,
43 + __in BURN_PAYLOADS* pPayloads
44 + );
45 +HRESULT CatalogElevatedUpdateCatalogFile(
46 + __in BURN_CATALOGS* pCatalogs,
47 + __in_z LPCWSTR wzId,
48 + __in_z LPCWSTR wzPath
49 + );
50 +void CatalogUninitialize(
51 + __in BURN_CATALOGS* pCatalogs
52 + );
53 +
54 +#if defined(__cplusplus)
55 +}
56 +#endif
src/engine/condition.cpp new
+1030
@@ -0,0 +1,1030 @@
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 +//
7 +// parse rules
8 +//
9 +// value variable | literal | integer | version
10 +// comparison-operator < | > | <= | >= | = | <> | >< | << | >>
11 +// term value | value comparison-operator value | ( expression )
12 +// boolean-factor term | NOT term
13 +// boolean-term boolean-factor | boolean-factor AND boolean-term
14 +// expression boolean-term | boolean-term OR expression
15 +//
16 +
17 +
18 +// constants
19 +
20 +#define COMPARISON 0x00010000
21 +#define INSENSITIVE 0x00020000
22 +
23 +enum BURN_SYMBOL_TYPE
24 +{
25 + // terminals
26 + BURN_SYMBOL_TYPE_NONE = 0,
27 + BURN_SYMBOL_TYPE_END = 1,
28 + BURN_SYMBOL_TYPE_OR = 2, // OR
29 + BURN_SYMBOL_TYPE_AND = 3, // AND
30 + BURN_SYMBOL_TYPE_NOT = 4, // NOT
31 + BURN_SYMBOL_TYPE_LT = 5 | COMPARISON, // <
32 + BURN_SYMBOL_TYPE_GT = 6 | COMPARISON, // >
33 + BURN_SYMBOL_TYPE_LE = 7 | COMPARISON, // <=
34 + BURN_SYMBOL_TYPE_GE = 8 | COMPARISON, // >=
35 + BURN_SYMBOL_TYPE_EQ = 9 | COMPARISON, // =
36 + BURN_SYMBOL_TYPE_NE = 10 | COMPARISON, // <>
37 + BURN_SYMBOL_TYPE_BAND = 11 | COMPARISON, // ><
38 + BURN_SYMBOL_TYPE_HIEQ = 12 | COMPARISON, // <<
39 + BURN_SYMBOL_TYPE_LOEQ = 13 | COMPARISON, // >>
40 + BURN_SYMBOL_TYPE_LT_I = 5 | COMPARISON | INSENSITIVE, // ~<
41 + BURN_SYMBOL_TYPE_GT_I = 6 | COMPARISON | INSENSITIVE, // ~>
42 + BURN_SYMBOL_TYPE_LE_I = 7 | COMPARISON | INSENSITIVE, // ~<=
43 + BURN_SYMBOL_TYPE_GE_I = 8 | COMPARISON | INSENSITIVE, // ~>=
44 + BURN_SYMBOL_TYPE_EQ_I = 9 | COMPARISON | INSENSITIVE, // ~=
45 + BURN_SYMBOL_TYPE_NE_I = 10 | COMPARISON | INSENSITIVE, // ~<>
46 + BURN_SYMBOL_TYPE_BAND_I = 11 | COMPARISON | INSENSITIVE, // ~><
47 + BURN_SYMBOL_TYPE_HIEQ_I = 12 | COMPARISON | INSENSITIVE, // ~<<
48 + BURN_SYMBOL_TYPE_LOEQ_I = 13 | COMPARISON | INSENSITIVE, // ~>>
49 + BURN_SYMBOL_TYPE_LPAREN = 14, // (
50 + BURN_SYMBOL_TYPE_RPAREN = 15, // )
51 + BURN_SYMBOL_TYPE_NUMBER = 16,
52 + BURN_SYMBOL_TYPE_IDENTIFIER = 17,
53 + BURN_SYMBOL_TYPE_LITERAL = 18,
54 + BURN_SYMBOL_TYPE_VERSION = 19,
55 +};
56 +
57 +
58 +// structs
59 +
60 +struct BURN_SYMBOL
61 +{
62 + BURN_SYMBOL_TYPE Type;
63 + DWORD iPosition;
64 + BURN_VARIANT Value;
65 +};
66 +
67 +struct BURN_CONDITION_PARSE_CONTEXT
68 +{
69 + BURN_VARIABLES* pVariables;
70 + LPCWSTR wzCondition;
71 + LPCWSTR wzRead;
72 + BURN_SYMBOL NextSymbol;
73 + BOOL fError;
74 +};
75 +
76 +
77 +// internal function declarations
78 +
79 +static HRESULT ParseExpression(
80 + __in BURN_CONDITION_PARSE_CONTEXT* pContext,
81 + __out BOOL* pf
82 + );
83 +static HRESULT ParseBooleanTerm(
84 + __in BURN_CONDITION_PARSE_CONTEXT* pContext,
85 + __out BOOL* pf
86 + );
87 +static HRESULT ParseBooleanFactor(
88 + __in BURN_CONDITION_PARSE_CONTEXT* pContext,
89 + __out BOOL* pf
90 + );
91 +static HRESULT ParseTerm(
92 + __in BURN_CONDITION_PARSE_CONTEXT* pContext,
93 + __out BOOL* pf
94 + );
95 +static HRESULT ParseValue(
96 + __in BURN_CONDITION_PARSE_CONTEXT* pContext,
97 + __out BURN_VARIANT* pValue
98 + );
99 +static HRESULT Expect(
100 + __in BURN_CONDITION_PARSE_CONTEXT* pContext,
101 + __in BURN_SYMBOL_TYPE symbolType
102 + );
103 +static HRESULT NextSymbol(
104 + __in BURN_CONDITION_PARSE_CONTEXT* pContext
105 + );
106 +static HRESULT CompareValues(
107 + __in BURN_SYMBOL_TYPE comparison,
108 + __in BURN_VARIANT leftOperand,
109 + __in BURN_VARIANT rightOperand,
110 + __out BOOL* pfResult
111 + );
112 +static HRESULT CompareStringValues(
113 + __in BURN_SYMBOL_TYPE comparison,
114 + __in_z LPCWSTR wzLeftOperand,
115 + __in_z LPCWSTR wzRightOperand,
116 + __out BOOL* pfResult
117 + );
118 +static HRESULT CompareIntegerValues(
119 + __in BURN_SYMBOL_TYPE comparison,
120 + __in LONGLONG llLeftOperand,
121 + __in LONGLONG llRightOperand,
122 + __out BOOL* pfResult
123 + );
124 +static HRESULT CompareVersionValues(
125 + __in BURN_SYMBOL_TYPE comparison,
126 + __in DWORD64 qwLeftOperand,
127 + __in DWORD64 qwRightOperand,
128 + __out BOOL* pfResult
129 + );
130 +
131 +
132 +// function definitions
133 +
134 +extern "C" HRESULT ConditionEvaluate(
135 + __in BURN_VARIABLES* pVariables,
136 + __in_z LPCWSTR wzCondition,
137 + __out BOOL* pf
138 + )
139 +{
140 + HRESULT hr = S_OK;
141 + BURN_CONDITION_PARSE_CONTEXT context = { };
142 + BOOL f = FALSE;
143 +
144 + context.pVariables = pVariables;
145 + context.wzCondition = wzCondition;
146 + context.wzRead = wzCondition;
147 +
148 + hr = NextSymbol(&context);
149 + ExitOnFailure(hr, "Failed to read next symbol.");
150 +
151 + hr = ParseExpression(&context, &f);
152 + ExitOnFailure(hr, "Failed to parse expression.");
153 +
154 + hr = Expect(&context, BURN_SYMBOL_TYPE_END);
155 + ExitOnFailure(hr, "Failed to expect end symbol.");
156 +
157 + LogId(REPORT_VERBOSE, MSG_CONDITION_RESULT, wzCondition, LoggingTrueFalseToString(f));
158 +
159 + *pf = f;
160 + hr = S_OK;
161 +
162 +LExit:
163 + if (context.fError)
164 + {
165 + Assert(FAILED(hr));
166 + LogErrorId(hr, MSG_FAILED_PARSE_CONDITION, wzCondition, NULL, NULL);
167 + }
168 +
169 + return hr;
170 +}
171 +
172 +extern "C" HRESULT ConditionGlobalCheck(
173 + __in BURN_VARIABLES* pVariables,
174 + __in BURN_CONDITION* pCondition,
175 + __in BOOTSTRAPPER_DISPLAY display,
176 + __in_z LPCWSTR wzBundleName,
177 + __out DWORD *pdwExitCode,
178 + __out BOOL *pfContinueExecution
179 + )
180 +{
181 + HRESULT hr = S_OK;
182 + BOOL fSuccess = TRUE;
183 + HRESULT hrError = HRESULT_FROM_WIN32(ERROR_OLD_WIN_VERSION);
184 + OS_VERSION osv = OS_VERSION_UNKNOWN;
185 + DWORD dwServicePack = 0;
186 +
187 + OsGetVersion(&osv, &dwServicePack);
188 +
189 + // Always error on Windows 2000 or lower
190 + if (OS_VERSION_WIN2000 >= osv)
191 + {
192 + fSuccess = FALSE;
193 + }
194 + else
195 + {
196 + if (NULL != pCondition->sczConditionString)
197 + {
198 + hr = ConditionEvaluate(pVariables, pCondition->sczConditionString, &fSuccess);
199 + ExitOnFailure(hr, "Failed to evaluate condition: %ls", pCondition->sczConditionString);
200 + }
201 + }
202 +
203 + if (!fSuccess)
204 + {
205 + // Display the error messagebox, as long as we're in an appropriate display mode
206 + hr = SplashScreenDisplayError(display, wzBundleName, hrError);
207 + ExitOnFailure(hr, "Failed to display error dialog");
208 +
209 + *pdwExitCode = static_cast<DWORD>(hrError);
210 + *pfContinueExecution = FALSE;
211 + }
212 +
213 +LExit:
214 + return hr;
215 +}
216 +
217 +HRESULT ConditionGlobalParseFromXml(
218 + __in BURN_CONDITION* pCondition,
219 + __in IXMLDOMNode* pixnBundle
220 + )
221 +{
222 + HRESULT hr = S_OK;
223 + IXMLDOMNode* pixnNode = NULL;
224 + BSTR bstrExpression = NULL;
225 +
226 + // select variable nodes
227 + hr = XmlSelectSingleNode(pixnBundle, L"Condition", &pixnNode);
228 + if (S_FALSE == hr)
229 + {
230 + ExitFunction1(hr = S_OK);
231 + }
232 + ExitOnFailure(hr, "Failed to select condition node.");
233 +
234 + // @Condition
235 + hr = XmlGetText(pixnNode, &bstrExpression);
236 + ExitOnFailure(hr, "Failed to get Condition inner text.");
237 +
238 + hr = StrAllocString(&pCondition->sczConditionString, bstrExpression, 0);
239 + ExitOnFailure(hr, "Failed to copy condition string from BSTR");
240 +
241 +LExit:
242 + ReleaseBSTR(bstrExpression);
243 + ReleaseObject(pixnNode);
244 +
245 + return hr;
246 +}
247 +
248 +
249 +// internal function definitions
250 +
251 +static HRESULT ParseExpression(
252 + __in BURN_CONDITION_PARSE_CONTEXT* pContext,
253 + __out BOOL* pf
254 + )
255 +{
256 + HRESULT hr = S_OK;
257 + BOOL fFirst = FALSE;
258 + BOOL fSecond = FALSE;
259 +
260 + hr = ParseBooleanTerm(pContext, &fFirst);
261 + ExitOnFailure(hr, "Failed to parse boolean-term.");
262 +
263 + if (BURN_SYMBOL_TYPE_OR == pContext->NextSymbol.Type)
264 + {
265 + hr = NextSymbol(pContext);
266 + ExitOnFailure(hr, "Failed to read next symbol.");
267 +
268 + hr = ParseExpression(pContext, &fSecond);
269 + ExitOnFailure(hr, "Failed to parse expression.");
270 +
271 + *pf = fFirst || fSecond;
272 + }
273 + else
274 + {
275 + *pf = fFirst;
276 + }
277 +
278 +LExit:
279 + return hr;
280 +}
281 +
282 +static HRESULT ParseBooleanTerm(
283 + __in BURN_CONDITION_PARSE_CONTEXT* pContext,
284 + __out BOOL* pf
285 + )
286 +{
287 + HRESULT hr = S_OK;
288 + BOOL fFirst = FALSE;
289 + BOOL fSecond = FALSE;
290 +
291 + hr = ParseBooleanFactor(pContext, &fFirst);
292 + ExitOnFailure(hr, "Failed to parse boolean-factor.");
293 +
294 + if (BURN_SYMBOL_TYPE_AND == pContext->NextSymbol.Type)
295 + {
296 + hr = NextSymbol(pContext);
297 + ExitOnFailure(hr, "Failed to read next symbol.");
298 +
299 + hr = ParseBooleanTerm(pContext, &fSecond);
300 + ExitOnFailure(hr, "Failed to parse boolean-term.");
301 +
302 + *pf = fFirst && fSecond;
303 + }
304 + else
305 + {
306 + *pf = fFirst;
307 + }
308 +
309 +LExit:
310 + return hr;
311 +}
312 +
313 +static HRESULT ParseBooleanFactor(
314 + __in BURN_CONDITION_PARSE_CONTEXT* pContext,
315 + __out BOOL* pf
316 + )
317 +{
318 + HRESULT hr = S_OK;
319 + BOOL fNot = FALSE;
320 + BOOL f = FALSE;
321 +
322 + if (BURN_SYMBOL_TYPE_NOT == pContext->NextSymbol.Type)
323 + {
324 + hr = NextSymbol(pContext);
325 + ExitOnFailure(hr, "Failed to read next symbol.");
326 +
327 + fNot = TRUE;
328 + }
329 +
330 + hr = ParseTerm(pContext, &f);
331 + ExitOnFailure(hr, "Failed to parse term.");
332 +
333 + *pf = fNot ? !f : f;
334 +
335 +LExit:
336 + return hr;
337 +}
338 +
339 +static HRESULT ParseTerm(
340 + __in BURN_CONDITION_PARSE_CONTEXT* pContext,
341 + __out BOOL* pf
342 + )
343 +{
344 + HRESULT hr = S_OK;
345 + BURN_VARIANT firstValue = { };
346 + BURN_VARIANT secondValue = { };
347 +
348 + if (BURN_SYMBOL_TYPE_LPAREN == pContext->NextSymbol.Type)
349 + {
350 + hr = NextSymbol(pContext);
351 + ExitOnFailure(hr, "Failed to read next symbol.");
352 +
353 + hr = ParseExpression(pContext, pf);
354 + ExitOnFailure(hr, "Failed to parse expression.");
355 +
356 + hr = Expect(pContext, BURN_SYMBOL_TYPE_RPAREN);
357 + ExitOnFailure(hr, "Failed to expect right parenthesis.");
358 +
359 + ExitFunction1(hr = S_OK);
360 + }
361 +
362 + hr = ParseValue(pContext, &firstValue);
363 + ExitOnFailure(hr, "Failed to parse value.");
364 +
365 + if (COMPARISON & pContext->NextSymbol.Type)
366 + {
367 + BURN_SYMBOL_TYPE comparison = pContext->NextSymbol.Type;
368 +
369 + hr = NextSymbol(pContext);
370 + ExitOnFailure(hr, "Failed to read next symbol.");
371 +
372 + hr = ParseValue(pContext, &secondValue);
373 + ExitOnFailure(hr, "Failed to parse value.");
374 +
375 + hr = CompareValues(comparison, firstValue, secondValue, pf);
376 + ExitOnFailure(hr, "Failed to compare value.");
377 + }
378 + else
379 + {
380 + LONGLONG llValue = 0;
381 + LPWSTR sczValue = NULL;
382 + DWORD64 qwValue = 0;
383 + switch (firstValue.Type)
384 + {
385 + case BURN_VARIANT_TYPE_NONE:
386 + *pf = FALSE;
387 + break;
388 + case BURN_VARIANT_TYPE_STRING:
389 + hr = BVariantGetString(&firstValue, &sczValue);
390 + if (SUCCEEDED(hr))
391 + {
392 + *pf = sczValue && *sczValue;
393 + }
394 + StrSecureZeroFreeString(sczValue);
395 + break;
396 + case BURN_VARIANT_TYPE_NUMERIC:
397 + hr = BVariantGetNumeric(&firstValue, &llValue);
398 + if (SUCCEEDED(hr))
399 + {
400 + *pf = 0 != llValue;
401 + }
402 + SecureZeroMemory(&llValue, sizeof(llValue));
403 + break;
404 + case BURN_VARIANT_TYPE_VERSION:
405 + hr = BVariantGetVersion(&firstValue, &qwValue);
406 + if (SUCCEEDED(hr))
407 + {
408 + *pf = 0 != qwValue;
409 + }
410 + SecureZeroMemory(&llValue, sizeof(qwValue));
411 + break;
412 + default:
413 + ExitFunction1(hr = E_UNEXPECTED);
414 + }
415 + }
416 +
417 +LExit:
418 + BVariantUninitialize(&firstValue);
419 + BVariantUninitialize(&secondValue);
420 + return hr;
421 +}
422 +
423 +static HRESULT ParseValue(
424 + __in BURN_CONDITION_PARSE_CONTEXT* pContext,
425 + __out BURN_VARIANT* pValue
426 + )
427 +{
428 + HRESULT hr = S_OK;
429 +
430 + // Symbols don't encrypt their value, so can access the value directly.
431 + switch (pContext->NextSymbol.Type)
432 + {
433 + case BURN_SYMBOL_TYPE_IDENTIFIER:
434 + Assert(BURN_VARIANT_TYPE_STRING == pContext->NextSymbol.Value.Type);
435 +
436 + // find variable
437 + hr = VariableGetVariant(pContext->pVariables, pContext->NextSymbol.Value.sczValue, pValue);
438 + if (E_NOTFOUND != hr)
439 + {
440 + ExitOnRootFailure(hr, "Failed to find variable.");
441 + }
442 + break;
443 +
444 + case BURN_SYMBOL_TYPE_NUMBER: __fallthrough;
445 + case BURN_SYMBOL_TYPE_LITERAL: __fallthrough;
446 + case BURN_SYMBOL_TYPE_VERSION:
447 + // steal value of symbol
448 + memcpy_s(pValue, sizeof(BURN_VARIANT), &pContext->NextSymbol.Value, sizeof(BURN_VARIANT));
449 + memset(&pContext->NextSymbol.Value, 0, sizeof(BURN_VARIANT));
450 + break;
451 +
452 + default:
453 + pContext->fError = TRUE;
454 + hr = E_INVALIDDATA;
455 + ExitOnRootFailure(hr, "Failed to parse condition '%ls' at position: %u", pContext->wzCondition, pContext->NextSymbol.iPosition);
456 + }
457 +
458 + // get next symbol
459 + hr = NextSymbol(pContext);
460 + ExitOnFailure(hr, "Failed to read next symbol.");
461 +
462 +LExit:
463 + return hr;
464 +}
465 +
466 +//
467 +// Expect - expects a symbol.
468 +//
469 +static HRESULT Expect(
470 + __in BURN_CONDITION_PARSE_CONTEXT* pContext,
471 + __in BURN_SYMBOL_TYPE symbolType
472 + )
473 +{
474 + HRESULT hr = S_OK;
475 +
476 + if (pContext->NextSymbol.Type != symbolType)
477 + {
478 + pContext->fError = TRUE;
479 + hr = E_INVALIDDATA;
480 + ExitOnRootFailure(hr, "Failed to parse condition '%ls' at position: %u", pContext->wzCondition, pContext->NextSymbol.iPosition);
481 + }
482 +
483 + hr = NextSymbol(pContext);
484 + ExitOnFailure(hr, "Failed to read next symbol.");
485 +
486 +LExit:
487 + return hr;
488 +}
489 +
490 +//
491 +// NextSymbol - finds the next symbol in an expression string.
492 +//
493 +static HRESULT NextSymbol(
494 + __in BURN_CONDITION_PARSE_CONTEXT* pContext
495 + )
496 +{
497 + HRESULT hr = S_OK;
498 + WORD charType = 0;
499 + DWORD iPosition = 0;
500 + DWORD n = 0;
501 +
502 + // free existing symbol
503 + BVariantUninitialize(&pContext->NextSymbol.Value);
504 + memset(&pContext->NextSymbol, 0, sizeof(BURN_SYMBOL));
505 +
506 + // skip past blanks
507 + while (L'\0' != pContext->wzRead[0])
508 + {
509 + ::GetStringTypeW(CT_CTYPE1, pContext->wzRead, 1, &charType);
510 + if (0 == (C1_BLANK & charType))
511 + {
512 + break; // no blank, done
513 + }
514 + ++pContext->wzRead;
515 + }
516 + iPosition = (DWORD)(pContext->wzRead - pContext->wzCondition);
517 +
518 + // read depending on first character type
519 + switch (pContext->wzRead[0])
520 + {
521 + case L'\0':
522 + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_END;
523 + break;
524 + case L'~':
525 + switch (pContext->wzRead[1])
526 + {
527 + case L'=':
528 + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_EQ_I;
529 + n = 2;
530 + break;
531 + case L'>':
532 + switch (pContext->wzRead[2])
533 + {
534 + case '=':
535 + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_GE_I;
536 + n = 3;
537 + break;
538 + case L'>':
539 + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LOEQ_I;
540 + n = 3;
541 + break;
542 + case L'<':
543 + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_BAND_I;
544 + n = 3;
545 + break;
546 + default:
547 + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_GT_I;
548 + n = 2;
549 + }
550 + break;
551 + case L'<':
552 + switch (pContext->wzRead[2])
553 + {
554 + case '=':
555 + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LE_I;
556 + n = 3;
557 + break;
558 + case L'<':
559 + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_HIEQ_I;
560 + n = 3;
561 + break;
562 + case '>':
563 + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_NE_I;
564 + n = 3;
565 + break;
566 + default:
567 + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LT_I;
568 + n = 2;
569 + }
570 + break;
571 + default:
572 + // error
573 + pContext->fError = TRUE;
574 + hr = E_INVALIDDATA;
575 + ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Unexpected '~' operator at position %d.", pContext->wzCondition, iPosition);
576 + }
577 + break;
578 + case L'>':
579 + switch (pContext->wzRead[1])
580 + {
581 + case L'=':
582 + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_GE;
583 + n = 2;
584 + break;
585 + case L'>':
586 + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LOEQ;
587 + n = 2;
588 + break;
589 + case L'<':
590 + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_BAND;
591 + n = 2;
592 + break;
593 + default:
594 + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_GT;
595 + n = 1;
596 + }
597 + break;
598 + case L'<':
599 + switch (pContext->wzRead[1])
600 + {
601 + case L'=':
602 + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LE;
603 + n = 2;
604 + break;
605 + case L'<':
606 + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_HIEQ;
607 + n = 2;
608 + break;
609 + case L'>':
610 + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_NE;
611 + n = 2;
612 + break;
613 + default:
614 + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LT;
615 + n = 1;
616 + }
617 + break;
618 + case L'=':
619 + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_EQ;
620 + n = 1;
621 + break;
622 + case L'(':
623 + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LPAREN;
624 + n = 1;
625 + break;
626 + case L')':
627 + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_RPAREN;
628 + n = 1;
629 + break;
630 + case L'"': // literal
631 + do
632 + {
633 + ++n;
634 + if (L'\0' == pContext->wzRead[n])
635 + {
636 + // error
637 + pContext->fError = TRUE;
638 + hr = E_INVALIDDATA;
639 + ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Unterminated literal at position %d.", pContext->wzCondition, iPosition);
640 + }
641 + } while (L'"' != pContext->wzRead[n]);
642 + ++n; // terminating '"'
643 +
644 + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LITERAL;
645 + hr = BVariantSetString(&pContext->NextSymbol.Value, &pContext->wzRead[1], n - 2);
646 + ExitOnFailure(hr, "Failed to set symbol value.");
647 + break;
648 + default:
649 + if (C1_DIGIT & charType || L'-' == pContext->wzRead[0])
650 + {
651 + do
652 + {
653 + ++n;
654 + ::GetStringTypeW(CT_CTYPE1, &pContext->wzRead[n], 1, &charType);
655 + if (C1_ALPHA & charType || L'_' == pContext->wzRead[n])
656 + {
657 + // error, identifier cannot start with a digit
658 + pContext->fError = TRUE;
659 + hr = E_INVALIDDATA;
660 + ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Identifier cannot start at a digit, at position %d.", pContext->wzCondition, iPosition);
661 + }
662 + } while (C1_DIGIT & charType);
663 +
664 + // number
665 + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_NUMBER;
666 +
667 + LONGLONG ll = 0;
668 + hr = StrStringToInt64(pContext->wzRead, n, &ll);
669 + if (FAILED(hr))
670 + {
671 + pContext->fError = TRUE;
672 + hr = E_INVALIDDATA;
673 + ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Constant too big, at position %d.", pContext->wzCondition, iPosition);
674 + }
675 +
676 + hr = BVariantSetNumeric(&pContext->NextSymbol.Value, ll);
677 + ExitOnFailure(hr, "Failed to set symbol value.");
678 + }
679 + else if (C1_ALPHA & charType || L'_' == pContext->wzRead[0])
680 + {
681 + ::GetStringTypeW(CT_CTYPE1, &pContext->wzRead[1], 1, &charType);
682 + if (L'v' == pContext->wzRead[0] && C1_DIGIT & charType)
683 + {
684 + // version
685 + DWORD cParts = 1;
686 + for (;;)
687 + {
688 + ++n;
689 + if (L'.' == pContext->wzRead[n])
690 + {
691 + ++cParts;
692 + if (4 < cParts)
693 + {
694 + // error, too many parts in version
695 + pContext->fError = TRUE;
696 + hr = E_INVALIDDATA;
697 + ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Version can have a maximum of 4 parts, at position %d.", pContext->wzCondition, iPosition);
698 + }
699 + }
700 + else
701 + {
702 + ::GetStringTypeW(CT_CTYPE1, &pContext->wzRead[n], 1, &charType);
703 + if (C1_DIGIT != (C1_DIGIT & charType))
704 + {
705 + break;
706 + }
707 + }
708 + }
709 +
710 + // Symbols don't encrypt their value, so can access the value directly.
711 + hr = FileVersionFromStringEx(&pContext->wzRead[1], n - 1, &pContext->NextSymbol.Value.qwValue);
712 + if (FAILED(hr))
713 + {
714 + pContext->fError = TRUE;
715 + hr = E_INVALIDDATA;
716 + ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Invalid version format, at position %d.", pContext->wzCondition, iPosition);
717 + }
718 +
719 + pContext->NextSymbol.Value.Type = BURN_VARIANT_TYPE_VERSION;
720 + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_VERSION;
721 + }
722 + else
723 + {
724 + do
725 + {
726 + ++n;
727 + ::GetStringTypeW(CT_CTYPE1, &pContext->wzRead[n], 1, &charType);
728 + } while (C1_ALPHA & charType || C1_DIGIT & charType || L'_' == pContext->wzRead[n]);
729 +
730 + if (2 == n && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pContext->wzRead, 2, L"OR", 2))
731 + {
732 + // OR
733 + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_OR;
734 + }
735 + else if (3 == n && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pContext->wzRead, 3, L"AND", 3))
736 + {
737 + // AND
738 + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_AND;
739 + }
740 + else if (3 == n && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pContext->wzRead, 3, L"NOT", 3))
741 + {
742 + // NOT
743 + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_NOT;
744 + }
745 + else
746 + {
747 + // identifier
748 + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_IDENTIFIER;
749 + hr = BVariantSetString(&pContext->NextSymbol.Value, pContext->wzRead, n);
750 + ExitOnFailure(hr, "Failed to set symbol value.");
751 + }
752 + }
753 + }
754 + else
755 + {
756 + // error, unexpected character
757 + pContext->fError = TRUE;
758 + hr = E_INVALIDDATA;
759 + ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Unexpected character at position %d.", pContext->wzCondition, iPosition);
760 + }
761 + }
762 + pContext->NextSymbol.iPosition = iPosition;
763 + pContext->wzRead += n;
764 +
765 +LExit:
766 + return hr;
767 +}
768 +
769 +//
770 +// CompareValues - compares two variant values using a given comparison.
771 +//
772 +static HRESULT CompareValues(
773 + __in BURN_SYMBOL_TYPE comparison,
774 + __in BURN_VARIANT leftOperand,
775 + __in BURN_VARIANT rightOperand,
776 + __out BOOL* pfResult
777 + )
778 +{
779 + HRESULT hr = S_OK;
780 + LONGLONG llLeft = 0;
781 + DWORD64 qwLeft = 0;
782 + LPWSTR sczLeft = NULL;
783 + LONGLONG llRight = 0;
784 + DWORD64 qwRight = 0;
785 + LPWSTR sczRight = NULL;
786 +
787 + // get values to compare based on type
788 + if (BURN_VARIANT_TYPE_STRING == leftOperand.Type && BURN_VARIANT_TYPE_STRING == rightOperand.Type)
789 + {
790 + hr = BVariantGetString(&leftOperand, &sczLeft);
791 + ExitOnFailure(hr, "Failed to get the left string");
792 + hr = BVariantGetString(&rightOperand, &sczRight);
793 + ExitOnFailure(hr, "Failed to get the right string");
794 + hr = CompareStringValues(comparison, sczLeft, sczRight, pfResult);
795 + }
796 + else if (BURN_VARIANT_TYPE_NUMERIC == leftOperand.Type && BURN_VARIANT_TYPE_NUMERIC == rightOperand.Type)
797 + {
798 + hr = BVariantGetNumeric(&leftOperand, &llLeft);
799 + ExitOnFailure(hr, "Failed to get the left numeric");
800 + hr = BVariantGetNumeric(&rightOperand, &llRight);
801 + ExitOnFailure(hr, "Failed to get the right numeric");
802 + hr = CompareIntegerValues(comparison, llLeft, llRight, pfResult);
803 + }
804 + else if (BURN_VARIANT_TYPE_VERSION == leftOperand.Type && BURN_VARIANT_TYPE_VERSION == rightOperand.Type)
805 + {
806 + hr = BVariantGetVersion(&leftOperand, &qwLeft);
807 + ExitOnFailure(hr, "Failed to get the left version");
808 + hr = BVariantGetVersion(&rightOperand, &qwRight);
809 + ExitOnFailure(hr, "Failed to get the right version");
810 + hr = CompareVersionValues(comparison, qwLeft, qwRight, pfResult);
811 + }
812 + else if (BURN_VARIANT_TYPE_VERSION == leftOperand.Type && BURN_VARIANT_TYPE_STRING == rightOperand.Type)
813 + {
814 + hr = BVariantGetVersion(&leftOperand, &qwLeft);
815 + ExitOnFailure(hr, "Failed to get the left version");
816 + hr = BVariantGetVersion(&rightOperand, &qwRight);
817 + if (FAILED(hr))
818 + {
819 + if (DISP_E_TYPEMISMATCH != hr)
820 + {
821 + ExitOnFailure(hr, "Failed to get the right version");
822 + }
823 + *pfResult = (BURN_SYMBOL_TYPE_NE == comparison);
824 + hr = S_OK;
825 + }
826 + else
827 + {
828 + hr = CompareVersionValues(comparison, qwLeft, qwRight, pfResult);
829 + }
830 + }
831 + else if (BURN_VARIANT_TYPE_STRING == leftOperand.Type && BURN_VARIANT_TYPE_VERSION == rightOperand.Type)
832 + {
833 + hr = BVariantGetVersion(&rightOperand, &qwRight);
834 + ExitOnFailure(hr, "Failed to get the right version");
835 + hr = BVariantGetVersion(&leftOperand, &qwLeft);
836 + if (FAILED(hr))
837 + {
838 + if (DISP_E_TYPEMISMATCH != hr)
839 + {
840 + ExitOnFailure(hr, "Failed to get the left version");
841 + }
842 + *pfResult = (BURN_SYMBOL_TYPE_NE == comparison);
843 + hr = S_OK;
844 + }
845 + else
846 + {
847 + hr = CompareVersionValues(comparison, qwLeft, qwRight, pfResult);
848 + }
849 + }
850 + else if (BURN_VARIANT_TYPE_NUMERIC == leftOperand.Type && BURN_VARIANT_TYPE_STRING == rightOperand.Type)
851 + {
852 + hr = BVariantGetNumeric(&leftOperand, &llLeft);
853 + ExitOnFailure(hr, "Failed to get the left numeric");
854 + hr = BVariantGetNumeric(&rightOperand, &llRight);
855 + if (FAILED(hr))
856 + {
857 + if (DISP_E_TYPEMISMATCH != hr)
858 + {
859 + ExitOnFailure(hr, "Failed to get the right numeric");
860 + }
861 + *pfResult = (BURN_SYMBOL_TYPE_NE == comparison);
862 + hr = S_OK;
863 + }
864 + else
865 + {
866 + hr = CompareIntegerValues(comparison, llLeft, llRight, pfResult);
867 + }
868 + }
869 + else if (BURN_VARIANT_TYPE_STRING == leftOperand.Type && BURN_VARIANT_TYPE_NUMERIC == rightOperand.Type)
870 + {
871 + hr = BVariantGetNumeric(&rightOperand, &llRight);
872 + ExitOnFailure(hr, "Failed to get the right numeric");
873 + hr = BVariantGetNumeric(&leftOperand, &llLeft);
874 + if (FAILED(hr))
875 + {
876 + if (DISP_E_TYPEMISMATCH != hr)
877 + {
878 + ExitOnFailure(hr, "Failed to get the left numeric");
879 + }
880 + *pfResult = (BURN_SYMBOL_TYPE_NE == comparison);
881 + hr = S_OK;
882 + }
883 + else
884 + {
885 + hr = CompareIntegerValues(comparison, llLeft, llRight, pfResult);
886 + }
887 + }
888 + else
889 + {
890 + // not a combination that can be compared
891 + *pfResult = (BURN_SYMBOL_TYPE_NE == comparison);
892 + }
893 +
894 +LExit:
895 + SecureZeroMemory(&qwLeft, sizeof(DWORD64));
896 + SecureZeroMemory(&llLeft, sizeof(LONGLONG));
897 + StrSecureZeroFreeString(sczLeft);
898 + SecureZeroMemory(&qwRight, sizeof(DWORD64));
899 + SecureZeroMemory(&llRight, sizeof(LONGLONG));
900 + StrSecureZeroFreeString(sczRight);
901 +
902 + return hr;
903 +}
904 +
905 +//
906 +// CompareStringValues - compares two string values using a given comparison.
907 +//
908 +static HRESULT CompareStringValues(
909 + __in BURN_SYMBOL_TYPE comparison,
910 + __in_z LPCWSTR wzLeftOperand,
911 + __in_z LPCWSTR wzRightOperand,
912 + __out BOOL* pfResult
913 + )
914 +{
915 + HRESULT hr = S_OK;
916 + DWORD dwCompareString = (comparison & INSENSITIVE) ? NORM_IGNORECASE : 0;
917 + int cchLeft = lstrlenW(wzLeftOperand);
918 + int cchRight = lstrlenW(wzRightOperand);
919 +
920 + switch (comparison)
921 + {
922 + case BURN_SYMBOL_TYPE_LT:
923 + case BURN_SYMBOL_TYPE_GT:
924 + case BURN_SYMBOL_TYPE_LE:
925 + case BURN_SYMBOL_TYPE_GE:
926 + case BURN_SYMBOL_TYPE_EQ:
927 + case BURN_SYMBOL_TYPE_NE:
928 + case BURN_SYMBOL_TYPE_LT_I:
929 + case BURN_SYMBOL_TYPE_GT_I:
930 + case BURN_SYMBOL_TYPE_LE_I:
931 + case BURN_SYMBOL_TYPE_GE_I:
932 + case BURN_SYMBOL_TYPE_EQ_I:
933 + case BURN_SYMBOL_TYPE_NE_I:
934 + {
935 + int i = ::CompareStringW(LOCALE_INVARIANT, dwCompareString, wzLeftOperand, cchLeft, wzRightOperand, cchRight);
936 + hr = CompareIntegerValues(comparison, i, CSTR_EQUAL, pfResult);
937 + }
938 + break;
939 + case BURN_SYMBOL_TYPE_BAND:
940 + case BURN_SYMBOL_TYPE_BAND_I:
941 + // test if left string contains right string
942 + for (int i = 0; (i + cchRight) <= cchLeft; ++i)
943 + {
944 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, dwCompareString, wzLeftOperand + i, cchRight, wzRightOperand, cchRight))
945 + {
946 + *pfResult = TRUE;
947 + ExitFunction();
948 + }
949 + }
950 + *pfResult = FALSE;
951 + break;
952 + case BURN_SYMBOL_TYPE_HIEQ:
953 + case BURN_SYMBOL_TYPE_HIEQ_I:
954 + // test if left string starts with right string
955 + *pfResult = cchLeft >= cchRight && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, dwCompareString, wzLeftOperand, cchRight, wzRightOperand, cchRight);
956 + break;
957 + case BURN_SYMBOL_TYPE_LOEQ:
958 + case BURN_SYMBOL_TYPE_LOEQ_I:
959 + // test if left string ends with right string
960 + *pfResult = cchLeft >= cchRight && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, dwCompareString, wzLeftOperand + (cchLeft - cchRight), cchRight, wzRightOperand, cchRight);
961 + break;
962 + default:
963 + ExitFunction1(hr = E_INVALIDARG);
964 + }
965 +
966 +LExit:
967 + return hr;
968 +}
969 +
970 +//
971 +// CompareIntegerValues - compares two integer values using a given comparison.
972 +//
973 +static HRESULT CompareIntegerValues(
974 + __in BURN_SYMBOL_TYPE comparison,
975 + __in LONGLONG llLeftOperand,
976 + __in LONGLONG llRightOperand,
977 + __out BOOL* pfResult
978 + )
979 +{
980 + HRESULT hr = S_OK;
981 +
982 + switch (comparison)
983 + {
984 + case BURN_SYMBOL_TYPE_LT: case BURN_SYMBOL_TYPE_LT_I: *pfResult = llLeftOperand < llRightOperand; break;
985 + case BURN_SYMBOL_TYPE_GT: case BURN_SYMBOL_TYPE_GT_I: *pfResult = llLeftOperand > llRightOperand; break;
986 + case BURN_SYMBOL_TYPE_LE: case BURN_SYMBOL_TYPE_LE_I: *pfResult = llLeftOperand <= llRightOperand; break;
987 + case BURN_SYMBOL_TYPE_GE: case BURN_SYMBOL_TYPE_GE_I: *pfResult = llLeftOperand >= llRightOperand; break;
988 + case BURN_SYMBOL_TYPE_EQ: case BURN_SYMBOL_TYPE_EQ_I: *pfResult = llLeftOperand == llRightOperand; break;
989 + case BURN_SYMBOL_TYPE_NE: case BURN_SYMBOL_TYPE_NE_I: *pfResult = llLeftOperand != llRightOperand; break;
990 + case BURN_SYMBOL_TYPE_BAND: case BURN_SYMBOL_TYPE_BAND_I: *pfResult = (llLeftOperand & llRightOperand) ? TRUE : FALSE; break;
991 + case BURN_SYMBOL_TYPE_HIEQ: case BURN_SYMBOL_TYPE_HIEQ_I: *pfResult = ((llLeftOperand >> 16) & 0xFFFF) == llRightOperand; break;
992 + case BURN_SYMBOL_TYPE_LOEQ: case BURN_SYMBOL_TYPE_LOEQ_I: *pfResult = (llLeftOperand & 0xFFFF) == llRightOperand; break;
993 + default:
994 + ExitFunction1(hr = E_INVALIDARG);
995 + }
996 +
997 +LExit:
998 + return hr;
999 +}
1000 +
1001 +//
1002 +// CompareVersionValues - compares two quad-word version values using a given comparison.
1003 +//
1004 +static HRESULT CompareVersionValues(
1005 + __in BURN_SYMBOL_TYPE comparison,
1006 + __in DWORD64 qwLeftOperand,
1007 + __in DWORD64 qwRightOperand,
1008 + __out BOOL* pfResult
1009 + )
1010 +{
1011 + HRESULT hr = S_OK;
1012 +
1013 + switch (comparison)
1014 + {
1015 + case BURN_SYMBOL_TYPE_LT: *pfResult = qwLeftOperand < qwRightOperand; break;
1016 + case BURN_SYMBOL_TYPE_GT: *pfResult = qwLeftOperand > qwRightOperand; break;
1017 + case BURN_SYMBOL_TYPE_LE: *pfResult = qwLeftOperand <= qwRightOperand; break;
1018 + case BURN_SYMBOL_TYPE_GE: *pfResult = qwLeftOperand >= qwRightOperand; break;
1019 + case BURN_SYMBOL_TYPE_EQ: *pfResult = qwLeftOperand == qwRightOperand; break;
1020 + case BURN_SYMBOL_TYPE_NE: *pfResult = qwLeftOperand != qwRightOperand; break;
1021 + case BURN_SYMBOL_TYPE_BAND: *pfResult = (qwLeftOperand & qwRightOperand) ? TRUE : FALSE; break;
1022 + case BURN_SYMBOL_TYPE_HIEQ: *pfResult = ((qwLeftOperand >> 16) & 0xFFFF) == qwRightOperand; break;
1023 + case BURN_SYMBOL_TYPE_LOEQ: *pfResult = (qwLeftOperand & 0xFFFF) == qwRightOperand; break;
1024 + default:
1025 + ExitFunction1(hr = E_INVALIDARG);
1026 + }
1027 +
1028 +LExit:
1029 + return hr;
1030 +}
src/engine/condition.h new
+39
@@ -0,0 +1,39 @@
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 +#if defined(__cplusplus)
6 +extern "C" {
7 +#endif
8 +
9 +
10 +typedef struct _BURN_CONDITION
11 +{
12 + // The is an expression a condition string to fire the built-in "need newer OS" message
13 + LPWSTR sczConditionString;
14 +} BURN_CONDITION;
15 +
16 +
17 +// function declarations
18 +
19 +HRESULT ConditionEvaluate(
20 + __in BURN_VARIABLES* pVariables,
21 + __in_z LPCWSTR wzCondition,
22 + __out BOOL* pf
23 + );
24 +HRESULT ConditionGlobalCheck(
25 + __in BURN_VARIABLES* pVariables,
26 + __in BURN_CONDITION* pBlock,
27 + __in BOOTSTRAPPER_DISPLAY display,
28 + __in_z LPCWSTR wzBundleName,
29 + __out DWORD *pdwExitCode,
30 + __out BOOL *pfContinueExecution
31 + );
32 +HRESULT ConditionGlobalParseFromXml(
33 + __in BURN_CONDITION* pBlock,
34 + __in IXMLDOMNode* pixnBundle
35 + );
36 +
37 +#if defined(__cplusplus)
38 +}
39 +#endif
src/engine/container.cpp new
+386
@@ -0,0 +1,386 @@
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 +// internal function declarations
7 +
8 +static HRESULT GetAttachedContainerInfo(
9 + __in HANDLE hFile,
10 + __in DWORD iContainerIndex,
11 + __out DWORD* pdwFormat,
12 + __out DWORD64* pqwOffset,
13 + __out DWORD64* pqwSize
14 + );
15 +
16 +
17 +// function definitions
18 +
19 +extern "C" HRESULT ContainersParseFromXml(
20 + __in BURN_SECTION* pSection,
21 + __in BURN_CONTAINERS* pContainers,
22 + __in IXMLDOMNode* pixnBundle
23 + )
24 +{
25 + HRESULT hr = S_OK;
26 + IXMLDOMNodeList* pixnNodes = NULL;
27 + IXMLDOMNode* pixnNode = NULL;
28 + DWORD cNodes = 0;
29 + LPWSTR scz = NULL;
30 +
31 + // select container nodes
32 + hr = XmlSelectNodes(pixnBundle, L"Container", &pixnNodes);
33 + ExitOnFailure(hr, "Failed to select container nodes.");
34 +
35 + // get container node count
36 + hr = pixnNodes->get_length((long*)&cNodes);
37 + ExitOnFailure(hr, "Failed to get container node count.");
38 +
39 + if (!cNodes)
40 + {
41 + ExitFunction();
42 + }
43 +
44 + // allocate memory for searches
45 + pContainers->rgContainers = (BURN_CONTAINER*)MemAlloc(sizeof(BURN_CONTAINER) * cNodes, TRUE);
46 + ExitOnNull(pContainers->rgContainers, hr, E_OUTOFMEMORY, "Failed to allocate memory for container structs.");
47 +
48 + pContainers->cContainers = cNodes;
49 +
50 + // parse search elements
51 + for (DWORD i = 0; i < cNodes; ++i)
52 + {
53 + BURN_CONTAINER* pContainer = &pContainers->rgContainers[i];
54 +
55 + hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
56 + ExitOnFailure(hr, "Failed to get next node.");
57 +
58 + // TODO: Read type from manifest. Today only CABINET is supported.
59 + pContainer->type = BURN_CONTAINER_TYPE_CABINET;
60 +
61 + // @Id
62 + hr = XmlGetAttributeEx(pixnNode, L"Id", &pContainer->sczId);
63 + ExitOnFailure(hr, "Failed to get @Id.");
64 +
65 + // @Primary
66 + hr = XmlGetYesNoAttribute(pixnNode, L"Primary", &pContainer->fPrimary);
67 + if (E_NOTFOUND != hr)
68 + {
69 + ExitOnFailure(hr, "Failed to get @Primary.");
70 + }
71 +
72 + // @Attached
73 + hr = XmlGetYesNoAttribute(pixnNode, L"Attached", &pContainer->fAttached);
74 + if (E_NOTFOUND != hr || pContainer->fPrimary) // if it is a primary container, it has to be attached
75 + {
76 + ExitOnFailure(hr, "Failed to get @Attached.");
77 + }
78 +
79 + // @AttachedIndex
80 + hr = XmlGetAttributeNumber(pixnNode, L"AttachedIndex", &pContainer->dwAttachedIndex);
81 + if (E_NOTFOUND != hr || pContainer->fAttached) // if it is an attached container it must have an index
82 + {
83 + ExitOnFailure(hr, "Failed to get @AttachedIndex.");
84 + }
85 +
86 + // Attached containers are always found attached to the current process, so use the current proccess's
87 + // name instead of what may be in the manifest.
88 + if (pContainer->fAttached)
89 + {
90 + hr = PathForCurrentProcess(&scz, NULL);
91 + ExitOnFailure(hr, "Failed to get path to current process for attached container.");
92 +
93 + LPCWSTR wzFileName = PathFile(scz);
94 +
95 + hr = StrAllocString(&pContainer->sczFilePath, wzFileName, 0);
96 + ExitOnFailure(hr, "Failed to set attached container file path.");
97 + }
98 + else
99 + {
100 + // @FilePath
101 + hr = XmlGetAttributeEx(pixnNode, L"FilePath", &pContainer->sczFilePath);
102 + if (E_NOTFOUND != hr)
103 + {
104 + ExitOnFailure(hr, "Failed to get @FilePath.");
105 + }
106 + }
107 +
108 + // The source path starts as the file path.
109 + hr = StrAllocString(&pContainer->sczSourcePath, pContainer->sczFilePath, 0);
110 + ExitOnFailure(hr, "Failed to copy @FilePath");
111 +
112 + // @DownloadUrl
113 + hr = XmlGetAttributeEx(pixnNode, L"DownloadUrl", &pContainer->downloadSource.sczUrl);
114 + if (E_NOTFOUND != hr || (!pContainer->fPrimary && !pContainer->sczSourcePath)) // if the package is not a primary package, it must have a source path or a download url
115 + {
116 + ExitOnFailure(hr, "Failed to get @DownloadUrl. Either @SourcePath or @DownloadUrl needs to be provided.");
117 + }
118 +
119 + // @Hash
120 + hr = XmlGetAttributeEx(pixnNode, L"Hash", &pContainer->sczHash);
121 + if (SUCCEEDED(hr))
122 + {
123 + hr = StrAllocHexDecode(pContainer->sczHash, &pContainer->pbHash, &pContainer->cbHash);
124 + ExitOnFailure(hr, "Failed to hex decode the Container/@Hash.");
125 + }
126 + else if (E_NOTFOUND != hr)
127 + {
128 + ExitOnFailure(hr, "Failed to get @Hash.");
129 + }
130 +
131 + // If the container is attached, make sure the information in the section matches what the
132 + // manifest contained and get the offset to the container.
133 + if (pContainer->fAttached)
134 + {
135 + hr = SectionGetAttachedContainerInfo(pSection, pContainer->dwAttachedIndex, pContainer->type, &pContainer->qwAttachedOffset, &pContainer->qwFileSize, &pContainer->fActuallyAttached);
136 + ExitOnFailure(hr, "Failed to get attached container information.");
137 + }
138 +
139 + // prepare next iteration
140 + ReleaseNullObject(pixnNode);
141 + }
142 +
143 + hr = S_OK;
144 +
145 +LExit:
146 + ReleaseObject(pixnNodes);
147 + ReleaseObject(pixnNode);
148 + ReleaseStr(scz);
149 +
150 + return hr;
151 +}
152 +
153 +extern "C" void ContainersUninitialize(
154 + __in BURN_CONTAINERS* pContainers
155 + )
156 +{
157 + if (pContainers->rgContainers)
158 + {
159 + for (DWORD i = 0; i < pContainers->cContainers; ++i)
160 + {
161 + BURN_CONTAINER* pContainer = &pContainers->rgContainers[i];
162 +
163 + ReleaseStr(pContainer->sczId);
164 + ReleaseStr(pContainer->sczHash);
165 + ReleaseStr(pContainer->sczSourcePath);
166 + ReleaseStr(pContainer->sczFilePath);
167 + ReleaseMem(pContainer->pbHash);
168 + ReleaseStr(pContainer->downloadSource.sczUrl);
169 + ReleaseStr(pContainer->downloadSource.sczUser);
170 + ReleaseStr(pContainer->downloadSource.sczPassword);
171 + }
172 + MemFree(pContainers->rgContainers);
173 + }
174 +
175 + // clear struct
176 + memset(pContainers, 0, sizeof(BURN_CONTAINERS));
177 +}
178 +
179 +extern "C" HRESULT ContainerOpenUX(
180 + __in BURN_SECTION* pSection,
181 + __in BURN_CONTAINER_CONTEXT* pContext
182 + )
183 +{
184 + HRESULT hr = S_OK;
185 + BURN_CONTAINER container = { };
186 + LPWSTR sczExecutablePath = NULL;
187 +
188 + // open attached container
189 + container.type = BURN_CONTAINER_TYPE_CABINET;
190 + container.fPrimary = TRUE;
191 + container.fAttached = TRUE;
192 + container.dwAttachedIndex = 0;
193 +
194 + hr = SectionGetAttachedContainerInfo(pSection, container.dwAttachedIndex, container.type, &container.qwAttachedOffset, &container.qwFileSize, &container.fActuallyAttached);
195 + ExitOnFailure(hr, "Failed to get container information for UX container.");
196 +
197 + AssertSz(container.fActuallyAttached, "The BA container must always be found attached.");
198 +
199 + hr = PathForCurrentProcess(&sczExecutablePath, NULL);
200 + ExitOnFailure(hr, "Failed to get path for executing module.");
201 +
202 + hr = ContainerOpen(pContext, &container, pSection->hEngineFile, sczExecutablePath);
203 + ExitOnFailure(hr, "Failed to open attached container.");
204 +
205 +LExit:
206 + ReleaseStr(sczExecutablePath);
207 +
208 + return hr;
209 +}
210 +
211 +extern "C" HRESULT ContainerOpen(
212 + __in BURN_CONTAINER_CONTEXT* pContext,
213 + __in BURN_CONTAINER* pContainer,
214 + __in HANDLE hContainerFile,
215 + __in_z LPCWSTR wzFilePath
216 + )
217 +{
218 + HRESULT hr = S_OK;
219 + LARGE_INTEGER li = { };
220 +
221 + // initialize context
222 + pContext->type = pContainer->type;
223 + pContext->qwSize = pContainer->qwFileSize;
224 + pContext->qwOffset = pContainer->qwAttachedOffset;
225 +
226 + // If the handle to the container is not open already, open container file
227 + if (INVALID_HANDLE_VALUE == hContainerFile)
228 + {
229 + pContext->hFile = ::CreateFileW(wzFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
230 + ExitOnInvalidHandleWithLastError(pContext->hFile, hr, "Failed to open file: %ls", wzFilePath);
231 + }
232 + else // use the container file handle.
233 + {
234 + if (!::DuplicateHandle(::GetCurrentProcess(), hContainerFile, ::GetCurrentProcess(), &pContext->hFile, 0, FALSE, DUPLICATE_SAME_ACCESS))
235 + {
236 + ExitWithLastError(hr, "Failed to duplicate handle to container: %ls", wzFilePath);
237 + }
238 + }
239 +
240 + // If it is a container attached to an executable, seek to the container offset.
241 + if (pContainer->fAttached)
242 + {
243 + li.QuadPart = (LONGLONG)pContext->qwOffset;
244 + }
245 +
246 + if (!::SetFilePointerEx(pContext->hFile, li, NULL, FILE_BEGIN))
247 + {
248 + ExitWithLastError(hr, "Failed to move file pointer to container offset.");
249 + }
250 +
251 + // open the archive
252 + switch (pContext->type)
253 + {
254 + case BURN_CONTAINER_TYPE_CABINET:
255 + hr = CabExtractOpen(pContext, wzFilePath);
256 + break;
257 + }
258 + ExitOnFailure(hr, "Failed to open container.");
259 +
260 +LExit:
261 + return hr;
262 +}
263 +
264 +extern "C" HRESULT ContainerNextStream(
265 + __in BURN_CONTAINER_CONTEXT* pContext,
266 + __inout_z LPWSTR* psczStreamName
267 + )
268 +{
269 + HRESULT hr = S_OK;
270 +
271 + switch (pContext->type)
272 + {
273 + case BURN_CONTAINER_TYPE_CABINET:
274 + hr = CabExtractNextStream(pContext, psczStreamName);
275 + break;
276 + }
277 +
278 +//LExit:
279 + return hr;
280 +}
281 +
282 +extern "C" HRESULT ContainerStreamToFile(
283 + __in BURN_CONTAINER_CONTEXT* pContext,
284 + __in_z LPCWSTR wzFileName
285 + )
286 +{
287 + HRESULT hr = S_OK;
288 +
289 + switch (pContext->type)
290 + {
291 + case BURN_CONTAINER_TYPE_CABINET:
292 + hr = CabExtractStreamToFile(pContext, wzFileName);
293 + break;
294 + }
295 +
296 +//LExit:
297 + return hr;
298 +}
299 +
300 +extern "C" HRESULT ContainerStreamToBuffer(
301 + __in BURN_CONTAINER_CONTEXT* pContext,
302 + __out BYTE** ppbBuffer,
303 + __out SIZE_T* pcbBuffer
304 + )
305 +{
306 + HRESULT hr = S_OK;
307 +
308 + switch (pContext->type)
309 + {
310 + case BURN_CONTAINER_TYPE_CABINET:
311 + hr = CabExtractStreamToBuffer(pContext, ppbBuffer, pcbBuffer);
312 + break;
313 + }
314 +
315 +//LExit:
316 + return hr;
317 +}
318 +
319 +extern "C" HRESULT ContainerSkipStream(
320 + __in BURN_CONTAINER_CONTEXT* pContext
321 + )
322 +{
323 + HRESULT hr = S_OK;
324 +
325 + switch (pContext->type)
326 + {
327 + case BURN_CONTAINER_TYPE_CABINET:
328 + hr = CabExtractSkipStream(pContext);
329 + break;
330 + }
331 +
332 +//LExit:
333 + return hr;
334 +}
335 +
336 +extern "C" HRESULT ContainerClose(
337 + __in BURN_CONTAINER_CONTEXT* pContext
338 + )
339 +{
340 + HRESULT hr = S_OK;
341 +
342 + // close container
343 + switch (pContext->type)
344 + {
345 + case BURN_CONTAINER_TYPE_CABINET:
346 + hr = CabExtractClose(pContext);
347 + ExitOnFailure(hr, "Failed to close cabinet.");
348 + break;
349 + }
350 +
351 +LExit:
352 + ReleaseFile(pContext->hFile);
353 +
354 + if (SUCCEEDED(hr))
355 + {
356 + memset(pContext, 0, sizeof(BURN_CONTAINER_CONTEXT));
357 + }
358 +
359 + return hr;
360 +}
361 +
362 +extern "C" HRESULT ContainerFindById(
363 + __in BURN_CONTAINERS* pContainers,
364 + __in_z LPCWSTR wzId,
365 + __out BURN_CONTAINER** ppContainer
366 + )
367 +{
368 + HRESULT hr = S_OK;
369 + BURN_CONTAINER* pContainer = NULL;
370 +
371 + for (DWORD i = 0; i < pContainers->cContainers; ++i)
372 + {
373 + pContainer = &pContainers->rgContainers[i];
374 +
375 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pContainer->sczId, -1, wzId, -1))
376 + {
377 + *ppContainer = pContainer;
378 + ExitFunction1(hr = S_OK);
379 + }
380 + }
381 +
382 + hr = E_NOTFOUND;
383 +
384 +LExit:
385 + return hr;
386 +}
src/engine/container.h new
+183
@@ -0,0 +1,183 @@
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 +#if defined(__cplusplus)
6 +extern "C" {
7 +#endif
8 +
9 +
10 +// typedefs
11 +
12 +//typedef HRESULT (*PFN_EXTRACTOPEN)(
13 +// __in HANDLE hFile,
14 +// __in DWORD64 qwOffset,
15 +// __in DWORD64 qwSize,
16 +// __out void** ppCookie
17 +// );
18 +//typedef HRESULT (*PFN_EXTRACTNEXTSTREAM)(
19 +// __in void* pCookie,
20 +// __inout_z LPWSTR* psczStreamName
21 +// );
22 +//typedef HRESULT (*PFN_EXTRACTSTREAMTOFILE)(
23 +// __in void* pCookie,
24 +// __in_z LPCWSTR wzFileName
25 +// );
26 +//typedef HRESULT (*PFN_EXTRACTSTREAMTOBUFFER)(
27 +// __in void* pCookie,
28 +// __out BYTE** ppbBuffer,
29 +// __out SIZE_T* pcbBuffer
30 +// );
31 +//typedef HRESULT (*PFN_EXTRACTCLOSE)(
32 +// __in void* pCookie
33 +// );
34 +
35 +
36 +// constants
37 +
38 +enum BURN_CONTAINER_TYPE
39 +{
40 + BURN_CONTAINER_TYPE_NONE,
41 + BURN_CONTAINER_TYPE_CABINET,
42 + BURN_CONTAINER_TYPE_SEVENZIP,
43 +};
44 +
45 +enum BURN_CAB_OPERATION
46 +{
47 + BURN_CAB_OPERATION_NONE,
48 + BURN_CAB_OPERATION_NEXT_STREAM,
49 + BURN_CAB_OPERATION_STREAM_TO_FILE,
50 + BURN_CAB_OPERATION_STREAM_TO_BUFFER,
51 + BURN_CAB_OPERATION_SKIP_STREAM,
52 + BURN_CAB_OPERATION_CLOSE,
53 +};
54 +
55 +
56 +// structs
57 +
58 +typedef struct _BURN_CONTAINER
59 +{
60 + LPWSTR sczId;
61 + BURN_CONTAINER_TYPE type;
62 + BOOL fPrimary;
63 + BOOL fAttached;
64 + DWORD dwAttachedIndex;
65 + DWORD64 qwFileSize;
66 + LPWSTR sczHash;
67 + LPWSTR sczFilePath; // relative path to container.
68 + LPWSTR sczSourcePath;
69 + DOWNLOAD_SOURCE downloadSource;
70 +
71 + BYTE* pbHash;
72 + DWORD cbHash;
73 + DWORD64 qwAttachedOffset;
74 + BOOL fActuallyAttached; // indicates whether an attached container is attached or missing.
75 +
76 + //LPWSTR* rgsczPayloads;
77 + //DWORD cPayloads;
78 +} BURN_CONTAINER;
79 +
80 +typedef struct _BURN_CONTAINERS
81 +{
82 + BURN_CONTAINER* rgContainers;
83 + DWORD cContainers;
84 +} BURN_CONTAINERS;
85 +
86 +typedef struct _BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER
87 +{
88 + HANDLE hFile;
89 + LARGE_INTEGER liPosition;
90 +} BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER;
91 +
92 +typedef struct _BURN_CONTAINER_CONTEXT_CABINET
93 +{
94 + LPWSTR sczFile;
95 +
96 + HANDLE hThread;
97 + HANDLE hBeginOperationEvent;
98 + HANDLE hOperationCompleteEvent;
99 +
100 + BURN_CAB_OPERATION operation;
101 + HRESULT hrError;
102 +
103 + LPWSTR* psczStreamName;
104 + LPCWSTR wzTargetFile;
105 + HANDLE hTargetFile;
106 + BYTE* pbTargetBuffer;
107 + DWORD cbTargetBuffer;
108 + DWORD iTargetBuffer;
109 +
110 + BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* rgVirtualFilePointers;
111 + DWORD cVirtualFilePointers;
112 +} BURN_CONTAINER_CONTEXT_CABINET;
113 +
114 +typedef struct _BURN_CONTAINER_CONTEXT
115 +{
116 + HANDLE hFile;
117 + DWORD64 qwOffset;
118 + DWORD64 qwSize;
119 +
120 + //PFN_EXTRACTOPEN pfnExtractOpen;
121 + //PFN_EXTRACTNEXTSTREAM pfnExtractNextStream;
122 + //PFN_EXTRACTSTREAMTOFILE pfnExtractStreamToFile;
123 + //PFN_EXTRACTSTREAMTOBUFFER pfnExtractStreamToBuffer;
124 + //PFN_EXTRACTCLOSE pfnExtractClose;
125 + //void* pCookie;
126 + BURN_CONTAINER_TYPE type;
127 + union
128 + {
129 + BURN_CONTAINER_CONTEXT_CABINET Cabinet;
130 + };
131 +
132 +} BURN_CONTAINER_CONTEXT;
133 +
134 +
135 +// functions
136 +
137 +HRESULT ContainersParseFromXml(
138 + __in BURN_SECTION* pSection,
139 + __in BURN_CONTAINERS* pContainers,
140 + __in IXMLDOMNode* pixnBundle
141 + );
142 +void ContainersUninitialize(
143 + __in BURN_CONTAINERS* pContainers
144 + );
145 +HRESULT ContainerOpenUX(
146 + __in BURN_SECTION* pSection,
147 + __in BURN_CONTAINER_CONTEXT* pContext
148 + );
149 +HRESULT ContainerOpen(
150 + __in BURN_CONTAINER_CONTEXT* pContext,
151 + __in BURN_CONTAINER* pContainer,
152 + __in HANDLE hContainerFile,
153 + __in_z LPCWSTR wzFilePath
154 + );
155 +HRESULT ContainerNextStream(
156 + __inout BURN_CONTAINER_CONTEXT* pContext,
157 + __inout_z LPWSTR* psczStreamName
158 + );
159 +HRESULT ContainerStreamToFile(
160 + __in BURN_CONTAINER_CONTEXT* pContext,
161 + __in_z LPCWSTR wzFileName
162 + );
163 +HRESULT ContainerStreamToBuffer(
164 + __in BURN_CONTAINER_CONTEXT* pContext,
165 + __out BYTE** ppbBuffer,
166 + __out SIZE_T* pcbBuffer
167 + );
168 +HRESULT ContainerSkipStream(
169 + __in BURN_CONTAINER_CONTEXT* pContext
170 + );
171 +HRESULT ContainerClose(
172 + __in BURN_CONTAINER_CONTEXT* pContext
173 + );
174 +HRESULT ContainerFindById(
175 + __in BURN_CONTAINERS* pContainers,
176 + __in_z LPCWSTR wzId,
177 + __out BURN_CONTAINER** ppContainer
178 + );
179 +
180 +
181 +#if defined(__cplusplus)
182 +}
183 +#endif
src/engine/core.cpp new
+1705
@@ -0,0 +1,1705 @@
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 +// structs
7 +
8 +struct BURN_CACHE_THREAD_CONTEXT
9 +{
10 + BURN_ENGINE_STATE* pEngineState;
11 + DWORD* pcOverallProgressTicks;
12 + BOOL* pfRollback;
13 +};
14 +
15 +
16 +// internal function declarations
17 +
18 +static HRESULT ParseCommandLine(
19 + __in int argc,
20 + __in LPWSTR* argv,
21 + __in BOOTSTRAPPER_COMMAND* pCommand,
22 + __in BURN_PIPE_CONNECTION* pCompanionConnection,
23 + __in BURN_PIPE_CONNECTION* pEmbeddedConnection,
24 + __in BURN_VARIABLES* pVariables,
25 + __out BURN_MODE* pMode,
26 + __out BURN_AU_PAUSE_ACTION* pAutomaticUpdates,
27 + __out BOOL* pfDisableSystemRestore,
28 + __out_z LPWSTR* psczSourceProcessPath,
29 + __out_z LPWSTR* psczOriginalSource,
30 + __out BOOL* pfDisableUnelevate,
31 + __out DWORD *pdwLoggingAttributes,
32 + __out_z LPWSTR* psczLogFile,
33 + __out_z LPWSTR* psczActiveParent,
34 + __out_z LPWSTR* psczIgnoreDependencies,
35 + __out_z LPWSTR* psczAncestors,
36 + __out_z LPWSTR* psczSanitizedCommandLine
37 + );
38 +static HRESULT ParsePipeConnection(
39 + __in LPWSTR* rgArgs,
40 + __in BURN_PIPE_CONNECTION* pConnection
41 + );
42 +static HRESULT DetectPackage(
43 + __in BURN_ENGINE_STATE* pEngineState,
44 + __in BURN_PACKAGE* pPackage
45 + );
46 +static HRESULT DetectPackagePayloadsCached(
47 + __in BURN_PACKAGE* pPackage
48 + );
49 +static DWORD WINAPI CacheThreadProc(
50 + __in LPVOID lpThreadParameter
51 + );
52 +static HRESULT WaitForCacheThread(
53 + __in HANDLE hCacheThread
54 + );
55 +static void LogPackages(
56 + __in_opt const BURN_PACKAGE* pUpgradeBundlePackage,
57 + __in_opt const BURN_PACKAGE* pForwardCompatibleBundlePackage,
58 + __in const BURN_PACKAGES* pPackages,
59 + __in const BURN_RELATED_BUNDLES* pRelatedBundles,
60 + __in const BOOTSTRAPPER_ACTION action
61 + );
62 +
63 +
64 +// function definitions
65 +
66 +extern "C" HRESULT CoreInitialize(
67 + __in BURN_ENGINE_STATE* pEngineState
68 + )
69 +{
70 + HRESULT hr = S_OK;
71 + LPWSTR sczSanitizedCommandLine = NULL;
72 + LPWSTR sczStreamName = NULL;
73 + BYTE* pbBuffer = NULL;
74 + SIZE_T cbBuffer = 0;
75 + BURN_CONTAINER_CONTEXT containerContext = { };
76 + BOOL fElevated = FALSE;
77 + LPWSTR sczSourceProcessPath = NULL;
78 + LPWSTR sczSourceProcessFolder = NULL;
79 + LPWSTR sczOriginalSource = NULL;
80 +
81 + // Initialize variables.
82 + hr = VariableInitialize(&pEngineState->variables);
83 + ExitOnFailure(hr, "Failed to initialize variables.");
84 +
85 + // Open attached UX container.
86 + hr = ContainerOpenUX(&pEngineState->section, &containerContext);
87 + ExitOnFailure(hr, "Failed to open attached UX container.");
88 +
89 + // Load manifest.
90 + hr = ContainerNextStream(&containerContext, &sczStreamName);
91 + ExitOnFailure(hr, "Failed to open manifest stream.");
92 +
93 + hr = ContainerStreamToBuffer(&containerContext, &pbBuffer, &cbBuffer);
94 + ExitOnFailure(hr, "Failed to get manifest stream from container.");
95 +
96 + hr = ManifestLoadXmlFromBuffer(pbBuffer, cbBuffer, pEngineState);
97 + ExitOnFailure(hr, "Failed to load manifest.");
98 +
99 + // Parse command line.
100 + hr = ParseCommandLine(pEngineState->argc, pEngineState->argv, &pEngineState->command, &pEngineState->companionConnection, &pEngineState->embeddedConnection, &pEngineState->variables, &pEngineState->mode, &pEngineState->automaticUpdates, &pEngineState->fDisableSystemRestore, &sczSourceProcessPath, &sczOriginalSource, &pEngineState->fDisableUnelevate, &pEngineState->log.dwAttributes, &pEngineState->log.sczPath, &pEngineState->registration.sczActiveParent, &pEngineState->sczIgnoreDependencies, &pEngineState->registration.sczAncestors, &sczSanitizedCommandLine);
101 + ExitOnFailure(hr, "Failed to parse command line.");
102 +
103 + LogId(REPORT_STANDARD, MSG_BURN_COMMAND_LINE, sczSanitizedCommandLine ? sczSanitizedCommandLine : L"");
104 +
105 + // Retain whether bundle was initially run elevated.
106 + ProcElevated(::GetCurrentProcess(), &fElevated);
107 +
108 + hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_ELEVATED, fElevated, TRUE);
109 + ExitOnFailure(hr, "Failed to overwrite the %ls built-in variable.", BURN_BUNDLE_ELEVATED);
110 +
111 + hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_UILEVEL, pEngineState->command.display, TRUE);
112 + ExitOnFailure(hr, "Failed to overwrite the %ls built-in variable.", BURN_BUNDLE_UILEVEL);
113 +
114 + if (sczSourceProcessPath)
115 + {
116 + hr = VariableSetLiteralString(&pEngineState->variables, BURN_BUNDLE_SOURCE_PROCESS_PATH, sczSourceProcessPath, TRUE);
117 + ExitOnFailure(hr, "Failed to set source process path variable.");
118 +
119 + hr = PathGetDirectory(sczSourceProcessPath, &sczSourceProcessFolder);
120 + ExitOnFailure(hr, "Failed to get source process folder from path.");
121 +
122 + hr = VariableSetLiteralString(&pEngineState->variables, BURN_BUNDLE_SOURCE_PROCESS_FOLDER, sczSourceProcessFolder, TRUE);
123 + ExitOnFailure(hr, "Failed to set source process folder variable.");
124 + }
125 +
126 + // Set BURN_BUNDLE_ORIGINAL_SOURCE, if it was passed in on the command line.
127 + // Needs to be done after ManifestLoadXmlFromBuffer.
128 + if (sczOriginalSource)
129 + {
130 + hr = VariableSetLiteralString(&pEngineState->variables, BURN_BUNDLE_ORIGINAL_SOURCE, sczOriginalSource, FALSE);
131 + ExitOnFailure(hr, "Failed to set original source variable.");
132 + }
133 +
134 + if (BURN_MODE_UNTRUSTED == pEngineState->mode || BURN_MODE_NORMAL == pEngineState->mode || BURN_MODE_EMBEDDED == pEngineState->mode)
135 + {
136 + hr = CacheInitialize(&pEngineState->registration, &pEngineState->variables, sczSourceProcessPath);
137 + ExitOnFailure(hr, "Failed to initialize internal cache functionality.");
138 + }
139 +
140 + // If we're not elevated then we'll be loading the bootstrapper application, so extract
141 + // the payloads from the BA container.
142 + if (BURN_MODE_NORMAL == pEngineState->mode || BURN_MODE_EMBEDDED == pEngineState->mode)
143 + {
144 + // Extract all UX payloads to working folder.
145 + hr = UserExperienceEnsureWorkingFolder(pEngineState->registration.sczId, &pEngineState->userExperience.sczTempDirectory);
146 + ExitOnFailure(hr, "Failed to get unique temporary folder for bootstrapper application.");
147 +
148 + hr = PayloadExtractFromContainer(&pEngineState->userExperience.payloads, NULL, &containerContext, pEngineState->userExperience.sczTempDirectory);
149 + ExitOnFailure(hr, "Failed to extract bootstrapper application payloads.");
150 +
151 + // Load the catalog files as soon as they are extracted.
152 + hr = CatalogLoadFromPayload(&pEngineState->catalogs, &pEngineState->userExperience.payloads);
153 + ExitOnFailure(hr, "Failed to load catalog files.");
154 + }
155 +
156 +LExit:
157 + ReleaseStr(sczOriginalSource);
158 + ReleaseStr(sczSourceProcessFolder);
159 + ReleaseStr(sczSourceProcessPath);
160 + ContainerClose(&containerContext);
161 + ReleaseStr(sczStreamName);
162 + ReleaseStr(sczSanitizedCommandLine);
163 + ReleaseMem(pbBuffer);
164 +
165 + return hr;
166 +}
167 +
168 +extern "C" HRESULT CoreSerializeEngineState(
169 + __in BURN_ENGINE_STATE* pEngineState,
170 + __inout BYTE** ppbBuffer,
171 + __inout SIZE_T* piBuffer
172 + )
173 +{
174 + HRESULT hr = S_OK;
175 +
176 + hr = VariableSerialize(&pEngineState->variables, TRUE, ppbBuffer, piBuffer);
177 + ExitOnFailure(hr, "Failed to serialize variables.");
178 +
179 +LExit:
180 + return hr;
181 +}
182 +
183 +extern "C" HRESULT CoreQueryRegistration(
184 + __in BURN_ENGINE_STATE* pEngineState
185 + )
186 +{
187 + HRESULT hr = S_OK;
188 + BYTE* pbBuffer = NULL;
189 + SIZE_T cbBuffer = 0;
190 + SIZE_T iBuffer = 0;
191 +
192 + // Detect if bundle is already installed.
193 + hr = RegistrationDetectInstalled(&pEngineState->registration, &pEngineState->registration.fInstalled);
194 + ExitOnFailure(hr, "Failed to detect bundle install state.");
195 +
196 + // detect resume type
197 + hr = RegistrationDetectResumeType(&pEngineState->registration, &pEngineState->command.resumeType);
198 + ExitOnFailure(hr, "Failed to detect resume type.");
199 +
200 + // If we have a resume mode that suggests the bundle might already be present, try to load any
201 + // previously stored state.
202 + if (BOOTSTRAPPER_RESUME_TYPE_INVALID < pEngineState->command.resumeType)
203 + {
204 + // load resume state
205 + hr = RegistrationLoadState(&pEngineState->registration, &pbBuffer, &cbBuffer);
206 + if (SUCCEEDED(hr))
207 + {
208 + hr = VariableDeserialize(&pEngineState->variables, TRUE, pbBuffer, cbBuffer, &iBuffer);
209 + }
210 +
211 + // Log any failures and continue.
212 + if (FAILED(hr))
213 + {
214 + LogId(REPORT_STANDARD, MSG_CANNOT_LOAD_STATE_FILE, hr, pEngineState->registration.sczStateFile);
215 + hr = S_OK;
216 + }
217 + }
218 +
219 +LExit:
220 + ReleaseBuffer(pbBuffer);
221 +
222 + return hr;
223 +}
224 +
225 +extern "C" HRESULT CoreDetect(
226 + __in BURN_ENGINE_STATE* pEngineState,
227 + __in_opt HWND hwndParent
228 + )
229 +{
230 + HRESULT hr = S_OK;
231 + BOOL fActivated = FALSE;
232 + BOOL fDetectBegan = FALSE;
233 + BURN_PACKAGE* pPackage = NULL;
234 + HRESULT hrFirstPackageFailure = S_OK;
235 +
236 + LogId(REPORT_STANDARD, MSG_DETECT_BEGIN, pEngineState->packages.cPackages);
237 +
238 + hr = UserExperienceActivateEngine(&pEngineState->userExperience, &fActivated);
239 + ExitOnFailure(hr, "Engine cannot start detect because it is busy with another action.");
240 +
241 + // Detect if bundle installed state has changed since start up. This
242 + // only happens if Apply() changed the state of bundle (installed or
243 + // uninstalled). In that case, Detect() can be used here to reset
244 + // the installed state.
245 + hr = RegistrationDetectInstalled(&pEngineState->registration, &pEngineState->registration.fInstalled);
246 + ExitOnFailure(hr, "Failed to detect bundle install state.");
247 +
248 + if (pEngineState->registration.fInstalled)
249 + {
250 + hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_INSTALLED, 1, TRUE);
251 + ExitOnFailure(hr, "Failed to set the bundle installed built-in variable.");
252 + }
253 + else
254 + {
255 + hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_INSTALLED, NULL, TRUE);
256 + ExitOnFailure(hr, "Failed to unset the bundle installed built-in variable.");
257 + }
258 +
259 + fDetectBegan = TRUE;
260 + hr = UserExperienceOnDetectBegin(&pEngineState->userExperience, pEngineState->registration.fInstalled, pEngineState->packages.cPackages);
261 + ExitOnRootFailure(hr, "UX aborted detect begin.");
262 +
263 + pEngineState->userExperience.hwndDetect = hwndParent;
264 +
265 + // Always reset the detect state which means the plan should be reset too.
266 + DetectReset(&pEngineState->registration, &pEngineState->packages);
267 + PlanReset(&pEngineState->plan, &pEngineState->packages);
268 +
269 + hr = SearchesExecute(&pEngineState->searches, &pEngineState->variables);
270 + ExitOnFailure(hr, "Failed to execute searches.");
271 +
272 + // Load all of the related bundles.
273 + hr = RegistrationDetectRelatedBundles(&pEngineState->registration);
274 + ExitOnFailure(hr, "Failed to detect related bundles.");
275 +
276 + hr = DependencyDetectProviderKeyBundleId(&pEngineState->registration);
277 + if (SUCCEEDED(hr))
278 + {
279 + hr = DetectForwardCompatibleBundle(&pEngineState->userExperience, &pEngineState->command, &pEngineState->registration);
280 + ExitOnFailure(hr, "Failed to detect forward compatible bundle.");
281 +
282 + // If a forward compatible bundle was detected, skip rest of bundle detection
283 + // since we will passthrough.
284 + if (pEngineState->registration.fEnabledForwardCompatibleBundle)
285 + {
286 + ExitFunction();
287 + }
288 + }
289 + else if (E_NOTFOUND == hr)
290 + {
291 + hr = S_OK;
292 + }
293 + ExitOnFailure(hr, "Failed to detect provider key bundle id.");
294 +
295 + // Report the related bundles.
296 + hr = DetectReportRelatedBundles(&pEngineState->userExperience, &pEngineState->registration, pEngineState->command.relationType, pEngineState->command.action);
297 + ExitOnFailure(hr, "Failed to report detected related bundles.");
298 +
299 + // Do update detection.
300 + hr = DetectUpdate(pEngineState->registration.sczId, &pEngineState->userExperience, &pEngineState->update);
301 + ExitOnFailure(hr, "Failed to detect update.");
302 +
303 + // Detecting MSPs requires special initialization before processing each package but
304 + // only do the detection if there are actually patch packages to detect because it
305 + // can be expensive.
306 + if (pEngineState->packages.cPatchInfo)
307 + {
308 + hr = MspEngineDetectInitialize(&pEngineState->packages);
309 + ExitOnFailure(hr, "Failed to initialize MSP engine detection.");
310 + }
311 +
312 + for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i)
313 + {
314 + pPackage = pEngineState->packages.rgPackages + i;
315 +
316 + hr = DetectPackage(pEngineState, pPackage);
317 +
318 + // If the package detection failed, ensure the package state is set to unknown.
319 + if (FAILED(hr))
320 + {
321 + if (SUCCEEDED(hrFirstPackageFailure))
322 + {
323 + hrFirstPackageFailure = hr;
324 + }
325 +
326 + pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN;
327 + }
328 + }
329 +
330 + // Log the detected states.
331 + for (DWORD iPackage = 0; iPackage < pEngineState->packages.cPackages; ++iPackage)
332 + {
333 + pPackage = pEngineState->packages.rgPackages + iPackage;
334 +
335 + LogId(REPORT_STANDARD, MSG_DETECTED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingCacheStateToString(pPackage->cache));
336 +
337 + if (BURN_PACKAGE_TYPE_MSI == pPackage->type)
338 + {
339 + for (DWORD iFeature = 0; iFeature < pPackage->Msi.cFeatures; ++iFeature)
340 + {
341 + const BURN_MSIFEATURE* pFeature = pPackage->Msi.rgFeatures + iFeature;
342 + LogId(REPORT_STANDARD, MSG_DETECTED_MSI_FEATURE, pPackage->sczId, pFeature->sczId, LoggingMsiFeatureStateToString(pFeature->currentState));
343 + }
344 + }
345 + else if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
346 + {
347 + for (DWORD iTargetProduct = 0; iTargetProduct < pPackage->Msp.cTargetProductCodes; ++iTargetProduct)
348 + {
349 + const BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + iTargetProduct;
350 + LogId(REPORT_STANDARD, MSG_DETECTED_MSP_TARGET, pPackage->sczId, pTargetProduct->wzTargetProductCode, LoggingPackageStateToString(pTargetProduct->patchPackageState));
351 + }
352 + }
353 + }
354 +
355 +LExit:
356 + if (SUCCEEDED(hr))
357 + {
358 + hr = hrFirstPackageFailure;
359 + }
360 +
361 + if (fActivated)
362 + {
363 + UserExperienceDeactivateEngine(&pEngineState->userExperience);
364 + }
365 +
366 + if (fDetectBegan)
367 + {
368 + UserExperienceOnDetectComplete(&pEngineState->userExperience, hr);
369 + }
370 +
371 + pEngineState->userExperience.hwndDetect = NULL;
372 +
373 + LogId(REPORT_STANDARD, MSG_DETECT_COMPLETE, hr);
374 +
375 + return hr;
376 +}
377 +
378 +extern "C" HRESULT CorePlan(
379 + __in BURN_ENGINE_STATE* pEngineState,
380 + __in BOOTSTRAPPER_ACTION action
381 + )
382 +{
383 + HRESULT hr = S_OK;
384 + BOOL fActivated = FALSE;
385 + BOOL fPlanBegan = FALSE;
386 + LPWSTR sczLayoutDirectory = NULL;
387 + HANDLE hSyncpointEvent = NULL;
388 + BURN_PACKAGE* pUpgradeBundlePackage = NULL;
389 + BURN_PACKAGE* pForwardCompatibleBundlePackage = NULL;
390 +
391 + LogId(REPORT_STANDARD, MSG_PLAN_BEGIN, pEngineState->packages.cPackages, LoggingBurnActionToString(action));
392 +
393 + hr = UserExperienceActivateEngine(&pEngineState->userExperience, &fActivated);
394 + ExitOnFailure(hr, "Engine cannot start plan because it is busy with another action.");
395 +
396 + fPlanBegan = TRUE;
397 + hr = UserExperienceOnPlanBegin(&pEngineState->userExperience, pEngineState->packages.cPackages);
398 + ExitOnRootFailure(hr, "BA aborted plan begin.");
399 +
400 + // Always reset the plan.
401 + PlanReset(&pEngineState->plan, &pEngineState->packages);
402 +
403 + // Remember the overall action state in the plan since it shapes the changes
404 + // we make everywhere.
405 + pEngineState->plan.action = action;
406 + pEngineState->plan.wzBundleId = pEngineState->registration.sczId;
407 + pEngineState->plan.wzBundleProviderKey = pEngineState->registration.sczId;
408 +
409 + hr = PlanSetVariables(action, &pEngineState->variables);
410 + ExitOnFailure(hr, "Failed to update action.");
411 +
412 + // Set resume commandline
413 + hr = PlanSetResumeCommand(&pEngineState->registration, action, &pEngineState->command, &pEngineState->log);
414 + ExitOnFailure(hr, "Failed to set resume command");
415 +
416 + hr = DependencyPlanInitialize(pEngineState, &pEngineState->plan);
417 + ExitOnFailure(hr, "Failed to initialize the dependencies for the plan.");
418 +
419 + if (BOOTSTRAPPER_ACTION_LAYOUT == action)
420 + {
421 + Assert(!pEngineState->plan.fPerMachine);
422 +
423 + // Plan the bundle's layout.
424 + hr = PlanLayoutBundle(&pEngineState->plan, pEngineState->registration.sczExecutableName, pEngineState->section.qwBundleSize, &pEngineState->variables, &pEngineState->payloads, &sczLayoutDirectory);
425 + ExitOnFailure(hr, "Failed to plan the layout of the bundle.");
426 +
427 + // Plan the packages' layout.
428 + hr = PlanPackages(&pEngineState->registration, &pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, FALSE, pEngineState->command.display, pEngineState->command.relationType, sczLayoutDirectory, &hSyncpointEvent);
429 + ExitOnFailure(hr, "Failed to plan packages.");
430 + }
431 + else if (BOOTSTRAPPER_ACTION_UPDATE_REPLACE == action || BOOTSTRAPPER_ACTION_UPDATE_REPLACE_EMBEDDED == action)
432 + {
433 + Assert(!pEngineState->plan.fPerMachine);
434 +
435 + pUpgradeBundlePackage = &pEngineState->update.package;
436 +
437 + hr = PlanUpdateBundle(&pEngineState->userExperience, pUpgradeBundlePackage, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType, &hSyncpointEvent);
438 + ExitOnFailure(hr, "Failed to plan update.");
439 + }
440 + else if (pEngineState->registration.fEnabledForwardCompatibleBundle)
441 + {
442 + Assert(!pEngineState->plan.fPerMachine);
443 +
444 + pForwardCompatibleBundlePackage = &pEngineState->registration.forwardCompatibleBundle;
445 +
446 + hr = PlanPassThroughBundle(&pEngineState->userExperience, pForwardCompatibleBundlePackage, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType, &hSyncpointEvent);
447 + ExitOnFailure(hr, "Failed to plan passthrough.");
448 + }
449 + else // doing an action that modifies the machine state.
450 + {
451 + BOOL fContinuePlanning = TRUE; // assume we'll be able to keep planning after registration.
452 + pEngineState->plan.fPerMachine = pEngineState->registration.fPerMachine; // default the scope of the plan to the per-machine state of the bundle.
453 +
454 + hr = PlanRegistration(&pEngineState->plan, &pEngineState->registration, pEngineState->command.resumeType, pEngineState->command.relationType, pEngineState->sczIgnoreDependencies, &fContinuePlanning);
455 + ExitOnFailure(hr, "Failed to plan registration.");
456 +
457 + if (fContinuePlanning)
458 + {
459 + // Remember the early index, because we want to be able to insert some related bundles
460 + // into the plan before other executed packages. This particularly occurs for uninstallation
461 + // of addons and patches, which should be uninstalled before the main product.
462 + DWORD dwExecuteActionEarlyIndex = pEngineState->plan.cExecuteActions;
463 +
464 + // Plan the related bundles first to support downgrades with ref-counting.
465 + hr = PlanRelatedBundlesBegin(&pEngineState->userExperience, &pEngineState->registration, pEngineState->command.relationType, &pEngineState->plan);
466 + ExitOnFailure(hr, "Failed to plan related bundles.");
467 +
468 + hr = PlanPackages(&pEngineState->registration, &pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->registration.fInstalled, pEngineState->command.display, pEngineState->command.relationType, NULL, &hSyncpointEvent);
469 + ExitOnFailure(hr, "Failed to plan packages.");
470 +
471 + // Schedule the update of related bundles last.
472 + hr = PlanRelatedBundlesComplete(&pEngineState->registration, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, &hSyncpointEvent, dwExecuteActionEarlyIndex);
473 + ExitOnFailure(hr, "Failed to schedule related bundles.");
474 + }
475 + }
476 +
477 + // Remove unnecessary actions.
478 + hr = PlanFinalizeActions(&pEngineState->plan);
479 + ExitOnFailure(hr, "Failed to remove unnecessary actions from plan.");
480 +
481 + // Finally, display all packages and related bundles in the log.
482 + LogPackages(pUpgradeBundlePackage, pForwardCompatibleBundlePackage, &pEngineState->packages, &pEngineState->registration.relatedBundles, action);
483 +
484 +#ifdef DEBUG
485 + PlanDump(&pEngineState->plan);
486 +#endif
487 +
488 +LExit:
489 + if (fActivated)
490 + {
491 + UserExperienceDeactivateEngine(&pEngineState->userExperience);
492 + }
493 +
494 + if (fPlanBegan)
495 + {
496 + UserExperienceOnPlanComplete(&pEngineState->userExperience, hr);
497 + }
498 +
499 + LogId(REPORT_STANDARD, MSG_PLAN_COMPLETE, hr);
500 + ReleaseStr(sczLayoutDirectory);
501 +
502 + return hr;
503 +}
504 +
505 +extern "C" HRESULT CoreElevate(
506 + __in BURN_ENGINE_STATE* pEngineState,
507 + __in_opt HWND hwndParent
508 + )
509 +{
510 + HRESULT hr = S_OK;
511 +
512 + // If the elevated companion pipe isn't created yet, let's make that happen.
513 + if (INVALID_HANDLE_VALUE == pEngineState->companionConnection.hPipe)
514 + {
515 + if (!pEngineState->sczBundleEngineWorkingPath)
516 + {
517 + hr = CacheBundleToWorkingDirectory(pEngineState->registration.sczId, pEngineState->registration.sczExecutableName, &pEngineState->userExperience.payloads, &pEngineState->section, &pEngineState->sczBundleEngineWorkingPath);
518 + ExitOnFailure(hr, "Failed to cache engine to working directory.");
519 + }
520 +
521 + hr = ElevationElevate(pEngineState, hwndParent);
522 + ExitOnFailure(hr, "Failed to actually elevate.");
523 +
524 + hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_ELEVATED, TRUE, TRUE);
525 + ExitOnFailure(hr, "Failed to overwrite the %ls built-in variable.", BURN_BUNDLE_ELEVATED);
526 + }
527 +
528 +LExit:
529 + return hr;
530 +}
531 +
532 +extern "C" HRESULT CoreApply(
533 + __in BURN_ENGINE_STATE* pEngineState,
534 + __in_opt HWND hwndParent
535 + )
536 +{
537 + HRESULT hr = S_OK;
538 + BOOL fActivated = FALSE;
539 + HANDLE hLock = NULL;
540 + DWORD cOverallProgressTicks = 0;
541 + HANDLE hCacheThread = NULL;
542 + BOOL fElevated = FALSE;
543 + BOOL fRegistered = FALSE;
544 + BOOL fKeepRegistration = pEngineState->plan.fKeepRegistrationDefault;
545 + BOOL fRollback = FALSE;
546 + BOOL fSuspend = FALSE;
547 + BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE;
548 + BURN_CACHE_THREAD_CONTEXT cacheThreadContext = { };
549 + DWORD dwPhaseCount = 0;
550 + BOOTSTRAPPER_APPLYCOMPLETE_ACTION applyCompleteAction = BOOTSTRAPPER_APPLYCOMPLETE_ACTION_NONE;
551 +
552 + LogId(REPORT_STANDARD, MSG_APPLY_BEGIN);
553 +
554 + hr = UserExperienceActivateEngine(&pEngineState->userExperience, &fActivated);
555 + ExitOnFailure(hr, "Engine cannot start apply because it is busy with another action.");
556 +
557 + // Ensure any previous attempts to execute are reset.
558 + ApplyReset(&pEngineState->userExperience, &pEngineState->packages);
559 +
560 + if (pEngineState->plan.cCacheActions)
561 + {
562 + ++dwPhaseCount;
563 + }
564 + if (pEngineState->plan.cExecuteActions)
565 + {
566 + ++dwPhaseCount;
567 + }
568 +
569 + hr = UserExperienceOnApplyBegin(&pEngineState->userExperience, dwPhaseCount);
570 + ExitOnRootFailure(hr, "BA aborted apply begin.");
571 +
572 + // Abort if this bundle already requires a restart.
573 + if (BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING == pEngineState->command.resumeType)
574 + {
575 + restart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED;
576 +
577 + hr = HRESULT_FROM_WIN32(ERROR_FAIL_NOACTION_REBOOT);
578 + UserExperienceSendError(&pEngineState->userExperience, BOOTSTRAPPER_ERROR_TYPE_APPLY, NULL, hr, NULL, MB_ICONERROR | MB_OK, IDNOACTION); // ignore return value.
579 + ExitFunction();
580 + }
581 +
582 + hr = ApplyLock(FALSE, &hLock);
583 + ExitOnFailure(hr, "Another per-user setup is already executing.");
584 +
585 + // Initialize only after getting a lock.
586 + ApplyInitialize();
587 +
588 + pEngineState->userExperience.hwndApply = hwndParent;
589 +
590 + hr = ApplySetVariables(&pEngineState->variables);
591 + ExitOnFailure(hr, "Failed to set initial apply variables.");
592 +
593 + // If the plan is empty of work to do, skip everything.
594 + if (!(pEngineState->plan.cRegistrationActions || pEngineState->plan.cCacheActions || pEngineState->plan.cExecuteActions || pEngineState->plan.cCleanActions))
595 + {
596 + LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED);
597 + ExitFunction();
598 + }
599 +
600 + // Ensure the engine is cached to the working path.
601 + if (!pEngineState->sczBundleEngineWorkingPath)
602 + {
603 + hr = CacheBundleToWorkingDirectory(pEngineState->registration.sczId, pEngineState->registration.sczExecutableName, &pEngineState->userExperience.payloads, &pEngineState->section, &pEngineState->sczBundleEngineWorkingPath);
604 + ExitOnFailure(hr, "Failed to cache engine to working directory.");
605 + }
606 +
607 + // Elevate.
608 + if (pEngineState->plan.fPerMachine)
609 + {
610 + hr = CoreElevate(pEngineState, pEngineState->userExperience.hwndApply);
611 + ExitOnFailure(hr, "Failed to elevate.");
612 +
613 + hr = ElevationApplyInitialize(pEngineState->companionConnection.hPipe, &pEngineState->variables, pEngineState->plan.action, pEngineState->automaticUpdates, !pEngineState->fDisableSystemRestore);
614 + ExitOnFailure(hr, "Another per-machine setup is already executing.");
615 +
616 + fElevated = TRUE;
617 + }
618 +
619 + // Register.
620 + if (pEngineState->plan.fRegister)
621 + {
622 + hr = ApplyRegister(pEngineState);
623 + ExitOnFailure(hr, "Failed to register bundle.");
624 + fRegistered = TRUE;
625 + }
626 +
627 + // Cache.
628 + if (pEngineState->plan.cCacheActions)
629 + {
630 + // Launch the cache thread.
631 + cacheThreadContext.pEngineState = pEngineState;
632 + cacheThreadContext.pcOverallProgressTicks = &cOverallProgressTicks;
633 + cacheThreadContext.pfRollback = &fRollback;
634 +
635 + hCacheThread = ::CreateThread(NULL, 0, CacheThreadProc, &cacheThreadContext, 0, NULL);
636 + ExitOnNullWithLastError(hCacheThread, hr, "Failed to create cache thread.");
637 +
638 + // If we're not caching in parallel, wait for the cache thread to terminate.
639 + if (!pEngineState->fParallelCacheAndExecute)
640 + {
641 + hr = WaitForCacheThread(hCacheThread);
642 + ExitOnFailure(hr, "Failed while caching, aborting execution.");
643 +
644 + ReleaseHandle(hCacheThread);
645 + }
646 + }
647 +
648 + // Execute.
649 + if (pEngineState->plan.cExecuteActions)
650 + {
651 + hr = ApplyExecute(pEngineState, hCacheThread, &cOverallProgressTicks, &fKeepRegistration, &fRollback, &fSuspend, &restart);
652 + UserExperienceExecutePhaseComplete(&pEngineState->userExperience, hr); // signal that execute completed.
653 + }
654 +
655 + // Wait for cache thread to terminate, this should return immediately unless we're waiting for layout to complete.
656 + if (hCacheThread)
657 + {
658 + HRESULT hrCached = WaitForCacheThread(hCacheThread);
659 + if (SUCCEEDED(hr))
660 + {
661 + hr = hrCached;
662 + }
663 + }
664 +
665 + // If something went wrong or force restarted, skip cleaning.
666 + if (FAILED(hr) || fRollback || fSuspend || BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart)
667 + {
668 + ExitFunction();
669 + }
670 +
671 + // Clean.
672 + if (pEngineState->plan.cCleanActions)
673 + {
674 + ApplyClean(&pEngineState->userExperience, &pEngineState->plan, pEngineState->companionConnection.hPipe);
675 + }
676 +
677 +LExit:
678 + // Unregister.
679 + if (fRegistered)
680 + {
681 + ApplyUnregister(pEngineState, FAILED(hr) || fRollback, fKeepRegistration || pEngineState->plan.fDisallowRemoval, fSuspend, restart);
682 + }
683 +
684 + if (fElevated)
685 + {
686 + ElevationApplyUninitialize(pEngineState->companionConnection.hPipe);
687 + }
688 +
689 + pEngineState->userExperience.hwndApply = NULL;
690 +
691 + ApplyUninitialize();
692 +
693 + if (hLock)
694 + {
695 + ::ReleaseMutex(hLock);
696 + ::CloseHandle(hLock);
697 + }
698 +
699 + if (fActivated)
700 + {
701 + UserExperienceDeactivateEngine(&pEngineState->userExperience);
702 + }
703 +
704 + ReleaseHandle(hCacheThread);
705 +
706 + UserExperienceOnApplyComplete(&pEngineState->userExperience, hr, restart, &applyCompleteAction);
707 + if (BOOTSTRAPPER_APPLYCOMPLETE_ACTION_RESTART == applyCompleteAction)
708 + {
709 + pEngineState->fRestart = TRUE;
710 + }
711 +
712 + LogId(REPORT_STANDARD, MSG_APPLY_COMPLETE, hr, LoggingRestartToString(restart), LoggingBoolToString(pEngineState->fRestart));
713 +
714 + return hr;
715 +}
716 +
717 +extern "C" HRESULT CoreLaunchApprovedExe(
718 + __in BURN_ENGINE_STATE* pEngineState,
719 + __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe
720 + )
721 +{
722 + HRESULT hr = S_OK;
723 + BOOL fActivated = FALSE;
724 + DWORD dwProcessId = 0;
725 +
726 + LogId(REPORT_STANDARD, MSG_LAUNCH_APPROVED_EXE_BEGIN, pLaunchApprovedExe->sczId);
727 +
728 + hr = UserExperienceActivateEngine(&pEngineState->userExperience, &fActivated);
729 + ExitOnFailure(hr, "Engine cannot start LaunchApprovedExe because it is busy with another action.");
730 +
731 + hr = UserExperienceOnLaunchApprovedExeBegin(&pEngineState->userExperience);
732 + ExitOnRootFailure(hr, "BA aborted LaunchApprovedExe begin.");
733 +
734 + // Elevate.
735 + hr = CoreElevate(pEngineState, pLaunchApprovedExe->hwndParent);
736 + ExitOnFailure(hr, "Failed to elevate.");
737 +
738 + // Launch.
739 + hr = ElevationLaunchApprovedExe(pEngineState->companionConnection.hPipe, pLaunchApprovedExe, &dwProcessId);
740 +
741 +LExit:
742 + if (fActivated)
743 + {
744 + UserExperienceDeactivateEngine(&pEngineState->userExperience);
745 + }
746 +
747 + UserExperienceOnLaunchApprovedExeComplete(&pEngineState->userExperience, hr, dwProcessId);
748 +
749 + LogId(REPORT_STANDARD, MSG_LAUNCH_APPROVED_EXE_COMPLETE, hr, dwProcessId);
750 +
751 + ApprovedExesUninitializeLaunch(pLaunchApprovedExe);
752 +
753 + return hr;
754 +}
755 +
756 +extern "C" HRESULT CoreQuit(
757 + __in BURN_ENGINE_STATE* pEngineState,
758 + __in int nExitCode
759 + )
760 +{
761 + HRESULT hr = S_OK;
762 +
763 + // Save engine state if resume mode is unequal to "none".
764 + if (BURN_RESUME_MODE_NONE != pEngineState->resumeMode)
765 + {
766 + hr = CoreSaveEngineState(pEngineState);
767 + if (FAILED(hr))
768 + {
769 + LogErrorId(hr, MSG_STATE_NOT_SAVED, NULL, NULL, NULL);
770 + hr = S_OK;
771 + }
772 + }
773 +
774 + LogId(REPORT_STANDARD, MSG_QUIT, nExitCode);
775 +
776 + ::PostQuitMessage(nExitCode); // go bye-bye.
777 +
778 + return hr;
779 +}
780 +
781 +extern "C" HRESULT CoreSaveEngineState(
782 + __in BURN_ENGINE_STATE* pEngineState
783 + )
784 +{
785 + HRESULT hr = S_OK;
786 + BYTE* pbBuffer = NULL;
787 + SIZE_T cbBuffer = 0;
788 +
789 + // serialize engine state
790 + hr = CoreSerializeEngineState(pEngineState, &pbBuffer, &cbBuffer);
791 + ExitOnFailure(hr, "Failed to serialize engine state.");
792 +
793 + // write to registration store
794 + if (pEngineState->registration.fPerMachine)
795 + {
796 + hr = ElevationSaveState(pEngineState->companionConnection.hPipe, pbBuffer, cbBuffer);
797 + ExitOnFailure(hr, "Failed to save engine state in per-machine process.");
798 + }
799 + else
800 + {
801 + hr = RegistrationSaveState(&pEngineState->registration, pbBuffer, cbBuffer);
802 + ExitOnFailure(hr, "Failed to save engine state.");
803 + }
804 +
805 +LExit:
806 + ReleaseBuffer(pbBuffer);
807 +
808 + return hr;
809 +}
810 +
811 +extern "C" LPCWSTR CoreRelationTypeToCommandLineString(
812 + __in BOOTSTRAPPER_RELATION_TYPE relationType
813 + )
814 +{
815 + LPCWSTR wzRelationTypeCommandLine = NULL;
816 + switch (relationType)
817 + {
818 + case BOOTSTRAPPER_RELATION_DETECT:
819 + wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_DETECT;
820 + break;
821 + case BOOTSTRAPPER_RELATION_UPGRADE:
822 + wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_UPGRADE;
823 + break;
824 + case BOOTSTRAPPER_RELATION_ADDON:
825 + wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_ADDON;
826 + break;
827 + case BOOTSTRAPPER_RELATION_PATCH:
828 + wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_PATCH;
829 + break;
830 + case BOOTSTRAPPER_RELATION_UPDATE:
831 + wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_UPDATE;
832 + break;
833 + case BOOTSTRAPPER_RELATION_DEPENDENT:
834 + break;
835 + case BOOTSTRAPPER_RELATION_NONE: __fallthrough;
836 + default:
837 + wzRelationTypeCommandLine = NULL;
838 + break;
839 + }
840 +
841 + return wzRelationTypeCommandLine;
842 +}
843 +
844 +extern "C" HRESULT CoreRecreateCommandLine(
845 + __deref_inout_z LPWSTR* psczCommandLine,
846 + __in BOOTSTRAPPER_ACTION action,
847 + __in BOOTSTRAPPER_DISPLAY display,
848 + __in BOOTSTRAPPER_RESTART restart,
849 + __in BOOTSTRAPPER_RELATION_TYPE relationType,
850 + __in BOOL fPassthrough,
851 + __in_z_opt LPCWSTR wzActiveParent,
852 + __in_z_opt LPCWSTR wzAncestors,
853 + __in_z_opt LPCWSTR wzAppendLogPath,
854 + __in_z_opt LPCWSTR wzAdditionalCommandLineArguments
855 + )
856 +{
857 + HRESULT hr = S_OK;
858 + LPWSTR scz = NULL;
859 + LPCWSTR wzRelationTypeCommandLine = CoreRelationTypeToCommandLineString(relationType);
860 +
861 + hr = StrAllocString(psczCommandLine, L"", 0);
862 + ExitOnFailure(hr, "Failed to empty command line.");
863 +
864 + switch (display)
865 + {
866 + case BOOTSTRAPPER_DISPLAY_NONE:
867 + hr = StrAllocConcat(psczCommandLine, L" /quiet", 0);
868 + break;
869 + case BOOTSTRAPPER_DISPLAY_PASSIVE:
870 + hr = StrAllocConcat(psczCommandLine, L" /passive", 0);
871 + break;
872 + }
873 + ExitOnFailure(hr, "Failed to append display state to command-line");
874 +
875 + switch (action)
876 + {
877 + case BOOTSTRAPPER_ACTION_MODIFY:
878 + hr = StrAllocConcat(psczCommandLine, L" /modify", 0);
879 + break;
880 + case BOOTSTRAPPER_ACTION_REPAIR:
881 + hr = StrAllocConcat(psczCommandLine, L" /repair", 0);
882 + break;
883 + case BOOTSTRAPPER_ACTION_UNINSTALL:
884 + hr = StrAllocConcat(psczCommandLine, L" /uninstall", 0);
885 + break;
886 + }
887 + ExitOnFailure(hr, "Failed to append action state to command-line");
888 +
889 + switch (restart)
890 + {
891 + case BOOTSTRAPPER_RESTART_ALWAYS:
892 + hr = StrAllocConcat(psczCommandLine, L" /forcerestart", 0);
893 + break;
894 + case BOOTSTRAPPER_RESTART_NEVER:
895 + hr = StrAllocConcat(psczCommandLine, L" /norestart", 0);
896 + break;
897 + }
898 + ExitOnFailure(hr, "Failed to append restart state to command-line");
899 +
900 + if (wzActiveParent)
901 + {
902 + if (*wzActiveParent)
903 + {
904 + hr = StrAllocFormatted(&scz, L" /%ls \"%ls\"", BURN_COMMANDLINE_SWITCH_PARENT, wzActiveParent);
905 + ExitOnFailure(hr, "Failed to format active parent command-line for command-line.");
906 + }
907 + else
908 + {
909 + hr = StrAllocFormatted(&scz, L" /%ls", BURN_COMMANDLINE_SWITCH_PARENT_NONE);
910 + ExitOnFailure(hr, "Failed to format parent:none command-line for command-line.");
911 + }
912 +
913 + hr = StrAllocConcat(psczCommandLine, scz, 0);
914 + ExitOnFailure(hr, "Failed to append active parent command-line to command-line.");
915 + }
916 +
917 + if (wzAncestors)
918 + {
919 + hr = StrAllocFormatted(&scz, L" /%ls=%ls", BURN_COMMANDLINE_SWITCH_ANCESTORS, wzAncestors);
920 + ExitOnFailure(hr, "Failed to format ancestors for command-line.");
921 +
922 + hr = StrAllocConcat(psczCommandLine, scz, 0);
923 + ExitOnFailure(hr, "Failed to append ancestors to command-line.");
924 + }
925 +
926 + if (wzRelationTypeCommandLine)
927 + {
928 + hr = StrAllocFormatted(&scz, L" /%ls", wzRelationTypeCommandLine);
929 + ExitOnFailure(hr, "Failed to format relation type for command-line.");
930 +
931 + hr = StrAllocConcat(psczCommandLine, scz, 0);
932 + ExitOnFailure(hr, "Failed to append relation type to command-line.");
933 + }
934 +
935 + if (fPassthrough)
936 + {
937 + hr = StrAllocFormatted(&scz, L" /%ls", BURN_COMMANDLINE_SWITCH_PASSTHROUGH);
938 + ExitOnFailure(hr, "Failed to format passthrough for command-line.");
939 +
940 + hr = StrAllocConcat(psczCommandLine, scz, 0);
941 + ExitOnFailure(hr, "Failed to append passthrough to command-line.");
942 + }
943 +
944 + if (wzAppendLogPath && *wzAppendLogPath)
945 + {
946 + hr = StrAllocFormatted(&scz, L" /%ls \"%ls\"", BURN_COMMANDLINE_SWITCH_LOG_APPEND, wzAppendLogPath);
947 + ExitOnFailure(hr, "Failed to format append log command-line for command-line.");
948 +
949 + hr = StrAllocConcat(psczCommandLine, scz, 0);
950 + ExitOnFailure(hr, "Failed to append log command-line to command-line");
951 + }
952 +
953 + if (wzAdditionalCommandLineArguments && *wzAdditionalCommandLineArguments)
954 + {
955 + hr = StrAllocConcat(psczCommandLine, L" ", 0);
956 + ExitOnFailure(hr, "Failed to append space to command-line.");
957 +
958 + hr = StrAllocConcat(psczCommandLine, wzAdditionalCommandLineArguments, 0);
959 + ExitOnFailure(hr, "Failed to append command-line to command-line.");
960 + }
961 +
962 +LExit:
963 + ReleaseStr(scz);
964 +
965 + return hr;
966 +}
967 +
968 +extern "C" HRESULT CoreAppendFileHandleAttachedToCommandLine(
969 + __in HANDLE hFileWithAttachedContainer,
970 + __out HANDLE* phExecutableFile,
971 + __deref_inout_z LPWSTR* psczCommandLine
972 + )
973 +{
974 + HRESULT hr = S_OK;
975 + HANDLE hExecutableFile = INVALID_HANDLE_VALUE;
976 +
977 + *phExecutableFile = INVALID_HANDLE_VALUE;
978 +
979 + if (!::DuplicateHandle(::GetCurrentProcess(), hFileWithAttachedContainer, ::GetCurrentProcess(), &hExecutableFile, 0, TRUE, DUPLICATE_SAME_ACCESS))
980 + {
981 + ExitWithLastError(hr, "Failed to duplicate file handle for attached container.");
982 + }
983 +
984 + hr = StrAllocFormattedSecure(psczCommandLine, L"%ls -%ls=%u", *psczCommandLine, BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED, hExecutableFile);
985 + ExitOnFailure(hr, "Failed to append the file handle to the command line.");
986 +
987 + *phExecutableFile = hExecutableFile;
988 + hExecutableFile = INVALID_HANDLE_VALUE;
989 +
990 +LExit:
991 + ReleaseFileHandle(hExecutableFile);
992 +
993 + return hr;
994 +}
995 +
996 +extern "C" HRESULT CoreAppendFileHandleSelfToCommandLine(
997 + __in LPCWSTR wzExecutablePath,
998 + __out HANDLE* phExecutableFile,
999 + __deref_inout_z LPWSTR* psczCommandLine,
1000 + __deref_inout_z_opt LPWSTR* psczObfuscatedCommandLine
1001 + )
1002 +{
1003 + HRESULT hr = S_OK;
1004 + HANDLE hExecutableFile = INVALID_HANDLE_VALUE;
1005 + SECURITY_ATTRIBUTES securityAttributes = { };
1006 + securityAttributes.bInheritHandle = TRUE;
1007 + *phExecutableFile = INVALID_HANDLE_VALUE;
1008 +
1009 + hExecutableFile = ::CreateFileW(wzExecutablePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, &securityAttributes, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1010 + if (INVALID_HANDLE_VALUE != hExecutableFile)
1011 + {
1012 + hr = StrAllocFormattedSecure(psczCommandLine, L"%ls -%ls=%u", *psczCommandLine, BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, hExecutableFile);
1013 + ExitOnFailure(hr, "Failed to append the file handle to the command line.");
1014 +
1015 + if (psczObfuscatedCommandLine)
1016 + {
1017 + hr = StrAllocFormatted(psczObfuscatedCommandLine, L"%ls -%ls=%u", *psczObfuscatedCommandLine, BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, hExecutableFile);
1018 + ExitOnFailure(hr, "Failed to append the file handle to the obfuscated command line.");
1019 + }
1020 +
1021 + *phExecutableFile = hExecutableFile;
1022 + hExecutableFile = INVALID_HANDLE_VALUE;
1023 + }
1024 +
1025 +LExit:
1026 + ReleaseFileHandle(hExecutableFile);
1027 +
1028 + return hr;
1029 +}
1030 +
1031 +// internal helper functions
1032 +
1033 +static HRESULT ParseCommandLine(
1034 + __in int argc,
1035 + __in LPWSTR* argv,
1036 + __in BOOTSTRAPPER_COMMAND* pCommand,
1037 + __in BURN_PIPE_CONNECTION* pCompanionConnection,
1038 + __in BURN_PIPE_CONNECTION* pEmbeddedConnection,
1039 + __in BURN_VARIABLES* pVariables,
1040 + __out BURN_MODE* pMode,
1041 + __out BURN_AU_PAUSE_ACTION* pAutomaticUpdates,
1042 + __out BOOL* pfDisableSystemRestore,
1043 + __out_z LPWSTR* psczSourceProcessPath,
1044 + __out_z LPWSTR* psczOriginalSource,
1045 + __out BOOL* pfDisableUnelevate,
1046 + __out DWORD *pdwLoggingAttributes,
1047 + __out_z LPWSTR* psczLogFile,
1048 + __out_z LPWSTR* psczActiveParent,
1049 + __out_z LPWSTR* psczIgnoreDependencies,
1050 + __out_z LPWSTR* psczAncestors,
1051 + __out_z LPWSTR* psczSanitizedCommandLine
1052 + )
1053 +{
1054 + HRESULT hr = S_OK;
1055 + BOOL fUnknownArg = FALSE;
1056 + BOOL fHidden = FALSE;
1057 + LPWSTR sczCommandLine = NULL;
1058 + LPWSTR sczSanitizedArgument = NULL;
1059 + LPWSTR sczVariableName = NULL;
1060 +
1061 + for (int i = 0; i < argc; ++i)
1062 + {
1063 + fUnknownArg = FALSE;
1064 + int originalIndex = i;
1065 + ReleaseNullStr(sczSanitizedArgument);
1066 +
1067 + if (argv[i][0] == L'-' || argv[i][0] == L'/')
1068 + {
1069 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"l", -1) ||
1070 + CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"log", -1))
1071 + {
1072 + *pdwLoggingAttributes &= ~BURN_LOGGING_ATTRIBUTE_APPEND;
1073 +
1074 + if (i + 1 >= argc)
1075 + {
1076 + ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a path for log.");
1077 + }
1078 +
1079 + ++i;
1080 +
1081 + hr = StrAllocString(psczLogFile, argv[i], 0);
1082 + ExitOnFailure(hr, "Failed to copy log file path.");
1083 + }
1084 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"?", -1) ||
1085 + CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"h", -1) ||
1086 + CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"help", -1))
1087 + {
1088 + pCommand->action = BOOTSTRAPPER_ACTION_HELP;
1089 + }
1090 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"q", -1) ||
1091 + CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"quiet", -1) ||
1092 + CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"s", -1) ||
1093 + CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"silent", -1))
1094 + {
1095 + pCommand->display = BOOTSTRAPPER_DISPLAY_NONE;
1096 +
1097 + if (BOOTSTRAPPER_RESTART_UNKNOWN == pCommand->restart)
1098 + {
1099 + pCommand->restart = BOOTSTRAPPER_RESTART_AUTOMATIC;
1100 + }
1101 + }
1102 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"passive", -1))
1103 + {
1104 + pCommand->display = BOOTSTRAPPER_DISPLAY_PASSIVE;
1105 +
1106 + if (BOOTSTRAPPER_RESTART_UNKNOWN == pCommand->restart)
1107 + {
1108 + pCommand->restart = BOOTSTRAPPER_RESTART_AUTOMATIC;
1109 + }
1110 + }
1111 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"norestart", -1))
1112 + {
1113 + pCommand->restart = BOOTSTRAPPER_RESTART_NEVER;
1114 + }
1115 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"forcerestart", -1))
1116 + {
1117 + pCommand->restart = BOOTSTRAPPER_RESTART_ALWAYS;
1118 + }
1119 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"promptrestart", -1))
1120 + {
1121 + pCommand->restart = BOOTSTRAPPER_RESTART_PROMPT;
1122 + }
1123 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"layout", -1))
1124 + {
1125 + if (BOOTSTRAPPER_ACTION_HELP != pCommand->action)
1126 + {
1127 + pCommand->action = BOOTSTRAPPER_ACTION_LAYOUT;
1128 + }
1129 +
1130 + // If there is another command line argument and it is not a switch, use that as the layout directory.
1131 + if (i + 1 < argc && argv[i + 1][0] != L'-' && argv[i + 1][0] != L'/')
1132 + {
1133 + ++i;
1134 +
1135 + hr = PathExpand(&pCommand->wzLayoutDirectory, argv[i], PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH);
1136 + ExitOnFailure(hr, "Failed to copy path for layout directory.");
1137 + }
1138 + }
1139 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"uninstall", -1))
1140 + {
1141 + if (BOOTSTRAPPER_ACTION_HELP != pCommand->action)
1142 + {
1143 + pCommand->action = BOOTSTRAPPER_ACTION_UNINSTALL;
1144 + }
1145 + }
1146 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"repair", -1))
1147 + {
1148 + if (BOOTSTRAPPER_ACTION_HELP != pCommand->action)
1149 + {
1150 + pCommand->action = BOOTSTRAPPER_ACTION_REPAIR;
1151 + }
1152 + }
1153 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"modify", -1))
1154 + {
1155 + if (BOOTSTRAPPER_ACTION_HELP != pCommand->action)
1156 + {
1157 + pCommand->action = BOOTSTRAPPER_ACTION_MODIFY;
1158 + }
1159 + }
1160 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"package", -1) ||
1161 + CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"update", -1))
1162 + {
1163 + if (BOOTSTRAPPER_ACTION_UNKNOWN == pCommand->action)
1164 + {
1165 + pCommand->action = BOOTSTRAPPER_ACTION_INSTALL;
1166 + }
1167 + }
1168 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"noaupause", -1))
1169 + {
1170 + *pAutomaticUpdates = BURN_AU_PAUSE_ACTION_NONE;
1171 + }
1172 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"keepaupaused", -1))
1173 + {
1174 + // Switch /noaupause takes precedence.
1175 + if (BURN_AU_PAUSE_ACTION_NONE != *pAutomaticUpdates)
1176 + {
1177 + *pAutomaticUpdates = BURN_AU_PAUSE_ACTION_IFELEVATED_NORESUME;
1178 + }
1179 + }
1180 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"disablesystemrestore", -1))
1181 + {
1182 + *pfDisableSystemRestore = TRUE;
1183 + }
1184 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"originalsource", -1))
1185 + {
1186 + if (i + 1 >= argc)
1187 + {
1188 + ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a path for original source.");
1189 + }
1190 +
1191 + ++i;
1192 + hr = StrAllocString(psczOriginalSource, argv[i], 0);
1193 + ExitOnFailure(hr, "Failed to copy last used source.");
1194 + }
1195 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_PARENT, -1))
1196 + {
1197 + if (i + 1 >= argc)
1198 + {
1199 + ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a value for parent.");
1200 + }
1201 +
1202 + ++i;
1203 +
1204 + hr = StrAllocString(psczActiveParent, argv[i], 0);
1205 + ExitOnFailure(hr, "Failed to copy parent.");
1206 + }
1207 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_PARENT_NONE, -1))
1208 + {
1209 + hr = StrAllocString(psczActiveParent, L"", 0);
1210 + ExitOnFailure(hr, "Failed to initialize parent to none.");
1211 + }
1212 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_LOG_APPEND, -1))
1213 + {
1214 + if (i + 1 >= argc)
1215 + {
1216 + ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a path for append log.");
1217 + }
1218 +
1219 + ++i;
1220 +
1221 + hr = StrAllocString(psczLogFile, argv[i], 0);
1222 + ExitOnFailure(hr, "Failed to copy append log file path.");
1223 +
1224 + *pdwLoggingAttributes |= BURN_LOGGING_ATTRIBUTE_APPEND;
1225 + }
1226 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_ELEVATED, -1))
1227 + {
1228 + if (i + 3 >= argc)
1229 + {
1230 + ExitOnRootFailure(hr = E_INVALIDARG, "Must specify the elevated name, token and parent process id.");
1231 + }
1232 +
1233 + if (BURN_MODE_UNTRUSTED != *pMode)
1234 + {
1235 + ExitOnRootFailure(hr = E_INVALIDARG, "Multiple mode command-line switches were provided.");
1236 + }
1237 +
1238 + *pMode = BURN_MODE_ELEVATED;
1239 +
1240 + ++i;
1241 +
1242 + hr = ParsePipeConnection(argv + i, pCompanionConnection);
1243 + ExitOnFailure(hr, "Failed to parse elevated connection.");
1244 +
1245 + i += 2;
1246 + }
1247 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_CLEAN_ROOM), BURN_COMMANDLINE_SWITCH_CLEAN_ROOM, lstrlenW(BURN_COMMANDLINE_SWITCH_CLEAN_ROOM)))
1248 + {
1249 + // Get a pointer to the next character after the switch.
1250 + LPCWSTR wzParam = &argv[i][1 + lstrlenW(BURN_COMMANDLINE_SWITCH_CLEAN_ROOM)];
1251 + if (L'=' != wzParam[0] || L'\0' == wzParam[1])
1252 + {
1253 + ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_CLEAN_ROOM);
1254 + }
1255 +
1256 + if (BURN_MODE_UNTRUSTED != *pMode)
1257 + {
1258 + ExitOnRootFailure(hr = E_INVALIDARG, "Multiple mode command-line switches were provided.");
1259 + }
1260 +
1261 + *pMode = BURN_MODE_NORMAL;
1262 +
1263 + hr = StrAllocString(psczSourceProcessPath, wzParam + 1, 0);
1264 + ExitOnFailure(hr, "Failed to copy source process path.");
1265 + }
1266 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_EMBEDDED, -1))
1267 + {
1268 + if (i + 3 >= argc)
1269 + {
1270 + ExitOnRootFailure(hr = E_INVALIDARG, "Must specify the embedded name, token and parent process id.");
1271 + }
1272 +
1273 + switch (*pMode)
1274 + {
1275 + case BURN_MODE_UNTRUSTED:
1276 + // Leave mode as UNTRUSTED to launch the clean room process.
1277 + break;
1278 + case BURN_MODE_NORMAL:
1279 + // The initialization code already assumes that the
1280 + // clean room switch is at the beginning of the command line,
1281 + // so it's safe to assume that the mode is NORMAL in the clean room.
1282 + *pMode = BURN_MODE_EMBEDDED;
1283 + break;
1284 + default:
1285 + ExitOnRootFailure(hr = E_INVALIDARG, "Multiple mode command-line switches were provided.");
1286 + }
1287 +
1288 + ++i;
1289 +
1290 + hr = ParsePipeConnection(argv + i, pEmbeddedConnection);
1291 + ExitOnFailure(hr, "Failed to parse embedded connection.");
1292 +
1293 + i += 2;
1294 + }
1295 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_DETECT, -1))
1296 + {
1297 + pCommand->relationType = BOOTSTRAPPER_RELATION_DETECT;
1298 +
1299 + LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType));
1300 + }
1301 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_UPGRADE, -1))
1302 + {
1303 + pCommand->relationType = BOOTSTRAPPER_RELATION_UPGRADE;
1304 +
1305 + LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType));
1306 + }
1307 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_ADDON, -1))
1308 + {
1309 + pCommand->relationType = BOOTSTRAPPER_RELATION_ADDON;
1310 +
1311 + LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType));
1312 + }
1313 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_PATCH, -1))
1314 + {
1315 + pCommand->relationType = BOOTSTRAPPER_RELATION_PATCH;
1316 +
1317 + LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType));
1318 + }
1319 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_UPDATE, -1))
1320 + {
1321 + pCommand->relationType = BOOTSTRAPPER_RELATION_UPDATE;
1322 +
1323 + LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType));
1324 + }
1325 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_PASSTHROUGH, -1))
1326 + {
1327 + pCommand->fPassthrough = TRUE;
1328 + }
1329 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_DISABLE_UNELEVATE, -1))
1330 + {
1331 + *pfDisableUnelevate = TRUE;
1332 + }
1333 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RUNONCE, -1))
1334 + {
1335 + if (BURN_MODE_UNTRUSTED != *pMode)
1336 + {
1337 + ExitOnRootFailure(hr = E_INVALIDARG, "Multiple mode command-line switches were provided.");
1338 + }
1339 +
1340 + *pMode = BURN_MODE_RUNONCE;
1341 + }
1342 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES), BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, lstrlenW(BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES)))
1343 + {
1344 + // Get a pointer to the next character after the switch.
1345 + LPCWSTR wzParam = &argv[i][1 + lstrlenW(BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES)];
1346 + if (L'=' != wzParam[0] || L'\0' == wzParam[1])
1347 + {
1348 + ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES);
1349 + }
1350 +
1351 + hr = StrAllocString(psczIgnoreDependencies, &wzParam[1], 0);
1352 + ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore.");
1353 + }
1354 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_ANCESTORS), BURN_COMMANDLINE_SWITCH_ANCESTORS, lstrlenW(BURN_COMMANDLINE_SWITCH_ANCESTORS)))
1355 + {
1356 + // Get a pointer to the next character after the switch.
1357 + LPCWSTR wzParam = &argv[i][1 + lstrlenW(BURN_COMMANDLINE_SWITCH_ANCESTORS)];
1358 + if (L'=' != wzParam[0] || L'\0' == wzParam[1])
1359 + {
1360 + ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_ANCESTORS);
1361 + }
1362 +
1363 + hr = StrAllocString(psczAncestors, &wzParam[1], 0);
1364 + ExitOnFailure(hr, "Failed to allocate the list of ancestors.");
1365 + }
1366 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED), BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED, lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED)))
1367 + {
1368 + // Already processed in InitializeEngineState.
1369 + }
1370 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF), BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF)))
1371 + {
1372 + // Already processed in InitializeEngineState.
1373 + }
1374 + else if (lstrlenW(&argv[i][1]) >= lstrlenW(BURN_COMMANDLINE_SWITCH_PREFIX) &&
1375 + CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_PREFIX), BURN_COMMANDLINE_SWITCH_PREFIX, lstrlenW(BURN_COMMANDLINE_SWITCH_PREFIX)))
1376 + {
1377 + // Skip (but log) any other private burn switches we don't recognize, so that
1378 + // adding future private variables doesn't break old bundles
1379 + LogId(REPORT_STANDARD, MSG_BURN_UNKNOWN_PRIVATE_SWITCH, &argv[i][1]);
1380 + }
1381 + else
1382 + {
1383 + fUnknownArg = TRUE;
1384 + }
1385 + }
1386 + else
1387 + {
1388 + fUnknownArg = TRUE;
1389 +
1390 + const wchar_t* pwc = wcschr(argv[i], L'=');
1391 + if (pwc)
1392 + {
1393 + hr = StrAllocString(&sczVariableName, argv[i], pwc - argv[i]);
1394 + ExitOnFailure(hr, "Failed to copy variable name.");
1395 +
1396 + hr = VariableIsHidden(pVariables, sczVariableName, &fHidden);
1397 + ExitOnFailure(hr, "Failed to determine whether variable is hidden.");
1398 +
1399 + if (fHidden)
1400 + {
1401 + hr = StrAllocFormatted(&sczSanitizedArgument, L"%ls=*****", sczVariableName);
1402 + ExitOnFailure(hr, "Failed to copy sanitized argument.");
1403 + }
1404 + }
1405 + }
1406 +
1407 + // Remember command-line switch to pass off to UX.
1408 + if (fUnknownArg)
1409 + {
1410 + PathCommandLineAppend(&pCommand->wzCommandLine, argv[i]);
1411 + }
1412 +
1413 + if (sczSanitizedArgument)
1414 + {
1415 + PathCommandLineAppend(psczSanitizedCommandLine, sczSanitizedArgument);
1416 + }
1417 + else
1418 + {
1419 + for (; originalIndex <= i; ++originalIndex)
1420 + {
1421 + PathCommandLineAppend(psczSanitizedCommandLine, argv[originalIndex]);
1422 + }
1423 + }
1424 + }
1425 +
1426 + // If embedded, ensure the display goes embedded as well.
1427 + if (BURN_MODE_EMBEDDED == *pMode)
1428 + {
1429 + pCommand->display = BOOTSTRAPPER_DISPLAY_EMBEDDED;
1430 + }
1431 +
1432 + // Set the defaults if nothing was set above.
1433 + if (BOOTSTRAPPER_ACTION_UNKNOWN == pCommand->action)
1434 + {
1435 + pCommand->action = BOOTSTRAPPER_ACTION_INSTALL;
1436 + }
1437 +
1438 + if (BOOTSTRAPPER_DISPLAY_UNKNOWN == pCommand->display)
1439 + {
1440 + pCommand->display = BOOTSTRAPPER_DISPLAY_FULL;
1441 + }
1442 +
1443 + if (BOOTSTRAPPER_RESTART_UNKNOWN == pCommand->restart)
1444 + {
1445 + pCommand->restart = BOOTSTRAPPER_RESTART_PROMPT;
1446 + }
1447 +
1448 +LExit:
1449 + ReleaseStr(sczVariableName);
1450 + ReleaseStr(sczSanitizedArgument);
1451 + ReleaseStr(sczCommandLine);
1452 +
1453 + return hr;
1454 +}
1455 +
1456 +static HRESULT ParsePipeConnection(
1457 + __in_ecount(3) LPWSTR* rgArgs,
1458 + __in BURN_PIPE_CONNECTION* pConnection
1459 + )
1460 +{
1461 + HRESULT hr = S_OK;
1462 +
1463 + hr = StrAllocString(&pConnection->sczName, rgArgs[0], 0);
1464 + ExitOnFailure(hr, "Failed to copy connection name from command line.");
1465 +
1466 + hr = StrAllocString(&pConnection->sczSecret, rgArgs[1], 0);
1467 + ExitOnFailure(hr, "Failed to copy connection secret from command line.");
1468 +
1469 + hr = StrStringToUInt32(rgArgs[2], 0, reinterpret_cast<UINT*>(&pConnection->dwProcessId));
1470 + ExitOnFailure(hr, "Failed to copy parent process id from command line.");
1471 +
1472 +LExit:
1473 + return hr;
1474 +}
1475 +
1476 +static HRESULT DetectPackage(
1477 + __in BURN_ENGINE_STATE* pEngineState,
1478 + __in BURN_PACKAGE* pPackage
1479 + )
1480 +{
1481 + HRESULT hr = S_OK;
1482 + BOOL fBegan = FALSE;
1483 +
1484 + fBegan = TRUE;
1485 + hr = UserExperienceOnDetectPackageBegin(&pEngineState->userExperience, pPackage->sczId);
1486 + ExitOnRootFailure(hr, "BA aborted detect package begin.");
1487 +
1488 + // Detect the cache state of the package.
1489 + hr = DetectPackagePayloadsCached(pPackage);
1490 + ExitOnFailure(hr, "Failed to detect if payloads are all cached for package: %ls", pPackage->sczId);
1491 +
1492 + // Use the correct engine to detect the package.
1493 + switch (pPackage->type)
1494 + {
1495 + case BURN_PACKAGE_TYPE_EXE:
1496 + hr = ExeEngineDetectPackage(pPackage, &pEngineState->variables);
1497 + break;
1498 +
1499 + case BURN_PACKAGE_TYPE_MSI:
1500 + hr = MsiEngineDetectPackage(pPackage, &pEngineState->userExperience);
1501 + break;
1502 +
1503 + case BURN_PACKAGE_TYPE_MSP:
1504 + hr = MspEngineDetectPackage(pPackage, &pEngineState->userExperience);
1505 + break;
1506 +
1507 + case BURN_PACKAGE_TYPE_MSU:
1508 + hr = MsuEngineDetectPackage(pPackage, &pEngineState->variables);
1509 + break;
1510 +
1511 + default:
1512 + hr = E_NOTIMPL;
1513 + ExitOnRootFailure(hr, "Package type not supported by detect yet.");
1514 + }
1515 +
1516 + // TODO: consider how to notify the UX that a package is cached.
1517 + //else if (BOOTSTRAPPER_PACKAGE_STATE_CACHED > pPackage->currentState && pPackage->fCached)
1518 + //{
1519 + // pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_CACHED;
1520 + //}
1521 +
1522 +LExit:
1523 + if (FAILED(hr))
1524 + {
1525 + LogErrorId(hr, MSG_FAILED_DETECT_PACKAGE, pPackage->sczId, NULL, NULL);
1526 + }
1527 +
1528 + if (fBegan)
1529 + {
1530 + UserExperienceOnDetectPackageComplete(&pEngineState->userExperience, pPackage->sczId, hr, pPackage->currentState);
1531 + }
1532 +
1533 + return hr;
1534 +}
1535 +
1536 +static HRESULT DetectPackagePayloadsCached(
1537 + __in BURN_PACKAGE* pPackage
1538 + )
1539 +{
1540 + HRESULT hr = S_OK;
1541 + LPWSTR sczCachePath = NULL;
1542 + BURN_CACHE_STATE cache = BURN_CACHE_STATE_NONE; // assume the package will not be cached.
1543 + LPWSTR sczPayloadCachePath = NULL;
1544 + LONGLONG llSize = 0;
1545 +
1546 + if (pPackage->sczCacheId && *pPackage->sczCacheId)
1547 + {
1548 + hr = CacheGetCompletedPath(pPackage->fPerMachine, pPackage->sczCacheId, &sczCachePath);
1549 + ExitOnFailure(hr, "Failed to get completed cache path.");
1550 +
1551 + // If the cached directory exists, we have something.
1552 + if (DirExists(sczCachePath, NULL))
1553 + {
1554 + cache = BURN_CACHE_STATE_COMPLETE; // assume all payloads are cached.
1555 +
1556 + // Check all payloads to see if any are missing or not the right size.
1557 + for (DWORD i = 0; i < pPackage->cPayloads; ++i)
1558 + {
1559 + BURN_PACKAGE_PAYLOAD* pPackagePayload = pPackage->rgPayloads + i;
1560 +
1561 + hr = PathConcat(sczCachePath, pPackagePayload->pPayload->sczFilePath, &sczPayloadCachePath);
1562 + ExitOnFailure(hr, "Failed to concat payload cache path.");
1563 +
1564 + hr = FileSize(sczPayloadCachePath, &llSize);
1565 + if (SUCCEEDED(hr) && static_cast<DWORD64>(llSize) != pPackagePayload->pPayload->qwFileSize)
1566 + {
1567 + hr = HRESULT_FROM_WIN32(ERROR_FILE_CORRUPT); // size did not match expectations, so cache must have the wrong file.
1568 + }
1569 +
1570 + if (SUCCEEDED(hr))
1571 + {
1572 + // TODO: should we do a full on hash verification on the file to ensure
1573 + // the exact right file is cached?
1574 +
1575 + pPackagePayload->fCached = TRUE;
1576 + }
1577 + else
1578 + {
1579 + LogId(REPORT_STANDARD, MSG_DETECT_PACKAGE_NOT_FULLY_CACHED, pPackage->sczId, pPackagePayload->pPayload->sczKey, hr);
1580 +
1581 + cache = BURN_CACHE_STATE_PARTIAL; // found a payload that was not cached so we are partial.
1582 + hr = S_OK;
1583 + }
1584 + }
1585 + }
1586 + }
1587 +
1588 + pPackage->cache = cache;
1589 +
1590 +LExit:
1591 + ReleaseStr(sczPayloadCachePath);
1592 + ReleaseStr(sczCachePath);
1593 + return hr;
1594 +}
1595 +
1596 +static DWORD WINAPI CacheThreadProc(
1597 + __in LPVOID lpThreadParameter
1598 + )
1599 +{
1600 + HRESULT hr = S_OK;
1601 + BURN_CACHE_THREAD_CONTEXT* pContext = reinterpret_cast<BURN_CACHE_THREAD_CONTEXT*>(lpThreadParameter);
1602 + BURN_ENGINE_STATE* pEngineState = pContext->pEngineState;
1603 + DWORD* pcOverallProgressTicks = pContext->pcOverallProgressTicks;
1604 + BOOL* pfRollback = pContext->pfRollback;
1605 + BOOL fComInitialized = FALSE;
1606 +
1607 + // initialize COM
1608 + hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
1609 + ExitOnFailure(hr, "Failed to initialize COM on cache thread.");
1610 + fComInitialized = TRUE;
1611 +
1612 + // cache packages
1613 + hr = ApplyCache(pEngineState->section.hSourceEngineFile, &pEngineState->userExperience, &pEngineState->variables, &pEngineState->plan, pEngineState->companionConnection.hCachePipe, pcOverallProgressTicks, pfRollback);
1614 +
1615 +LExit:
1616 + UserExperienceExecutePhaseComplete(&pEngineState->userExperience, hr); // signal that cache completed.
1617 +
1618 + if (fComInitialized)
1619 + {
1620 + ::CoUninitialize();
1621 + }
1622 +
1623 + return (DWORD)hr;
1624 +}
1625 +
1626 +static HRESULT WaitForCacheThread(
1627 + __in HANDLE hCacheThread
1628 + )
1629 +{
1630 + HRESULT hr = S_OK;
1631 +
1632 + if (WAIT_OBJECT_0 != ::WaitForSingleObject(hCacheThread, INFINITE))
1633 + {
1634 + ExitWithLastError(hr, "Failed to wait for cache thread to terminate.");
1635 + }
1636 +
1637 + if (!::GetExitCodeThread(hCacheThread, (DWORD*)&hr))
1638 + {
1639 + ExitWithLastError(hr, "Failed to get cache thread exit code.");
1640 + }
1641 +
1642 +LExit:
1643 + return hr;
1644 +}
1645 +
1646 +static void LogPackages(
1647 + __in_opt const BURN_PACKAGE* pUpgradeBundlePackage,
1648 + __in_opt const BURN_PACKAGE* pForwardCompatibleBundlePackage,
1649 + __in const BURN_PACKAGES* pPackages,
1650 + __in const BURN_RELATED_BUNDLES* pRelatedBundles,
1651 + __in const BOOTSTRAPPER_ACTION action
1652 + )
1653 +{
1654 + if (pUpgradeBundlePackage)
1655 + {
1656 + LogId(REPORT_STANDARD, MSG_PLANNED_UPGRADE_BUNDLE, pUpgradeBundlePackage->sczId, LoggingRequestStateToString(pUpgradeBundlePackage->defaultRequested), LoggingRequestStateToString(pUpgradeBundlePackage->requested), LoggingActionStateToString(pUpgradeBundlePackage->execute), LoggingActionStateToString(pUpgradeBundlePackage->rollback), LoggingDependencyActionToString(pUpgradeBundlePackage->dependencyExecute));
1657 + }
1658 + else if (pForwardCompatibleBundlePackage)
1659 + {
1660 + LogId(REPORT_STANDARD, MSG_PLANNED_FORWARD_COMPATIBLE_BUNDLE, pForwardCompatibleBundlePackage->sczId, LoggingRequestStateToString(pForwardCompatibleBundlePackage->defaultRequested), LoggingRequestStateToString(pForwardCompatibleBundlePackage->requested), LoggingActionStateToString(pForwardCompatibleBundlePackage->execute), LoggingActionStateToString(pForwardCompatibleBundlePackage->rollback), LoggingDependencyActionToString(pForwardCompatibleBundlePackage->dependencyExecute));
1661 + }
1662 + else
1663 + {
1664 + // Display related bundles first if uninstalling.
1665 + if (BOOTSTRAPPER_ACTION_UNINSTALL == action && 0 < pRelatedBundles->cRelatedBundles)
1666 + {
1667 + for (int i = pRelatedBundles->cRelatedBundles - 1; 0 <= i; --i)
1668 + {
1669 + const BURN_RELATED_BUNDLE* pRelatedBundle = &pRelatedBundles->rgRelatedBundles[i];
1670 + const BURN_PACKAGE* pPackage = &pRelatedBundle->package;
1671 +
1672 + LogId(REPORT_STANDARD, MSG_PLANNED_RELATED_BUNDLE, pPackage->sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingDependencyActionToString(pPackage->dependencyExecute));
1673 + }
1674 + }
1675 +
1676 + // Display all the packages in the log.
1677 + for (DWORD i = 0; i < pPackages->cPackages; ++i)
1678 + {
1679 + const DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == action) ? pPackages->cPackages - 1 - i : i;
1680 + const BURN_PACKAGE* pPackage = &pPackages->rgPackages[iPackage];
1681 +
1682 + LogId(REPORT_STANDARD, MSG_PLANNED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingBoolToString(pPackage->fAcquire), LoggingBoolToString(pPackage->fUncache), LoggingDependencyActionToString(pPackage->dependencyExecute));
1683 + }
1684 +
1685 + for (DWORD i = 0; i < pPackages->cCompatiblePackages; ++i)
1686 + {
1687 + const DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == action) ? pPackages->cCompatiblePackages - 1 - i : i;
1688 + const BURN_PACKAGE* pPackage = &pPackages->rgCompatiblePackages[iPackage];
1689 +
1690 + LogId(REPORT_STANDARD, MSG_PLANNED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingBoolToString(pPackage->fAcquire), LoggingBoolToString(pPackage->fUncache), LoggingDependencyActionToString(pPackage->dependencyExecute));
1691 + }
1692 +
1693 + // Display related bundles last if caching, installing, modifying, or repairing.
1694 + if (BOOTSTRAPPER_ACTION_UNINSTALL < action && 0 < pRelatedBundles->cRelatedBundles)
1695 + {
1696 + for (DWORD i = 0; i < pRelatedBundles->cRelatedBundles; ++i)
1697 + {
1698 + const BURN_RELATED_BUNDLE* pRelatedBundle = &pRelatedBundles->rgRelatedBundles[i];
1699 + const BURN_PACKAGE* pPackage = &pRelatedBundle->package;
1700 +
1701 + LogId(REPORT_STANDARD, MSG_PLANNED_RELATED_BUNDLE, pPackage->sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingDependencyActionToString(pPackage->dependencyExecute));
1702 + }
1703 + }
1704 + }
1705 +}
src/engine/core.h new
+211
@@ -0,0 +1,211 @@
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 +#if defined(__cplusplus)
6 +extern "C" {
7 +#endif
8 +
9 +
10 +// constants
11 +
12 +const LPCWSTR BURN_POLICY_REGISTRY_PATH = L"WiX\\Burn";
13 +
14 +const LPCWSTR BURN_COMMANDLINE_SWITCH_PARENT = L"parent";
15 +const LPCWSTR BURN_COMMANDLINE_SWITCH_PARENT_NONE = L"parent:none";
16 +const LPCWSTR BURN_COMMANDLINE_SWITCH_CLEAN_ROOM = L"burn.clean.room";
17 +const LPCWSTR BURN_COMMANDLINE_SWITCH_ELEVATED = L"burn.elevated";
18 +const LPCWSTR BURN_COMMANDLINE_SWITCH_EMBEDDED = L"burn.embedded";
19 +const LPCWSTR BURN_COMMANDLINE_SWITCH_RUNONCE = L"burn.runonce";
20 +const LPCWSTR BURN_COMMANDLINE_SWITCH_LOG_APPEND = L"burn.log.append";
21 +const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_DETECT = L"burn.related.detect";
22 +const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_UPGRADE = L"burn.related.upgrade";
23 +const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_ADDON = L"burn.related.addon";
24 +const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_PATCH = L"burn.related.patch";
25 +const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_UPDATE = L"burn.related.update";
26 +const LPCWSTR BURN_COMMANDLINE_SWITCH_PASSTHROUGH = L"burn.passthrough";
27 +const LPCWSTR BURN_COMMANDLINE_SWITCH_DISABLE_UNELEVATE = L"burn.disable.unelevate";
28 +const LPCWSTR BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES = L"burn.ignoredependencies";
29 +const LPCWSTR BURN_COMMANDLINE_SWITCH_ANCESTORS = L"burn.ancestors";
30 +const LPCWSTR BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED = L"burn.filehandle.attached";
31 +const LPCWSTR BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF = L"burn.filehandle.self";
32 +const LPCWSTR BURN_COMMANDLINE_SWITCH_PREFIX = L"burn.";
33 +
34 +const LPCWSTR BURN_BUNDLE_LAYOUT_DIRECTORY = L"WixBundleLayoutDirectory";
35 +const LPCWSTR BURN_BUNDLE_ACTION = L"WixBundleAction";
36 +const LPCWSTR BURN_BUNDLE_ACTIVE_PARENT = L"WixBundleActiveParent";
37 +const LPCWSTR BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER = L"WixBundleExecutePackageCacheFolder";
38 +const LPCWSTR BURN_BUNDLE_EXECUTE_PACKAGE_ACTION = L"WixBundleExecutePackageAction";
39 +const LPCWSTR BURN_BUNDLE_FORCED_RESTART_PACKAGE = L"WixBundleForcedRestartPackage";
40 +const LPCWSTR BURN_BUNDLE_INSTALLED = L"WixBundleInstalled";
41 +const LPCWSTR BURN_BUNDLE_ELEVATED = L"WixBundleElevated";
42 +const LPCWSTR BURN_BUNDLE_PROVIDER_KEY = L"WixBundleProviderKey";
43 +const LPCWSTR BURN_BUNDLE_MANUFACTURER = L"WixBundleManufacturer";
44 +const LPCWSTR BURN_BUNDLE_SOURCE_PROCESS_PATH = L"WixBundleSourceProcessPath";
45 +const LPCWSTR BURN_BUNDLE_SOURCE_PROCESS_FOLDER = L"WixBundleSourceProcessFolder";
46 +const LPCWSTR BURN_BUNDLE_TAG = L"WixBundleTag";
47 +const LPCWSTR BURN_BUNDLE_UILEVEL = L"WixBundleUILevel";
48 +const LPCWSTR BURN_BUNDLE_VERSION = L"WixBundleVersion";
49 +
50 +// The following constants must stay in sync with src\wix\Binder.cs
51 +const LPCWSTR BURN_BUNDLE_NAME = L"WixBundleName";
52 +const LPCWSTR BURN_BUNDLE_ORIGINAL_SOURCE = L"WixBundleOriginalSource";
53 +const LPCWSTR BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER = L"WixBundleOriginalSourceFolder";
54 +const LPCWSTR BURN_BUNDLE_LAST_USED_SOURCE = L"WixBundleLastUsedSource";
55 +
56 +
57 +// enums
58 +
59 +enum BURN_MODE
60 +{
61 + BURN_MODE_UNTRUSTED,
62 + BURN_MODE_NORMAL,
63 + BURN_MODE_ELEVATED,
64 + BURN_MODE_EMBEDDED,
65 + BURN_MODE_RUNONCE,
66 +};
67 +
68 +enum BURN_AU_PAUSE_ACTION
69 +{
70 + BURN_AU_PAUSE_ACTION_NONE,
71 + BURN_AU_PAUSE_ACTION_IFELEVATED,
72 + BURN_AU_PAUSE_ACTION_IFELEVATED_NORESUME,
73 +};
74 +
75 +
76 +// structs
77 +
78 +typedef struct _BURN_ENGINE_STATE
79 +{
80 + // synchronization
81 + CRITICAL_SECTION csActive; // Any call from the UX that reads or alters the engine state
82 + // needs to be syncronized through this critical section.
83 + // Note: The engine must never do a UX callback while in this critical section.
84 +
85 + // UX flow control
86 + //BOOL fSuspend; // Is TRUE when UX made Suspend() call on core.
87 + //BOOL fForcedReboot; // Is TRUE when UX made Reboot() call on core.
88 + //BOOL fCancelled; // Is TRUE when UX return cancel on UX OnXXX() methods.
89 + //BOOL fReboot; // Is TRUE when UX confirms OnRestartRequried().
90 + BOOL fRestart; // Set TRUE when UX returns IDRESTART during Apply().
91 +
92 + // engine data
93 + BOOTSTRAPPER_COMMAND command;
94 + BURN_SECTION section;
95 + BURN_VARIABLES variables;
96 + BURN_CONDITION condition;
97 + BURN_SEARCHES searches;
98 + BURN_USER_EXPERIENCE userExperience;
99 + BURN_REGISTRATION registration;
100 + BURN_CONTAINERS containers;
101 + BURN_CATALOGS catalogs;
102 + BURN_PAYLOADS payloads;
103 + BURN_PACKAGES packages;
104 + BURN_UPDATE update;
105 + BURN_APPROVED_EXES approvedExes;
106 +
107 + HWND hMessageWindow;
108 + HANDLE hMessageWindowThread;
109 +
110 + BOOL fDisableRollback;
111 + BOOL fDisableSystemRestore;
112 + BOOL fParallelCacheAndExecute;
113 +
114 + BURN_LOGGING log;
115 +
116 + BURN_PLAN plan;
117 +
118 + BURN_MODE mode;
119 + BURN_AU_PAUSE_ACTION automaticUpdates;
120 +
121 + DWORD dwElevatedLoggingTlsId;
122 +
123 + LPWSTR sczBundleEngineWorkingPath;
124 + BURN_PIPE_CONNECTION companionConnection;
125 + BURN_PIPE_CONNECTION embeddedConnection;
126 +
127 + BURN_RESUME_MODE resumeMode;
128 + BOOL fDisableUnelevate;
129 +
130 + LPWSTR sczIgnoreDependencies;
131 +
132 + int argc;
133 + LPWSTR* argv;
134 +} BURN_ENGINE_STATE;
135 +
136 +
137 +// function declarations
138 +
139 +HRESULT CoreInitialize(
140 + __in BURN_ENGINE_STATE* pEngineState
141 + );
142 +HRESULT CoreSerializeEngineState(
143 + __in BURN_ENGINE_STATE* pEngineState,
144 + __inout BYTE** ppbBuffer,
145 + __inout SIZE_T* piBuffer
146 + );
147 +HRESULT CoreQueryRegistration(
148 + __in BURN_ENGINE_STATE* pEngineState
149 + );
150 +//HRESULT CoreDeserializeEngineState(
151 +// __in BURN_ENGINE_STATE* pEngineState,
152 +// __in_bcount(cbBuffer) BYTE* pbBuffer,
153 +// __in SIZE_T cbBuffer
154 +// );
155 +HRESULT CoreDetect(
156 + __in BURN_ENGINE_STATE* pEngineState,
157 + __in_opt HWND hwndParent
158 + );
159 +HRESULT CorePlan(
160 + __in BURN_ENGINE_STATE* pEngineState,
161 + __in BOOTSTRAPPER_ACTION action
162 + );
163 +HRESULT CoreElevate(
164 + __in BURN_ENGINE_STATE* pEngineState,
165 + __in_opt HWND hwndParent
166 + );
167 +HRESULT CoreApply(
168 + __in BURN_ENGINE_STATE* pEngineState,
169 + __in_opt HWND hwndParent
170 + );
171 +HRESULT CoreLaunchApprovedExe(
172 + __in BURN_ENGINE_STATE* pEngineState,
173 + __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe
174 + );
175 +HRESULT CoreQuit(
176 + __in BURN_ENGINE_STATE* pEngineState,
177 + __in int nExitCode
178 + );
179 +HRESULT CoreSaveEngineState(
180 + __in BURN_ENGINE_STATE* pEngineState
181 + );
182 +LPCWSTR CoreRelationTypeToCommandLineString(
183 + __in BOOTSTRAPPER_RELATION_TYPE relationType
184 + );
185 +HRESULT CoreRecreateCommandLine(
186 + __deref_inout_z LPWSTR* psczCommandLine,
187 + __in BOOTSTRAPPER_ACTION action,
188 + __in BOOTSTRAPPER_DISPLAY display,
189 + __in BOOTSTRAPPER_RESTART restart,
190 + __in BOOTSTRAPPER_RELATION_TYPE relationType,
191 + __in BOOL fPassthrough,
192 + __in_z_opt LPCWSTR wzActiveParent,
193 + __in_z_opt LPCWSTR wzAncestors,
194 + __in_z_opt LPCWSTR wzAppendLogPath,
195 + __in_z_opt LPCWSTR wzAdditionalCommandLineArguments
196 + );
197 +HRESULT CoreAppendFileHandleAttachedToCommandLine(
198 + __in HANDLE hFileWithAttachedContainer,
199 + __out HANDLE* phExecutableFile,
200 + __deref_inout_z LPWSTR* psczCommandLine
201 + );
202 +HRESULT CoreAppendFileHandleSelfToCommandLine(
203 + __in LPCWSTR wzExecutablePath,
204 + __out HANDLE* phExecutableFile,
205 + __deref_inout_z LPWSTR* psczCommandLine,
206 + __deref_inout_z_opt LPWSTR* psczObfuscatedCommandLine
207 + );
208 +
209 +#if defined(__cplusplus)
210 +}
211 +#endif
src/engine/dependency.cpp new
+1203
@@ -0,0 +1,1203 @@
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 +// constants
6 +
7 +#define INITIAL_STRINGDICT_SIZE 48
8 +const LPCWSTR vcszIgnoreDependenciesDelim = L";";
9 +
10 +
11 +// internal function declarations
12 +
13 +static HRESULT SplitIgnoreDependencies(
14 + __in_z LPCWSTR wzIgnoreDependencies,
15 + __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies,
16 + __inout LPUINT pcDependencies
17 + );
18 +
19 +static HRESULT JoinIgnoreDependencies(
20 + __out_z LPWSTR* psczIgnoreDependencies,
21 + __in_ecount(cDependencies) const DEPENDENCY* rgDependencies,
22 + __in UINT cDependencies
23 + );
24 +
25 +static HRESULT GetIgnoredDependents(
26 + __in const BURN_PACKAGE* pPackage,
27 + __in const BURN_PLAN* pPlan,
28 + __deref_inout STRINGDICT_HANDLE* psdIgnoredDependents
29 + );
30 +
31 +static HRESULT GetProviderInformation(
32 + __in HKEY hkRoot,
33 + __in_z LPCWSTR wzProviderKey,
34 + __deref_opt_out_z_opt LPWSTR* psczProviderKey,
35 + __deref_opt_out_z_opt LPWSTR* psczId
36 + );
37 +
38 +static void CalculateDependencyActionStates(
39 + __in const BURN_PACKAGE* pPackage,
40 + __in const BOOTSTRAPPER_ACTION action,
41 + __out BURN_DEPENDENCY_ACTION* pDependencyExecuteAction,
42 + __out BURN_DEPENDENCY_ACTION* pDependencyRollbackAction
43 + );
44 +
45 +static HRESULT AddPackageDependencyActions(
46 + __in_opt DWORD *pdwInsertSequence,
47 + __in const BURN_PACKAGE* pPackage,
48 + __in BURN_PLAN* pPlan,
49 + __in const BURN_DEPENDENCY_ACTION dependencyExecuteAction,
50 + __in const BURN_DEPENDENCY_ACTION dependencyRollbackAction
51 + );
52 +
53 +static HRESULT RegisterPackageProvider(
54 + __in const BURN_PACKAGE* pPackage
55 + );
56 +
57 +static void UnregisterPackageProvider(
58 + __in const BURN_PACKAGE* pPackage
59 + );
60 +
61 +static HRESULT RegisterPackageDependency(
62 + __in BOOL fPerMachine,
63 + __in const BURN_PACKAGE* pPackage,
64 + __in_z LPCWSTR wzDependentProviderKey
65 + );
66 +
67 +static void UnregisterPackageDependency(
68 + __in BOOL fPerMachine,
69 + __in const BURN_PACKAGE* pPackage,
70 + __in_z LPCWSTR wzDependentProviderKey
71 + );
72 +
73 +static BOOL PackageProviderExists(
74 + __in const BURN_PACKAGE* pPackage
75 + );
76 +
77 +
78 +// functions
79 +
80 +extern "C" void DependencyUninitialize(
81 + __in BURN_DEPENDENCY_PROVIDER* pProvider
82 + )
83 +{
84 + ReleaseStr(pProvider->sczKey);
85 + ReleaseStr(pProvider->sczVersion);
86 + ReleaseStr(pProvider->sczDisplayName);
87 + memset(pProvider, 0, sizeof(BURN_DEPENDENCY_PROVIDER));
88 +}
89 +
90 +extern "C" HRESULT DependencyParseProvidersFromXml(
91 + __in BURN_PACKAGE* pPackage,
92 + __in IXMLDOMNode* pixnPackage
93 + )
94 +{
95 + HRESULT hr = S_OK;
96 + IXMLDOMNodeList* pixnNodes = NULL;
97 + DWORD cNodes = 0;
98 + IXMLDOMNode* pixnNode = NULL;
99 +
100 + // Select dependency provider nodes.
101 + hr = XmlSelectNodes(pixnPackage, L"Provides", &pixnNodes);
102 + ExitOnFailure(hr, "Failed to select dependency provider nodes.");
103 +
104 + // Get dependency provider node count.
105 + hr = pixnNodes->get_length((long*)&cNodes);
106 + ExitOnFailure(hr, "Failed to get the dependency provider node count.");
107 +
108 + if (!cNodes)
109 + {
110 + ExitFunction1(hr = S_OK);
111 + }
112 +
113 + // Allocate memory for dependency provider pointers.
114 + pPackage->rgDependencyProviders = (BURN_DEPENDENCY_PROVIDER*)MemAlloc(sizeof(BURN_DEPENDENCY_PROVIDER) * cNodes, TRUE);
115 + ExitOnNull(pPackage->rgDependencyProviders, hr, E_OUTOFMEMORY, "Failed to allocate memory for dependency providers.");
116 +
117 + pPackage->cDependencyProviders = cNodes;
118 +
119 + // Parse dependency provider elements.
120 + for (DWORD i = 0; i < cNodes; i++)
121 + {
122 + BURN_DEPENDENCY_PROVIDER* pDependencyProvider = &pPackage->rgDependencyProviders[i];
123 +
124 + hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
125 + ExitOnFailure(hr, "Failed to get the next dependency provider node.");
126 +
127 + // @Key
128 + hr = XmlGetAttributeEx(pixnNode, L"Key", &pDependencyProvider->sczKey);
129 + ExitOnFailure(hr, "Failed to get the Key attribute.");
130 +
131 + // @Version
132 + hr = XmlGetAttributeEx(pixnNode, L"Version", &pDependencyProvider->sczVersion);
133 + if (E_NOTFOUND != hr)
134 + {
135 + ExitOnFailure(hr, "Failed to get the Version attribute.");
136 + }
137 +
138 + // @DisplayName
139 + hr = XmlGetAttributeEx(pixnNode, L"DisplayName", &pDependencyProvider->sczDisplayName);
140 + if (E_NOTFOUND != hr)
141 + {
142 + ExitOnFailure(hr, "Failed to get the DisplayName attribute.");
143 + }
144 +
145 + // @Imported
146 + hr = XmlGetYesNoAttribute(pixnNode, L"Imported", &pDependencyProvider->fImported);
147 + if (E_NOTFOUND != hr)
148 + {
149 + ExitOnFailure(hr, "Failed to get the Imported attribute.");
150 + }
151 + else
152 + {
153 + pDependencyProvider->fImported = FALSE;
154 + hr = S_OK;
155 + }
156 +
157 + // Prepare next iteration.
158 + ReleaseNullObject(pixnNode);
159 + }
160 +
161 + hr = S_OK;
162 +
163 +LExit:
164 + ReleaseObject(pixnNode);
165 + ReleaseObject(pixnNodes);
166 +
167 + return hr;
168 +}
169 +
170 +extern "C" HRESULT DependencyDetectProviderKeyPackageId(
171 + __in const BURN_PACKAGE* pPackage,
172 + __deref_opt_out_z_opt LPWSTR* psczProviderKey,
173 + __deref_opt_out_z_opt LPWSTR* psczId
174 + )
175 +{
176 + HRESULT hr = E_NOTFOUND;
177 + LPWSTR wzProviderKey = NULL;
178 + HKEY hkRoot = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
179 +
180 + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
181 + {
182 + const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i];
183 +
184 + // Find the first package id registered for the provider key.
185 + hr = GetProviderInformation(hkRoot, pProvider->sczKey, psczProviderKey, psczId);
186 + if (E_NOTFOUND == hr)
187 + {
188 + continue;
189 + }
190 + ExitOnFailure(hr, "Failed to get the package provider information.");
191 +
192 + ExitFunction();
193 + }
194 +
195 + // Older bundles may not have written the id so try the default.
196 + if (BURN_PACKAGE_TYPE_MSI == pPackage->type)
197 + {
198 + wzProviderKey = pPackage->Msi.sczProductCode;
199 + }
200 +
201 + if (wzProviderKey)
202 + {
203 + hr = GetProviderInformation(hkRoot, wzProviderKey, psczProviderKey, psczId);
204 + if (E_NOTFOUND == hr)
205 + {
206 + ExitFunction();
207 + }
208 + ExitOnFailure(hr, "Failed to get the package default provider information.");
209 + }
210 +
211 +LExit:
212 + return hr;
213 +}
214 +
215 +extern "C" HRESULT DependencyDetectProviderKeyBundleId(
216 + __in BURN_REGISTRATION* pRegistration
217 + )
218 +{
219 + HRESULT hr = S_OK;
220 +
221 + hr = DepGetProviderInformation(pRegistration->hkRoot, pRegistration->sczProviderKey, &pRegistration->sczDetectedProviderKeyBundleId, NULL, NULL);
222 + if (E_NOTFOUND == hr)
223 + {
224 + ExitFunction();
225 + }
226 + ExitOnFailure(hr, "Failed to get provider key bundle id.");
227 +
228 + // If a bundle id was not explicitly set, default the provider key bundle id to this bundle's provider key.
229 + if (!pRegistration->sczDetectedProviderKeyBundleId || !*pRegistration->sczDetectedProviderKeyBundleId)
230 + {
231 + hr = StrAllocString(&pRegistration->sczDetectedProviderKeyBundleId, pRegistration->sczProviderKey, 0);
232 + ExitOnFailure(hr, "Failed to initialize provider key bundle id.");
233 + }
234 +
235 +LExit:
236 + return hr;
237 +}
238 +
239 +extern "C" HRESULT DependencyPlanInitialize(
240 + __in const BURN_ENGINE_STATE* pEngineState,
241 + __in BURN_PLAN* pPlan
242 + )
243 +{
244 + HRESULT hr = S_OK;
245 +
246 + // The current bundle provider key should always be ignored for dependency checks.
247 + hr = DepDependencyArrayAlloc(&pPlan->rgPlannedProviders, &pPlan->cPlannedProviders, pEngineState->registration.sczProviderKey, NULL);
248 + ExitOnFailure(hr, "Failed to add the bundle provider key to the list of dependencies to ignore.");
249 +
250 + // Add the list of dependencies to ignore to the plan.
251 + if (pEngineState->sczIgnoreDependencies)
252 + {
253 + // TODO: After adding enumeration to STRINGDICT, a single STRINGDICT_HANDLE can be used everywhere.
254 + hr = SplitIgnoreDependencies(pEngineState->sczIgnoreDependencies, &pPlan->rgPlannedProviders, &pPlan->cPlannedProviders);
255 + ExitOnFailure(hr, "Failed to split the list of dependencies to ignore.");
256 + }
257 +
258 +LExit:
259 + return hr;
260 +}
261 +
262 +extern "C" HRESULT DependencyAllocIgnoreDependencies(
263 + __in const BURN_PLAN *pPlan,
264 + __out_z LPWSTR* psczIgnoreDependencies
265 + )
266 +{
267 + HRESULT hr = S_OK;
268 +
269 + // Join the list of dependencies to ignore for each related bundle.
270 + if (0 < pPlan->cPlannedProviders)
271 + {
272 + hr = JoinIgnoreDependencies(psczIgnoreDependencies, pPlan->rgPlannedProviders, pPlan->cPlannedProviders);
273 + ExitOnFailure(hr, "Failed to join the list of dependencies to ignore.");
274 + }
275 +
276 +LExit:
277 + return hr;
278 +}
279 +
280 +extern "C" HRESULT DependencyAddIgnoreDependencies(
281 + __in STRINGDICT_HANDLE sdIgnoreDependencies,
282 + __in_z LPCWSTR wzAddIgnoreDependencies
283 + )
284 +{
285 + HRESULT hr = S_OK;
286 + LPWSTR wzContext = NULL;
287 +
288 + // Parse through the semicolon-delimited tokens and add to the array.
289 + for (LPCWSTR wzToken = ::wcstok_s(const_cast<LPWSTR>(wzAddIgnoreDependencies), vcszIgnoreDependenciesDelim, &wzContext); wzToken; wzToken = ::wcstok_s(NULL, vcszIgnoreDependenciesDelim, &wzContext))
290 + {
291 + hr = DictKeyExists(sdIgnoreDependencies, wzToken);
292 + if (E_NOTFOUND != hr)
293 + {
294 + ExitOnFailure(hr, "Failed to check the dictionary of unique dependencies.");
295 + }
296 + else
297 + {
298 + hr = DictAddKey(sdIgnoreDependencies, wzToken);
299 + ExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", wzToken);
300 + }
301 + }
302 +
303 +LExit:
304 + return hr;
305 +}
306 +
307 +extern "C" BOOL DependencyDependentExists(
308 + __in const BURN_REGISTRATION* pRegistration,
309 + __in_z LPCWSTR wzDependentProviderKey
310 + )
311 +{
312 + HRESULT hr = S_OK;
313 +
314 + hr = DepDependentExists(pRegistration->hkRoot, pRegistration->sczProviderKey, wzDependentProviderKey);
315 + return SUCCEEDED(hr);
316 +}
317 +
318 +extern "C" HRESULT DependencyPlanPackageBegin(
319 + __in BOOL fPerMachine,
320 + __in BURN_PACKAGE* pPackage,
321 + __in BURN_PLAN* pPlan
322 + )
323 +{
324 + HRESULT hr = S_OK;
325 + STRINGDICT_HANDLE sdIgnoredDependents = NULL;
326 + DEPENDENCY* rgDependents = NULL;
327 + UINT cDependents = 0;
328 + HKEY hkHive = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
329 + BURN_DEPENDENCY_ACTION dependencyExecuteAction = BURN_DEPENDENCY_ACTION_NONE;
330 + BURN_DEPENDENCY_ACTION dependencyRollbackAction = BURN_DEPENDENCY_ACTION_NONE;
331 +
332 + pPackage->dependencyExecute = BURN_DEPENDENCY_ACTION_NONE;
333 + pPackage->dependencyRollback = BURN_DEPENDENCY_ACTION_NONE;
334 +
335 + // Make sure the package defines at least one provider.
336 + if (0 == pPackage->cDependencyProviders)
337 + {
338 + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_SKIP_NOPROVIDERS, pPackage->sczId);
339 + ExitFunction1(hr = S_OK);
340 + }
341 +
342 + // Make sure the package is in the same scope as the bundle.
343 + if (fPerMachine != pPackage->fPerMachine)
344 + {
345 + LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_SKIP_WRONGSCOPE, pPackage->sczId, LoggingPerMachineToString(fPerMachine), LoggingPerMachineToString(pPackage->fPerMachine));
346 + ExitFunction1(hr = S_OK);
347 + }
348 +
349 + // If we're uninstalling the package, check if any dependents are registered.
350 + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute)
351 + {
352 + // Build up a list of dependents to ignore, including the current bundle.
353 + hr = GetIgnoredDependents(pPackage, pPlan, &sdIgnoredDependents);
354 + ExitOnFailure(hr, "Failed to build the list of ignored dependents.");
355 +
356 + // Skip the dependency check if "ALL" was authored for IGNOREDEPENDENCIES.
357 + hr = DictKeyExists(sdIgnoredDependents, L"ALL");
358 + if (E_NOTFOUND != hr)
359 + {
360 + ExitOnFailure(hr, "Failed to check if \"ALL\" was set in IGNOREDEPENDENCIES.");
361 + }
362 + else
363 + {
364 + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
365 + {
366 + const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i];
367 +
368 + hr = DepCheckDependents(hkHive, pProvider->sczKey, 0, sdIgnoredDependents, &rgDependents, &cDependents);
369 + if (E_FILENOTFOUND != hr)
370 + {
371 + ExitOnFailure(hr, "Failed dependents check on package provider: %ls", pProvider->sczKey);
372 + }
373 + else
374 + {
375 + hr = S_OK;
376 + }
377 + }
378 + }
379 + }
380 +
381 + // Calculate the dependency actions before the package itself is planned.
382 + CalculateDependencyActionStates(pPackage, pPlan->action, &dependencyExecuteAction, &dependencyRollbackAction);
383 +
384 + // If dependents were found, change the action to not uninstall the package.
385 + if (0 < cDependents)
386 + {
387 + LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_HASDEPENDENTS, pPackage->sczId, cDependents);
388 +
389 + for (DWORD i = 0; i < cDependents; ++i)
390 + {
391 + const DEPENDENCY* pDependency = &rgDependents[i];
392 + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_DEPENDENT, pDependency->sczKey, LoggingStringOrUnknownIfNull(pDependency->sczName));
393 + }
394 +
395 + pPackage->fDependencyManagerWasHere = TRUE;
396 + pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE;
397 + pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
398 + }
399 + // Use the calculated dependency actions as the provider actions if there
400 + // are any non-imported providers that need to be registered and the package
401 + // is current (not obsolete).
402 + else if (BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE != pPackage->currentState)
403 + {
404 + BOOL fAllImportedProviders = TRUE; // assume all providers were imported.
405 + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
406 + {
407 + const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i];
408 + if (!pProvider->fImported)
409 + {
410 + fAllImportedProviders = FALSE;
411 + break;
412 + }
413 + }
414 +
415 + if (!fAllImportedProviders)
416 + {
417 + pPackage->providerExecute = dependencyExecuteAction;
418 + pPackage->providerRollback = dependencyRollbackAction;
419 + }
420 + }
421 +
422 + // If the package will be removed, add its providers to the growing list in the plan.
423 + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute)
424 + {
425 + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
426 + {
427 + const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i];
428 +
429 + hr = DepDependencyArrayAlloc(&pPlan->rgPlannedProviders, &pPlan->cPlannedProviders, pProvider->sczKey, NULL);
430 + ExitOnFailure(hr, "Failed to add the package provider key \"%ls\" to the planned list.", pProvider->sczKey);
431 + }
432 + }
433 +
434 + pPackage->dependencyExecute = dependencyExecuteAction;
435 + pPackage->dependencyRollback = dependencyRollbackAction;
436 +
437 +LExit:
438 + ReleaseDependencyArray(rgDependents, cDependents);
439 + ReleaseDict(sdIgnoredDependents);
440 +
441 + return hr;
442 +}
443 +
444 +extern "C" HRESULT DependencyPlanPackage(
445 + __in_opt DWORD *pdwInsertSequence,
446 + __in const BURN_PACKAGE* pPackage,
447 + __in BURN_PLAN* pPlan
448 + )
449 +{
450 + HRESULT hr = S_OK;
451 + BURN_EXECUTE_ACTION* pAction = NULL;
452 +
453 + // If the dependency execution action is to unregister, add the dependency actions to the plan
454 + // *before* the provider key is potentially removed.
455 + if (BURN_DEPENDENCY_ACTION_UNREGISTER == pPackage->dependencyExecute)
456 + {
457 + hr = AddPackageDependencyActions(pdwInsertSequence, pPackage, pPlan, pPackage->dependencyExecute, pPackage->dependencyRollback);
458 + ExitOnFailure(hr, "Failed to plan the dependency actions for package: %ls", pPackage->sczId);
459 + }
460 +
461 + // Add the provider rollback plan.
462 + if (BURN_DEPENDENCY_ACTION_NONE != pPackage->providerRollback)
463 + {
464 + hr = PlanAppendRollbackAction(pPlan, &pAction);
465 + ExitOnFailure(hr, "Failed to append provider rollback action.");
466 +
467 + pAction->type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER;
468 + pAction->packageProvider.pPackage = const_cast<BURN_PACKAGE*>(pPackage);
469 + pAction->packageProvider.action = pPackage->providerRollback;
470 +
471 + // Put a checkpoint before the execute action so that rollback happens
472 + // if execute fails.
473 + hr = PlanExecuteCheckpoint(pPlan);
474 + ExitOnFailure(hr, "Failed to plan provider checkpoint action.");
475 + }
476 +
477 + // Add the provider execute plan. This comes after rollback so if something goes wrong
478 + // rollback will try to clean up after us.
479 + if (BURN_DEPENDENCY_ACTION_NONE != pPackage->providerExecute)
480 + {
481 + if (NULL != pdwInsertSequence)
482 + {
483 + hr = PlanInsertExecuteAction(*pdwInsertSequence, pPlan, &pAction);
484 + ExitOnFailure(hr, "Failed to insert provider execute action.");
485 +
486 + // Always move the sequence after this dependency action so the provider registration
487 + // stays in front of the inserted actions.
488 + ++(*pdwInsertSequence);
489 + }
490 + else
491 + {
492 + hr = PlanAppendExecuteAction(pPlan, &pAction);
493 + ExitOnFailure(hr, "Failed to append provider execute action.");
494 + }
495 +
496 + pAction->type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER;
497 + pAction->packageProvider.pPackage = const_cast<BURN_PACKAGE*>(pPackage);
498 + pAction->packageProvider.action = pPackage->providerExecute;
499 + }
500 +
501 +LExit:
502 + return hr;
503 +}
504 +
505 +extern "C" HRESULT DependencyPlanPackageComplete(
506 + __in BURN_PACKAGE* pPackage,
507 + __in BURN_PLAN* pPlan
508 + )
509 +{
510 + HRESULT hr = S_OK;
511 +
512 + // Registration of dependencies happens here, after the package is planned to be
513 + // installed and all that good stuff.
514 + if (BURN_DEPENDENCY_ACTION_REGISTER == pPackage->dependencyExecute)
515 + {
516 + // Recalculate the dependency actions in case other operations may have changed
517 + // the package execution state.
518 + CalculateDependencyActionStates(pPackage, pPlan->action, &pPackage->dependencyExecute, &pPackage->dependencyRollback);
519 +
520 + // If the dependency execution action is *still* to register, add the dependency actions to the plan.
521 + if (BURN_DEPENDENCY_ACTION_REGISTER == pPackage->dependencyExecute)
522 + {
523 + hr = AddPackageDependencyActions(NULL, pPackage, pPlan, pPackage->dependencyExecute, pPackage->dependencyRollback);
524 + ExitOnFailure(hr, "Failed to plan the dependency actions for package: %ls", pPackage->sczId);
525 + }
526 + }
527 +
528 +LExit:
529 + return hr;
530 +}
531 +
532 +extern "C" HRESULT DependencyExecutePackageProviderAction(
533 + __in const BURN_EXECUTE_ACTION* pAction
534 + )
535 +{
536 + AssertSz(BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER == pAction->type, "Execute action type not supported by this function.");
537 +
538 + HRESULT hr = S_OK;
539 + const BURN_PACKAGE* pPackage = pAction->packageProvider.pPackage;
540 +
541 + // Register or unregister the package provider(s).
542 + if (BURN_DEPENDENCY_ACTION_REGISTER == pAction->packageProvider.action)
543 + {
544 + hr = RegisterPackageProvider(pPackage);
545 + ExitOnFailure(hr, "Failed to register the package providers.");
546 + }
547 + else if (BURN_DEPENDENCY_ACTION_UNREGISTER == pAction->packageProvider.action)
548 + {
549 + UnregisterPackageProvider(pPackage);
550 + }
551 +
552 +LExit:
553 + if (!pPackage->fVital)
554 + {
555 + hr = S_OK;
556 + }
557 +
558 + return hr;
559 +}
560 +
561 +extern "C" HRESULT DependencyExecutePackageDependencyAction(
562 + __in BOOL fPerMachine,
563 + __in const BURN_EXECUTE_ACTION* pAction
564 + )
565 +{
566 + AssertSz(BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY == pAction->type, "Execute action type not supported by this function.");
567 +
568 + HRESULT hr = S_OK;
569 + const BURN_PACKAGE* pPackage = pAction->packageDependency.pPackage;
570 +
571 + // Register or unregister the bundle as a dependent of each package dependency provider.
572 + if (BURN_DEPENDENCY_ACTION_REGISTER == pAction->packageDependency.action)
573 + {
574 + hr = RegisterPackageDependency(fPerMachine, pPackage, pAction->packageDependency.sczBundleProviderKey);
575 + ExitOnFailure(hr, "Failed to register the dependency on the package provider.");
576 + }
577 + else if (BURN_DEPENDENCY_ACTION_UNREGISTER == pAction->packageDependency.action)
578 + {
579 + UnregisterPackageDependency(fPerMachine, pPackage, pAction->packageDependency.sczBundleProviderKey);
580 + }
581 +
582 +LExit:
583 + if (!pPackage->fVital)
584 + {
585 + hr = S_OK;
586 + }
587 +
588 + return hr;
589 +}
590 +
591 +extern "C" HRESULT DependencyRegisterBundle(
592 + __in const BURN_REGISTRATION* pRegistration
593 + )
594 +{
595 + HRESULT hr = S_OK;
596 + LPWSTR sczVersion = NULL;
597 +
598 + hr = FileVersionToStringEx(pRegistration->qwVersion, &sczVersion);
599 + ExitOnFailure(hr, "Failed to format the registration version string.");
600 +
601 + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_REGISTER, pRegistration->sczProviderKey, sczVersion);
602 +
603 + // Register the bundle provider key.
604 + hr = DepRegisterDependency(pRegistration->hkRoot, pRegistration->sczProviderKey, sczVersion, pRegistration->sczDisplayName, pRegistration->sczId, 0);
605 + ExitOnFailure(hr, "Failed to register the bundle dependency provider.");
606 +
607 +LExit:
608 + ReleaseStr(sczVersion);
609 +
610 + return hr;
611 +}
612 +
613 +extern "C" HRESULT DependencyProcessDependentRegistration(
614 + __in const BURN_REGISTRATION* pRegistration,
615 + __in const BURN_DEPENDENT_REGISTRATION_ACTION* pAction
616 + )
617 +{
618 + HRESULT hr = S_OK;
619 +
620 + switch (pAction->type)
621 + {
622 + case BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER:
623 + hr = DepRegisterDependent(pRegistration->hkRoot, pRegistration->sczProviderKey, pAction->sczDependentProviderKey, NULL, NULL, 0);
624 + ExitOnFailure(hr, "Failed to register dependent: %ls", pAction->sczDependentProviderKey);
625 + break;
626 +
627 + case BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER:
628 + hr = DepUnregisterDependent(pRegistration->hkRoot, pRegistration->sczProviderKey, pAction->sczDependentProviderKey);
629 + ExitOnFailure(hr, "Failed to unregister dependent: %ls", pAction->sczDependentProviderKey);
630 + break;
631 +
632 + default:
633 + hr = E_INVALIDARG;
634 + ExitOnRootFailure(hr, "Unrecognized registration action type: %d", pAction->type);
635 + }
636 +
637 +LExit:
638 + return hr;
639 +}
640 +
641 +extern "C" void DependencyUnregisterBundle(
642 + __in const BURN_REGISTRATION* pRegistration
643 + )
644 +{
645 + HRESULT hr = S_OK;
646 +
647 + // Remove the bundle provider key.
648 + hr = DepUnregisterDependency(pRegistration->hkRoot, pRegistration->sczProviderKey);
649 + if (SUCCEEDED(hr))
650 + {
651 + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_UNREGISTERED, pRegistration->sczProviderKey);
652 + }
653 + else if (FAILED(hr) && E_FILENOTFOUND != hr)
654 + {
655 + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_UNREGISTERED_FAILED, pRegistration->sczProviderKey, hr);
656 + }
657 +}
658 +
659 +// internal functions
660 +
661 +/********************************************************************
662 + SplitIgnoreDependencies - Splits a semicolon-delimited
663 + string into a list of unique dependencies to ignore.
664 +
665 +*********************************************************************/
666 +static HRESULT SplitIgnoreDependencies(
667 + __in_z LPCWSTR wzIgnoreDependencies,
668 + __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies,
669 + __inout LPUINT pcDependencies
670 + )
671 +{
672 + HRESULT hr = S_OK;
673 + LPWSTR wzContext = NULL;
674 + STRINGDICT_HANDLE sdIgnoreDependencies = NULL;
675 +
676 + // Create a dictionary to hold unique dependencies.
677 + hr = DictCreateStringList(&sdIgnoreDependencies, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE);
678 + ExitOnFailure(hr, "Failed to create the string dictionary.");
679 +
680 + // Parse through the semicolon-delimited tokens and add to the array.
681 + for (LPCWSTR wzToken = ::wcstok_s(const_cast<LPWSTR>(wzIgnoreDependencies), vcszIgnoreDependenciesDelim, &wzContext); wzToken; wzToken = ::wcstok_s(NULL, vcszIgnoreDependenciesDelim, &wzContext))
682 + {
683 + hr = DictKeyExists(sdIgnoreDependencies, wzToken);
684 + if (E_NOTFOUND != hr)
685 + {
686 + ExitOnFailure(hr, "Failed to check the dictionary of unique dependencies.");
687 + }
688 + else
689 + {
690 + hr = DepDependencyArrayAlloc(prgDependencies, pcDependencies, wzToken, NULL);
691 + ExitOnFailure(hr, "Failed to add \"%ls\" to the list of dependencies to ignore.", wzToken);
692 +
693 + hr = DictAddKey(sdIgnoreDependencies, wzToken);
694 + ExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", wzToken);
695 + }
696 + }
697 +
698 +LExit:
699 + ReleaseDict(sdIgnoreDependencies);
700 +
701 + return hr;
702 +}
703 +
704 +/********************************************************************
705 + JoinIgnoreDependencies - Joins a list of dependencies
706 + to ignore into a semicolon-delimited string of unique values.
707 +
708 +*********************************************************************/
709 +static HRESULT JoinIgnoreDependencies(
710 + __out_z LPWSTR* psczIgnoreDependencies,
711 + __in_ecount(cDependencies) const DEPENDENCY* rgDependencies,
712 + __in UINT cDependencies
713 + )
714 +{
715 + HRESULT hr = S_OK;
716 + STRINGDICT_HANDLE sdIgnoreDependencies = NULL;
717 +
718 + // Make sure we pass back an empty string if there are no dependencies.
719 + if (0 == cDependencies)
720 + {
721 + ExitFunction1(hr = S_OK);
722 + }
723 +
724 + // Create a dictionary to hold unique dependencies.
725 + hr = DictCreateStringList(&sdIgnoreDependencies, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE);
726 + ExitOnFailure(hr, "Failed to create the string dictionary.");
727 +
728 + for (UINT i = 0; i < cDependencies; ++i)
729 + {
730 + const DEPENDENCY* pDependency = &rgDependencies[i];
731 +
732 + hr = DictKeyExists(sdIgnoreDependencies, pDependency->sczKey);
733 + if (E_NOTFOUND != hr)
734 + {
735 + ExitOnFailure(hr, "Failed to check the dictionary of unique dependencies.");
736 + }
737 + else
738 + {
739 + if (0 < i)
740 + {
741 + hr = StrAllocConcat(psczIgnoreDependencies, vcszIgnoreDependenciesDelim, 1);
742 + ExitOnFailure(hr, "Failed to append the string delimiter.");
743 + }
744 +
745 + hr = StrAllocConcat(psczIgnoreDependencies, pDependency->sczKey, 0);
746 + ExitOnFailure(hr, "Failed to append the key \"%ls\".", pDependency->sczKey);
747 +
748 + hr = DictAddKey(sdIgnoreDependencies, pDependency->sczKey);
749 + ExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", pDependency->sczKey);
750 + }
751 + }
752 +
753 +LExit:
754 + ReleaseDict(sdIgnoreDependencies);
755 +
756 + return hr;
757 +}
758 +
759 +/********************************************************************
760 + GetIgnoredDependents - Combines the current bundle's
761 + provider key, packages' provider keys that are being uninstalled,
762 + and any ignored dependencies authored for packages into a string
763 + list to pass to deputil.
764 +
765 +*********************************************************************/
766 +static HRESULT GetIgnoredDependents(
767 + __in const BURN_PACKAGE* pPackage,
768 + __in const BURN_PLAN* pPlan,
769 + __deref_inout STRINGDICT_HANDLE* psdIgnoredDependents
770 + )
771 +{
772 + HRESULT hr = S_OK;
773 + LPWSTR sczIgnoreDependencies = NULL;
774 +
775 + // Create the dictionary and add the bundle provider key initially.
776 + hr = DictCreateStringList(psdIgnoredDependents, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE);
777 + ExitOnFailure(hr, "Failed to create the string dictionary.");
778 +
779 + hr = DictAddKey(*psdIgnoredDependents, pPlan->wzBundleProviderKey);
780 + ExitOnFailure(hr, "Failed to add the bundle provider key \"%ls\" to the list of ignored dependencies.", pPlan->wzBundleProviderKey);
781 +
782 + // Add previously planned package providers to the dictionary.
783 + for (DWORD i = 0; i < pPlan->cPlannedProviders; ++i)
784 + {
785 + const DEPENDENCY* pDependency = &pPlan->rgPlannedProviders[i];
786 +
787 + hr = DictAddKey(*psdIgnoredDependents, pDependency->sczKey);
788 + ExitOnFailure(hr, "Failed to add the package provider key \"%ls\" to the list of ignored dependencies.", pDependency->sczKey);
789 + }
790 +
791 + // Get the IGNOREDEPENDENCIES property if defined.
792 + hr = PackageGetProperty(pPackage, DEPENDENCY_IGNOREDEPENDENCIES, &sczIgnoreDependencies);
793 + if (E_NOTFOUND != hr)
794 + {
795 + ExitOnFailure(hr, "Failed to get the package property: %ls", DEPENDENCY_IGNOREDEPENDENCIES);
796 +
797 + hr = DependencyAddIgnoreDependencies(*psdIgnoredDependents, sczIgnoreDependencies);
798 + ExitOnFailure(hr, "Failed to add the authored ignored dependencies to the cumulative list of ignored dependencies.");
799 + }
800 + else
801 + {
802 + hr = S_OK;
803 + }
804 +
805 +LExit:
806 + ReleaseStr(sczIgnoreDependencies);
807 +
808 + return hr;
809 +}
810 +
811 +/********************************************************************
812 + GetProviderId - Gets the ID of the package given the provider key.
813 +
814 +*********************************************************************/
815 +static HRESULT GetProviderInformation(
816 + __in HKEY hkRoot,
817 + __in_z LPCWSTR wzProviderKey,
818 + __deref_opt_out_z_opt LPWSTR* psczProviderKey,
819 + __deref_opt_out_z_opt LPWSTR* psczId
820 + )
821 +{
822 + HRESULT hr = S_OK;
823 + LPWSTR sczId = NULL;
824 +
825 + hr = DepGetProviderInformation(hkRoot, wzProviderKey, &sczId, NULL, NULL);
826 + if (E_NOTFOUND == hr)
827 + {
828 + ExitFunction();
829 + }
830 + ExitOnFailure(hr, "Failed to get the provider key package id.");
831 +
832 + // If the id was registered return it and exit.
833 + if (sczId && *sczId)
834 + {
835 + if (psczProviderKey)
836 + {
837 + hr = StrAllocString(psczProviderKey, wzProviderKey, 0);
838 + ExitOnFailure(hr, "Failed to copy the provider key.");
839 + }
840 +
841 + if (psczId)
842 + {
843 + *psczId = sczId;
844 + sczId = NULL;
845 + }
846 +
847 + ExitFunction();
848 + }
849 + else
850 + {
851 + hr = E_NOTFOUND;
852 + }
853 +
854 +LExit:
855 + ReleaseStr(sczId);
856 +
857 + return hr;
858 +}
859 +
860 +/********************************************************************
861 + CalculateDependencyActionStates - Calculates the dependency execute and
862 + rollback actions for a package.
863 +
864 +*********************************************************************/
865 +static void CalculateDependencyActionStates(
866 + __in const BURN_PACKAGE* pPackage,
867 + __in const BOOTSTRAPPER_ACTION action,
868 + __out BURN_DEPENDENCY_ACTION* pDependencyExecuteAction,
869 + __out BURN_DEPENDENCY_ACTION* pDependencyRollbackAction
870 + )
871 +{
872 + switch (action)
873 + {
874 + case BOOTSTRAPPER_ACTION_UNINSTALL:
875 + // Always remove the dependency when uninstalling a bundle even if the package is absent.
876 + *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_UNREGISTER;
877 + break;
878 + case BOOTSTRAPPER_ACTION_INSTALL: __fallthrough;
879 + case BOOTSTRAPPER_ACTION_CACHE:
880 + // Always remove the dependency during rollback when installing a bundle.
881 + *pDependencyRollbackAction = BURN_DEPENDENCY_ACTION_UNREGISTER;
882 + __fallthrough;
883 + case BOOTSTRAPPER_ACTION_MODIFY: __fallthrough;
884 + case BOOTSTRAPPER_ACTION_REPAIR:
885 + switch (pPackage->execute)
886 + {
887 + case BOOTSTRAPPER_ACTION_STATE_NONE:
888 + switch (pPackage->requested)
889 + {
890 + case BOOTSTRAPPER_REQUEST_STATE_NONE:
891 + // Register if a newer, compatible package is already installed.
892 + switch (pPackage->currentState)
893 + {
894 + case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE:
895 + if (!PackageProviderExists(pPackage))
896 + {
897 + break;
898 + }
899 + __fallthrough;
900 + case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED:
901 + *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_REGISTER;
902 + break;
903 + }
904 + break;
905 + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
906 + case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
907 + // Register if the package is requested but already installed.
908 + switch (pPackage->currentState)
909 + {
910 + case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE:
911 + if (!PackageProviderExists(pPackage))
912 + {
913 + break;
914 + }
915 + __fallthrough;
916 + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough;
917 + case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED:
918 + *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_REGISTER;
919 + break;
920 + }
921 + break;
922 + }
923 + break;
924 + case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
925 + *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_UNREGISTER;
926 + break;
927 + case BOOTSTRAPPER_ACTION_STATE_INSTALL: __fallthrough;
928 + case BOOTSTRAPPER_ACTION_STATE_MODIFY: __fallthrough;
929 + case BOOTSTRAPPER_ACTION_STATE_REPAIR: __fallthrough;
930 + case BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE: __fallthrough;
931 + case BOOTSTRAPPER_ACTION_STATE_MAJOR_UPGRADE: __fallthrough;
932 + case BOOTSTRAPPER_ACTION_STATE_PATCH:
933 + *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_REGISTER;
934 + break;
935 + }
936 + break;
937 + }
938 +
939 + switch (*pDependencyExecuteAction)
940 + {
941 + case BURN_DEPENDENCY_ACTION_REGISTER:
942 + switch (pPackage->currentState)
943 + {
944 + case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough;
945 + case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: __fallthrough;
946 + case BOOTSTRAPPER_PACKAGE_STATE_CACHED:
947 + *pDependencyRollbackAction = BURN_DEPENDENCY_ACTION_UNREGISTER;
948 + break;
949 + }
950 + break;
951 + case BURN_DEPENDENCY_ACTION_UNREGISTER:
952 + switch (pPackage->currentState)
953 + {
954 + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough;
955 + case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED:
956 + *pDependencyRollbackAction = BURN_DEPENDENCY_ACTION_REGISTER;
957 + break;
958 + }
959 + break;
960 + }
961 +}
962 +
963 +/********************************************************************
964 + AddPackageDependencyActions - Adds the dependency execute and rollback
965 + actions to the plan.
966 +
967 +*********************************************************************/
968 +static HRESULT AddPackageDependencyActions(
969 + __in_opt DWORD *pdwInsertSequence,
970 + __in const BURN_PACKAGE* pPackage,
971 + __in BURN_PLAN* pPlan,
972 + __in const BURN_DEPENDENCY_ACTION dependencyExecuteAction,
973 + __in const BURN_DEPENDENCY_ACTION dependencyRollbackAction
974 + )
975 +{
976 + HRESULT hr = S_OK;
977 + BURN_EXECUTE_ACTION* pAction = NULL;
978 +
979 + // Add the rollback plan.
980 + if (BURN_DEPENDENCY_ACTION_NONE != dependencyRollbackAction)
981 + {
982 + hr = PlanAppendRollbackAction(pPlan, &pAction);
983 + ExitOnFailure(hr, "Failed to append rollback action.");
984 +
985 + pAction->type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY;
986 + pAction->packageDependency.pPackage = const_cast<BURN_PACKAGE*>(pPackage);
987 + pAction->packageDependency.action = dependencyRollbackAction;
988 +
989 + hr = StrAllocString(&pAction->packageDependency.sczBundleProviderKey, pPlan->wzBundleProviderKey, 0);
990 + ExitOnFailure(hr, "Failed to copy the bundle dependency provider.");
991 +
992 + // Put a checkpoint before the execute action so that rollback happens
993 + // if execute fails.
994 + hr = PlanExecuteCheckpoint(pPlan);
995 + ExitOnFailure(hr, "Failed to plan dependency checkpoint action.");
996 + }
997 +
998 + // Add the execute plan. This comes after rollback so if something goes wrong
999 + // rollback will try to clean up after us correctly.
1000 + if (BURN_DEPENDENCY_ACTION_NONE != dependencyExecuteAction)
1001 + {
1002 + if (NULL != pdwInsertSequence)
1003 + {
1004 + hr = PlanInsertExecuteAction(*pdwInsertSequence, pPlan, &pAction);
1005 + ExitOnFailure(hr, "Failed to insert execute action.");
1006 +
1007 + // Always move the sequence after this dependency action so the dependency registration
1008 + // stays in front of the inserted actions.
1009 + ++(*pdwInsertSequence);
1010 + }
1011 + else
1012 + {
1013 + hr = PlanAppendExecuteAction(pPlan, &pAction);
1014 + ExitOnFailure(hr, "Failed to append execute action.");
1015 + }
1016 +
1017 + pAction->type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY;
1018 + pAction->packageDependency.pPackage = const_cast<BURN_PACKAGE*>(pPackage);
1019 + pAction->packageDependency.action = dependencyExecuteAction;
1020 +
1021 + hr = StrAllocString(&pAction->packageDependency.sczBundleProviderKey, pPlan->wzBundleProviderKey, 0);
1022 + ExitOnFailure(hr, "Failed to copy the bundle dependency provider.");
1023 + }
1024 +
1025 +LExit:
1026 + return hr;
1027 +}
1028 +
1029 +static HRESULT RegisterPackageProvider(
1030 + __in const BURN_PACKAGE* pPackage
1031 + )
1032 +{
1033 + HRESULT hr = S_OK;
1034 + LPWSTR wzId = NULL;
1035 + HKEY hkRoot = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
1036 +
1037 + if (pPackage->rgDependencyProviders)
1038 + {
1039 + if (BURN_PACKAGE_TYPE_MSI == pPackage->type)
1040 + {
1041 + wzId = pPackage->Msi.sczProductCode;
1042 + }
1043 + else if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
1044 + {
1045 + wzId = pPackage->Msp.sczPatchCode;
1046 + }
1047 +
1048 + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
1049 + {
1050 + const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i];
1051 +
1052 + if (!pProvider->fImported)
1053 + {
1054 + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_REGISTER, pProvider->sczKey, pProvider->sczVersion, pPackage->sczId);
1055 +
1056 + hr = DepRegisterDependency(hkRoot, pProvider->sczKey, pProvider->sczVersion, pProvider->sczDisplayName, wzId, 0);
1057 + ExitOnFailure(hr, "Failed to register the package dependency provider: %ls", pProvider->sczKey);
1058 + }
1059 + }
1060 + }
1061 +
1062 +LExit:
1063 + if (!pPackage->fVital)
1064 + {
1065 + hr = S_OK;
1066 + }
1067 +
1068 + return hr;
1069 +}
1070 +
1071 +/********************************************************************
1072 + UnregisterPackageProvider - Removes each dependency provider
1073 + for the package (if not imported from the package itself).
1074 +
1075 + Note: Does not check for existing dependents before removing the key.
1076 +*********************************************************************/
1077 +static void UnregisterPackageProvider(
1078 + __in const BURN_PACKAGE* pPackage
1079 + )
1080 +{
1081 + HRESULT hr = S_OK;
1082 + HKEY hkRoot = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
1083 +
1084 + if (pPackage->rgDependencyProviders)
1085 + {
1086 + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
1087 + {
1088 + const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i];
1089 +
1090 + if (!pProvider->fImported)
1091 + {
1092 + hr = DepUnregisterDependency(hkRoot, pProvider->sczKey);
1093 + if (SUCCEEDED(hr))
1094 + {
1095 + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_UNREGISTERED, pProvider->sczKey, pPackage->sczId);
1096 + }
1097 + else if (FAILED(hr) && E_FILENOTFOUND != hr)
1098 + {
1099 + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_UNREGISTERED_FAILED, pProvider->sczKey, pPackage->sczId, hr);
1100 + }
1101 + }
1102 + }
1103 + }
1104 +}
1105 +
1106 +/********************************************************************
1107 + RegisterPackageDependency - Registers the provider key
1108 + as a dependent of a package.
1109 +
1110 +*********************************************************************/
1111 +static HRESULT RegisterPackageDependency(
1112 + __in BOOL fPerMachine,
1113 + __in const BURN_PACKAGE* pPackage,
1114 + __in_z LPCWSTR wzDependentProviderKey
1115 + )
1116 +{
1117 + HRESULT hr = S_OK;
1118 + HKEY hkRoot = fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
1119 +
1120 + // Do not register a dependency on a package in a different install context.
1121 + if (fPerMachine != pPackage->fPerMachine)
1122 + {
1123 + LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_SKIP_WRONGSCOPE, pPackage->sczId, LoggingPerMachineToString(fPerMachine), LoggingPerMachineToString(pPackage->fPerMachine));
1124 + ExitFunction1(hr = S_OK);
1125 + }
1126 +
1127 + if (pPackage->rgDependencyProviders)
1128 + {
1129 + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
1130 + {
1131 + const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i];
1132 +
1133 + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_REGISTER_DEPENDENCY, wzDependentProviderKey, pProvider->sczKey, pPackage->sczId);
1134 +
1135 + hr = DepRegisterDependent(hkRoot, pProvider->sczKey, wzDependentProviderKey, NULL, NULL, 0);
1136 + if (E_FILENOTFOUND != hr || pPackage->fVital)
1137 + {
1138 + ExitOnFailure(hr, "Failed to register the dependency on package dependency provider: %ls", pProvider->sczKey);
1139 + }
1140 + else
1141 + {
1142 + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_SKIP_MISSING, pProvider->sczKey, pPackage->sczId);
1143 + hr = S_OK;
1144 + }
1145 + }
1146 + }
1147 +
1148 +LExit:
1149 + return hr;
1150 +}
1151 +
1152 +/********************************************************************
1153 + UnregisterPackageDependency - Unregisters the provider key
1154 + as a dependent of a package.
1155 +
1156 +*********************************************************************/
1157 +static void UnregisterPackageDependency(
1158 + __in BOOL fPerMachine,
1159 + __in const BURN_PACKAGE* pPackage,
1160 + __in_z LPCWSTR wzDependentProviderKey
1161 + )
1162 +{
1163 + HRESULT hr = S_OK;
1164 + HKEY hkRoot = fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
1165 +
1166 + // Should be no registration to remove since we don't write keys across contexts.
1167 + if (fPerMachine != pPackage->fPerMachine)
1168 + {
1169 + LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_SKIP_WRONGSCOPE, pPackage->sczId, LoggingPerMachineToString(fPerMachine), LoggingPerMachineToString(pPackage->fPerMachine));
1170 + return;
1171 + }
1172 +
1173 + // Loop through each package provider and remove the bundle dependency key.
1174 + if (pPackage->rgDependencyProviders)
1175 + {
1176 + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
1177 + {
1178 + const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i];
1179 +
1180 + hr = DepUnregisterDependent(hkRoot, pProvider->sczKey, wzDependentProviderKey);
1181 + if (SUCCEEDED(hr))
1182 + {
1183 + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_UNREGISTERED_DEPENDENCY, wzDependentProviderKey, pProvider->sczKey, pPackage->sczId);
1184 + }
1185 + else if (FAILED(hr) && E_FILENOTFOUND != hr)
1186 + {
1187 + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_UNREGISTERED_DEPENDENCY_FAILED, wzDependentProviderKey, pProvider->sczKey, pPackage->sczId, hr);
1188 + }
1189 + }
1190 + }
1191 +}
1192 +
1193 +/********************************************************************
1194 + PackageProviderExists - Checks if a package provider is registered.
1195 +
1196 +*********************************************************************/
1197 +static BOOL PackageProviderExists(
1198 + __in const BURN_PACKAGE* pPackage
1199 + )
1200 +{
1201 + HRESULT hr = DependencyDetectProviderKeyPackageId(pPackage, NULL, NULL);
1202 + return SUCCEEDED(hr);
1203 +}
src/engine/dependency.h new
+176
@@ -0,0 +1,176 @@
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 +#if defined(__cplusplus)
6 +extern "C" {
7 +#endif
8 +
9 +// constants
10 +
11 +const LPCWSTR DEPENDENCY_IGNOREDEPENDENCIES = L"IGNOREDEPENDENCIES";
12 +
13 +
14 +// function declarations
15 +
16 +/********************************************************************
17 + DependencyUninitialize - Frees and zeros memory allocated in the
18 + dependency.
19 +
20 +*********************************************************************/
21 +void DependencyUninitialize(
22 + __in BURN_DEPENDENCY_PROVIDER* pProvider
23 + );
24 +
25 +/********************************************************************
26 + DependencyParseProvidersFromXml - Parses dependency information
27 + from the manifest for the specified package.
28 +
29 +*********************************************************************/
30 +HRESULT DependencyParseProvidersFromXml(
31 + __in BURN_PACKAGE* pPackage,
32 + __in IXMLDOMNode* pixnPackage
33 + );
34 +
35 +/********************************************************************
36 + DependencyDetectProviderKeyPackageId - Detect if the provider key is
37 + registered and if so what package code is registered.
38 +
39 + Note: Returns E_NOTFOUND if the provider key is not registered.
40 +*********************************************************************/
41 +HRESULT DependencyDetectProviderKeyPackageId(
42 + __in const BURN_PACKAGE* pPackage,
43 + __deref_opt_out_z_opt LPWSTR* psczProviderKey,
44 + __deref_opt_out_z_opt LPWSTR* psczId
45 + );
46 +
47 +/********************************************************************
48 + DependencyDetectProviderKeyBundleId - Detect if the provider key is
49 + registered and if so what bundle is registered.
50 +
51 + Note: Returns E_NOTFOUND if the provider key is not registered.
52 +*********************************************************************/
53 +HRESULT DependencyDetectProviderKeyBundleId(
54 + __in BURN_REGISTRATION* pRegistration
55 + );
56 +
57 +/********************************************************************
58 + DependencyPlanInitialize - Initializes the plan.
59 +
60 +*********************************************************************/
61 +HRESULT DependencyPlanInitialize(
62 + __in const BURN_ENGINE_STATE* pEngineState,
63 + __in BURN_PLAN* pPlan
64 + );
65 +
66 +/********************************************************************
67 + DependencyAllocIgnoreDependencies - Allocates the dependencies to
68 + ignore as a semicolon-delimited string.
69 +
70 +*********************************************************************/
71 +HRESULT DependencyAllocIgnoreDependencies(
72 + __in const BURN_PLAN *pPlan,
73 + __out_z LPWSTR* psczIgnoreDependencies
74 + );
75 +
76 +/********************************************************************
77 + DependencyAddIgnoreDependencies - Populates the ignore dependency
78 + names.
79 +
80 +*********************************************************************/
81 +HRESULT DependencyAddIgnoreDependencies(
82 + __in STRINGDICT_HANDLE sdIgnoreDependencies,
83 + __in_z LPCWSTR wzAddIgnoreDependencies
84 + );
85 +
86 +/********************************************************************
87 + DependencyDependentExists - Checks to see if the provider key is
88 + already dependent on this bundle.
89 +
90 +*********************************************************************/
91 +BOOL DependencyDependentExists(
92 + __in const BURN_REGISTRATION* pRegistration,
93 + __in_z LPCWSTR wzDependentProviderKey
94 + );
95 +
96 +/********************************************************************
97 + DependencyPlanPackageBegin - Updates the dependency registration
98 + action depending on the calculated state for the package.
99 +
100 +*********************************************************************/
101 +HRESULT DependencyPlanPackageBegin(
102 + __in BOOL fPerMachine,
103 + __in BURN_PACKAGE* pPackage,
104 + __in BURN_PLAN* pPlan
105 + );
106 +
107 +/********************************************************************
108 + DependencyPlanPackage - adds dependency related actions to the plan
109 + for this package.
110 +
111 +*********************************************************************/
112 +HRESULT DependencyPlanPackage(
113 + __in_opt DWORD *pdwInsertSequence,
114 + __in const BURN_PACKAGE* pPackage,
115 + __in BURN_PLAN* pPlan
116 + );
117 +
118 +/********************************************************************
119 + DependencyPlanPackageComplete - Updates the dependency registration
120 + action depending on the planned action for the package.
121 +
122 +*********************************************************************/
123 +HRESULT DependencyPlanPackageComplete(
124 + __in BURN_PACKAGE* pPackage,
125 + __in BURN_PLAN* pPlan
126 + );
127 +
128 +/********************************************************************
129 + DependencyExecutePackageProviderAction - Registers or unregisters
130 + provider information for the package contained within the action.
131 +
132 +*********************************************************************/
133 +HRESULT DependencyExecutePackageProviderAction(
134 + __in const BURN_EXECUTE_ACTION* pAction
135 + );
136 +
137 +/********************************************************************
138 + DependencyExecutePackageDependencyAction - Registers or unregisters
139 + dependency information for the package contained within the action.
140 +
141 +*********************************************************************/
142 +HRESULT DependencyExecutePackageDependencyAction(
143 + __in BOOL fPerMachine,
144 + __in const BURN_EXECUTE_ACTION* pAction
145 + );
146 +
147 +/********************************************************************
148 + DependencyRegisterBundle - Registers the bundle dependency provider.
149 +
150 +*********************************************************************/
151 +HRESULT DependencyRegisterBundle(
152 + __in const BURN_REGISTRATION* pRegistration
153 + );
154 +
155 +/********************************************************************
156 + DependencyProcessDependentRegistration - Registers or unregisters dependents
157 + on the bundle based on the action.
158 +
159 +*********************************************************************/
160 +HRESULT DependencyProcessDependentRegistration(
161 + __in const BURN_REGISTRATION* pRegistration,
162 + __in const BURN_DEPENDENT_REGISTRATION_ACTION* pAction
163 + );
164 +
165 +/********************************************************************
166 + DependencyUnregisterBundle - Removes the bundle dependency provider.
167 +
168 + Note: Does not check for existing dependents before removing the key.
169 +*********************************************************************/
170 +void DependencyUnregisterBundle(
171 + __in const BURN_REGISTRATION* pRegistration
172 + );
173 +
174 +#if defined(__cplusplus)
175 +}
176 +#endif
src/engine/detect.cpp new
+431
@@ -0,0 +1,431 @@
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 +typedef struct _DETECT_AUTHENTICATION_REQUIRED_DATA
6 +{
7 + BURN_USER_EXPERIENCE* pUX;
8 + LPCWSTR wzPackageOrContainerId;
9 +} DETECT_AUTHENTICATION_REQUIRED_DATA;
10 +
11 +// internal function definitions
12 +static HRESULT AuthenticationRequired(
13 + __in LPVOID pData,
14 + __in HINTERNET hUrl,
15 + __in long lHttpCode,
16 + __out BOOL* pfRetrySend,
17 + __out BOOL* pfRetry
18 + );
19 +
20 +static HRESULT DetectAtomFeedUpdate(
21 + __in_z LPCWSTR wzBundleId,
22 + __in BURN_USER_EXPERIENCE* pUX,
23 + __in BURN_UPDATE* pUpdate
24 + );
25 +
26 +static HRESULT DownloadUpdateFeed(
27 + __in_z LPCWSTR wzBundleId,
28 + __in BURN_USER_EXPERIENCE* pUX,
29 + __in BURN_UPDATE* pUpdate,
30 + __deref_inout_z LPWSTR* psczTempFile
31 + );
32 +
33 +// function definitions
34 +
35 +extern "C" void DetectReset(
36 + __in BURN_REGISTRATION* pRegistration,
37 + __in BURN_PACKAGES* pPackages
38 + )
39 +{
40 + RelatedBundlesUninitialize(&pRegistration->relatedBundles);
41 + ReleaseNullStr(pRegistration->sczDetectedProviderKeyBundleId);
42 + pRegistration->fEnabledForwardCompatibleBundle = FALSE;
43 + PackageUninitialize(&pRegistration->forwardCompatibleBundle);
44 +
45 + for (DWORD iPackage = 0; iPackage < pPackages->cPackages; ++iPackage)
46 + {
47 + BURN_PACKAGE* pPackage = pPackages->rgPackages + iPackage;
48 +
49 + pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN;
50 +
51 + pPackage->cache = BURN_CACHE_STATE_NONE;
52 + for (DWORD iPayload = 0; iPayload < pPackage->cPayloads; ++iPayload)
53 + {
54 + BURN_PACKAGE_PAYLOAD* pPayload = pPackage->rgPayloads + iPayload;
55 + pPayload->fCached = FALSE;
56 + }
57 +
58 + if (BURN_PACKAGE_TYPE_MSI == pPackage->type)
59 + {
60 + for (DWORD iFeature = 0; iFeature < pPackage->Msi.cFeatures; ++iFeature)
61 + {
62 + BURN_MSIFEATURE* pFeature = pPackage->Msi.rgFeatures + iFeature;
63 +
64 + pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN;
65 + }
66 +
67 + pPackage->Msi.fCompatibleInstalled = FALSE;
68 + }
69 + else if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
70 + {
71 + ReleaseNullMem(pPackage->Msp.rgTargetProducts);
72 + pPackage->Msp.cTargetProductCodes = 0;
73 + }
74 + }
75 +
76 + for (DWORD iPatchInfo = 0; iPatchInfo < pPackages->cPatchInfo; ++iPatchInfo)
77 + {
78 + MSIPATCHSEQUENCEINFOW* pPatchInfo = pPackages->rgPatchInfo + iPatchInfo;
79 + pPatchInfo->dwOrder = 0;
80 + pPatchInfo->uStatus = 0;
81 + }
82 +}
83 +
84 +extern "C" HRESULT DetectForwardCompatibleBundle(
85 + __in BURN_USER_EXPERIENCE* pUX,
86 + __in BOOTSTRAPPER_COMMAND* pCommand,
87 + __in BURN_REGISTRATION* pRegistration
88 + )
89 +{
90 + HRESULT hr = S_OK;
91 + BOOL fRecommendIgnore = TRUE;
92 + BOOL fIgnoreBundle = FALSE;
93 +
94 + if (pRegistration->sczDetectedProviderKeyBundleId &&
95 + CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczDetectedProviderKeyBundleId, -1, pRegistration->sczId, -1))
96 + {
97 + // Only change the recommendation if an active parent was provided.
98 + if (pRegistration->sczActiveParent && *pRegistration->sczActiveParent)
99 + {
100 + // On install, recommend running the forward compatible bundle because there is an active parent. This
101 + // will essentially register the parent with the forward compatible bundle.
102 + if (BOOTSTRAPPER_ACTION_INSTALL == pCommand->action)
103 + {
104 + fRecommendIgnore = FALSE;
105 + }
106 + else if (BOOTSTRAPPER_ACTION_UNINSTALL == pCommand->action ||
107 + BOOTSTRAPPER_ACTION_MODIFY == pCommand->action ||
108 + BOOTSTRAPPER_ACTION_REPAIR == pCommand->action)
109 + {
110 + // When modifying the bundle, only recommend running the forward compatible bundle if the parent
111 + // is already registered as a dependent of the provider key.
112 + if (DependencyDependentExists(pRegistration, pRegistration->sczActiveParent))
113 + {
114 + fRecommendIgnore = FALSE;
115 + }
116 + }
117 + }
118 +
119 + for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle)
120 + {
121 + BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + iRelatedBundle;
122 + fIgnoreBundle = fRecommendIgnore;
123 +
124 + if (BOOTSTRAPPER_RELATION_UPGRADE == pRelatedBundle->relationType &&
125 + pRegistration->qwVersion <= pRelatedBundle->qwVersion &&
126 + CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczDetectedProviderKeyBundleId, -1, pRelatedBundle->package.sczId, -1))
127 + {
128 + hr = UserExperienceOnDetectForwardCompatibleBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->qwVersion, &fIgnoreBundle);
129 + ExitOnRootFailure(hr, "BA aborted detect forward compatible bundle.");
130 +
131 + if (!fIgnoreBundle)
132 + {
133 + hr = PseudoBundleInitializePassthrough(&pRegistration->forwardCompatibleBundle, pCommand, NULL, pRegistration->sczActiveParent, pRegistration->sczAncestors, &pRelatedBundle->package);
134 + ExitOnFailure(hr, "Failed to initialize update bundle.");
135 +
136 + pRegistration->fEnabledForwardCompatibleBundle = TRUE;
137 + }
138 +
139 + LogId(REPORT_STANDARD, MSG_DETECTED_FORWARD_COMPATIBLE_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), LoggingVersionToString(pRelatedBundle->qwVersion), LoggingBoolToString(pRegistration->fEnabledForwardCompatibleBundle));
140 + break;
141 + }
142 + }
143 + }
144 +
145 +LExit:
146 + return hr;
147 +}
148 +
149 +extern "C" HRESULT DetectReportRelatedBundles(
150 + __in BURN_USER_EXPERIENCE* pUX,
151 + __in BURN_REGISTRATION* pRegistration,
152 + __in BOOTSTRAPPER_RELATION_TYPE relationType,
153 + __in BOOTSTRAPPER_ACTION action
154 + )
155 +{
156 + HRESULT hr = S_OK;
157 +
158 + for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle)
159 + {
160 + const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + iRelatedBundle;
161 + BOOTSTRAPPER_RELATED_OPERATION operation = BOOTSTRAPPER_RELATED_OPERATION_NONE;
162 +
163 + switch (pRelatedBundle->relationType)
164 + {
165 + case BOOTSTRAPPER_RELATION_UPGRADE:
166 + if (BOOTSTRAPPER_RELATION_UPGRADE != relationType && BOOTSTRAPPER_ACTION_UNINSTALL < action)
167 + {
168 + if (pRegistration->qwVersion > pRelatedBundle->qwVersion)
169 + {
170 + operation = BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE;
171 + }
172 + else if (pRegistration->qwVersion < pRelatedBundle->qwVersion)
173 + {
174 + operation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE;
175 + }
176 + }
177 + break;
178 +
179 + case BOOTSTRAPPER_RELATION_PATCH: __fallthrough;
180 + case BOOTSTRAPPER_RELATION_ADDON:
181 + if (BOOTSTRAPPER_ACTION_UNINSTALL == action)
182 + {
183 + operation = BOOTSTRAPPER_RELATED_OPERATION_REMOVE;
184 + }
185 + else if (BOOTSTRAPPER_ACTION_INSTALL == action || BOOTSTRAPPER_ACTION_MODIFY == action)
186 + {
187 + operation = BOOTSTRAPPER_RELATED_OPERATION_INSTALL;
188 + }
189 + else if (BOOTSTRAPPER_ACTION_REPAIR == action)
190 + {
191 + operation = BOOTSTRAPPER_RELATED_OPERATION_REPAIR;
192 + }
193 + break;
194 +
195 + case BOOTSTRAPPER_RELATION_DETECT: __fallthrough;
196 + case BOOTSTRAPPER_RELATION_DEPENDENT:
197 + break;
198 +
199 + default:
200 + hr = E_FAIL;
201 + ExitOnRootFailure(hr, "Unexpected relation type encountered: %d", pRelatedBundle->relationType);
202 + break;
203 + }
204 +
205 + LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), LoggingVersionToString(pRelatedBundle->qwVersion), LoggingRelatedOperationToString(operation));
206 +
207 + hr = UserExperienceOnDetectRelatedBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->qwVersion, operation);
208 + ExitOnRootFailure(hr, "BA aborted detect related bundle.");
209 + }
210 +
211 +LExit:
212 + return hr;
213 +}
214 +
215 +extern "C" HRESULT DetectUpdate(
216 + __in_z LPCWSTR wzBundleId,
217 + __in BURN_USER_EXPERIENCE* pUX,
218 + __in BURN_UPDATE* pUpdate
219 + )
220 +{
221 + HRESULT hr = S_OK;
222 + BOOL fBeginCalled = FALSE;
223 + BOOL fSkip = TRUE;
224 + BOOL fIgnoreError = FALSE;
225 +
226 + // If no update source was specified, skip update detection.
227 + if (!pUpdate->sczUpdateSource || !*pUpdate->sczUpdateSource)
228 + {
229 + ExitFunction();
230 + }
231 +
232 + fBeginCalled = TRUE;
233 + hr = UserExperienceOnDetectUpdateBegin(pUX, pUpdate->sczUpdateSource, &fSkip);
234 + ExitOnRootFailure(hr, "BA aborted detect update begin.");
235 +
236 + if (!fSkip)
237 + {
238 + hr = DetectAtomFeedUpdate(wzBundleId, pUX, pUpdate);
239 + ExitOnFailure(hr, "Failed to detect atom feed update.");
240 + }
241 +
242 +LExit:
243 + if (fBeginCalled)
244 + {
245 + UserExperienceOnDetectUpdateComplete(pUX, hr, &fIgnoreError);
246 + if (fIgnoreError)
247 + {
248 + hr = S_OK;
249 + }
250 + }
251 +
252 + return hr;
253 +}
254 +
255 +static HRESULT AuthenticationRequired(
256 + __in LPVOID pData,
257 + __in HINTERNET hUrl,
258 + __in long lHttpCode,
259 + __out BOOL* pfRetrySend,
260 + __out BOOL* pfRetry
261 + )
262 +{
263 + Assert(401 == lHttpCode || 407 == lHttpCode);
264 +
265 + HRESULT hr = S_OK;
266 + DWORD er = ERROR_SUCCESS;
267 + BOOTSTRAPPER_ERROR_TYPE errorType = (401 == lHttpCode) ? BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_SERVER : BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_PROXY;
268 + LPWSTR sczError = NULL;
269 + DETECT_AUTHENTICATION_REQUIRED_DATA* pAuthenticationData = reinterpret_cast<DETECT_AUTHENTICATION_REQUIRED_DATA*>(pData);
270 + int nResult = IDNOACTION;
271 +
272 + *pfRetrySend = FALSE;
273 + *pfRetry = FALSE;
274 +
275 + hr = StrAllocFromError(&sczError, HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED), NULL);
276 + ExitOnFailure(hr, "Failed to allocation error string.");
277 +
278 + UserExperienceOnError(pAuthenticationData->pUX, errorType, pAuthenticationData->wzPackageOrContainerId, ERROR_ACCESS_DENIED, sczError, MB_RETRYTRYAGAIN, 0, NULL, &nResult); // ignore return value.
279 + nResult = UserExperienceCheckExecuteResult(pAuthenticationData->pUX, FALSE, MB_RETRYTRYAGAIN, nResult);
280 + if (IDTRYAGAIN == nResult && pAuthenticationData->pUX->hwndDetect)
281 + {
282 + er = ::InternetErrorDlg(pAuthenticationData->pUX->hwndDetect, hUrl, ERROR_INTERNET_INCORRECT_PASSWORD, FLAGS_ERROR_UI_FILTER_FOR_ERRORS | FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS | FLAGS_ERROR_UI_FLAGS_GENERATE_DATA, NULL);
283 + if (ERROR_SUCCESS == er || ERROR_CANCELLED == er)
284 + {
285 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
286 + }
287 + else if (ERROR_INTERNET_FORCE_RETRY == er)
288 + {
289 + *pfRetrySend = TRUE;
290 + hr = S_OK;
291 + }
292 + else
293 + {
294 + hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
295 + }
296 + }
297 + else if (IDRETRY == nResult)
298 + {
299 + *pfRetry = TRUE;
300 + hr = S_OK;
301 + }
302 + else
303 + {
304 + hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
305 + }
306 +
307 +LExit:
308 + ReleaseStr(sczError);
309 +
310 + return hr;
311 +}
312 +
313 +static HRESULT DownloadUpdateFeed(
314 + __in_z LPCWSTR wzBundleId,
315 + __in BURN_USER_EXPERIENCE* pUX,
316 + __in BURN_UPDATE* pUpdate,
317 + __deref_inout_z LPWSTR* psczTempFile
318 + )
319 +{
320 + HRESULT hr = S_OK;
321 + DOWNLOAD_SOURCE downloadSource = { };
322 + DOWNLOAD_CACHE_CALLBACK cacheCallback = { };
323 + DOWNLOAD_AUTHENTICATION_CALLBACK authenticationCallback = { };
324 + DETECT_AUTHENTICATION_REQUIRED_DATA authenticationData = { };
325 + LPWSTR sczUpdateId = NULL;
326 + LPWSTR sczError = NULL;
327 + DWORD64 qwDownloadSize = 0;
328 +
329 + // Always do our work in the working folder, even if cached.
330 + hr = PathCreateTimeBasedTempFile(NULL, L"UpdateFeed", NULL, L"xml", psczTempFile, NULL);
331 + ExitOnFailure(hr, "Failed to create UpdateFeed based on current system time.");
332 +
333 + // Do we need a means of the BA to pass in a user name and password? If so, we should copy it to downloadSource here
334 + hr = StrAllocString(&downloadSource.sczUrl, pUpdate->sczUpdateSource, 0);
335 + ExitOnFailure(hr, "Failed to copy update url.");
336 +
337 + cacheCallback.pfnProgress = NULL; //UpdateProgressRoutine;
338 + cacheCallback.pfnCancel = NULL; // TODO: set this
339 + cacheCallback.pv = NULL; //pProgress;
340 +
341 + authenticationData.pUX = pUX;
342 + authenticationData.wzPackageOrContainerId = wzBundleId;
343 +
344 + authenticationCallback.pv = static_cast<LPVOID>(&authenticationData);
345 + authenticationCallback.pfnAuthenticate = &AuthenticationRequired;
346 +
347 + hr = DownloadUrl(&downloadSource, qwDownloadSize, *psczTempFile, &cacheCallback, &authenticationCallback);
348 + ExitOnFailure(hr, "Failed attempt to download update feed from URL: '%ls' to: '%ls'", downloadSource.sczUrl, *psczTempFile);
349 +
350 +LExit:
351 + if (FAILED(hr))
352 + {
353 + if (*psczTempFile)
354 + {
355 + FileEnsureDelete(*psczTempFile);
356 + }
357 +
358 + ReleaseNullStr(*psczTempFile);
359 + }
360 +
361 + ReleaseStr(downloadSource.sczUrl);
362 + ReleaseStr(downloadSource.sczUser);
363 + ReleaseStr(downloadSource.sczPassword);
364 + ReleaseStr(sczUpdateId);
365 + ReleaseStr(sczError);
366 + return hr;
367 +}
368 +
369 +
370 +static HRESULT DetectAtomFeedUpdate(
371 + __in_z LPCWSTR wzBundleId,
372 + __in BURN_USER_EXPERIENCE* pUX,
373 + __in BURN_UPDATE* pUpdate
374 + )
375 +{
376 + Assert(pUpdate && pUpdate->sczUpdateSource && *pUpdate->sczUpdateSource);
377 +#ifdef DEBUG
378 + LogStringLine(REPORT_STANDARD, "DetectAtomFeedUpdate() - update location: %ls", pUpdate->sczUpdateSource);
379 +#endif
380 +
381 +
382 + HRESULT hr = S_OK;
383 + LPWSTR sczUpdateFeedTempFile = NULL;
384 + ATOM_FEED* pAtomFeed = NULL;
385 + APPLICATION_UPDATE_CHAIN* pApupChain = NULL;
386 + BOOL fStopProcessingUpdates = FALSE;
387 +
388 + hr = AtomInitialize();
389 + ExitOnFailure(hr, "Failed to initialize Atom.");
390 +
391 + hr = DownloadUpdateFeed(wzBundleId, pUX, pUpdate, &sczUpdateFeedTempFile);
392 + ExitOnFailure(hr, "Failed to download update feed.");
393 +
394 + hr = AtomParseFromFile(sczUpdateFeedTempFile, &pAtomFeed);
395 + ExitOnFailure(hr, "Failed to parse update atom feed: %ls.", sczUpdateFeedTempFile);
396 +
397 + hr = ApupAllocChainFromAtom(pAtomFeed, &pApupChain);
398 + ExitOnFailure(hr, "Failed to allocate update chain from atom feed.");
399 +
400 + if (0 < pApupChain->cEntries)
401 + {
402 + for (DWORD i = 0; i < pApupChain->cEntries; ++i)
403 + {
404 + APPLICATION_UPDATE_ENTRY* pAppUpdateEntry = &pApupChain->rgEntries[i];
405 +
406 + hr = UserExperienceOnDetectUpdate(pUX, pAppUpdateEntry->rgEnclosures ? pAppUpdateEntry->rgEnclosures->wzUrl : NULL,
407 + pAppUpdateEntry->rgEnclosures ? pAppUpdateEntry->rgEnclosures->dw64Size : 0,
408 + pAppUpdateEntry->dw64Version, pAppUpdateEntry->wzTitle,
409 + pAppUpdateEntry->wzSummary, pAppUpdateEntry->wzContentType, pAppUpdateEntry->wzContent, &fStopProcessingUpdates);
410 + ExitOnRootFailure(hr, "BA aborted detect update.");
411 +
412 + if (fStopProcessingUpdates)
413 + {
414 + break;
415 + }
416 + }
417 + }
418 +
419 +LExit:
420 + if (sczUpdateFeedTempFile && *sczUpdateFeedTempFile)
421 + {
422 + FileEnsureDelete(sczUpdateFeedTempFile);
423 + }
424 +
425 + ApupFreeChain(pApupChain);
426 + AtomFreeFeed(pAtomFeed);
427 + ReleaseStr(sczUpdateFeedTempFile);
428 + AtomUninitialize();
429 +
430 + return hr;
431 +}
src/engine/detect.h new
+44
@@ -0,0 +1,44 @@
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 +#if defined(__cplusplus)
6 +extern "C" {
7 +#endif
8 +
9 +
10 +// constants
11 +
12 +
13 +// structs
14 +
15 +
16 +// functions
17 +
18 +void DetectReset(
19 + __in BURN_REGISTRATION* pRegistration,
20 + __in BURN_PACKAGES* pPackages
21 + );
22 +
23 +HRESULT DetectForwardCompatibleBundle(
24 + __in BURN_USER_EXPERIENCE* pUX,
25 + __in BOOTSTRAPPER_COMMAND* pCommand,
26 + __in BURN_REGISTRATION* pRegistration
27 + );
28 +
29 +HRESULT DetectReportRelatedBundles(
30 + __in BURN_USER_EXPERIENCE* pUX,
31 + __in BURN_REGISTRATION* pRegistration,
32 + __in BOOTSTRAPPER_RELATION_TYPE relationType,
33 + __in BOOTSTRAPPER_ACTION action
34 + );
35 +
36 +HRESULT DetectUpdate(
37 + __in_z LPCWSTR wzBundleId,
38 + __in BURN_USER_EXPERIENCE* pUX,
39 + __in BURN_UPDATE* pUpdate
40 + );
41 +
42 +#if defined(__cplusplus)
43 +}
44 +#endif
src/engine/elevation.cpp new
+2814
@@ -0,0 +1,2814 @@
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 DWORD BURN_TIMEOUT = 5 * 60 * 1000; // TODO: is 5 minutes good?
7 +
8 +typedef enum _BURN_ELEVATION_MESSAGE_TYPE
9 +{
10 + BURN_ELEVATION_MESSAGE_TYPE_UNKNOWN,
11 + BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE,
12 + BURN_ELEVATION_MESSAGE_TYPE_APPLY_UNINITIALIZE,
13 + BURN_ELEVATION_MESSAGE_TYPE_SESSION_BEGIN,
14 + BURN_ELEVATION_MESSAGE_TYPE_SESSION_RESUME,
15 + BURN_ELEVATION_MESSAGE_TYPE_SESSION_END,
16 + BURN_ELEVATION_MESSAGE_TYPE_SAVE_STATE,
17 + BURN_ELEVATION_MESSAGE_TYPE_LAYOUT_BUNDLE,
18 + BURN_ELEVATION_MESSAGE_TYPE_CACHE_OR_LAYOUT_CONTAINER_OR_PAYLOAD,
19 + BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP,
20 + BURN_ELEVATION_MESSAGE_TYPE_PROCESS_DEPENDENT_REGISTRATION,
21 + BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE,
22 + BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE,
23 + BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSP_PACKAGE,
24 + BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSU_PACKAGE,
25 + BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_PROVIDER,
26 + BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_DEPENDENCY,
27 + BURN_ELEVATION_MESSAGE_TYPE_LOAD_COMPATIBLE_PACKAGE,
28 + BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_EMBEDDED_CHILD,
29 + BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE,
30 + BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE,
31 +
32 + BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS,
33 + BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR,
34 + BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_MESSAGE,
35 + BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE,
36 + BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE_PROCESSID,
37 +
38 + BURN_ELEVATION_TRANSACTION_BEGIN,
39 + BURN_ELEVATION_TRANSACTION_COMMIT,
40 + BURN_ELEVATION_TRANSACTION_ROLLBACK
41 +
42 +} BURN_ELEVATION_MESSAGE_TYPE;
43 +
44 +
45 +// struct
46 +
47 +typedef struct _BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT
48 +{
49 + PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler;
50 + LPVOID pvContext;
51 +} BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT;
52 +
53 +typedef struct _BURN_ELEVATION_MSI_MESSAGE_CONTEXT
54 +{
55 + PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler;
56 + LPVOID pvContext;
57 +} BURN_ELEVATION_MSI_MESSAGE_CONTEXT;
58 +
59 +typedef struct _BURN_ELEVATION_LAUNCH_APPROVED_EXE_MESSAGE_CONTEXT
60 +{
61 + DWORD dwProcessId;
62 +} BURN_ELEVATION_LAUNCH_APPROVED_EXE_MESSAGE_CONTEXT;
63 +
64 +typedef struct _BURN_ELEVATION_CHILD_MESSAGE_CONTEXT
65 +{
66 + DWORD dwLoggingTlsId;
67 + HANDLE hPipe;
68 + HANDLE* phLock;
69 + BOOL* pfDisabledAutomaticUpdates;
70 + BURN_APPROVED_EXES* pApprovedExes;
71 + BURN_CONTAINERS* pContainers;
72 + BURN_PACKAGES* pPackages;
73 + BURN_PAYLOADS* pPayloads;
74 + BURN_VARIABLES* pVariables;
75 + BURN_REGISTRATION* pRegistration;
76 + BURN_USER_EXPERIENCE* pUserExperience;
77 +
78 + MSIHANDLE hMsiTrns;
79 + HANDLE hMsiTrnsEvent;
80 +} BURN_ELEVATION_CHILD_MESSAGE_CONTEXT;
81 +
82 +
83 +// internal function declarations
84 +
85 +static HRESULT OnMsiBeginTransaction(
86 + __in BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext
87 +);
88 +static HRESULT OnMsiCommitTransaction(
89 + __in BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext
90 +);
91 +static HRESULT OnMsiRollbackTransaction(
92 + __in BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext
93 +);
94 +
95 +static DWORD WINAPI ElevatedChildCacheThreadProc(
96 + __in LPVOID lpThreadParameter
97 + );
98 +static HRESULT WaitForElevatedChildCacheThread(
99 + __in HANDLE hCacheThread,
100 + __in DWORD dwExpectedExitCode
101 + );
102 +static HRESULT OnLoadCompatiblePackage(
103 + __in BURN_PACKAGES* pPackages,
104 + __in BYTE* pbData,
105 + __in DWORD cbData
106 + );
107 +static HRESULT ProcessGenericExecuteMessages(
108 + __in BURN_PIPE_MESSAGE* pMsg,
109 + __in_opt LPVOID pvContext,
110 + __out DWORD* pdwResult
111 + );
112 +static HRESULT ProcessMsiPackageMessages(
113 + __in BURN_PIPE_MESSAGE* pMsg,
114 + __in_opt LPVOID pvContext,
115 + __out DWORD* pdwResult
116 + );
117 +static HRESULT ProcessLaunchApprovedExeMessages(
118 + __in BURN_PIPE_MESSAGE* pMsg,
119 + __in_opt LPVOID pvContext,
120 + __out DWORD* pdwResult
121 + );
122 +static HRESULT ProcessElevatedChildMessage(
123 + __in BURN_PIPE_MESSAGE* pMsg,
124 + __in_opt LPVOID pvContext,
125 + __out DWORD* pdwResult
126 + );
127 +static HRESULT ProcessElevatedChildCacheMessage(
128 + __in BURN_PIPE_MESSAGE* pMsg,
129 + __in_opt LPVOID pvContext,
130 + __out DWORD* pdwResult
131 + );
132 +static HRESULT ProcessResult(
133 + __in DWORD dwResult,
134 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart
135 + );
136 +static HRESULT OnApplyInitialize(
137 + __in BURN_VARIABLES* pVariables,
138 + __in BURN_REGISTRATION* pRegistration,
139 + __in HANDLE* phLock,
140 + __in BOOL* pfDisabledWindowsUpdate,
141 + __in BYTE* pbData,
142 + __in DWORD cbData
143 + );
144 +static HRESULT OnApplyUninitialize(
145 + __in HANDLE* phLock
146 + );
147 +static HRESULT OnSessionBegin(
148 + __in BURN_REGISTRATION* pRegistration,
149 + __in BURN_VARIABLES* pVariables,
150 + __in BURN_USER_EXPERIENCE* pUserExperience,
151 + __in BYTE* pbData,
152 + __in DWORD cbData
153 + );
154 +static HRESULT OnSessionResume(
155 + __in BURN_REGISTRATION* pRegistration,
156 + __in BURN_VARIABLES* pVariables,
157 + __in BYTE* pbData,
158 + __in DWORD cbData
159 + );
160 +static HRESULT OnSessionEnd(
161 + __in BURN_REGISTRATION* pRegistration,
162 + __in BYTE* pbData,
163 + __in DWORD cbData
164 + );
165 +static HRESULT OnSaveState(
166 + __in BURN_REGISTRATION* pRegistration,
167 + __in BYTE* pbData,
168 + __in DWORD cbData
169 + );
170 +static HRESULT OnLayoutBundle(
171 + __in_z LPCWSTR wzExecutableName,
172 + __in BYTE* pbData,
173 + __in DWORD cbData
174 + );
175 +static HRESULT OnCacheOrLayoutContainerOrPayload(
176 + __in BURN_CONTAINERS* pContainers,
177 + __in BURN_PACKAGES* pPackages,
178 + __in BURN_PAYLOADS* pPayloads,
179 + __in BYTE* pbData,
180 + __in DWORD cbData
181 + );
182 +static void OnCacheCleanup(
183 + __in_z LPCWSTR wzBundleId
184 + );
185 +static HRESULT OnProcessDependentRegistration(
186 + __in const BURN_REGISTRATION* pRegistration,
187 + __in BYTE* pbData,
188 + __in DWORD cbData
189 + );
190 +static HRESULT OnExecuteExePackage(
191 + __in HANDLE hPipe,
192 + __in BURN_PACKAGES* pPackages,
193 + __in BURN_RELATED_BUNDLES* pRelatedBundles,
194 + __in BURN_VARIABLES* pVariables,
195 + __in BYTE* pbData,
196 + __in DWORD cbData
197 + );
198 +static HRESULT OnExecuteMsiPackage(
199 + __in HANDLE hPipe,
200 + __in BURN_PACKAGES* pPackages,
201 + __in BURN_VARIABLES* pVariables,
202 + __in BYTE* pbData,
203 + __in DWORD cbData
204 + );
205 +static HRESULT OnExecuteMspPackage(
206 + __in HANDLE hPipe,
207 + __in BURN_PACKAGES* pPackages,
208 + __in BURN_VARIABLES* pVariables,
209 + __in BYTE* pbData,
210 + __in DWORD cbData
211 + );
212 +static HRESULT OnExecuteMsuPackage(
213 + __in HANDLE hPipe,
214 + __in BURN_PACKAGES* pPackages,
215 + __in BURN_VARIABLES* pVariables,
216 + __in BYTE* pbData,
217 + __in DWORD cbData
218 + );
219 +static HRESULT OnExecutePackageProviderAction(
220 + __in BURN_PACKAGES* pPackages,
221 + __in BURN_RELATED_BUNDLES* pRelatedBundles,
222 + __in BYTE* pbData,
223 + __in DWORD cbData
224 + );
225 +static HRESULT OnExecutePackageDependencyAction(
226 + __in BURN_PACKAGES* pPackages,
227 + __in BURN_RELATED_BUNDLES* pRelatedBundles,
228 + __in BYTE* pbData,
229 + __in DWORD cbData
230 + );
231 +static int GenericExecuteMessageHandler(
232 + __in GENERIC_EXECUTE_MESSAGE* pMessage,
233 + __in LPVOID pvContext
234 + );
235 +static int MsiExecuteMessageHandler(
236 + __in WIU_MSI_EXECUTE_MESSAGE* pMessage,
237 + __in_opt LPVOID pvContext
238 + );
239 +static HRESULT OnCleanPackage(
240 + __in BURN_PACKAGES* pPackages,
241 + __in BYTE* pbData,
242 + __in DWORD cbData
243 + );
244 +static HRESULT OnLaunchApprovedExe(
245 + __in HANDLE hPipe,
246 + __in BURN_APPROVED_EXES* pApprovedExes,
247 + __in BURN_VARIABLES* pVariables,
248 + __in BYTE* pbData,
249 + __in DWORD cbData
250 + );
251 +
252 +
253 +// function definitions
254 +
255 +extern "C" HRESULT ElevationElevate(
256 + __in BURN_ENGINE_STATE* pEngineState,
257 + __in_opt HWND hwndParent
258 + )
259 +{
260 + Assert(BURN_MODE_ELEVATED != pEngineState->mode);
261 + Assert(!pEngineState->companionConnection.sczName);
262 + Assert(!pEngineState->companionConnection.sczSecret);
263 + Assert(!pEngineState->companionConnection.hProcess);
264 + Assert(!pEngineState->companionConnection.dwProcessId);
265 + Assert(INVALID_HANDLE_VALUE == pEngineState->companionConnection.hPipe);
266 + Assert(INVALID_HANDLE_VALUE == pEngineState->companionConnection.hCachePipe);
267 +
268 + HRESULT hr = S_OK;
269 + int nResult = IDOK;
270 + HANDLE hPipesCreatedEvent = INVALID_HANDLE_VALUE;
271 +
272 + hr = UserExperienceOnElevateBegin(&pEngineState->userExperience);
273 + ExitOnRootFailure(hr, "BA aborted elevation requirement.");
274 +
275 + hr = PipeCreateNameAndSecret(&pEngineState->companionConnection.sczName, &pEngineState->companionConnection.sczSecret);
276 + ExitOnFailure(hr, "Failed to create pipe name and client token.");
277 +
278 + hr = PipeCreatePipes(&pEngineState->companionConnection, TRUE, &hPipesCreatedEvent);
279 + ExitOnFailure(hr, "Failed to create pipe and cache pipe.");
280 +
281 + LogId(REPORT_STANDARD, MSG_LAUNCH_ELEVATED_ENGINE_STARTING);
282 +
283 + do
284 + {
285 + nResult = IDOK;
286 +
287 + // Create the elevated process and if successful, wait for it to connect.
288 + hr = PipeLaunchChildProcess(pEngineState->sczBundleEngineWorkingPath, &pEngineState->companionConnection, TRUE, hwndParent);
289 + if (SUCCEEDED(hr))
290 + {
291 + LogId(REPORT_STANDARD, MSG_LAUNCH_ELEVATED_ENGINE_SUCCESS);
292 +
293 + hr = PipeWaitForChildConnect(&pEngineState->companionConnection);
294 + ExitOnFailure(hr, "Failed to connect to elevated child process.");
295 +
296 + LogId(REPORT_STANDARD, MSG_CONNECT_TO_ELEVATED_ENGINE_SUCCESS);
297 + }
298 + else if (HRESULT_FROM_WIN32(ERROR_CANCELLED) == hr)
299 + {
300 + // The user clicked "Cancel" on the elevation prompt or the elevation prompt timed out, provide the notification with the option to retry.
301 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
302 + nResult = UserExperienceSendError(&pEngineState->userExperience, BOOTSTRAPPER_ERROR_TYPE_ELEVATE, NULL, hr, NULL, MB_ICONERROR | MB_RETRYCANCEL, IDNOACTION);
303 + }
304 + } while (IDRETRY == nResult);
305 + ExitOnFailure(hr, "Failed to elevate.");
306 +
307 +LExit:
308 + ReleaseHandle(hPipesCreatedEvent);
309 +
310 + if (FAILED(hr))
311 + {
312 + PipeConnectionUninitialize(&pEngineState->companionConnection);
313 + }
314 +
315 + UserExperienceOnElevateComplete(&pEngineState->userExperience, hr);
316 +
317 + return hr;
318 +}
319 +
320 +extern "C" HRESULT ElevationApplyInitialize(
321 + __in HANDLE hPipe,
322 + __in BURN_VARIABLES* pVariables,
323 + __in BOOTSTRAPPER_ACTION action,
324 + __in BURN_AU_PAUSE_ACTION auAction,
325 + __in BOOL fTakeSystemRestorePoint
326 + )
327 +{
328 + HRESULT hr = S_OK;
329 + BYTE* pbData = NULL;
330 + SIZE_T cbData = 0;
331 + DWORD dwResult = 0;
332 +
333 + // serialize message data
334 + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)action);
335 + ExitOnFailure(hr, "Failed to write action to message buffer.");
336 +
337 + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)auAction);
338 + ExitOnFailure(hr, "Failed to write update action to message buffer.");
339 +
340 + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)fTakeSystemRestorePoint);
341 + ExitOnFailure(hr, "Failed to write system restore point action to message buffer.");
342 +
343 + hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData);
344 + ExitOnFailure(hr, "Failed to write variables.");
345 +
346 + // send message
347 + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE, pbData, cbData, NULL, NULL, &dwResult);
348 + ExitOnFailure(hr, "Failed to send message to per-machine process.");
349 +
350 + hr = (HRESULT)dwResult;
351 +
352 +LExit:
353 + ReleaseBuffer(pbData);
354 +
355 + return hr;
356 +}
357 +
358 +extern "C" HRESULT ElevationApplyUninitialize(
359 + __in HANDLE hPipe
360 + )
361 +{
362 + HRESULT hr = S_OK;
363 + BYTE* pbData = NULL;
364 + SIZE_T cbData = 0;
365 + DWORD dwResult = 0;
366 +
367 + // send message
368 + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_APPLY_UNINITIALIZE, pbData, cbData, NULL, NULL, &dwResult);
369 + ExitOnFailure(hr, "Failed to send message to per-machine process.");
370 +
371 + hr = (HRESULT)dwResult;
372 +
373 +LExit:
374 + ReleaseBuffer(pbData);
375 +
376 + return hr;
377 +}
378 +
379 +/*******************************************************************
380 + ElevationSessionBegin -
381 +
382 +*******************************************************************/
383 +extern "C" HRESULT ElevationSessionBegin(
384 + __in HANDLE hPipe,
385 + __in_z LPCWSTR wzEngineWorkingPath,
386 + __in_z LPCWSTR wzResumeCommandLine,
387 + __in BOOL fDisableResume,
388 + __in BURN_VARIABLES* pVariables,
389 + __in DWORD dwRegistrationOperations,
390 + __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction,
391 + __in DWORD64 qwEstimatedSize
392 + )
393 +{
394 + HRESULT hr = S_OK;
395 + BYTE* pbData = NULL;
396 + SIZE_T cbData = 0;
397 + DWORD dwResult = 0;
398 +
399 + // serialize message data
400 + hr = BuffWriteString(&pbData, &cbData, wzEngineWorkingPath);
401 + ExitOnFailure(hr, "Failed to write engine working path to message buffer.");
402 +
403 + hr = BuffWriteString(&pbData, &cbData, wzResumeCommandLine);
404 + ExitOnFailure(hr, "Failed to write resume command line to message buffer.");
405 +
406 + hr = BuffWriteNumber(&pbData, &cbData, fDisableResume);
407 + ExitOnFailure(hr, "Failed to write resume flag.");
408 +
409 + hr = BuffWriteNumber(&pbData, &cbData, dwRegistrationOperations);
410 + ExitOnFailure(hr, "Failed to write registration operations to message buffer.");
411 +
412 + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)dependencyRegistrationAction);
413 + ExitOnFailure(hr, "Failed to write dependency registration action to message buffer.");
414 +
415 + hr = BuffWriteNumber64(&pbData, &cbData, qwEstimatedSize);
416 + ExitOnFailure(hr, "Failed to write estimated size to message buffer.");
417 +
418 + hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData);
419 + ExitOnFailure(hr, "Failed to write variables.");
420 +
421 + // send message
422 + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_SESSION_BEGIN, pbData, cbData, NULL, NULL, &dwResult);
423 + ExitOnFailure(hr, "Failed to send message to per-machine process.");
424 +
425 + hr = (HRESULT)dwResult;
426 +
427 +LExit:
428 + ReleaseBuffer(pbData);
429 +
430 + return hr;
431 +}
432 +
433 +/*******************************************************************
434 + ElevationSessionResume -
435 +
436 +*******************************************************************/
437 +extern "C" HRESULT ElevationSessionResume(
438 + __in HANDLE hPipe,
439 + __in_z LPCWSTR wzResumeCommandLine,
440 + __in BOOL fDisableResume,
441 + __in BURN_VARIABLES* pVariables
442 + )
443 +{
444 + HRESULT hr = S_OK;
445 + BYTE* pbData = NULL;
446 + SIZE_T cbData = 0;
447 + DWORD dwResult = 0;
448 +
449 + // serialize message data
450 + hr = BuffWriteString(&pbData, &cbData, wzResumeCommandLine);
451 + ExitOnFailure(hr, "Failed to write resume command line to message buffer.");
452 +
453 + hr = BuffWriteNumber(&pbData, &cbData, fDisableResume);
454 + ExitOnFailure(hr, "Failed to write resume flag.");
455 +
456 + hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData);
457 + ExitOnFailure(hr, "Failed to write variables.");
458 +
459 + // send message
460 + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_SESSION_RESUME, pbData, cbData, NULL, NULL, &dwResult);
461 + ExitOnFailure(hr, "Failed to send message to per-machine process.");
462 +
463 + hr = (HRESULT)dwResult;
464 +
465 +LExit:
466 + ReleaseBuffer(pbData);
467 +
468 + return hr;
469 +}
470 +
471 +/*******************************************************************
472 + ElevationSessionEnd -
473 +
474 +*******************************************************************/
475 +extern "C" HRESULT ElevationSessionEnd(
476 + __in HANDLE hPipe,
477 + __in BURN_RESUME_MODE resumeMode,
478 + __in BOOTSTRAPPER_APPLY_RESTART restart,
479 + __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction
480 + )
481 +{
482 + HRESULT hr = S_OK;
483 + BYTE* pbData = NULL;
484 + SIZE_T cbData = 0;
485 + DWORD dwResult = 0;
486 +
487 + // serialize message data
488 + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)resumeMode);
489 + ExitOnFailure(hr, "Failed to write resume mode to message buffer.");
490 +
491 + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)restart);
492 + ExitOnFailure(hr, "Failed to write restart enum to message buffer.");
493 +
494 + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)dependencyRegistrationAction);
495 + ExitOnFailure(hr, "Failed to write dependency registration action to message buffer.");
496 +
497 + // send message
498 + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_SESSION_END, pbData, cbData, NULL, NULL, &dwResult);
499 + ExitOnFailure(hr, "Failed to send message to per-machine process.");
500 +
501 + hr = (HRESULT)dwResult;
502 +
503 +LExit:
504 + ReleaseBuffer(pbData);
505 +
506 + return hr;
507 +}
508 +
509 +/*******************************************************************
510 + ElevationSaveState -
511 +
512 +*******************************************************************/
513 +HRESULT ElevationSaveState(
514 + __in HANDLE hPipe,
515 + __in_bcount(cbBuffer) BYTE* pbBuffer,
516 + __in SIZE_T cbBuffer
517 + )
518 +{
519 + HRESULT hr = S_OK;
520 + DWORD dwResult = 0;
521 +
522 + // send message
523 + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_SAVE_STATE, pbBuffer, (DWORD)cbBuffer, NULL, NULL, &dwResult);
524 + ExitOnFailure(hr, "Failed to send message to per-machine process.");
525 +
526 + hr = (HRESULT)dwResult;
527 +
528 +LExit:
529 + return hr;
530 +}
531 +
532 +/*******************************************************************
533 + ElevationLayoutBundle -
534 +
535 +*******************************************************************/
536 +extern "C" HRESULT ElevationLayoutBundle(
537 + __in HANDLE hPipe,
538 + __in_z LPCWSTR wzLayoutDirectory,
539 + __in_z LPCWSTR wzUnverifiedPath
540 + )
541 +{
542 + HRESULT hr = S_OK;
543 + BYTE* pbData = NULL;
544 + SIZE_T cbData = 0;
545 + DWORD dwResult = 0;
546 +
547 + // serialize message data
548 + hr = BuffWriteString(&pbData, &cbData, wzLayoutDirectory);
549 + ExitOnFailure(hr, "Failed to write layout directory to message buffer.");
550 +
551 + hr = BuffWriteString(&pbData, &cbData, wzUnverifiedPath);
552 + ExitOnFailure(hr, "Failed to write payload unverified path to message buffer.");
553 +
554 + // send message
555 + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_LAYOUT_BUNDLE, pbData, cbData, NULL, NULL, &dwResult);
556 + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_LAYOUT_BUNDLE message to per-machine process.");
557 +
558 + hr = (HRESULT)dwResult;
559 +
560 +LExit:
561 + ReleaseBuffer(pbData);
562 +
563 + return hr;
564 +}
565 +
566 +/*******************************************************************
567 + ElevationCacheOrLayoutPayload -
568 +
569 +*******************************************************************/
570 +extern "C" HRESULT ElevationCacheOrLayoutContainerOrPayload(
571 + __in HANDLE hPipe,
572 + __in_opt BURN_CONTAINER* pContainer,
573 + __in_opt BURN_PACKAGE* pPackage,
574 + __in_opt BURN_PAYLOAD* pPayload,
575 + __in_z_opt LPCWSTR wzLayoutDirectory,
576 + __in_z LPCWSTR wzUnverifiedPath,
577 + __in BOOL fMove
578 + )
579 +{
580 + HRESULT hr = S_OK;
581 + BYTE* pbData = NULL;
582 + SIZE_T cbData = 0;
583 + DWORD dwResult = 0;
584 +
585 + // serialize message data
586 + hr = BuffWriteString(&pbData, &cbData, pContainer ? pContainer->sczId : NULL);
587 + ExitOnFailure(hr, "Failed to write container id to message buffer.");
588 +
589 + hr = BuffWriteString(&pbData, &cbData, pPackage ? pPackage->sczId : NULL);
590 + ExitOnFailure(hr, "Failed to write package id to message buffer.");
591 +
592 + hr = BuffWriteString(&pbData, &cbData, pPayload ? pPayload->sczKey : NULL);
593 + ExitOnFailure(hr, "Failed to write payload id to message buffer.");
594 +
595 + hr = BuffWriteString(&pbData, &cbData, wzLayoutDirectory);
596 + ExitOnFailure(hr, "Failed to write layout directory to message buffer.");
597 +
598 + hr = BuffWriteString(&pbData, &cbData, wzUnverifiedPath);
599 + ExitOnFailure(hr, "Failed to write unverified path to message buffer.");
600 +
601 + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)fMove);
602 + ExitOnFailure(hr, "Failed to write move flag to message buffer.");
603 +
604 + // send message
605 + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_CACHE_OR_LAYOUT_CONTAINER_OR_PAYLOAD, pbData, cbData, NULL, NULL, &dwResult);
606 + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_CACHE_OR_LAYOUT_CONTAINER_OR_PAYLOAD message to per-machine process.");
607 +
608 + hr = (HRESULT)dwResult;
609 +
610 +LExit:
611 + ReleaseBuffer(pbData);
612 +
613 + return hr;
614 +}
615 +
616 +/*******************************************************************
617 + ElevationCacheCleanup -
618 +
619 +*******************************************************************/
620 +extern "C" HRESULT ElevationCacheCleanup(
621 + __in HANDLE hPipe
622 + )
623 +{
624 + HRESULT hr = S_OK;
625 + DWORD dwResult = 0;
626 +
627 + // send message
628 + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP, NULL, 0, NULL, NULL, &dwResult);
629 + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP message to per-machine process.");
630 +
631 + hr = (HRESULT)dwResult;
632 +
633 +LExit:
634 + return hr;
635 +}
636 +
637 +extern "C" HRESULT ElevationProcessDependentRegistration(
638 + __in HANDLE hPipe,
639 + __in const BURN_DEPENDENT_REGISTRATION_ACTION* pAction
640 + )
641 +{
642 + HRESULT hr = S_OK;
643 + BYTE* pbData = NULL;
644 + SIZE_T cbData = 0;
645 + DWORD dwResult = 0;
646 +
647 + // serialize message data
648 + hr = BuffWriteNumber(&pbData, &cbData, pAction->type);
649 + ExitOnFailure(hr, "Failed to write action type to message buffer.");
650 +
651 + hr = BuffWriteString(&pbData, &cbData, pAction->sczBundleId);
652 + ExitOnFailure(hr, "Failed to write bundle id to message buffer.");
653 +
654 + hr = BuffWriteString(&pbData, &cbData, pAction->sczDependentProviderKey);
655 + ExitOnFailure(hr, "Failed to write dependent provider key to message buffer.");
656 +
657 + // send message
658 + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_PROCESS_DEPENDENT_REGISTRATION, pbData, cbData, NULL, NULL, &dwResult);
659 + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_PROCESS_DEPENDENT_REGISTRATION message to per-machine process.");
660 +
661 + hr = (HRESULT)dwResult;
662 +
663 +LExit:
664 + ReleaseBuffer(pbData);
665 +
666 + return hr;
667 +}
668 +
669 +/*******************************************************************
670 + ElevationExecuteExePackage -
671 +
672 +*******************************************************************/
673 +extern "C" HRESULT ElevationExecuteExePackage(
674 + __in HANDLE hPipe,
675 + __in BURN_EXECUTE_ACTION* pExecuteAction,
676 + __in BURN_VARIABLES* pVariables,
677 + __in BOOL fRollback,
678 + __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
679 + __in LPVOID pvContext,
680 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart
681 + )
682 +{
683 + HRESULT hr = S_OK;
684 + BYTE* pbData = NULL;
685 + SIZE_T cbData = 0;
686 + BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT context = { };
687 + DWORD dwResult = 0;
688 +
689 + // serialize message data
690 + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->exePackage.pPackage->sczId);
691 + ExitOnFailure(hr, "Failed to write package id to message buffer.");
692 +
693 + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->exePackage.action);
694 + ExitOnFailure(hr, "Failed to write action to message buffer.");
695 +
696 + hr = BuffWriteNumber(&pbData, &cbData, fRollback);
697 + ExitOnFailure(hr, "Failed to write rollback.");
698 +
699 + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->exePackage.sczIgnoreDependencies);
700 + ExitOnFailure(hr, "Failed to write the list of dependencies to ignore to the message buffer.");
701 +
702 + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->exePackage.sczAncestors);
703 + ExitOnFailure(hr, "Failed to write the list of ancestors to the message buffer.");
704 +
705 + hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData);
706 + ExitOnFailure(hr, "Failed to write variables.");
707 +
708 + // send message
709 + context.pfnGenericMessageHandler = pfnGenericMessageHandler;
710 + context.pvContext = pvContext;
711 +
712 + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE, pbData, cbData, ProcessGenericExecuteMessages, &context, &dwResult);
713 + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE message to per-machine process.");
714 +
715 + hr = ProcessResult(dwResult, pRestart);
716 +
717 +LExit:
718 + ReleaseBuffer(pbData);
719 +
720 + return hr;
721 +}
722 +
723 +extern "C" HRESULT ElevationMsiBeginTransaction(
724 + __in HANDLE hPipe,
725 + __in_opt HWND hwndParent,
726 + __in LPVOID pvContext
727 +)
728 +{
729 + UNREFERENCED_PARAMETER(hwndParent);
730 + HRESULT hr = S_OK;
731 + BURN_ELEVATION_MSI_MESSAGE_CONTEXT context = {};
732 + DWORD dwResult = ERROR_SUCCESS;
733 +
734 + context.pvContext = pvContext;
735 +
736 + hr = PipeSendMessage(hPipe, BURN_ELEVATION_TRANSACTION_BEGIN, NULL, 0, NULL, &context, &dwResult);
737 + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE message to per-machine process.");
738 + ExitOnWin32Error(dwResult, hr, "Failed beginning an elevated MSI transaction");
739 +
740 +LExit:
741 + return hr;
742 +}
743 +
744 +extern "C" HRESULT ElevationMsiCommitTransaction(
745 + __in HANDLE hPipe,
746 + __in_opt HWND hwndParent,
747 + __in LPVOID pvContext
748 +)
749 +{
750 + UNREFERENCED_PARAMETER(hwndParent);
751 + HRESULT hr = S_OK;
752 + BURN_ELEVATION_MSI_MESSAGE_CONTEXT context = {};
753 + DWORD dwResult = ERROR_SUCCESS;
754 +
755 + context.pvContext = pvContext;
756 +
757 + hr = PipeSendMessage(hPipe, BURN_ELEVATION_TRANSACTION_COMMIT, NULL, 0, NULL, &context, &dwResult);
758 + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE message to per-machine process.");
759 + ExitOnWin32Error(dwResult, hr, "Failed committing an elevated MSI transaction");
760 +
761 +LExit:
762 + return hr;
763 +}
764 +
765 +extern "C" HRESULT ElevationMsiRollbackTransaction(
766 + __in HANDLE hPipe,
767 + __in_opt HWND hwndParent,
768 + __in LPVOID pvContext
769 +)
770 +{
771 + UNREFERENCED_PARAMETER(hwndParent);
772 + HRESULT hr = S_OK;
773 + BURN_ELEVATION_MSI_MESSAGE_CONTEXT context = {};
774 + DWORD dwResult = ERROR_SUCCESS;
775 +
776 + context.pvContext = pvContext;
777 +
778 + hr = PipeSendMessage(hPipe, BURN_ELEVATION_TRANSACTION_ROLLBACK, NULL, 0, NULL, &context, &dwResult);
779 + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE message to per-machine process.");
780 + ExitOnWin32Error(dwResult, hr, "Failed rolling back an elevated MSI transaction");
781 +
782 +LExit:
783 + return hr;
784 +}
785 +
786 +
787 +
788 +/*******************************************************************
789 + ElevationExecuteMsiPackage -
790 +
791 +*******************************************************************/
792 +extern "C" HRESULT ElevationExecuteMsiPackage(
793 + __in HANDLE hPipe,
794 + __in_opt HWND hwndParent,
795 + __in BURN_EXECUTE_ACTION* pExecuteAction,
796 + __in BURN_VARIABLES* pVariables,
797 + __in BOOL fRollback,
798 + __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
799 + __in LPVOID pvContext,
800 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart
801 + )
802 +{
803 + HRESULT hr = S_OK;
804 + BYTE* pbData = NULL;
805 + SIZE_T cbData = 0;
806 + BURN_ELEVATION_MSI_MESSAGE_CONTEXT context = { };
807 + DWORD dwResult = 0;
808 +
809 + // serialize message data
810 + // TODO: for patching we might not have a package
811 + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->msiPackage.pPackage->sczId);
812 + ExitOnFailure(hr, "Failed to write package id to message buffer.");
813 +
814 + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)hwndParent);
815 + ExitOnFailure(hr, "Failed to write parent hwnd to message buffer.");
816 +
817 + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->msiPackage.sczLogPath);
818 + ExitOnFailure(hr, "Failed to write package log to message buffer.");
819 +
820 + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.uiLevel);
821 + ExitOnFailure(hr, "Failed to write UI level to message buffer.");
822 +
823 + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.action);
824 + ExitOnFailure(hr, "Failed to write action to message buffer.");
825 +
826 + // Feature actions.
827 + for (DWORD i = 0; i < pExecuteAction->msiPackage.pPackage->Msi.cFeatures; ++i)
828 + {
829 + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.rgFeatures[i]);
830 + ExitOnFailure(hr, "Failed to write feature action to message buffer.");
831 + }
832 +
833 + // Slipstream patches actions.
834 + for (DWORD i = 0; i < pExecuteAction->msiPackage.pPackage->Msi.cSlipstreamMspPackages; ++i)
835 + {
836 + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.rgSlipstreamPatches[i]);
837 + ExitOnFailure(hr, "Failed to write slipstream patch action to message buffer.");
838 + }
839 +
840 + hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData);
841 + ExitOnFailure(hr, "Failed to write variables.");
842 +
843 + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)fRollback);
844 + ExitOnFailure(hr, "Failed to write rollback flag to message buffer.");
845 +
846 +
847 + // send message
848 + context.pfnMessageHandler = pfnMessageHandler;
849 + context.pvContext = pvContext;
850 +
851 + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE, pbData, cbData, ProcessMsiPackageMessages, &context, &dwResult);
852 + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE message to per-machine process.");
853 +
854 + hr = ProcessResult(dwResult, pRestart);
855 +
856 +LExit:
857 + ReleaseBuffer(pbData);
858 +
859 + return hr;
860 +}
861 +
862 +/*******************************************************************
863 + ElevationExecuteMspPackage -
864 +
865 +*******************************************************************/
866 +extern "C" HRESULT ElevationExecuteMspPackage(
867 + __in HANDLE hPipe,
868 + __in_opt HWND hwndParent,
869 + __in BURN_EXECUTE_ACTION* pExecuteAction,
870 + __in BURN_VARIABLES* pVariables,
871 + __in BOOL fRollback,
872 + __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
873 + __in LPVOID pvContext,
874 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart
875 + )
876 +{
877 + HRESULT hr = S_OK;
878 + BYTE* pbData = NULL;
879 + SIZE_T cbData = 0;
880 + BURN_ELEVATION_MSI_MESSAGE_CONTEXT context = { };
881 + DWORD dwResult = 0;
882 +
883 + // serialize message data
884 + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->mspTarget.pPackage->sczId);
885 + ExitOnFailure(hr, "Failed to write package id to message buffer.");
886 +
887 + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)hwndParent);
888 + ExitOnFailure(hr, "Failed to write parent hwnd to message buffer.");
889 +
890 + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->mspTarget.sczTargetProductCode);
891 + ExitOnFailure(hr, "Failed to write target product code to message buffer.");
892 +
893 + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->mspTarget.sczLogPath);
894 + ExitOnFailure(hr, "Failed to write package log to message buffer.");
895 +
896 + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->mspTarget.uiLevel);
897 + ExitOnFailure(hr, "Failed to write UI level to message buffer.");
898 +
899 + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->mspTarget.action);
900 + ExitOnFailure(hr, "Failed to write action to message buffer.");
901 +
902 + hr = BuffWriteNumber(&pbData, &cbData, pExecuteAction->mspTarget.cOrderedPatches);
903 + ExitOnFailure(hr, "Failed to write count of ordered patches to message buffer.");
904 +
905 + for (DWORD i = 0; i < pExecuteAction->mspTarget.cOrderedPatches; ++i)
906 + {
907 + hr = BuffWriteNumber(&pbData, &cbData, pExecuteAction->mspTarget.rgOrderedPatches[i].dwOrder);
908 + ExitOnFailure(hr, "Failed to write ordered patch order to message buffer.");
909 +
910 + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->mspTarget.rgOrderedPatches[i].pPackage->sczId);
911 + ExitOnFailure(hr, "Failed to write ordered patch id to message buffer.");
912 + }
913 +
914 + hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData);
915 + ExitOnFailure(hr, "Failed to write variables.");
916 +
917 + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)fRollback);
918 + ExitOnFailure(hr, "Failed to write rollback flag to message buffer.");
919 +
920 + // send message
921 + context.pfnMessageHandler = pfnMessageHandler;
922 + context.pvContext = pvContext;
923 +
924 + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSP_PACKAGE, pbData, cbData, ProcessMsiPackageMessages, &context, &dwResult);
925 + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSP_PACKAGE message to per-machine process.");
926 +
927 + hr = ProcessResult(dwResult, pRestart);
928 +
929 +LExit:
930 + ReleaseBuffer(pbData);
931 +
932 + return hr;
933 +}
934 +
935 +/*******************************************************************
936 + ElevationExecuteMsuPackage -
937 +
938 +*******************************************************************/
939 +extern "C" HRESULT ElevationExecuteMsuPackage(
940 + __in HANDLE hPipe,
941 + __in BURN_EXECUTE_ACTION* pExecuteAction,
942 + __in BOOL fRollback,
943 + __in BOOL fStopWusaService,
944 + __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
945 + __in LPVOID pvContext,
946 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart
947 + )
948 +{
949 + HRESULT hr = S_OK;
950 + BYTE* pbData = NULL;
951 + SIZE_T cbData = 0;
952 + BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT context = { };
953 + DWORD dwResult = 0;
954 +
955 + // serialize message data
956 + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->msuPackage.pPackage->sczId);
957 + ExitOnFailure(hr, "Failed to write package id to message buffer.");
958 +
959 + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->msuPackage.sczLogPath);
960 + ExitOnFailure(hr, "Failed to write package log to message buffer.");
961 +
962 + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msuPackage.action);
963 + ExitOnFailure(hr, "Failed to write action to message buffer.");
964 +
965 + hr = BuffWriteNumber(&pbData, &cbData, fRollback);
966 + ExitOnFailure(hr, "Failed to write rollback.");
967 +
968 + hr = BuffWriteNumber(&pbData, &cbData, fStopWusaService);
969 + ExitOnFailure(hr, "Failed to write StopWusaService.");
970 +
971 + // send message
972 + context.pfnGenericMessageHandler = pfnGenericMessageHandler;
973 + context.pvContext = pvContext;
974 +
975 + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSU_PACKAGE, pbData, cbData, ProcessGenericExecuteMessages, &context, &dwResult);
976 + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSU_PACKAGE message to per-machine process.");
977 +
978 + hr = ProcessResult(dwResult, pRestart);
979 +
980 +LExit:
981 + ReleaseBuffer(pbData);
982 +
983 + return hr;
984 +}
985 +
986 +extern "C" HRESULT ElevationExecutePackageProviderAction(
987 + __in HANDLE hPipe,
988 + __in BURN_EXECUTE_ACTION* pExecuteAction
989 + )
990 +{
991 + HRESULT hr = S_OK;
992 + BYTE* pbData = NULL;
993 + SIZE_T cbData = 0;
994 + DWORD dwResult = 0;
995 + BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE;
996 +
997 + // Serialize the message data.
998 + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->packageProvider.pPackage->sczId);
999 + ExitOnFailure(hr, "Failed to write package id to message buffer.");
1000 +
1001 + hr = BuffWriteNumber(&pbData, &cbData, pExecuteAction->packageProvider.action);
1002 + ExitOnFailure(hr, "Failed to write action to message buffer.");
1003 +
1004 + // Send the message.
1005 + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_PROVIDER, pbData, cbData, NULL, NULL, &dwResult);
1006 + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_PROVIDER message to per-machine process.");
1007 +
1008 + // Ignore the restart since this action only results in registry writes.
1009 + hr = ProcessResult(dwResult, &restart);
1010 +
1011 +LExit:
1012 + ReleaseBuffer(pbData);
1013 +
1014 + return hr;
1015 +}
1016 +
1017 +extern "C" HRESULT ElevationExecutePackageDependencyAction(
1018 + __in HANDLE hPipe,
1019 + __in BURN_EXECUTE_ACTION* pExecuteAction
1020 + )
1021 +{
1022 + HRESULT hr = S_OK;
1023 + BYTE* pbData = NULL;
1024 + SIZE_T cbData = 0;
1025 + DWORD dwResult = 0;
1026 + BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE;
1027 +
1028 + // Serialize the message data.
1029 + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->packageDependency.pPackage->sczId);
1030 + ExitOnFailure(hr, "Failed to write package id to message buffer.");
1031 +
1032 + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->packageDependency.sczBundleProviderKey);
1033 + ExitOnFailure(hr, "Failed to write bundle dependency key to message buffer.");
1034 +
1035 + hr = BuffWriteNumber(&pbData, &cbData, pExecuteAction->packageDependency.action);
1036 + ExitOnFailure(hr, "Failed to write action to message buffer.");
1037 +
1038 + // Send the message.
1039 + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_DEPENDENCY, pbData, cbData, NULL, NULL, &dwResult);
1040 + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_DEPENDENCY message to per-machine process.");
1041 +
1042 + // Ignore the restart since this action only results in registry writes.
1043 + hr = ProcessResult(dwResult, &restart);
1044 +
1045 +LExit:
1046 + ReleaseBuffer(pbData);
1047 +
1048 + return hr;
1049 +}
1050 +
1051 +/*******************************************************************
1052 + ElevationLoadCompatiblePackageAction - Load compatible package
1053 + information from the referenced package.
1054 +
1055 +*******************************************************************/
1056 +extern "C" HRESULT ElevationLoadCompatiblePackageAction(
1057 + __in HANDLE hPipe,
1058 + __in BURN_EXECUTE_ACTION* pExecuteAction
1059 + )
1060 +{
1061 + HRESULT hr = S_OK;
1062 + BYTE* pbData = NULL;
1063 + SIZE_T cbData = 0;
1064 + DWORD dwResult = 0;
1065 + BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE;
1066 +
1067 + // Serialize message data.
1068 + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->compatiblePackage.pReferencePackage->sczId);
1069 + ExitOnFailure(hr, "Failed to write package id to message buffer.");
1070 +
1071 + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->compatiblePackage.sczInstalledProductCode);
1072 + ExitOnFailure(hr, "Failed to write installed ProductCode to message buffer.");
1073 +
1074 + hr = BuffWriteNumber64(&pbData, &cbData, pExecuteAction->compatiblePackage.qwInstalledVersion);
1075 + ExitOnFailure(hr, "Failed to write installed version to message buffer.");
1076 +
1077 + // Send the message.
1078 + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_LOAD_COMPATIBLE_PACKAGE, pbData, cbData, NULL, NULL, &dwResult);
1079 + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_LOAD_COMPATIBLE_PACKAGE message to per-machine process.");
1080 +
1081 + // Ignore the restart since this action only loads data into memory.
1082 + hr = ProcessResult(dwResult, &restart);
1083 +
1084 +LExit:
1085 + ReleaseBuffer(pbData);
1086 +
1087 + return hr;
1088 +}
1089 +
1090 +/*******************************************************************
1091 + ElevationCleanPackage -
1092 +
1093 +*******************************************************************/
1094 +extern "C" HRESULT ElevationCleanPackage(
1095 + __in HANDLE hPipe,
1096 + __in BURN_PACKAGE* pPackage
1097 + )
1098 +{
1099 + HRESULT hr = S_OK;
1100 + BYTE* pbData = NULL;
1101 + SIZE_T cbData = 0;
1102 + DWORD dwResult = 0;
1103 +
1104 + // serialize message data
1105 + hr = BuffWriteString(&pbData, &cbData, pPackage->sczId);
1106 + ExitOnFailure(hr, "Failed to write clean package id to message buffer.");
1107 +
1108 + // send message
1109 + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE, pbData, cbData, NULL, NULL, &dwResult);
1110 + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE message to per-machine process.");
1111 +
1112 + hr = (HRESULT)dwResult;
1113 +
1114 +LExit:
1115 + ReleaseBuffer(pbData);
1116 +
1117 + return hr;
1118 +}
1119 +
1120 +extern "C" HRESULT ElevationLaunchApprovedExe(
1121 + __in HANDLE hPipe,
1122 + __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe,
1123 + __out DWORD* pdwProcessId
1124 + )
1125 +{
1126 + HRESULT hr = S_OK;
1127 + BYTE* pbData = NULL;
1128 + SIZE_T cbData = 0;
1129 + DWORD dwResult = 0;
1130 + BURN_ELEVATION_LAUNCH_APPROVED_EXE_MESSAGE_CONTEXT context = { };
1131 +
1132 + // Serialize message data.
1133 + hr = BuffWriteString(&pbData, &cbData, pLaunchApprovedExe->sczId);
1134 + ExitOnFailure(hr, "Failed to write approved exe id to message buffer.");
1135 +
1136 + hr = BuffWriteString(&pbData, &cbData, pLaunchApprovedExe->sczArguments);
1137 + ExitOnFailure(hr, "Failed to write approved exe arguments to message buffer.");
1138 +
1139 + hr = BuffWriteNumber(&pbData, &cbData, pLaunchApprovedExe->dwWaitForInputIdleTimeout);
1140 + ExitOnFailure(hr, "Failed to write approved exe WaitForInputIdle timeout to message buffer.");
1141 +
1142 + // Send the message.
1143 + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE, pbData, cbData, ProcessLaunchApprovedExeMessages, &context, &dwResult);
1144 + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE message to per-machine process.");
1145 +
1146 + hr = (HRESULT)dwResult;
1147 + *pdwProcessId = context.dwProcessId;
1148 +
1149 +LExit:
1150 + ReleaseBuffer(pbData);
1151 +
1152 + return hr;
1153 +}
1154 +
1155 +/*******************************************************************
1156 + ElevationChildPumpMessages -
1157 +
1158 +*******************************************************************/
1159 +extern "C" HRESULT ElevationChildPumpMessages(
1160 + __in DWORD dwLoggingTlsId,
1161 + __in HANDLE hPipe,
1162 + __in HANDLE hCachePipe,
1163 + __in BURN_APPROVED_EXES* pApprovedExes,
1164 + __in BURN_CONTAINERS* pContainers,
1165 + __in BURN_PACKAGES* pPackages,
1166 + __in BURN_PAYLOADS* pPayloads,
1167 + __in BURN_VARIABLES* pVariables,
1168 + __in BURN_REGISTRATION* pRegistration,
1169 + __in BURN_USER_EXPERIENCE* pUserExperience,
1170 + __out HANDLE* phLock,
1171 + __out BOOL* pfDisabledAutomaticUpdates,
1172 + __out DWORD* pdwChildExitCode,
1173 + __out BOOL* pfRestart
1174 + )
1175 +{
1176 + HRESULT hr = S_OK;
1177 + BURN_ELEVATION_CHILD_MESSAGE_CONTEXT cacheContext = { };
1178 + BURN_ELEVATION_CHILD_MESSAGE_CONTEXT context = { };
1179 + HANDLE hCacheThread = NULL;
1180 + BURN_PIPE_RESULT result = { };
1181 +
1182 + cacheContext.dwLoggingTlsId = dwLoggingTlsId;
1183 + cacheContext.hPipe = hCachePipe;
1184 + cacheContext.pContainers = pContainers;
1185 + cacheContext.pPackages = pPackages;
1186 + cacheContext.pPayloads = pPayloads;
1187 + cacheContext.pVariables = pVariables;
1188 + cacheContext.pRegistration = pRegistration;
1189 + cacheContext.pUserExperience = pUserExperience;
1190 +
1191 + context.dwLoggingTlsId = dwLoggingTlsId;
1192 + context.hPipe = hPipe;
1193 + context.phLock = phLock;
1194 + context.pfDisabledAutomaticUpdates = pfDisabledAutomaticUpdates;
1195 + context.pApprovedExes = pApprovedExes;
1196 + context.pContainers = pContainers;
1197 + context.pPackages = pPackages;
1198 + context.pPayloads = pPayloads;
1199 + context.pVariables = pVariables;
1200 + context.pRegistration = pRegistration;
1201 + context.pUserExperience = pUserExperience;
1202 +
1203 + hCacheThread = ::CreateThread(NULL, 0, ElevatedChildCacheThreadProc, &cacheContext, 0, NULL);
1204 + ExitOnNullWithLastError(hCacheThread, hr, "Failed to create elevated cache thread.");
1205 +
1206 + hr = PipePumpMessages(hPipe, ProcessElevatedChildMessage, &context, &result);
1207 + ExitOnFailure(hr, "Failed to pump messages in child process.");
1208 +
1209 + // Wait for the cache thread and verify it gets the right result but don't fail if things
1210 + // don't work out.
1211 + WaitForElevatedChildCacheThread(hCacheThread, result.dwResult);
1212 +
1213 + *pdwChildExitCode = result.dwResult;
1214 + *pfRestart = result.fRestart;
1215 +
1216 +LExit:
1217 + ReleaseHandle(hCacheThread);
1218 +
1219 + return hr;
1220 +}
1221 +
1222 +extern "C" HRESULT ElevationChildResumeAutomaticUpdates()
1223 +{
1224 + HRESULT hr = S_OK;
1225 +
1226 + LogId(REPORT_STANDARD, MSG_RESUME_AU_STARTING);
1227 +
1228 + hr = WuaResumeAutomaticUpdates();
1229 + ExitOnFailure(hr, "Failed to resume automatic updates after pausing them, continuing...");
1230 +
1231 + LogId(REPORT_STANDARD, MSG_RESUME_AU_SUCCEEDED);
1232 +
1233 +LExit:
1234 + return hr;
1235 +}
1236 +
1237 +// internal function definitions
1238 +
1239 +static DWORD WINAPI ElevatedChildCacheThreadProc(
1240 + __in LPVOID lpThreadParameter
1241 + )
1242 +{
1243 + HRESULT hr = S_OK;
1244 + BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext = reinterpret_cast<BURN_ELEVATION_CHILD_MESSAGE_CONTEXT*>(lpThreadParameter);
1245 + BOOL fComInitialized = FALSE;
1246 + BURN_PIPE_RESULT result = { };
1247 +
1248 + if (!::TlsSetValue(pContext->dwLoggingTlsId, pContext->hPipe))
1249 + {
1250 + ExitWithLastError(hr, "Failed to set elevated cache pipe into thread local storage for logging.");
1251 + }
1252 +
1253 + // initialize COM
1254 + hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
1255 + ExitOnFailure(hr, "Failed to initialize COM.");
1256 + fComInitialized = TRUE;
1257 +
1258 + hr = PipePumpMessages(pContext->hPipe, ProcessElevatedChildCacheMessage, pContext, &result);
1259 + ExitOnFailure(hr, "Failed to pump messages in child process.");
1260 +
1261 + hr = (HRESULT)result.dwResult;
1262 +
1263 +LExit:
1264 + if (fComInitialized)
1265 + {
1266 + ::CoUninitialize();
1267 + }
1268 +
1269 + return (DWORD)hr;
1270 +}
1271 +
1272 +static HRESULT WaitForElevatedChildCacheThread(
1273 + __in HANDLE hCacheThread,
1274 + __in DWORD dwExpectedExitCode
1275 + )
1276 +{
1277 + UNREFERENCED_PARAMETER(dwExpectedExitCode);
1278 +
1279 + HRESULT hr = S_OK;
1280 + DWORD dwExitCode = ERROR_SUCCESS;
1281 +
1282 + if (WAIT_OBJECT_0 != ::WaitForSingleObject(hCacheThread, BURN_TIMEOUT))
1283 + {
1284 + ExitWithLastError(hr, "Failed to wait for cache thread to terminate.");
1285 + }
1286 +
1287 + if (!::GetExitCodeThread(hCacheThread, &dwExitCode))
1288 + {
1289 + ExitWithLastError(hr, "Failed to get cache thread exit code.");
1290 + }
1291 +
1292 + AssertSz(dwExitCode == dwExpectedExitCode, "Cache thread should have exited with the expected exit code.");
1293 +
1294 +LExit:
1295 + return hr;
1296 +}
1297 +
1298 +static HRESULT ProcessGenericExecuteMessages(
1299 + __in BURN_PIPE_MESSAGE* pMsg,
1300 + __in_opt LPVOID pvContext,
1301 + __out DWORD* pdwResult
1302 + )
1303 +{
1304 + HRESULT hr = S_OK;
1305 + SIZE_T iData = 0;
1306 + BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT* pContext = static_cast<BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT*>(pvContext);
1307 + LPWSTR sczMessage = NULL;
1308 + DWORD cFiles = 0;
1309 + LPWSTR* rgwzFiles = NULL;
1310 + GENERIC_EXECUTE_MESSAGE message = { };
1311 +
1312 + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.dwAllowedResults);
1313 + ExitOnFailure(hr, "Failed to allowed results.");
1314 +
1315 + // Process the message.
1316 + switch (pMsg->dwMessage)
1317 + {
1318 + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS:
1319 + message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
1320 +
1321 + // read message parameters
1322 + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.progress.dwPercentage);
1323 + ExitOnFailure(hr, "Failed to progress.");
1324 + break;
1325 +
1326 + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR:
1327 + message.type = GENERIC_EXECUTE_MESSAGE_ERROR;
1328 +
1329 + // read message parameters
1330 + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.error.dwErrorCode);
1331 + ExitOnFailure(hr, "Failed to read error code.");
1332 +
1333 + hr = BuffReadString((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &sczMessage);
1334 + ExitOnFailure(hr, "Failed to read message.");
1335 +
1336 + message.error.wzMessage = sczMessage;
1337 + break;
1338 +
1339 + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE:
1340 + message.type = GENERIC_EXECUTE_MESSAGE_FILES_IN_USE;
1341 +
1342 + // read message parameters
1343 + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &cFiles);
1344 + ExitOnFailure(hr, "Failed to read file count.");
1345 +
1346 + rgwzFiles = (LPWSTR*)MemAlloc(sizeof(LPWSTR*) * cFiles, TRUE);
1347 + ExitOnNull(rgwzFiles, hr, E_OUTOFMEMORY, "Failed to allocate buffer for files in use.");
1348 +
1349 + for (DWORD i = 0; i < cFiles; ++i)
1350 + {
1351 + hr = BuffReadString((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &rgwzFiles[i]);
1352 + ExitOnFailure(hr, "Failed to read file name: %u", i);
1353 + }
1354 +
1355 + message.filesInUse.cFiles = cFiles;
1356 + message.filesInUse.rgwzFiles = (LPCWSTR*)rgwzFiles;
1357 + break;
1358 +
1359 + default:
1360 + hr = E_INVALIDARG;
1361 + ExitOnRootFailure(hr, "Invalid package message.");
1362 + break;
1363 + }
1364 +
1365 + // send message
1366 + *pdwResult = (DWORD)pContext->pfnGenericMessageHandler(&message, pContext->pvContext);;
1367 +
1368 +LExit:
1369 + ReleaseStr(sczMessage);
1370 +
1371 + if (rgwzFiles)
1372 + {
1373 + for (DWORD i = 0; i < cFiles; ++i)
1374 + {
1375 + ReleaseStr(rgwzFiles[i]);
1376 + }
1377 + MemFree(rgwzFiles);
1378 + }
1379 + return hr;
1380 +}
1381 +
1382 +static HRESULT ProcessMsiPackageMessages(
1383 + __in BURN_PIPE_MESSAGE* pMsg,
1384 + __in_opt LPVOID pvContext,
1385 + __out DWORD* pdwResult
1386 + )
1387 +{
1388 + HRESULT hr = S_OK;
1389 + SIZE_T iData = 0;
1390 + WIU_MSI_EXECUTE_MESSAGE message = { };
1391 + DWORD cMsiData = 0;
1392 + LPWSTR* rgwzMsiData = NULL;
1393 + BURN_ELEVATION_MSI_MESSAGE_CONTEXT* pContext = static_cast<BURN_ELEVATION_MSI_MESSAGE_CONTEXT*>(pvContext);
1394 + LPWSTR sczMessage = NULL;
1395 +
1396 + // Read MSI extended message data.
1397 + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &cMsiData);
1398 + ExitOnFailure(hr, "Failed to read MSI data count.");
1399 +
1400 + if (cMsiData)
1401 + {
1402 + rgwzMsiData = (LPWSTR*)MemAlloc(sizeof(LPWSTR*) * cMsiData, TRUE);
1403 + ExitOnNull(rgwzMsiData, hr, E_OUTOFMEMORY, "Failed to allocate buffer to read MSI data.");
1404 +
1405 + for (DWORD i = 0; i < cMsiData; ++i)
1406 + {
1407 + hr = BuffReadString((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &rgwzMsiData[i]);
1408 + ExitOnFailure(hr, "Failed to read MSI data: %u", i);
1409 + }
1410 +
1411 + message.cData = cMsiData;
1412 + message.rgwzData = (LPCWSTR*)rgwzMsiData;
1413 + }
1414 +
1415 + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, (DWORD*)&message.dwAllowedResults);
1416 + ExitOnFailure(hr, "Failed to read UI flags.");
1417 +
1418 + // Process the rest of the message.
1419 + switch (pMsg->dwMessage)
1420 + {
1421 + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS:
1422 + // read message parameters
1423 + message.type = WIU_MSI_EXECUTE_MESSAGE_PROGRESS;
1424 +
1425 + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.progress.dwPercentage);
1426 + ExitOnFailure(hr, "Failed to read progress.");
1427 + break;
1428 +
1429 + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR:
1430 + // read message parameters
1431 + message.type = WIU_MSI_EXECUTE_MESSAGE_ERROR;
1432 +
1433 + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.error.dwErrorCode);
1434 + ExitOnFailure(hr, "Failed to read error code.");
1435 +
1436 + hr = BuffReadString((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &sczMessage);
1437 + ExitOnFailure(hr, "Failed to read message.");
1438 + message.error.wzMessage = sczMessage;
1439 + break;
1440 +
1441 + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_MESSAGE:
1442 + // read message parameters
1443 + message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE;
1444 +
1445 + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, (DWORD*)&message.msiMessage.mt);
1446 + ExitOnFailure(hr, "Failed to read message type.");
1447 +
1448 + hr = BuffReadString((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &sczMessage);
1449 + ExitOnFailure(hr, "Failed to read message.");
1450 + message.msiMessage.wzMessage = sczMessage;
1451 + break;
1452 +
1453 + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE:
1454 + message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE;
1455 + message.msiFilesInUse.cFiles = cMsiData;
1456 + message.msiFilesInUse.rgwzFiles = (LPCWSTR*)rgwzMsiData;
1457 + break;
1458 +
1459 + default:
1460 + hr = E_INVALIDARG;
1461 + ExitOnRootFailure(hr, "Invalid package message.");
1462 + break;
1463 + }
1464 +
1465 + // send message
1466 + *pdwResult = (DWORD)pContext->pfnMessageHandler(&message, pContext->pvContext);
1467 +
1468 +LExit:
1469 + ReleaseStr(sczMessage);
1470 +
1471 + if (rgwzMsiData)
1472 + {
1473 + for (DWORD i = 0; i < cMsiData; ++i)
1474 + {
1475 + ReleaseStr(rgwzMsiData[i]);
1476 + }
1477 +
1478 + MemFree(rgwzMsiData);
1479 + }
1480 +
1481 + return hr;
1482 +}
1483 +
1484 +static HRESULT ProcessLaunchApprovedExeMessages(
1485 + __in BURN_PIPE_MESSAGE* pMsg,
1486 + __in_opt LPVOID pvContext,
1487 + __out DWORD* pdwResult
1488 + )
1489 +{
1490 + HRESULT hr = S_OK;
1491 + SIZE_T iData = 0;
1492 + BURN_ELEVATION_LAUNCH_APPROVED_EXE_MESSAGE_CONTEXT* pContext = static_cast<BURN_ELEVATION_LAUNCH_APPROVED_EXE_MESSAGE_CONTEXT*>(pvContext);
1493 + DWORD dwProcessId = 0;
1494 +
1495 + // Process the message.
1496 + switch (pMsg->dwMessage)
1497 + {
1498 + case BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE_PROCESSID:
1499 + // read message parameters
1500 + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &dwProcessId);
1501 + ExitOnFailure(hr, "Failed to read approved exe process id.");
1502 + pContext->dwProcessId = dwProcessId;
1503 + break;
1504 +
1505 + default:
1506 + hr = E_INVALIDARG;
1507 + ExitOnRootFailure(hr, "Invalid launch approved exe message.");
1508 + break;
1509 + }
1510 +
1511 + *pdwResult = static_cast<DWORD>(hr);
1512 +
1513 +LExit:
1514 + return hr;
1515 +}
1516 +
1517 +static HRESULT ProcessElevatedChildMessage(
1518 + __in BURN_PIPE_MESSAGE* pMsg,
1519 + __in_opt LPVOID pvContext,
1520 + __out DWORD* pdwResult
1521 +)
1522 +{
1523 + HRESULT hr = S_OK;
1524 + BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext = static_cast<BURN_ELEVATION_CHILD_MESSAGE_CONTEXT*>(pvContext);
1525 + HRESULT hrResult = S_OK;
1526 + DWORD dwPid = 0;
1527 +
1528 + switch (pMsg->dwMessage)
1529 + {
1530 + case BURN_ELEVATION_TRANSACTION_BEGIN:
1531 + hrResult = OnMsiBeginTransaction(pContext);
1532 + break;
1533 +
1534 + case BURN_ELEVATION_TRANSACTION_COMMIT:
1535 + hrResult = OnMsiCommitTransaction(pContext);
1536 + break;
1537 +
1538 + case BURN_ELEVATION_TRANSACTION_ROLLBACK:
1539 + hrResult = OnMsiRollbackTransaction(pContext);
1540 + break;
1541 +
1542 + case BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE:
1543 + hrResult = OnApplyInitialize(pContext->pVariables, pContext->pRegistration, pContext->phLock, pContext->pfDisabledAutomaticUpdates, (BYTE*)pMsg->pvData, pMsg->cbData);
1544 + break;
1545 +
1546 + case BURN_ELEVATION_MESSAGE_TYPE_APPLY_UNINITIALIZE:
1547 + hrResult = OnApplyUninitialize(pContext->phLock);
1548 + break;
1549 +
1550 + case BURN_ELEVATION_MESSAGE_TYPE_SESSION_BEGIN:
1551 + hrResult = OnSessionBegin(pContext->pRegistration, pContext->pVariables, pContext->pUserExperience, (BYTE*)pMsg->pvData, pMsg->cbData);
1552 + break;
1553 +
1554 + case BURN_ELEVATION_MESSAGE_TYPE_SESSION_RESUME:
1555 + hrResult = OnSessionResume(pContext->pRegistration, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData);
1556 + break;
1557 +
1558 + case BURN_ELEVATION_MESSAGE_TYPE_SESSION_END:
1559 + hrResult = OnSessionEnd(pContext->pRegistration, (BYTE*)pMsg->pvData, pMsg->cbData);
1560 + break;
1561 +
1562 + case BURN_ELEVATION_MESSAGE_TYPE_SAVE_STATE:
1563 + hrResult = OnSaveState(pContext->pRegistration, (BYTE*)pMsg->pvData, pMsg->cbData);
1564 + break;
1565 +
1566 + case BURN_ELEVATION_MESSAGE_TYPE_PROCESS_DEPENDENT_REGISTRATION:
1567 + hrResult = OnProcessDependentRegistration(pContext->pRegistration, (BYTE*)pMsg->pvData, pMsg->cbData);
1568 + break;
1569 +
1570 + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE:
1571 + hrResult = OnExecuteExePackage(pContext->hPipe, pContext->pPackages, &pContext->pRegistration->relatedBundles, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData);
1572 + break;
1573 +
1574 + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE:
1575 + hrResult = OnExecuteMsiPackage(pContext->hPipe, pContext->pPackages, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData);
1576 + break;
1577 +
1578 + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSP_PACKAGE:
1579 + hrResult = OnExecuteMspPackage(pContext->hPipe, pContext->pPackages, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData);
1580 + break;
1581 +
1582 + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSU_PACKAGE:
1583 + hrResult = OnExecuteMsuPackage(pContext->hPipe, pContext->pPackages, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData);
1584 + break;
1585 +
1586 + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_PROVIDER:
1587 + hrResult = OnExecutePackageProviderAction(pContext->pPackages, &pContext->pRegistration->relatedBundles, (BYTE*)pMsg->pvData, pMsg->cbData);
1588 + break;
1589 +
1590 + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_DEPENDENCY:
1591 + hrResult = OnExecutePackageDependencyAction(pContext->pPackages, &pContext->pRegistration->relatedBundles, (BYTE*)pMsg->pvData, pMsg->cbData);
1592 + break;
1593 +
1594 + case BURN_ELEVATION_MESSAGE_TYPE_LOAD_COMPATIBLE_PACKAGE:
1595 + hrResult = OnLoadCompatiblePackage(pContext->pPackages, (BYTE*)pMsg->pvData, pMsg->cbData);
1596 + break;
1597 +
1598 + case BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE:
1599 + hrResult = OnCleanPackage(pContext->pPackages, (BYTE*)pMsg->pvData, pMsg->cbData);
1600 + break;
1601 +
1602 + case BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE:
1603 + hrResult = OnLaunchApprovedExe(pContext->hPipe, pContext->pApprovedExes, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData);
1604 + break;
1605 +
1606 + default:
1607 + hr = E_INVALIDARG;
1608 + ExitOnRootFailure(hr, "Unexpected elevated message sent to child process, msg: %u", pMsg->dwMessage);
1609 + }
1610 +
1611 + *pdwResult = dwPid ? dwPid : (DWORD)hrResult;
1612 +
1613 +LExit:
1614 + return hr;
1615 +}
1616 +
1617 +static HRESULT ProcessElevatedChildCacheMessage(
1618 + __in BURN_PIPE_MESSAGE* pMsg,
1619 + __in_opt LPVOID pvContext,
1620 + __out DWORD* pdwResult
1621 + )
1622 +{
1623 + HRESULT hr = S_OK;
1624 + BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext = static_cast<BURN_ELEVATION_CHILD_MESSAGE_CONTEXT*>(pvContext);
1625 + HRESULT hrResult = S_OK;
1626 +
1627 + switch (pMsg->dwMessage)
1628 + {
1629 + case BURN_ELEVATION_MESSAGE_TYPE_LAYOUT_BUNDLE:
1630 + hrResult = OnLayoutBundle(pContext->pRegistration->sczExecutableName, (BYTE*)pMsg->pvData, pMsg->cbData);
1631 + break;
1632 +
1633 + case BURN_ELEVATION_MESSAGE_TYPE_CACHE_OR_LAYOUT_CONTAINER_OR_PAYLOAD:
1634 + hrResult = OnCacheOrLayoutContainerOrPayload(pContext->pContainers, pContext->pPackages, pContext->pPayloads, (BYTE*)pMsg->pvData, pMsg->cbData);
1635 + break;
1636 +
1637 + case BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP:
1638 + OnCacheCleanup(pContext->pRegistration->sczId);
1639 + hrResult = S_OK;
1640 + break;
1641 +
1642 + case BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE:
1643 + hrResult = OnCleanPackage(pContext->pPackages, (BYTE*)pMsg->pvData, pMsg->cbData);
1644 + break;
1645 +
1646 + default:
1647 + hr = E_INVALIDARG;
1648 + ExitOnRootFailure(hr, "Unexpected elevated cache message sent to child process, msg: %u", pMsg->dwMessage);
1649 + }
1650 +
1651 + *pdwResult = (DWORD)hrResult;
1652 +
1653 +LExit:
1654 + return hr;
1655 +}
1656 +
1657 +static HRESULT ProcessResult(
1658 + __in DWORD dwResult,
1659 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart
1660 + )
1661 +{
1662 + HRESULT hr = static_cast<HRESULT>(dwResult);
1663 + if (HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED) == hr)
1664 + {
1665 + *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED;
1666 + hr = S_OK;
1667 + }
1668 + else if (HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED) == hr)
1669 + {
1670 + *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED;
1671 + hr = S_OK;
1672 + }
1673 +
1674 + return hr;
1675 +}
1676 +
1677 +static HRESULT OnMsiBeginTransaction(
1678 + __in BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext
1679 +)
1680 +{
1681 + UINT uResult = ERROR_SUCCESS;
1682 + HRESULT hr = S_OK;
1683 +
1684 + pContext->hMsiTrns = NULL;
1685 + pContext->hMsiTrnsEvent = NULL;
1686 + uResult = MsiBeginTransaction(L"WiX", 0, &pContext->hMsiTrns, &pContext->hMsiTrnsEvent);
1687 + ExitOnWin32Error(uResult, hr, "Failed beginning an MSI transaction");
1688 +
1689 +LExit:
1690 + return hr;
1691 +}
1692 +
1693 +static HRESULT OnMsiCommitTransaction(
1694 + __in BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext
1695 +)
1696 +{
1697 + UINT uResult = ERROR_SUCCESS;
1698 + HRESULT hr = S_OK;
1699 +
1700 + uResult = MsiEndTransaction(MSITRANSACTIONSTATE_COMMIT);
1701 + ExitOnWin32Error(uResult, hr, "Failed committing an MSI transaction");
1702 +
1703 +LExit:
1704 + pContext->hMsiTrns = NULL;
1705 + pContext->hMsiTrnsEvent = NULL;
1706 + return hr;
1707 +}
1708 +
1709 +static HRESULT OnMsiRollbackTransaction(
1710 + __in BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext
1711 +) {
1712 + UINT uResult = ERROR_SUCCESS;
1713 + HRESULT hr = S_OK;
1714 +
1715 + uResult = MsiEndTransaction(MSITRANSACTIONSTATE_ROLLBACK);
1716 + ExitOnWin32Error(uResult, hr, "Failed rolling back an MSI transaction");
1717 +
1718 +LExit:
1719 + pContext->hMsiTrns = NULL;
1720 + pContext->hMsiTrnsEvent = NULL;
1721 + return hr;
1722 +}
1723 +
1724 +static HRESULT OnApplyInitialize(
1725 + __in BURN_VARIABLES* pVariables,
1726 + __in BURN_REGISTRATION* pRegistration,
1727 + __in HANDLE* phLock,
1728 + __in BOOL* pfDisabledWindowsUpdate,
1729 + __in BYTE* pbData,
1730 + __in DWORD cbData
1731 + )
1732 +{
1733 + HRESULT hr = S_OK;
1734 + SIZE_T iData = 0;
1735 + DWORD dwAction = 0;
1736 + DWORD dwAUAction = 0;
1737 + DWORD dwTakeSystemRestorePoint = 0;
1738 + LPWSTR sczBundleName = NULL;
1739 +
1740 + // Deserialize message data.
1741 + hr = BuffReadNumber(pbData, cbData, &iData, &dwAction);
1742 + ExitOnFailure(hr, "Failed to read action.");
1743 +
1744 + hr = BuffReadNumber(pbData, cbData, &iData, &dwAUAction);
1745 + ExitOnFailure(hr, "Failed to read update action.");
1746 +
1747 + hr = BuffReadNumber(pbData, cbData, &iData, &dwTakeSystemRestorePoint);
1748 + ExitOnFailure(hr, "Failed to read system restore point action.");
1749 +
1750 + hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData);
1751 + ExitOnFailure(hr, "Failed to read variables.");
1752 +
1753 + // Initialize.
1754 + hr = ApplyLock(TRUE, phLock);
1755 + ExitOnFailure(hr, "Failed to acquire lock due to setup in other session.");
1756 +
1757 + // Reset and reload the related bundles.
1758 + RelatedBundlesUninitialize(&pRegistration->relatedBundles);
1759 +
1760 + hr = RelatedBundlesInitializeForScope(TRUE, pRegistration, &pRegistration->relatedBundles);
1761 + ExitOnFailure(hr, "Failed to initialize per-machine related bundles.");
1762 +
1763 + // Attempt to pause AU with best effort.
1764 + if (BURN_AU_PAUSE_ACTION_IFELEVATED == dwAUAction || BURN_AU_PAUSE_ACTION_IFELEVATED_NORESUME == dwAUAction)
1765 + {
1766 + LogId(REPORT_STANDARD, MSG_PAUSE_AU_STARTING);
1767 +
1768 + hr = WuaPauseAutomaticUpdates();
1769 + if (FAILED(hr))
1770 + {
1771 + LogId(REPORT_STANDARD, MSG_FAILED_PAUSE_AU, hr);
1772 + hr = S_OK;
1773 + }
1774 + else
1775 + {
1776 + LogId(REPORT_STANDARD, MSG_PAUSE_AU_SUCCEEDED);
1777 + if (BURN_AU_PAUSE_ACTION_IFELEVATED == dwAUAction)
1778 + {
1779 + *pfDisabledWindowsUpdate = TRUE;
1780 + }
1781 + }
1782 + }
1783 +
1784 + if (dwTakeSystemRestorePoint)
1785 + {
1786 + hr = VariableGetString(pVariables, BURN_BUNDLE_NAME, &sczBundleName);
1787 + if (FAILED(hr))
1788 + {
1789 + hr = S_OK;
1790 + ExitFunction();
1791 + }
1792 +
1793 + LogId(REPORT_STANDARD, MSG_SYSTEM_RESTORE_POINT_STARTING);
1794 +
1795 + BOOTSTRAPPER_ACTION action = static_cast<BOOTSTRAPPER_ACTION>(dwAction);
1796 + SRP_ACTION restoreAction = (BOOTSTRAPPER_ACTION_INSTALL == action) ? SRP_ACTION_INSTALL : (BOOTSTRAPPER_ACTION_UNINSTALL == action) ? SRP_ACTION_UNINSTALL : SRP_ACTION_MODIFY;
1797 + hr = SrpCreateRestorePoint(sczBundleName, restoreAction);
1798 + if (SUCCEEDED(hr))
1799 + {
1800 + LogId(REPORT_STANDARD, MSG_SYSTEM_RESTORE_POINT_SUCCEEDED);
1801 + }
1802 + else if (E_NOTIMPL == hr)
1803 + {
1804 + LogId(REPORT_STANDARD, MSG_SYSTEM_RESTORE_POINT_DISABLED);
1805 + hr = S_OK;
1806 + }
1807 + else if (FAILED(hr))
1808 + {
1809 + LogId(REPORT_STANDARD, MSG_SYSTEM_RESTORE_POINT_FAILED, hr);
1810 + hr = S_OK;
1811 + }
1812 + }
1813 +
1814 +LExit:
1815 + ReleaseStr(sczBundleName);
1816 + return hr;
1817 +}
1818 +
1819 +static HRESULT OnApplyUninitialize(
1820 + __in HANDLE* phLock
1821 + )
1822 +{
1823 + Assert(phLock);
1824 +
1825 + // TODO: end system restore point.
1826 +
1827 + if (*phLock)
1828 + {
1829 + ::ReleaseMutex(*phLock);
1830 + ::CloseHandle(*phLock);
1831 + *phLock = NULL;
1832 + }
1833 +
1834 + return S_OK;
1835 +}
1836 +
1837 +static HRESULT OnSessionBegin(
1838 + __in BURN_REGISTRATION* pRegistration,
1839 + __in BURN_VARIABLES* pVariables,
1840 + __in BURN_USER_EXPERIENCE* pUserExperience,
1841 + __in BYTE* pbData,
1842 + __in DWORD cbData
1843 + )
1844 +{
1845 + HRESULT hr = S_OK;
1846 + SIZE_T iData = 0;
1847 + LPWSTR sczEngineWorkingPath = NULL;
1848 + DWORD dwRegistrationOperations = 0;
1849 + DWORD dwDependencyRegistrationAction = 0;
1850 + DWORD64 qwEstimatedSize = 0;
1851 +
1852 + // Deserialize message data.
1853 + hr = BuffReadString(pbData, cbData, &iData, &sczEngineWorkingPath);
1854 + ExitOnFailure(hr, "Failed to read engine working path.");
1855 +
1856 + hr = BuffReadString(pbData, cbData, &iData, &pRegistration->sczResumeCommandLine);
1857 + ExitOnFailure(hr, "Failed to read resume command line.");
1858 +
1859 + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&pRegistration->fDisableResume);
1860 + ExitOnFailure(hr, "Failed to read resume flag.");
1861 +
1862 + hr = BuffReadNumber(pbData, cbData, &iData, &dwRegistrationOperations);
1863 + ExitOnFailure(hr, "Failed to read registration operations.");
1864 +
1865 + hr = BuffReadNumber(pbData, cbData, &iData, &dwDependencyRegistrationAction);
1866 + ExitOnFailure(hr, "Failed to read dependency registration action.");
1867 +
1868 + hr = BuffReadNumber64(pbData, cbData, &iData, &qwEstimatedSize);
1869 + ExitOnFailure(hr, "Failed to read estimated size.");
1870 +
1871 + hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData);
1872 + ExitOnFailure(hr, "Failed to read variables.");
1873 +
1874 + // Begin session in per-machine process.
1875 + hr = RegistrationSessionBegin(sczEngineWorkingPath, pRegistration, pVariables, pUserExperience, dwRegistrationOperations, (BURN_DEPENDENCY_REGISTRATION_ACTION)dwDependencyRegistrationAction, qwEstimatedSize);
1876 + ExitOnFailure(hr, "Failed to begin registration session.");
1877 +
1878 +LExit:
1879 + ReleaseStr(sczEngineWorkingPath);
1880 +
1881 + return hr;
1882 +}
1883 +
1884 +static HRESULT OnSessionResume(
1885 + __in BURN_REGISTRATION* pRegistration,
1886 + __in BURN_VARIABLES* pVariables,
1887 + __in BYTE* pbData,
1888 + __in DWORD cbData
1889 + )
1890 +{
1891 + HRESULT hr = S_OK;
1892 + SIZE_T iData = 0;
1893 +
1894 + // Deserialize message data.
1895 + hr = BuffReadString(pbData, cbData, &iData, &pRegistration->sczResumeCommandLine);
1896 + ExitOnFailure(hr, "Failed to read resume command line.");
1897 +
1898 + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&pRegistration->fDisableResume);
1899 + ExitOnFailure(hr, "Failed to read resume flag.");
1900 +
1901 + hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData);
1902 + ExitOnFailure(hr, "Failed to read variables.");
1903 +
1904 + // resume session in per-machine process
1905 + hr = RegistrationSessionResume(pRegistration, pVariables);
1906 + ExitOnFailure(hr, "Failed to resume registration session.");
1907 +
1908 +LExit:
1909 + return hr;
1910 +}
1911 +
1912 +static HRESULT OnSessionEnd(
1913 + __in BURN_REGISTRATION* pRegistration,
1914 + __in BYTE* pbData,
1915 + __in DWORD cbData
1916 + )
1917 +{
1918 + HRESULT hr = S_OK;
1919 + SIZE_T iData = 0;
1920 + DWORD dwResumeMode = 0;
1921 + DWORD dwRestart = 0;
1922 + DWORD dwDependencyRegistrationAction = 0;
1923 +
1924 + // Deserialize message data.
1925 + hr = BuffReadNumber(pbData, cbData, &iData, &dwResumeMode);
1926 + ExitOnFailure(hr, "Failed to read resume mode enum.");
1927 +
1928 + hr = BuffReadNumber(pbData, cbData, &iData, &dwRestart);
1929 + ExitOnFailure(hr, "Failed to read restart enum.");
1930 +
1931 + hr = BuffReadNumber(pbData, cbData, &iData, &dwDependencyRegistrationAction);
1932 + ExitOnFailure(hr, "Failed to read dependency registration action.");
1933 +
1934 + // suspend session in per-machine process
1935 + hr = RegistrationSessionEnd(pRegistration, (BURN_RESUME_MODE)dwResumeMode, (BOOTSTRAPPER_APPLY_RESTART)dwRestart, (BURN_DEPENDENCY_REGISTRATION_ACTION)dwDependencyRegistrationAction);
1936 + ExitOnFailure(hr, "Failed to suspend registration session.");
1937 +
1938 +LExit:
1939 + return hr;
1940 +}
1941 +
1942 +static HRESULT OnSaveState(
1943 + __in BURN_REGISTRATION* pRegistration,
1944 + __in BYTE* pbData,
1945 + __in DWORD cbData
1946 + )
1947 +{
1948 + HRESULT hr = S_OK;
1949 +
1950 + // save state in per-machine process
1951 + hr = RegistrationSaveState(pRegistration, pbData, cbData);
1952 + ExitOnFailure(hr, "Failed to save state.");
1953 +
1954 +LExit:
1955 + return hr;
1956 +}
1957 +
1958 +static HRESULT OnLayoutBundle(
1959 + __in_z LPCWSTR wzExecutableName,
1960 + __in BYTE* pbData,
1961 + __in DWORD cbData
1962 + )
1963 +{
1964 + HRESULT hr = S_OK;
1965 + SIZE_T iData = 0;
1966 + LPWSTR sczLayoutDirectory = NULL;
1967 + LPWSTR sczUnverifiedPath = NULL;
1968 +
1969 + // Deserialize message data.
1970 + hr = BuffReadString(pbData, cbData, &iData, &sczLayoutDirectory);
1971 + ExitOnFailure(hr, "Failed to read layout directory.");
1972 +
1973 + hr = BuffReadString(pbData, cbData, &iData, &sczUnverifiedPath);
1974 + ExitOnFailure(hr, "Failed to read unverified bundle path.");
1975 +
1976 + // Layout the bundle.
1977 + hr = CacheLayoutBundle(wzExecutableName, sczLayoutDirectory, sczUnverifiedPath);
1978 + ExitOnFailure(hr, "Failed to layout bundle from: %ls", sczUnverifiedPath);
1979 +
1980 +LExit:
1981 + ReleaseStr(sczUnverifiedPath);
1982 + ReleaseStr(sczLayoutDirectory);
1983 +
1984 + return hr;
1985 +}
1986 +
1987 +static HRESULT OnCacheOrLayoutContainerOrPayload(
1988 + __in BURN_CONTAINERS* pContainers,
1989 + __in BURN_PACKAGES* pPackages,
1990 + __in BURN_PAYLOADS* pPayloads,
1991 + __in BYTE* pbData,
1992 + __in DWORD cbData
1993 + )
1994 +{
1995 + HRESULT hr = S_OK;
1996 + SIZE_T iData = 0;
1997 + LPWSTR scz = NULL;
1998 + BURN_CONTAINER* pContainer = NULL;
1999 + BURN_PACKAGE* pPackage = NULL;
2000 + BURN_PAYLOAD* pPayload = NULL;
2001 + LPWSTR sczLayoutDirectory = NULL;
2002 + LPWSTR sczUnverifiedPath = NULL;
2003 + BOOL fMove = FALSE;
2004 +
2005 + // Deserialize message data.
2006 + hr = BuffReadString(pbData, cbData, &iData, &scz);
2007 + ExitOnFailure(hr, "Failed to read package id.");
2008 +
2009 + if (scz && *scz)
2010 + {
2011 + hr = ContainerFindById(pContainers, scz, &pContainer);
2012 + ExitOnFailure(hr, "Failed to find container: %ls", scz);
2013 + }
2014 +
2015 + hr = BuffReadString(pbData, cbData, &iData, &scz);
2016 + ExitOnFailure(hr, "Failed to read package id.");
2017 +
2018 + if (scz && *scz)
2019 + {
2020 + hr = PackageFindById(pPackages, scz, &pPackage);
2021 + ExitOnFailure(hr, "Failed to find package: %ls", scz);
2022 + }
2023 +
2024 + hr = BuffReadString(pbData, cbData, &iData, &scz);
2025 + ExitOnFailure(hr, "Failed to read payload id.");
2026 +
2027 + if (scz && *scz)
2028 + {
2029 + hr = PayloadFindById(pPayloads, scz, &pPayload);
2030 + ExitOnFailure(hr, "Failed to find payload: %ls", scz);
2031 + }
2032 +
2033 + hr = BuffReadString(pbData, cbData, &iData, &sczLayoutDirectory);
2034 + ExitOnFailure(hr, "Failed to read layout directory.");
2035 +
2036 + hr = BuffReadString(pbData, cbData, &iData, &sczUnverifiedPath);
2037 + ExitOnFailure(hr, "Failed to read unverified path.");
2038 +
2039 + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&fMove);
2040 + ExitOnFailure(hr, "Failed to read move flag.");
2041 +
2042 + // Layout payload.
2043 + if (sczLayoutDirectory && *sczLayoutDirectory)
2044 + {
2045 + if (pContainer)
2046 + {
2047 + Assert(!pPackage);
2048 + Assert(!pPayload);
2049 +
2050 + hr = CacheLayoutContainer(pContainer, sczLayoutDirectory, sczUnverifiedPath, fMove);
2051 + ExitOnFailure(hr, "Failed to layout container from: %ls to %ls", sczUnverifiedPath, sczLayoutDirectory);
2052 + }
2053 + else
2054 + {
2055 + hr = CacheLayoutPayload(pPayload, sczLayoutDirectory, sczUnverifiedPath, fMove);
2056 + ExitOnFailure(hr, "Failed to layout payload from: %ls to %ls", sczUnverifiedPath, sczLayoutDirectory);
2057 + }
2058 + }
2059 + else if (pPackage) // complete payload.
2060 + {
2061 + Assert(!pContainer);
2062 +
2063 + hr = CacheCompletePayload(pPackage->fPerMachine, pPayload, pPackage->sczCacheId, sczUnverifiedPath, fMove);
2064 + ExitOnFailure(hr, "Failed to cache payload: %ls", pPayload->sczKey);
2065 + }
2066 + else
2067 + {
2068 + hr = E_INVALIDARG;
2069 + ExitOnRootFailure(hr, "Invalid data passed to cache or layout payload.");
2070 + }
2071 +
2072 +LExit:
2073 + ReleaseStr(sczUnverifiedPath);
2074 + ReleaseStr(sczLayoutDirectory);
2075 + ReleaseStr(scz);
2076 +
2077 + return hr;
2078 +}
2079 +
2080 +static void OnCacheCleanup(
2081 + __in_z LPCWSTR wzBundleId
2082 + )
2083 +{
2084 + CacheCleanup(TRUE, wzBundleId);
2085 +}
2086 +
2087 +static HRESULT OnProcessDependentRegistration(
2088 + __in const BURN_REGISTRATION* pRegistration,
2089 + __in BYTE* pbData,
2090 + __in DWORD cbData
2091 + )
2092 +{
2093 + HRESULT hr = S_OK;
2094 + SIZE_T iData = 0;
2095 + BURN_DEPENDENT_REGISTRATION_ACTION action = { };
2096 +
2097 + // Deserialize message data.
2098 + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&action.type);
2099 + ExitOnFailure(hr, "Failed to read action type.");
2100 +
2101 + hr = BuffReadString(pbData, cbData, &iData, &action.sczBundleId);
2102 + ExitOnFailure(hr, "Failed to read bundle id.");
2103 +
2104 + hr = BuffReadString(pbData, cbData, &iData, &action.sczDependentProviderKey);
2105 + ExitOnFailure(hr, "Failed to read dependent provider key.");
2106 +
2107 + // Execute the registration action.
2108 + hr = DependencyProcessDependentRegistration(pRegistration, &action);
2109 + ExitOnFailure(hr, "Failed to execute dependent registration action for provider key: %ls", action.sczDependentProviderKey);
2110 +
2111 +LExit:
2112 + // TODO: do the right thing here.
2113 + //DependencyUninitializeRegistrationAction(&action);
2114 + ReleaseStr(action.sczDependentProviderKey);
2115 + ReleaseStr(action.sczBundleId)
2116 +
2117 + return hr;
2118 +}
2119 +
2120 +static HRESULT OnExecuteExePackage(
2121 + __in HANDLE hPipe,
2122 + __in BURN_PACKAGES* pPackages,
2123 + __in BURN_RELATED_BUNDLES* pRelatedBundles,
2124 + __in BURN_VARIABLES* pVariables,
2125 + __in BYTE* pbData,
2126 + __in DWORD cbData
2127 + )
2128 +{
2129 + HRESULT hr = S_OK;
2130 + SIZE_T iData = 0;
2131 + LPWSTR sczPackage = NULL;
2132 + DWORD dwRollback = 0;
2133 + BURN_EXECUTE_ACTION executeAction = { };
2134 + LPWSTR sczIgnoreDependencies = NULL;
2135 + LPWSTR sczAncestors = NULL;
2136 + BOOTSTRAPPER_APPLY_RESTART exeRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
2137 +
2138 + executeAction.type = BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE;
2139 +
2140 + // Deserialize message data.
2141 + hr = BuffReadString(pbData, cbData, &iData, &sczPackage);
2142 + ExitOnFailure(hr, "Failed to read EXE package id.");
2143 +
2144 + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.exePackage.action);
2145 + ExitOnFailure(hr, "Failed to read action.");
2146 +
2147 + hr = BuffReadNumber(pbData, cbData, &iData, &dwRollback);
2148 + ExitOnFailure(hr, "Failed to read rollback.");
2149 +
2150 + hr = BuffReadString(pbData, cbData, &iData, &sczIgnoreDependencies);
2151 + ExitOnFailure(hr, "Failed to read the list of dependencies to ignore.");
2152 +
2153 + hr = BuffReadString(pbData, cbData, &iData, &sczAncestors);
2154 + ExitOnFailure(hr, "Failed to read the list of ancestors.");
2155 +
2156 + hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData);
2157 + ExitOnFailure(hr, "Failed to read variables.");
2158 +
2159 + hr = PackageFindById(pPackages, sczPackage, &executeAction.exePackage.pPackage);
2160 + if (E_NOTFOUND == hr)
2161 + {
2162 + hr = PackageFindRelatedById(pRelatedBundles, sczPackage, &executeAction.exePackage.pPackage);
2163 + }
2164 + ExitOnFailure(hr, "Failed to find package: %ls", sczPackage);
2165 +
2166 + // Pass the list of dependencies to ignore, if any, to the related bundle.
2167 + if (sczIgnoreDependencies && *sczIgnoreDependencies)
2168 + {
2169 + hr = StrAllocString(&executeAction.exePackage.sczIgnoreDependencies, sczIgnoreDependencies, 0);
2170 + ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore.");
2171 + }
2172 +
2173 + // Pass the list of ancestors, if any, to the related bundle.
2174 + if (sczAncestors && *sczAncestors)
2175 + {
2176 + hr = StrAllocString(&executeAction.exePackage.sczAncestors, sczAncestors, 0);
2177 + ExitOnFailure(hr, "Failed to allocate the list of ancestors.");
2178 + }
2179 +
2180 + // Execute EXE package.
2181 + hr = ExeEngineExecutePackage(&executeAction, pVariables, static_cast<BOOL>(dwRollback), GenericExecuteMessageHandler, hPipe, &exeRestart);
2182 + ExitOnFailure(hr, "Failed to execute EXE package.");
2183 +
2184 +LExit:
2185 + ReleaseStr(sczAncestors);
2186 + ReleaseStr(sczIgnoreDependencies);
2187 + ReleaseStr(sczPackage);
2188 + PlanUninitializeExecuteAction(&executeAction);
2189 +
2190 + if (SUCCEEDED(hr))
2191 + {
2192 + if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == exeRestart)
2193 + {
2194 + hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED);
2195 + }
2196 + else if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == exeRestart)
2197 + {
2198 + hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED);
2199 + }
2200 + }
2201 +
2202 + return hr;
2203 +}
2204 +
2205 +static HRESULT OnExecuteMsiPackage(
2206 + __in HANDLE hPipe,
2207 + __in BURN_PACKAGES* pPackages,
2208 + __in BURN_VARIABLES* pVariables,
2209 + __in BYTE* pbData,
2210 + __in DWORD cbData
2211 + )
2212 +{
2213 + HRESULT hr = S_OK;
2214 + SIZE_T iData = 0;
2215 + LPWSTR sczPackage = NULL;
2216 + HWND hwndParent = NULL;
2217 + BOOL fRollback = 0;
2218 + BURN_EXECUTE_ACTION executeAction = { };
2219 + BOOTSTRAPPER_APPLY_RESTART msiRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
2220 +
2221 + executeAction.type = BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE;
2222 +
2223 + // Deserialize message data.
2224 + hr = BuffReadString(pbData, cbData, &iData, &sczPackage);
2225 + ExitOnFailure(hr, "Failed to read MSI package id.");
2226 +
2227 + hr = PackageFindById(pPackages, sczPackage, &executeAction.msiPackage.pPackage);
2228 + ExitOnFailure(hr, "Failed to find package: %ls", sczPackage);
2229 +
2230 + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&hwndParent);
2231 + ExitOnFailure(hr, "Failed to read parent hwnd.");
2232 +
2233 + hr = BuffReadString(pbData, cbData, &iData, &executeAction.msiPackage.sczLogPath);
2234 + ExitOnFailure(hr, "Failed to read package log.");
2235 +
2236 + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.uiLevel);
2237 + ExitOnFailure(hr, "Failed to read UI level.");
2238 +
2239 + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.action);
2240 + ExitOnFailure(hr, "Failed to read action.");
2241 +
2242 + // Read feature actions.
2243 + if (executeAction.msiPackage.pPackage->Msi.cFeatures)
2244 + {
2245 + executeAction.msiPackage.rgFeatures = (BOOTSTRAPPER_FEATURE_ACTION*)MemAlloc(executeAction.msiPackage.pPackage->Msi.cFeatures * sizeof(BOOTSTRAPPER_FEATURE_ACTION), TRUE);
2246 + ExitOnNull(executeAction.msiPackage.rgFeatures, hr, E_OUTOFMEMORY, "Failed to allocate memory for feature actions.");
2247 +
2248 + for (DWORD i = 0; i < executeAction.msiPackage.pPackage->Msi.cFeatures; ++i)
2249 + {
2250 + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.rgFeatures[i]);
2251 + ExitOnFailure(hr, "Failed to read feature action.");
2252 + }
2253 + }
2254 +
2255 + // Read slipstream patches actions.
2256 + if (executeAction.msiPackage.pPackage->Msi.cSlipstreamMspPackages)
2257 + {
2258 + executeAction.msiPackage.rgSlipstreamPatches = (BOOTSTRAPPER_ACTION_STATE*)MemAlloc(executeAction.msiPackage.pPackage->Msi.cSlipstreamMspPackages * sizeof(BOOTSTRAPPER_ACTION_STATE), TRUE);
2259 + ExitOnNull(executeAction.msiPackage.rgSlipstreamPatches, hr, E_OUTOFMEMORY, "Failed to allocate memory for slipstream patch actions.");
2260 +
2261 + for (DWORD i = 0; i < executeAction.msiPackage.pPackage->Msi.cSlipstreamMspPackages; ++i)
2262 + {
2263 + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.rgSlipstreamPatches[i]);
2264 + ExitOnFailure(hr, "Failed to read slipstream action.");
2265 + }
2266 + }
2267 +
2268 + hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData);
2269 + ExitOnFailure(hr, "Failed to read variables.");
2270 +
2271 + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&fRollback);
2272 + ExitOnFailure(hr, "Failed to read rollback flag.");
2273 +
2274 + // Execute MSI package.
2275 + hr = MsiEngineExecutePackage(hwndParent, &executeAction, pVariables, fRollback, MsiExecuteMessageHandler, hPipe, &msiRestart);
2276 + ExitOnFailure(hr, "Failed to execute MSI package.");
2277 +
2278 +LExit:
2279 + ReleaseStr(sczPackage);
2280 + PlanUninitializeExecuteAction(&executeAction);
2281 +
2282 + if (SUCCEEDED(hr))
2283 + {
2284 + if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == msiRestart)
2285 + {
2286 + hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED);
2287 + }
2288 + else if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == msiRestart)
2289 + {
2290 + hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED);
2291 + }
2292 + }
2293 +
2294 + return hr;
2295 +}
2296 +
2297 +static HRESULT OnExecuteMspPackage(
2298 + __in HANDLE hPipe,
2299 + __in BURN_PACKAGES* pPackages,
2300 + __in BURN_VARIABLES* pVariables,
2301 + __in BYTE* pbData,
2302 + __in DWORD cbData
2303 + )
2304 +{
2305 + HRESULT hr = S_OK;
2306 + SIZE_T iData = 0;
2307 + LPWSTR sczPackage = NULL;
2308 + HWND hwndParent = NULL;
2309 + BOOL fRollback = 0;
2310 + BURN_EXECUTE_ACTION executeAction = { };
2311 + BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE;
2312 +
2313 + executeAction.type = BURN_EXECUTE_ACTION_TYPE_MSP_TARGET;
2314 +
2315 + // Deserialize message data.
2316 + hr = BuffReadString(pbData, cbData, &iData, &sczPackage);
2317 + ExitOnFailure(hr, "Failed to read MSP package id.");
2318 +
2319 + hr = PackageFindById(pPackages, sczPackage, &executeAction.mspTarget.pPackage);
2320 + ExitOnFailure(hr, "Failed to find package: %ls", sczPackage);
2321 +
2322 + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&hwndParent);
2323 + ExitOnFailure(hr, "Failed to read parent hwnd.");
2324 +
2325 + executeAction.mspTarget.fPerMachineTarget = TRUE; // we're in the elevated process, clearly we're targeting a per-machine product.
2326 +
2327 + hr = BuffReadString(pbData, cbData, &iData, &executeAction.mspTarget.sczTargetProductCode);
2328 + ExitOnFailure(hr, "Failed to read target product code.");
2329 +
2330 + hr = BuffReadString(pbData, cbData, &iData, &executeAction.mspTarget.sczLogPath);
2331 + ExitOnFailure(hr, "Failed to read package log.");
2332 +
2333 + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.mspTarget.uiLevel);
2334 + ExitOnFailure(hr, "Failed to read UI level.");
2335 +
2336 + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.mspTarget.action);
2337 + ExitOnFailure(hr, "Failed to read action.");
2338 +
2339 + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.mspTarget.cOrderedPatches);
2340 + ExitOnFailure(hr, "Failed to read count of ordered patches.");
2341 +
2342 + if (executeAction.mspTarget.cOrderedPatches)
2343 + {
2344 + executeAction.mspTarget.rgOrderedPatches = (BURN_ORDERED_PATCHES*)MemAlloc(executeAction.mspTarget.cOrderedPatches * sizeof(BURN_ORDERED_PATCHES), TRUE);
2345 + ExitOnNull(executeAction.mspTarget.rgOrderedPatches, hr, E_OUTOFMEMORY, "Failed to allocate memory for ordered patches.");
2346 +
2347 + for (DWORD i = 0; i < executeAction.mspTarget.cOrderedPatches; ++i)
2348 + {
2349 + hr = BuffReadNumber(pbData, cbData, &iData, &executeAction.mspTarget.rgOrderedPatches[i].dwOrder);
2350 + ExitOnFailure(hr, "Failed to read ordered patch order number.");
2351 +
2352 + hr = BuffReadString(pbData, cbData, &iData, &sczPackage);
2353 + ExitOnFailure(hr, "Failed to read ordered patch package id.");
2354 +
2355 + hr = PackageFindById(pPackages, sczPackage, &executeAction.mspTarget.rgOrderedPatches[i].pPackage);
2356 + ExitOnFailure(hr, "Failed to find ordered patch package: %ls", sczPackage);
2357 + }
2358 + }
2359 +
2360 + hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData);
2361 + ExitOnFailure(hr, "Failed to read variables.");
2362 +
2363 + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&fRollback);
2364 + ExitOnFailure(hr, "Failed to read rollback flag.");
2365 +
2366 + // Execute MSP package.
2367 + hr = MspEngineExecutePackage(hwndParent, &executeAction, pVariables, fRollback, MsiExecuteMessageHandler, hPipe, &restart);
2368 + ExitOnFailure(hr, "Failed to execute MSP package.");
2369 +
2370 +LExit:
2371 + ReleaseStr(sczPackage);
2372 + PlanUninitializeExecuteAction(&executeAction);
2373 +
2374 + if (SUCCEEDED(hr))
2375 + {
2376 + if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == restart)
2377 + {
2378 + hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED);
2379 + }
2380 + else if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart)
2381 + {
2382 + hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED);
2383 + }
2384 + }
2385 +
2386 + return hr;
2387 +}
2388 +
2389 +static HRESULT OnExecuteMsuPackage(
2390 + __in HANDLE hPipe,
2391 + __in BURN_PACKAGES* pPackages,
2392 + __in BURN_VARIABLES* pVariables,
2393 + __in BYTE* pbData,
2394 + __in DWORD cbData
2395 + )
2396 +{
2397 + HRESULT hr = S_OK;
2398 + SIZE_T iData = 0;
2399 + LPWSTR sczPackage = NULL;
2400 + DWORD dwRollback = 0;
2401 + DWORD dwStopWusaService = 0;
2402 + BURN_EXECUTE_ACTION executeAction = { };
2403 + BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE;
2404 +
2405 + executeAction.type = BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE;
2406 +
2407 + // Deserialize message data.
2408 + hr = BuffReadString(pbData, cbData, &iData, &sczPackage);
2409 + ExitOnFailure(hr, "Failed to read MSU package id.");
2410 +
2411 + hr = BuffReadString(pbData, cbData, &iData, &executeAction.msuPackage.sczLogPath);
2412 + ExitOnFailure(hr, "Failed to read package log.");
2413 +
2414 + hr = BuffReadNumber(pbData, cbData, &iData, reinterpret_cast<DWORD*>(&executeAction.msuPackage.action));
2415 + ExitOnFailure(hr, "Failed to read action.");
2416 +
2417 + hr = BuffReadNumber(pbData, cbData, &iData, &dwRollback);
2418 + ExitOnFailure(hr, "Failed to read rollback.");
2419 +
2420 + hr = BuffReadNumber(pbData, cbData, &iData, &dwStopWusaService);
2421 + ExitOnFailure(hr, "Failed to read StopWusaService.");
2422 +
2423 + hr = PackageFindById(pPackages, sczPackage, &executeAction.msuPackage.pPackage);
2424 + ExitOnFailure(hr, "Failed to find package: %ls", sczPackage);
2425 +
2426 + // execute MSU package
2427 + hr = MsuEngineExecutePackage(&executeAction, pVariables, static_cast<BOOL>(dwRollback), static_cast<BOOL>(dwStopWusaService), GenericExecuteMessageHandler, hPipe, &restart);
2428 + ExitOnFailure(hr, "Failed to execute MSU package.");
2429 +
2430 +LExit:
2431 + ReleaseStr(sczPackage);
2432 + PlanUninitializeExecuteAction(&executeAction);
2433 +
2434 + if (SUCCEEDED(hr))
2435 + {
2436 + if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == restart)
2437 + {
2438 + hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED);
2439 + }
2440 + else if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart)
2441 + {
2442 + hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED);
2443 + }
2444 + }
2445 +
2446 + return hr;
2447 +}
2448 +
2449 +static HRESULT OnExecutePackageProviderAction(
2450 + __in BURN_PACKAGES* pPackages,
2451 + __in BURN_RELATED_BUNDLES* pRelatedBundles,
2452 + __in BYTE* pbData,
2453 + __in DWORD cbData
2454 + )
2455 +{
2456 + HRESULT hr = S_OK;
2457 + SIZE_T iData = 0;
2458 + LPWSTR sczPackage = NULL;
2459 + BURN_EXECUTE_ACTION executeAction = { };
2460 +
2461 + executeAction.type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER;
2462 +
2463 + // Deserialize the message data.
2464 + hr = BuffReadString(pbData, cbData, &iData, &sczPackage);
2465 + ExitOnFailure(hr, "Failed to read package id from message buffer.");
2466 +
2467 + hr = BuffReadNumber(pbData, cbData, &iData, reinterpret_cast<DWORD*>(&executeAction.packageProvider.action));
2468 + ExitOnFailure(hr, "Failed to read action.");
2469 +
2470 + // Find the package again.
2471 + hr = PackageFindById(pPackages, sczPackage, &executeAction.packageProvider.pPackage);
2472 + if (E_NOTFOUND == hr)
2473 + {
2474 + hr = PackageFindRelatedById(pRelatedBundles, sczPackage, &executeAction.packageProvider.pPackage);
2475 + }
2476 + ExitOnFailure(hr, "Failed to find package: %ls", sczPackage);
2477 +
2478 + // Execute the package provider action.
2479 + hr = DependencyExecutePackageProviderAction(&executeAction);
2480 + ExitOnFailure(hr, "Failed to execute package provider action.");
2481 +
2482 +LExit:
2483 + ReleaseStr(sczPackage);
2484 + PlanUninitializeExecuteAction(&executeAction);
2485 +
2486 + return hr;
2487 +}
2488 +
2489 +static HRESULT OnExecutePackageDependencyAction(
2490 + __in BURN_PACKAGES* pPackages,
2491 + __in BURN_RELATED_BUNDLES* pRelatedBundles,
2492 + __in BYTE* pbData,
2493 + __in DWORD cbData
2494 + )
2495 +{
2496 + HRESULT hr = S_OK;
2497 + SIZE_T iData = 0;
2498 + LPWSTR sczPackage = NULL;
2499 + BURN_EXECUTE_ACTION executeAction = { };
2500 +
2501 + executeAction.type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY;
2502 +
2503 + // Deserialize the message data.
2504 + hr = BuffReadString(pbData, cbData, &iData, &sczPackage);
2505 + ExitOnFailure(hr, "Failed to read package id from message buffer.");
2506 +
2507 + hr = BuffReadString(pbData, cbData, &iData, &executeAction.packageDependency.sczBundleProviderKey);
2508 + ExitOnFailure(hr, "Failed to read bundle dependency key from message buffer.");
2509 +
2510 + hr = BuffReadNumber(pbData, cbData, &iData, reinterpret_cast<DWORD*>(&executeAction.packageDependency.action));
2511 + ExitOnFailure(hr, "Failed to read action.");
2512 +
2513 + // Find the package again.
2514 + hr = PackageFindById(pPackages, sczPackage, &executeAction.packageDependency.pPackage);
2515 + if (E_NOTFOUND == hr)
2516 + {
2517 + hr = PackageFindRelatedById(pRelatedBundles, sczPackage, &executeAction.packageDependency.pPackage);
2518 + }
2519 + ExitOnFailure(hr, "Failed to find package: %ls", sczPackage);
2520 +
2521 + // Execute the package dependency action.
2522 + hr = DependencyExecutePackageDependencyAction(TRUE, &executeAction);
2523 + ExitOnFailure(hr, "Failed to execute package dependency action.");
2524 +
2525 +LExit:
2526 + ReleaseStr(sczPackage);
2527 + PlanUninitializeExecuteAction(&executeAction);
2528 +
2529 + return hr;
2530 +}
2531 +
2532 +static HRESULT OnLoadCompatiblePackage(
2533 + __in BURN_PACKAGES* pPackages,
2534 + __in BYTE* pbData,
2535 + __in DWORD cbData
2536 + )
2537 +{
2538 + HRESULT hr = S_OK;
2539 + SIZE_T iData = 0;
2540 + LPWSTR sczPackage = NULL;
2541 + BURN_EXECUTE_ACTION executeAction = { };
2542 +
2543 + executeAction.type = BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE;
2544 +
2545 + // Deserialize the message data.
2546 + hr = BuffReadString(pbData, cbData, &iData, &sczPackage);
2547 + ExitOnFailure(hr, "Failed to read package id from message buffer.");
2548 +
2549 + // Find the reference package.
2550 + hr = PackageFindById(pPackages, sczPackage, &executeAction.compatiblePackage.pReferencePackage);
2551 + ExitOnFailure(hr, "Failed to find package: %ls", sczPackage);
2552 +
2553 + hr = BuffReadString(pbData, cbData, &iData, &executeAction.compatiblePackage.sczInstalledProductCode);
2554 + ExitOnFailure(hr, "Failed to read installed ProductCode from message buffer.");
2555 +
2556 + hr = BuffReadNumber64(pbData, cbData, &iData, &executeAction.compatiblePackage.qwInstalledVersion);
2557 + ExitOnFailure(hr, "Failed to read installed version from message buffer.");
2558 +
2559 + // Copy the installed data to the reference package.
2560 + hr = StrAllocString(&executeAction.compatiblePackage.pReferencePackage->Msi.sczInstalledProductCode, executeAction.compatiblePackage.sczInstalledProductCode, 0);
2561 + ExitOnFailure(hr, "Failed to copy installed ProductCode.");
2562 +
2563 + executeAction.compatiblePackage.pReferencePackage->Msi.qwInstalledVersion = executeAction.compatiblePackage.qwInstalledVersion;
2564 +
2565 + // Load the compatible package and add it to the list.
2566 + hr = MsiEngineAddCompatiblePackage(pPackages, executeAction.compatiblePackage.pReferencePackage, NULL);
2567 + ExitOnFailure(hr, "Failed to load compatible package.");
2568 +
2569 +LExit:
2570 + ReleaseStr(sczPackage);
2571 + PlanUninitializeExecuteAction(&executeAction);
2572 +
2573 + return hr;
2574 +}
2575 +
2576 +static int GenericExecuteMessageHandler(
2577 + __in GENERIC_EXECUTE_MESSAGE* pMessage,
2578 + __in LPVOID pvContext
2579 + )
2580 +{
2581 + HRESULT hr = S_OK;
2582 + int nResult = IDOK;
2583 + HANDLE hPipe = (HANDLE)pvContext;
2584 + BYTE* pbData = NULL;
2585 + SIZE_T cbData = 0;
2586 + DWORD dwMessage = 0;
2587 +
2588 + hr = BuffWriteNumber(&pbData, &cbData, pMessage->dwAllowedResults);
2589 + ExitOnFailure(hr, "Failed to write UI flags.");
2590 +
2591 + switch(pMessage->type)
2592 + {
2593 + case GENERIC_EXECUTE_MESSAGE_PROGRESS:
2594 + // serialize message data
2595 + hr = BuffWriteNumber(&pbData, &cbData, pMessage->progress.dwPercentage);
2596 + ExitOnFailure(hr, "Failed to write progress percentage to message buffer.");
2597 +
2598 + dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS;
2599 + break;
2600 +
2601 + case GENERIC_EXECUTE_MESSAGE_ERROR:
2602 + // serialize message data
2603 + hr = BuffWriteNumber(&pbData, &cbData, pMessage->error.dwErrorCode);
2604 + ExitOnFailure(hr, "Failed to write error code to message buffer.");
2605 +
2606 + hr = BuffWriteString(&pbData, &cbData, pMessage->error.wzMessage);
2607 + ExitOnFailure(hr, "Failed to write message to message buffer.");
2608 +
2609 + dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR;
2610 + break;
2611 +
2612 + case GENERIC_EXECUTE_MESSAGE_FILES_IN_USE:
2613 + hr = BuffWriteNumber(&pbData, &cbData, pMessage->filesInUse.cFiles);
2614 + ExitOnFailure(hr, "Failed to count of files in use to message buffer.");
2615 +
2616 + for (DWORD i = 0; i < pMessage->filesInUse.cFiles; ++i)
2617 + {
2618 + hr = BuffWriteString(&pbData, &cbData, pMessage->filesInUse.rgwzFiles[i]);
2619 + ExitOnFailure(hr, "Failed to write file in use to message buffer.");
2620 + }
2621 +
2622 + dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE;
2623 + break;
2624 + }
2625 +
2626 + // send message
2627 + hr = PipeSendMessage(hPipe, dwMessage, pbData, cbData, NULL, NULL, reinterpret_cast<DWORD*>(&nResult));
2628 + ExitOnFailure(hr, "Failed to send message to per-user process.");
2629 +
2630 +LExit:
2631 + ReleaseBuffer(pbData);
2632 +
2633 + return nResult;
2634 +}
2635 +
2636 +static int MsiExecuteMessageHandler(
2637 + __in WIU_MSI_EXECUTE_MESSAGE* pMessage,
2638 + __in_opt LPVOID pvContext
2639 + )
2640 +{
2641 + HRESULT hr = S_OK;
2642 + int nResult = IDOK;
2643 + HANDLE hPipe = (HANDLE)pvContext;
2644 + BYTE* pbData = NULL;
2645 + SIZE_T cbData = 0;
2646 + DWORD dwMessage = 0;
2647 +
2648 + // Always send any extra data via the struct first.
2649 + hr = BuffWriteNumber(&pbData, &cbData, pMessage->cData);
2650 + ExitOnFailure(hr, "Failed to write MSI data count to message buffer.");
2651 +
2652 + for (DWORD i = 0; i < pMessage->cData; ++i)
2653 + {
2654 + hr = BuffWriteString(&pbData, &cbData, pMessage->rgwzData[i]);
2655 + ExitOnFailure(hr, "Failed to write MSI data to message buffer.");
2656 + }
2657 +
2658 + hr = BuffWriteNumber(&pbData, &cbData, pMessage->dwAllowedResults);
2659 + ExitOnFailure(hr, "Failed to write UI flags.");
2660 +
2661 + switch (pMessage->type)
2662 + {
2663 + case WIU_MSI_EXECUTE_MESSAGE_PROGRESS:
2664 + // serialize message data
2665 + hr = BuffWriteNumber(&pbData, &cbData, pMessage->progress.dwPercentage);
2666 + ExitOnFailure(hr, "Failed to write progress percentage to message buffer.");
2667 +
2668 + // set message id
2669 + dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS;
2670 + break;
2671 +
2672 + case WIU_MSI_EXECUTE_MESSAGE_ERROR:
2673 + // serialize message data
2674 + hr = BuffWriteNumber(&pbData, &cbData, pMessage->error.dwErrorCode);
2675 + ExitOnFailure(hr, "Failed to write error code to message buffer.");
2676 +
2677 + hr = BuffWriteString(&pbData, &cbData, pMessage->error.wzMessage);
2678 + ExitOnFailure(hr, "Failed to write message to message buffer.");
2679 +
2680 + // set message id
2681 + dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR;
2682 + break;
2683 +
2684 + case WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE:
2685 + // serialize message data
2686 + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pMessage->msiMessage.mt);
2687 + ExitOnFailure(hr, "Failed to write MSI message type to message buffer.");
2688 +
2689 + hr = BuffWriteString(&pbData, &cbData, pMessage->msiMessage.wzMessage);
2690 + ExitOnFailure(hr, "Failed to write message to message buffer.");
2691 +
2692 + // set message id
2693 + dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_MESSAGE;
2694 + break;
2695 +
2696 + case WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE:
2697 + // NOTE: we do not serialize other message data here because all the "files in use" are in the data above.
2698 +
2699 + // set message id
2700 + dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE;
2701 + break;
2702 +
2703 + default:
2704 + hr = E_UNEXPECTED;
2705 + ExitOnFailure(hr, "Invalid message type: %d", pMessage->type);
2706 + }
2707 +
2708 + // send message
2709 + hr = PipeSendMessage(hPipe, dwMessage, pbData, cbData, NULL, NULL, (DWORD*)&nResult);
2710 + ExitOnFailure(hr, "Failed to send message to per-machine process.");
2711 +
2712 +LExit:
2713 + ReleaseBuffer(pbData);
2714 +
2715 + return nResult;
2716 +}
2717 +
2718 +static HRESULT OnCleanPackage(
2719 + __in BURN_PACKAGES* pPackages,
2720 + __in BYTE* pbData,
2721 + __in DWORD cbData
2722 + )
2723 +{
2724 + HRESULT hr = S_OK;
2725 + SIZE_T iData = 0;
2726 + LPWSTR sczPackage = NULL;
2727 + BURN_PACKAGE* pPackage = NULL;
2728 +
2729 + // Deserialize message data.
2730 + hr = BuffReadString(pbData, cbData, &iData, &sczPackage);
2731 + ExitOnFailure(hr, "Failed to read package id.");
2732 +
2733 + hr = PackageFindById(pPackages, sczPackage, &pPackage);
2734 + ExitOnFailure(hr, "Failed to find package: %ls", sczPackage);
2735 +
2736 + // Remove the package from the cache.
2737 + hr = CacheRemovePackage(TRUE, pPackage->sczId, pPackage->sczCacheId);
2738 + ExitOnFailure(hr, "Failed to remove from cache package: %ls", pPackage->sczId);
2739 +
2740 +LExit:
2741 + ReleaseStr(sczPackage);
2742 + return hr;
2743 +}
2744 +
2745 +static HRESULT OnLaunchApprovedExe(
2746 + __in HANDLE hPipe,
2747 + __in BURN_APPROVED_EXES* pApprovedExes,
2748 + __in BURN_VARIABLES* pVariables,
2749 + __in BYTE* pbData,
2750 + __in DWORD cbData
2751 + )
2752 +{
2753 + HRESULT hr = S_OK;
2754 + SIZE_T iData = 0;
2755 + BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe = NULL;
2756 + BURN_APPROVED_EXE* pApprovedExe = NULL;
2757 + REGSAM samDesired = KEY_QUERY_VALUE;
2758 + HKEY hKey = NULL;
2759 + DWORD dwProcessId = 0;
2760 + BYTE* pbSendData = NULL;
2761 + SIZE_T cbSendData = 0;
2762 + DWORD dwResult = 0;
2763 +
2764 + pLaunchApprovedExe = (BURN_LAUNCH_APPROVED_EXE*)MemAlloc(sizeof(BURN_LAUNCH_APPROVED_EXE), TRUE);
2765 +
2766 + // Deserialize message data.
2767 + hr = BuffReadString(pbData, cbData, &iData, &pLaunchApprovedExe->sczId);
2768 + ExitOnFailure(hr, "Failed to read approved exe id.");
2769 +
2770 + hr = BuffReadString(pbData, cbData, &iData, &pLaunchApprovedExe->sczArguments);
2771 + ExitOnFailure(hr, "Failed to read approved exe arguments.");
2772 +
2773 + hr = BuffReadNumber(pbData, cbData, &iData, &pLaunchApprovedExe->dwWaitForInputIdleTimeout);
2774 + ExitOnFailure(hr, "Failed to read approved exe WaitForInputIdle timeout.");
2775 +
2776 + hr = ApprovedExesFindById(pApprovedExes, pLaunchApprovedExe->sczId, &pApprovedExe);
2777 + ExitOnFailure(hr, "The per-user process requested unknown approved exe with id: %ls", pLaunchApprovedExe->sczId);
2778 +
2779 + LogId(REPORT_STANDARD, MSG_LAUNCH_APPROVED_EXE_SEARCH, pApprovedExe->sczKey, pApprovedExe->sczValueName ? pApprovedExe->sczValueName : L"", pApprovedExe->fWin64 ? L"yes" : L"no");
2780 +
2781 + if (pApprovedExe->fWin64)
2782 + {
2783 + samDesired |= KEY_WOW64_64KEY;
2784 + }
2785 +
2786 + hr = RegOpen(HKEY_LOCAL_MACHINE, pApprovedExe->sczKey, samDesired, &hKey);
2787 + ExitOnFailure(hr, "Failed to open the registry key for the approved exe path.");
2788 +
2789 + hr = RegReadString(hKey, pApprovedExe->sczValueName, &pLaunchApprovedExe->sczExecutablePath);
2790 + ExitOnFailure(hr, "Failed to read the value for the approved exe path.");
2791 +
2792 + hr = ApprovedExesVerifySecureLocation(pVariables, pLaunchApprovedExe);
2793 + ExitOnFailure(hr, "Failed to verify the executable path is in a secure location: %ls", pLaunchApprovedExe->sczExecutablePath);
2794 + if (S_FALSE == hr)
2795 + {
2796 + LogStringLine(REPORT_STANDARD, "The executable path is not in a secure location: %ls", pLaunchApprovedExe->sczExecutablePath);
2797 + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED));
2798 + }
2799 +
2800 + hr = ApprovedExesLaunch(pVariables, pLaunchApprovedExe, &dwProcessId);
2801 + ExitOnFailure(hr, "Failed to launch approved exe: %ls", pLaunchApprovedExe->sczExecutablePath);
2802 +
2803 + //send process id over pipe
2804 + hr = BuffWriteNumber(&pbSendData, &cbSendData, dwProcessId);
2805 + ExitOnFailure(hr, "Failed to write the approved exe process id to message buffer.");
2806 +
2807 + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE_PROCESSID, pbSendData, cbSendData, NULL, NULL, &dwResult);
2808 + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE_PROCESSID message to per-user process.");
2809 +
2810 +LExit:
2811 + ReleaseBuffer(pbSendData);
2812 + ApprovedExesUninitializeLaunch(pLaunchApprovedExe);
2813 + return hr;
2814 +}
src/engine/elevation.h new
+178
@@ -0,0 +1,178 @@
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 +// Parent (per-user process) side functions.
11 +HRESULT ElevationElevate(
12 + __in BURN_ENGINE_STATE* pEngineState,
13 + __in_opt HWND hwndParent
14 + );
15 +HRESULT ElevationApplyInitialize(
16 + __in HANDLE hPipe,
17 + __in BURN_VARIABLES* pVariables,
18 + __in BOOTSTRAPPER_ACTION action,
19 + __in BURN_AU_PAUSE_ACTION auAction,
20 + __in BOOL fTakeSystemRestorePoint
21 + );
22 +HRESULT ElevationApplyUninitialize(
23 + __in HANDLE hPipe
24 + );
25 +HRESULT ElevationSessionBegin(
26 + __in HANDLE hPipe,
27 + __in_z LPCWSTR wzEngineWorkingPath,
28 + __in_z LPCWSTR wzResumeCommandLine,
29 + __in BOOL fDisableResume,
30 + __in BURN_VARIABLES* pVariables,
31 + __in DWORD dwRegistrationOperations,
32 + __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction,
33 + __in DWORD64 qwEstimatedSize
34 + );
35 +HRESULT ElevationSessionResume(
36 + __in HANDLE hPipe,
37 + __in_z LPCWSTR wzResumeCommandLine,
38 + __in BOOL fDisableResume,
39 + __in BURN_VARIABLES* pVariables
40 + );
41 +HRESULT ElevationSessionEnd(
42 + __in HANDLE hPipe,
43 + __in BURN_RESUME_MODE resumeMode,
44 + __in BOOTSTRAPPER_APPLY_RESTART restart,
45 + __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction
46 + );
47 +HRESULT ElevationSaveState(
48 + __in HANDLE hPipe,
49 + __in_bcount(cbBuffer) BYTE* pbBuffer,
50 + __in SIZE_T cbBuffer
51 + );
52 +HRESULT ElevationLayoutBundle(
53 + __in HANDLE hPipe,
54 + __in_z LPCWSTR wzLayoutDirectory,
55 + __in_z LPCWSTR wzUnverifiedPath
56 + );
57 +HRESULT ElevationCacheOrLayoutContainerOrPayload(
58 + __in HANDLE hPipe,
59 + __in_opt BURN_CONTAINER* pContainer,
60 + __in_opt BURN_PACKAGE* pPackage,
61 + __in_opt BURN_PAYLOAD* pPayload,
62 + __in_z_opt LPCWSTR wzLayoutDirectory,
63 + __in_z LPCWSTR wzUnverifiedPath,
64 + __in BOOL fMove
65 + );
66 +HRESULT ElevationCacheCleanup(
67 + __in HANDLE hPipe
68 + );
69 +HRESULT ElevationProcessDependentRegistration(
70 + __in HANDLE hPipe,
71 + __in const BURN_DEPENDENT_REGISTRATION_ACTION* pAction
72 + );
73 +HRESULT ElevationExecuteExePackage(
74 + __in HANDLE hPipe,
75 + __in BURN_EXECUTE_ACTION* pExecuteAction,
76 + __in BURN_VARIABLES* pVariables,
77 + __in BOOL fRollback,
78 + __in PFN_GENERICMESSAGEHANDLER pfnGenericExecuteProgress,
79 + __in LPVOID pvContext,
80 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart
81 + );
82 +HRESULT ElevationExecuteMsiPackage(
83 + __in HANDLE hPipe,
84 + __in_opt HWND hwndParent,
85 + __in BURN_EXECUTE_ACTION* pExecuteAction,
86 + __in BURN_VARIABLES* pVariables,
87 + __in BOOL fRollback,
88 + __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
89 + __in LPVOID pvContext,
90 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart
91 + );
92 +HRESULT ElevationExecuteMspPackage(
93 + __in HANDLE hPipe,
94 + __in_opt HWND hwndParent,
95 + __in BURN_EXECUTE_ACTION* pExecuteAction,
96 + __in BURN_VARIABLES* pVariables,
97 + __in BOOL fRollback,
98 + __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
99 + __in LPVOID pvContext,
100 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart
101 + );
102 +HRESULT ElevationExecuteMsuPackage(
103 + __in HANDLE hPipe,
104 + __in BURN_EXECUTE_ACTION* pExecuteAction,
105 + __in BOOL fRollback,
106 + __in BOOL fStopWusaService,
107 + __in PFN_GENERICMESSAGEHANDLER pfnGenericExecuteProgress,
108 + __in LPVOID pvContext,
109 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart
110 + );
111 +HRESULT ElevationExecutePackageProviderAction(
112 + __in HANDLE hPipe,
113 + __in BURN_EXECUTE_ACTION* pExecuteAction
114 + );
115 +HRESULT ElevationExecutePackageDependencyAction(
116 + __in HANDLE hPipe,
117 + __in BURN_EXECUTE_ACTION* pExecuteAction
118 + );
119 +HRESULT ElevationLoadCompatiblePackageAction(
120 + __in HANDLE hPipe,
121 + __in BURN_EXECUTE_ACTION* pExecuteAction
122 + );
123 +HRESULT ElevationLaunchElevatedChild(
124 + __in HANDLE hPipe,
125 + __in BURN_PACKAGE* pPackage,
126 + __in LPCWSTR wzPipeName,
127 + __in LPCWSTR wzPipeToken,
128 + __out DWORD* pdwChildPid
129 + );
130 +HRESULT ElevationCleanPackage(
131 + __in HANDLE hPipe,
132 + __in BURN_PACKAGE* pPackage
133 + );
134 +HRESULT ElevationLaunchApprovedExe(
135 + __in HANDLE hPipe,
136 + __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe,
137 + __out DWORD* pdwProcessId
138 + );
139 +
140 +// Child (per-machine process) side functions.
141 +HRESULT ElevationChildPumpMessages(
142 + __in DWORD dwLoggingTlsId,
143 + __in HANDLE hPipe,
144 + __in HANDLE hCachePipe,
145 + __in BURN_APPROVED_EXES* pApprovedExes,
146 + __in BURN_CONTAINERS* pContainers,
147 + __in BURN_PACKAGES* pPackages,
148 + __in BURN_PAYLOADS* pPayloads,
149 + __in BURN_VARIABLES* pVariables,
150 + __in BURN_REGISTRATION* pRegistration,
151 + __in BURN_USER_EXPERIENCE* pUserExperience,
152 + __out HANDLE* phLock,
153 + __out BOOL* pfDisabledAutomaticUpdates,
154 + __out DWORD* pdwChildExitCode,
155 + __out BOOL* pfRestart
156 + );
157 +HRESULT ElevationChildResumeAutomaticUpdates();
158 +
159 +
160 +HRESULT ElevationMsiBeginTransaction(
161 + __in HANDLE hPipe,
162 + __in_opt HWND hwndParent,
163 + __in LPVOID pvContext
164 +);
165 +HRESULT ElevationMsiCommitTransaction(
166 + __in HANDLE hPipe,
167 + __in_opt HWND hwndParent,
168 + __in LPVOID pvContext
169 +);
170 +HRESULT ElevationMsiRollbackTransaction(
171 + __in HANDLE hPipe,
172 + __in_opt HWND hwndParent,
173 + __in LPVOID pvContext
174 +);
175 +
176 +#ifdef __cplusplus
177 +}
178 +#endif
src/engine/embedded.cpp new
+197
@@ -0,0 +1,197 @@
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 +// struct
7 +
8 +struct BURN_EMBEDDED_CALLBACK_CONTEXT
9 +{
10 + PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler;
11 + LPVOID pvContext;
12 +};
13 +
14 +// internal function declarations
15 +
16 +static HRESULT ProcessEmbeddedMessages(
17 + __in BURN_PIPE_MESSAGE* pMsg,
18 + __in_opt LPVOID pvContext,
19 + __out DWORD* pdwResult
20 + );
21 +static HRESULT OnEmbeddedErrorMessage(
22 + __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler,
23 + __in LPVOID pvContext,
24 + __in_bcount(cbData) BYTE* pbData,
25 + __in DWORD cbData,
26 + __out DWORD* pdwResult
27 + );
28 +static HRESULT OnEmbeddedProgress(
29 + __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler,
30 + __in LPVOID pvContext,
31 + __in_bcount(cbData) BYTE* pbData,
32 + __in DWORD cbData,
33 + __out DWORD* pdwResult
34 + );
35 +
36 +// function definitions
37 +
38 +/*******************************************************************
39 + EmbeddedLaunchChildProcess -
40 +
41 +*******************************************************************/
42 +extern "C" HRESULT EmbeddedRunBundle(
43 + __in LPCWSTR wzExecutablePath,
44 + __in LPCWSTR wzArguments,
45 + __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
46 + __in LPVOID pvContext,
47 + __out DWORD* pdwExitCode
48 + )
49 +{
50 + HRESULT hr = S_OK;
51 + DWORD dwCurrentProcessId = ::GetCurrentProcessId();
52 + HANDLE hCreatedPipesEvent = NULL;
53 + LPWSTR sczCommand = NULL;
54 + STARTUPINFOW si = { };
55 + PROCESS_INFORMATION pi = { };
56 + BURN_PIPE_RESULT result = { };
57 +
58 + BURN_PIPE_CONNECTION connection = { };
59 + PipeConnectionInitialize(&connection);
60 +
61 + BURN_EMBEDDED_CALLBACK_CONTEXT context = { };
62 + context.pfnGenericMessageHandler = pfnGenericMessageHandler;
63 + context.pvContext = pvContext;
64 +
65 + hr = PipeCreateNameAndSecret(&connection.sczName, &connection.sczSecret);
66 + ExitOnFailure(hr, "Failed to create embedded pipe name and client token.");
67 +
68 + hr = PipeCreatePipes(&connection, FALSE, &hCreatedPipesEvent);
69 + ExitOnFailure(hr, "Failed to create embedded pipe.");
70 +
71 + hr = StrAllocFormattedSecure(&sczCommand, L"%ls -%ls %ls %ls %u", wzArguments, BURN_COMMANDLINE_SWITCH_EMBEDDED, connection.sczName, connection.sczSecret, dwCurrentProcessId);
72 + ExitOnFailure(hr, "Failed to allocate embedded command.");
73 +
74 + if (!::CreateProcessW(wzExecutablePath, sczCommand, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
75 + {
76 + ExitWithLastError(hr, "Failed to create embedded process at path: %ls", wzExecutablePath);
77 + }
78 +
79 + connection.dwProcessId = ::GetProcessId(pi.hProcess);
80 + connection.hProcess = pi.hProcess;
81 + pi.hProcess = NULL;
82 +
83 + hr = PipeWaitForChildConnect(&connection);
84 + ExitOnFailure(hr, "Failed to wait for embedded process to connect to pipe.");
85 +
86 + hr = PipePumpMessages(connection.hPipe, ProcessEmbeddedMessages, &context, &result);
87 + ExitOnFailure(hr, "Failed to process messages from embedded message.");
88 +
89 + // Get the return code from the embedded process.
90 + hr = ProcWaitForCompletion(connection.hProcess, INFINITE, pdwExitCode);
91 + ExitOnFailure(hr, "Failed to wait for embedded executable: %ls", wzExecutablePath);
92 +
93 +LExit:
94 + ReleaseHandle(pi.hThread);
95 + ReleaseHandle(pi.hProcess);
96 +
97 + StrSecureZeroFreeString(sczCommand);
98 + ReleaseHandle(hCreatedPipesEvent);
99 + PipeConnectionUninitialize(&connection);
100 +
101 + return hr;
102 +}
103 +
104 +
105 +// internal function definitions
106 +
107 +static HRESULT ProcessEmbeddedMessages(
108 + __in BURN_PIPE_MESSAGE* pMsg,
109 + __in_opt LPVOID pvContext,
110 + __out DWORD* pdwResult
111 + )
112 +{
113 + HRESULT hr = S_OK;
114 + BURN_EMBEDDED_CALLBACK_CONTEXT* pContext = static_cast<BURN_EMBEDDED_CALLBACK_CONTEXT*>(pvContext);
115 + DWORD dwResult = 0;
116 +
117 + // Process the message.
118 + switch (pMsg->dwMessage)
119 + {
120 + case BURN_EMBEDDED_MESSAGE_TYPE_ERROR:
121 + hr = OnEmbeddedErrorMessage(pContext->pfnGenericMessageHandler, pContext->pvContext, static_cast<BYTE*>(pMsg->pvData), pMsg->cbData, &dwResult);
122 + ExitOnFailure(hr, "Failed to process embedded error message.");
123 + break;
124 +
125 + case BURN_EMBEDDED_MESSAGE_TYPE_PROGRESS:
126 + hr = OnEmbeddedProgress(pContext->pfnGenericMessageHandler, pContext->pvContext, static_cast<BYTE*>(pMsg->pvData), pMsg->cbData, &dwResult);
127 + ExitOnFailure(hr, "Failed to process embedded progress message.");
128 + break;
129 +
130 + default:
131 + hr = E_INVALIDARG;
132 + ExitOnRootFailure(hr, "Unexpected embedded message sent to child process, msg: %u", pMsg->dwMessage);
133 + }
134 +
135 + *pdwResult = dwResult;
136 +
137 +LExit:
138 + return hr;
139 +}
140 +
141 +static HRESULT OnEmbeddedErrorMessage(
142 + __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler,
143 + __in LPVOID pvContext,
144 + __in_bcount(cbData) BYTE* pbData,
145 + __in DWORD cbData,
146 + __out DWORD* pdwResult
147 + )
148 +{
149 + HRESULT hr = S_OK;
150 + DWORD iData = 0;
151 + GENERIC_EXECUTE_MESSAGE message = { };
152 + LPWSTR sczMessage = NULL;
153 +
154 + message.type = GENERIC_EXECUTE_MESSAGE_ERROR;
155 +
156 + hr = BuffReadNumber(pbData, cbData, &iData, &message.error.dwErrorCode);
157 + ExitOnFailure(hr, "Failed to read error code from buffer.");
158 +
159 + hr = BuffReadString(pbData, cbData, &iData, &sczMessage);
160 + ExitOnFailure(hr, "Failed to read error message from buffer.");
161 +
162 + message.error.wzMessage = sczMessage;
163 +
164 + hr = BuffReadNumber(pbData, cbData, &iData, &message.dwAllowedResults);
165 + ExitOnFailure(hr, "Failed to read UI hint from buffer.");
166 +
167 + *pdwResult = (DWORD)pfnMessageHandler(&message, pvContext);
168 +
169 +LExit:
170 + ReleaseStr(sczMessage);
171 +
172 + return hr;
173 +}
174 +
175 +static HRESULT OnEmbeddedProgress(
176 + __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler,
177 + __in LPVOID pvContext,
178 + __in_bcount(cbData) BYTE* pbData,
179 + __in DWORD cbData,
180 + __out DWORD* pdwResult
181 + )
182 +{
183 + HRESULT hr = S_OK;
184 + DWORD iData = 0;
185 + GENERIC_EXECUTE_MESSAGE message = { };
186 +
187 + message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
188 + message.dwAllowedResults = MB_OKCANCEL;
189 +
190 + hr = BuffReadNumber(pbData, cbData, &iData, &message.progress.dwPercentage);
191 + ExitOnFailure(hr, "Failed to read progress from buffer.");
192 +
193 + *pdwResult = (DWORD)pfnMessageHandler(&message, pvContext);
194 +
195 +LExit:
196 + return hr;
197 +}
src/engine/embedded.h new
+27
@@ -0,0 +1,27 @@
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 +typedef enum _BURN_EMBEDDED_MESSAGE_TYPE
10 +{
11 + BURN_EMBEDDED_MESSAGE_TYPE_UNKNOWN,
12 + BURN_EMBEDDED_MESSAGE_TYPE_ERROR,
13 + BURN_EMBEDDED_MESSAGE_TYPE_PROGRESS,
14 +} BURN_EMBEDDED_MESSAGE_TYPE;
15 +
16 +
17 +HRESULT EmbeddedRunBundle(
18 + __in LPCWSTR wzExecutablePath,
19 + __in LPCWSTR wzArguments,
20 + __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
21 + __in LPVOID pvContext,
22 + __out DWORD* pdwExitCode
23 + );
24 +
25 +#ifdef __cplusplus
26 +}
27 +#endif
src/engine/engine.cpp new
+889
@@ -0,0 +1,889 @@
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 +// constants
7 +
8 +const DWORD RESTART_RETRIES = 10;
9 +
10 +// internal function declarations
11 +
12 +static HRESULT InitializeEngineState(
13 + __in BURN_ENGINE_STATE* pEngineState,
14 + __in HANDLE hEngineFile
15 + );
16 +static void UninitializeEngineState(
17 + __in BURN_ENGINE_STATE* pEngineState
18 + );
19 +static HRESULT RunUntrusted(
20 + __in LPCWSTR wzCommandLine,
21 + __in BURN_ENGINE_STATE* pEngineState
22 + );
23 +static HRESULT RunNormal(
24 + __in HINSTANCE hInstance,
25 + __in BURN_ENGINE_STATE* pEngineState
26 + );
27 +static HRESULT RunElevated(
28 + __in HINSTANCE hInstance,
29 + __in LPCWSTR wzCommandLine,
30 + __in BURN_ENGINE_STATE* pEngineState
31 + );
32 +static HRESULT RunEmbedded(
33 + __in HINSTANCE hInstance,
34 + __in BURN_ENGINE_STATE* pEngineState
35 + );
36 +static HRESULT RunRunOnce(
37 + __in const BURN_REGISTRATION* pRegistration,
38 + __in int nCmdShow
39 + );
40 +static HRESULT RunApplication(
41 + __in BURN_ENGINE_STATE* pEngineState,
42 + __out BOOL* pfReloadApp
43 + );
44 +static HRESULT ProcessMessage(
45 + __in BURN_ENGINE_STATE* pEngineState,
46 + __in const MSG* pmsg
47 + );
48 +static HRESULT DAPI RedirectLoggingOverPipe(
49 + __in_z LPCSTR szString,
50 + __in_opt LPVOID pvContext
51 + );
52 +static HRESULT Restart();
53 +
54 +
55 +// function definitions
56 +
57 +extern "C" BOOL EngineInCleanRoom(
58 + __in_z_opt LPCWSTR wzCommandLine
59 + )
60 +{
61 + // Be very careful with the functions you call from here.
62 + // This function will be called before ::SetDefaultDllDirectories()
63 + // has been called so dependencies outside of kernel32.dll are
64 + // very likely to introduce DLL hijacking opportunities.
65 +
66 + static DWORD cchCleanRoomSwitch = lstrlenW(BURN_COMMANDLINE_SWITCH_CLEAN_ROOM);
67 +
68 + // This check is wholly dependent on the clean room command line switch being
69 + // present at the beginning of the command line. Since Burn is the only thing
70 + // that should be setting this command line option, that is in our control.
71 + BOOL fInCleanRoom = (wzCommandLine &&
72 + (wzCommandLine[0] == L'-' || wzCommandLine[0] == L'/') &&
73 + CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzCommandLine + 1, cchCleanRoomSwitch, BURN_COMMANDLINE_SWITCH_CLEAN_ROOM, cchCleanRoomSwitch) &&
74 + wzCommandLine[1 + cchCleanRoomSwitch] == L'='
75 + );
76 +
77 + return fInCleanRoom;
78 +}
79 +
80 +extern "C" HRESULT EngineRun(
81 + __in HINSTANCE hInstance,
82 + __in HANDLE hEngineFile,
83 + __in_z_opt LPCWSTR wzCommandLine,
84 + __in int nCmdShow,
85 + __out DWORD* pdwExitCode
86 + )
87 +{
88 + HRESULT hr = S_OK;
89 + BOOL fComInitialized = FALSE;
90 + BOOL fLogInitialized = FALSE;
91 + BOOL fCrypInitialized = FALSE;
92 + BOOL fRegInitialized = FALSE;
93 + BOOL fWiuInitialized = FALSE;
94 + BOOL fXmlInitialized = FALSE;
95 + OSVERSIONINFOEXW ovix = { };
96 + LPWSTR sczExePath = NULL;
97 + BOOL fRunNormal = FALSE;
98 + BOOL fRestart = FALSE;
99 +
100 + BURN_ENGINE_STATE engineState = { };
101 +
102 + // Always initialize logging first
103 + LogInitialize(::GetModuleHandleW(NULL));
104 + fLogInitialized = TRUE;
105 +
106 + // Ensure that log contains approriate level of information
107 +#ifdef _DEBUG
108 + LogSetLevel(REPORT_DEBUG, FALSE);
109 +#else
110 + LogSetLevel(REPORT_VERBOSE, FALSE); // FALSE means don't write an additional text line to the log saying the level changed
111 +#endif
112 +
113 + hr = AppParseCommandLine(wzCommandLine, &engineState.argc, &engineState.argv);
114 + ExitOnFailure(hr, "Failed to parse command line.");
115 +
116 + hr = InitializeEngineState(&engineState, hEngineFile);
117 + ExitOnFailure(hr, "Failed to initialize engine state.");
118 +
119 + engineState.command.nCmdShow = nCmdShow;
120 +
121 + // initialize platform layer
122 + PlatformInitialize();
123 +
124 + // initialize COM
125 + hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
126 + ExitOnFailure(hr, "Failed to initialize COM.");
127 + fComInitialized = TRUE;
128 +
129 + // Initialize dutil.
130 + hr = CrypInitialize();
131 + ExitOnFailure(hr, "Failed to initialize Cryputil.");
132 + fCrypInitialized = TRUE;
133 +
134 + hr = RegInitialize();
135 + ExitOnFailure(hr, "Failed to initialize Regutil.");
136 + fRegInitialized = TRUE;
137 +
138 + hr = WiuInitialize();
139 + ExitOnFailure(hr, "Failed to initialize Wiutil.");
140 + fWiuInitialized = TRUE;
141 +
142 + hr = XmlInitialize();
143 + ExitOnFailure(hr, "Failed to initialize XML util.");
144 + fXmlInitialized = TRUE;
145 +
146 + ovix.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
147 + if (!::GetVersionExW((LPOSVERSIONINFOW)&ovix))
148 + {
149 + ExitWithLastError(hr, "Failed to get OS info.");
150 + }
151 +
152 + PathForCurrentProcess(&sczExePath, NULL); // Ignore failure.
153 + LogId(REPORT_STANDARD, MSG_BURN_INFO, szVerMajorMinorBuild, ovix.dwMajorVersion, ovix.dwMinorVersion, ovix.dwBuildNumber, ovix.wServicePackMajor, sczExePath);
154 + ReleaseNullStr(sczExePath);
155 +
156 + // initialize core
157 + hr = CoreInitialize(&engineState);
158 + ExitOnFailure(hr, "Failed to initialize core.");
159 +
160 + // Select run mode.
161 + switch (engineState.mode)
162 + {
163 + case BURN_MODE_UNTRUSTED:
164 + hr = RunUntrusted(wzCommandLine, &engineState);
165 + ExitOnFailure(hr, "Failed to run untrusted mode.");
166 + break;
167 +
168 + case BURN_MODE_NORMAL:
169 + fRunNormal = TRUE;
170 +
171 + hr = RunNormal(hInstance, &engineState);
172 + ExitOnFailure(hr, "Failed to run per-user mode.");
173 + break;
174 +
175 + case BURN_MODE_ELEVATED:
176 + hr = RunElevated(hInstance, wzCommandLine, &engineState);
177 + ExitOnFailure(hr, "Failed to run per-machine mode.");
178 + break;
179 +
180 + case BURN_MODE_EMBEDDED:
181 + fRunNormal = TRUE;
182 +
183 + hr = RunEmbedded(hInstance, &engineState);
184 + ExitOnFailure(hr, "Failed to run embedded mode.");
185 + break;
186 +
187 + case BURN_MODE_RUNONCE:
188 + hr = RunRunOnce(&engineState.registration, nCmdShow);
189 + ExitOnFailure(hr, "Failed to run RunOnce mode.");
190 + break;
191 +
192 + default:
193 + hr = E_UNEXPECTED;
194 + ExitOnFailure(hr, "Invalid run mode.");
195 + }
196 +
197 + // set exit code and remember if we are supposed to restart.
198 + *pdwExitCode = engineState.userExperience.dwExitCode;
199 + fRestart = engineState.fRestart;
200 +
201 +LExit:
202 + ReleaseStr(sczExePath);
203 +
204 + // If anything went wrong but the log was never open, try to open a "failure" log
205 + // and that will dump anything captured in the log memory buffer to the log.
206 + if (FAILED(hr) && BURN_LOGGING_STATE_CLOSED == engineState.log.state)
207 + {
208 + LoggingOpenFailed();
209 + }
210 +
211 + UserExperienceRemove(&engineState.userExperience);
212 +
213 + CacheRemoveWorkingFolder(engineState.registration.sczId);
214 + CacheUninitialize();
215 +
216 + // If this is a related bundle (but not an update) suppress restart and return the standard restart error code.
217 + if (fRestart && BOOTSTRAPPER_RELATION_NONE != engineState.command.relationType && BOOTSTRAPPER_RELATION_UPDATE != engineState.command.relationType)
218 + {
219 + LogId(REPORT_STANDARD, MSG_RESTART_ABORTED, LoggingRelationTypeToString(engineState.command.relationType));
220 +
221 + fRestart = FALSE;
222 + hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED);
223 + }
224 +
225 + UninitializeEngineState(&engineState);
226 +
227 + if (fXmlInitialized)
228 + {
229 + XmlUninitialize();
230 + }
231 +
232 + if (fWiuInitialized)
233 + {
234 + WiuUninitialize();
235 + }
236 +
237 + if (fRegInitialized)
238 + {
239 + RegUninitialize();
240 + }
241 +
242 + if (fCrypInitialized)
243 + {
244 + CrypUninitialize();
245 + }
246 +
247 + if (fComInitialized)
248 + {
249 + ::CoUninitialize();
250 + }
251 +
252 + if (fRunNormal)
253 + {
254 + LogId(REPORT_STANDARD, MSG_EXITING, FAILED(hr) ? (int)hr : *pdwExitCode, LoggingBoolToString(fRestart));
255 +
256 + if (fRestart)
257 + {
258 + LogId(REPORT_STANDARD, MSG_RESTARTING);
259 + }
260 + }
261 +
262 + if (fLogInitialized)
263 + {
264 + LogClose(FALSE);
265 + }
266 +
267 + if (fRestart)
268 + {
269 + Restart();
270 + }
271 +
272 + if (fLogInitialized)
273 + {
274 + LogUninitialize(FALSE);
275 + }
276 +
277 + return hr;
278 +}
279 +
280 +
281 +// internal function definitions
282 +
283 +static HRESULT InitializeEngineState(
284 + __in BURN_ENGINE_STATE* pEngineState,
285 + __in HANDLE hEngineFile
286 + )
287 +{
288 + HRESULT hr = S_OK;
289 + LPCWSTR wzParam = NULL;
290 + HANDLE hSectionFile = hEngineFile;
291 + HANDLE hSourceEngineFile = INVALID_HANDLE_VALUE;
292 +
293 + pEngineState->automaticUpdates = BURN_AU_PAUSE_ACTION_IFELEVATED;
294 + pEngineState->dwElevatedLoggingTlsId = TLS_OUT_OF_INDEXES;
295 + ::InitializeCriticalSection(&pEngineState->csActive);
296 + ::InitializeCriticalSection(&pEngineState->userExperience.csEngineActive);
297 + PipeConnectionInitialize(&pEngineState->companionConnection);
298 + PipeConnectionInitialize(&pEngineState->embeddedConnection);
299 +
300 + for (int i = 0; i < pEngineState->argc; ++i)
301 + {
302 + if (pEngineState->argv[i][0] == L'-')
303 + {
304 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &pEngineState->argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED), BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED, lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED)))
305 + {
306 + wzParam = &pEngineState->argv[i][2 + lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED)];
307 + if (L'=' != wzParam[-1] || L'\0' == wzParam[0])
308 + {
309 + ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED);
310 + }
311 +
312 + hr = StrStringToUInt32(wzParam, 0, reinterpret_cast<UINT*>(&hSourceEngineFile));
313 + ExitOnFailure(hr, "Failed to parse file handle: '%ls'", (wzParam));
314 + }
315 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &pEngineState->argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF), BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF)))
316 + {
317 + wzParam = &pEngineState->argv[i][2 + lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF)];
318 + if (L'=' != wzParam[-1] || L'\0' == wzParam[0])
319 + {
320 + ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF);
321 + }
322 +
323 + hr = StrStringToUInt32(wzParam, 0, reinterpret_cast<UINT*>(&hSectionFile));
324 + ExitOnFailure(hr, "Failed to parse file handle: '%ls'", (wzParam));
325 + }
326 + }
327 + }
328 +
329 + hr = SectionInitialize(&pEngineState->section, hSectionFile, hSourceEngineFile);
330 + ExitOnFailure(hr, "Failed to initialize engine section.");
331 +
332 +LExit:
333 + return hr;
334 +}
335 +
336 +static void UninitializeEngineState(
337 + __in BURN_ENGINE_STATE* pEngineState
338 + )
339 +{
340 + if (pEngineState->argv)
341 + {
342 + AppFreeCommandLineArgs(pEngineState->argv);
343 + }
344 +
345 + ReleaseStr(pEngineState->sczIgnoreDependencies);
346 +
347 + PipeConnectionUninitialize(&pEngineState->embeddedConnection);
348 + PipeConnectionUninitialize(&pEngineState->companionConnection);
349 + ReleaseStr(pEngineState->sczBundleEngineWorkingPath)
350 +
351 + ReleaseHandle(pEngineState->hMessageWindowThread);
352 +
353 + ::DeleteCriticalSection(&pEngineState->userExperience.csEngineActive);
354 + UserExperienceUninitialize(&pEngineState->userExperience);
355 +
356 + ApprovedExesUninitialize(&pEngineState->approvedExes);
357 + UpdateUninitialize(&pEngineState->update);
358 + VariablesUninitialize(&pEngineState->variables);
359 + SearchesUninitialize(&pEngineState->searches);
360 + RegistrationUninitialize(&pEngineState->registration);
361 + PayloadsUninitialize(&pEngineState->payloads);
362 + PackagesUninitialize(&pEngineState->packages);
363 + CatalogUninitialize(&pEngineState->catalogs);
364 + SectionUninitialize(&pEngineState->section);
365 + ContainersUninitialize(&pEngineState->containers);
366 +
367 + ReleaseStr(pEngineState->command.wzLayoutDirectory);
368 + ReleaseStr(pEngineState->command.wzCommandLine);
369 +
370 + ReleaseStr(pEngineState->log.sczExtension);
371 + ReleaseStr(pEngineState->log.sczPrefix);
372 + ReleaseStr(pEngineState->log.sczPath);
373 + ReleaseStr(pEngineState->log.sczPathVariable);
374 +
375 + if (TLS_OUT_OF_INDEXES != pEngineState->dwElevatedLoggingTlsId)
376 + {
377 + ::TlsFree(pEngineState->dwElevatedLoggingTlsId);
378 + }
379 +
380 + ::DeleteCriticalSection(&pEngineState->csActive);
381 +
382 + // clear struct
383 + memset(pEngineState, 0, sizeof(BURN_ENGINE_STATE));
384 +}
385 +
386 +static HRESULT RunUntrusted(
387 + __in LPCWSTR wzCommandLine,
388 + __in BURN_ENGINE_STATE* pEngineState
389 + )
390 +{
391 + HRESULT hr = S_OK;
392 + LPWSTR sczCurrentProcessPath = NULL;
393 + LPWSTR wzCleanRoomBundlePath = NULL;
394 + LPWSTR sczCachedCleanRoomBundlePath = NULL;
395 + LPWSTR sczParameters = NULL;
396 + LPWSTR sczFullCommandLine = NULL;
397 + STARTUPINFOW si = { };
398 + PROCESS_INFORMATION pi = { };
399 + HANDLE hFileAttached = NULL;
400 + HANDLE hFileSelf = NULL;
401 + HANDLE hProcess = NULL;
402 +
403 + hr = PathForCurrentProcess(&sczCurrentProcessPath, NULL);
404 + ExitOnFailure(hr, "Failed to get path for current process.");
405 +
406 + BOOL fRunningFromCache = CacheBundleRunningFromCache();
407 +
408 + // If we're running from the package cache, we're in a secure
409 + // folder (DLLs cannot be inserted here for hijacking purposes)
410 + // so just launch the current process's path as the clean room
411 + // process. Technically speaking, we'd be able to skip creating
412 + // a clean room process at all (since we're already running from
413 + // a secure folder) but it makes the code that only wants to run
414 + // in clean room more complicated if we don't launch an explicit
415 + // clean room process.
416 + if (fRunningFromCache)
417 + {
418 + wzCleanRoomBundlePath = sczCurrentProcessPath;
419 + }
420 + else
421 + {
422 + hr = CacheBundleToCleanRoom(&pEngineState->userExperience.payloads, &pEngineState->section, &sczCachedCleanRoomBundlePath);
423 + ExitOnFailure(hr, "Failed to cache to clean room.");
424 +
425 + wzCleanRoomBundlePath = sczCachedCleanRoomBundlePath;
426 + }
427 +
428 + // The clean room switch must always be at the front of the command line so
429 + // the EngineInCleanRoom function will operate correctly.
430 + hr = StrAllocFormatted(&sczParameters, L"-%ls=\"%ls\"", BURN_COMMANDLINE_SWITCH_CLEAN_ROOM, sczCurrentProcessPath);
431 + ExitOnFailure(hr, "Failed to allocate parameters for unelevated process.");
432 +
433 + // Send a file handle for the child Burn process to access the attached container.
434 + hr = CoreAppendFileHandleAttachedToCommandLine(pEngineState->section.hEngineFile, &hFileAttached, &sczParameters);
435 + ExitOnFailure(hr, "Failed to append %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED);
436 +
437 + // Grab a file handle for the child Burn process.
438 + hr = CoreAppendFileHandleSelfToCommandLine(wzCleanRoomBundlePath, &hFileSelf, &sczParameters, NULL);
439 + ExitOnFailure(hr, "Failed to append %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF);
440 +
441 + hr = StrAllocFormattedSecure(&sczParameters, L"%ls %ls", sczParameters, wzCommandLine);
442 + ExitOnFailure(hr, "Failed to append original command line.");
443 +
444 +#ifdef ENABLE_UNELEVATE
445 + // TODO: Pass file handle to unelevated process if this ever gets reenabled.
446 + if (!pEngineState->fDisableUnelevate)
447 + {
448 + // Try to launch unelevated and if that fails for any reason, we'll launch our process normally (even though that may make it elevated).
449 + hr = ProcExecuteAsInteractiveUser(wzCleanRoomBundlePath, sczParameters, &hProcess);
450 + }
451 +#endif
452 +
453 + if (!hProcess)
454 + {
455 + hr = StrAllocFormattedSecure(&sczFullCommandLine, L"\"%ls\" %ls", wzCleanRoomBundlePath, sczParameters);
456 + ExitOnFailure(hr, "Failed to allocate full command-line.");
457 +
458 + si.cb = sizeof(si);
459 + si.wShowWindow = static_cast<WORD>(pEngineState->command.nCmdShow);
460 + if (!::CreateProcessW(wzCleanRoomBundlePath, sczFullCommandLine, NULL, NULL, TRUE, 0, 0, NULL, &si, &pi))
461 + {
462 + ExitWithLastError(hr, "Failed to launch clean room process: %ls", sczFullCommandLine);
463 + }
464 +
465 + hProcess = pi.hProcess;
466 + pi.hProcess = NULL;
467 + }
468 +
469 + hr = ProcWaitForCompletion(hProcess, INFINITE, &pEngineState->userExperience.dwExitCode);
470 + ExitOnFailure(hr, "Failed to wait for clean room process: %ls", wzCleanRoomBundlePath);
471 +
472 +LExit:
473 + ReleaseHandle(pi.hThread);
474 + ReleaseFileHandle(hFileSelf);
475 + ReleaseFileHandle(hFileAttached);
476 + ReleaseHandle(hProcess);
477 + StrSecureZeroFreeString(sczFullCommandLine);
478 + StrSecureZeroFreeString(sczParameters);
479 + ReleaseStr(sczCachedCleanRoomBundlePath);
480 + ReleaseStr(sczCurrentProcessPath);
481 +
482 + return hr;
483 +}
484 +
485 +static HRESULT RunNormal(
486 + __in HINSTANCE hInstance,
487 + __in BURN_ENGINE_STATE* pEngineState
488 + )
489 +{
490 + HRESULT hr = S_OK;
491 + HANDLE hPipesCreatedEvent = NULL;
492 + BOOL fContinueExecution = TRUE;
493 + BOOL fReloadApp = FALSE;
494 +
495 + // Initialize logging.
496 + hr = LoggingOpen(&pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->registration.sczDisplayName);
497 + ExitOnFailure(hr, "Failed to open log.");
498 +
499 + // Ensure we're on a supported operating system.
500 + hr = ConditionGlobalCheck(&pEngineState->variables, &pEngineState->condition, pEngineState->command.display, pEngineState->registration.sczDisplayName, &pEngineState->userExperience.dwExitCode, &fContinueExecution);
501 + ExitOnFailure(hr, "Failed to check global conditions");
502 +
503 + if (!fContinueExecution)
504 + {
505 + LogId(REPORT_STANDARD, MSG_FAILED_CONDITION_CHECK);
506 +
507 + // If the block told us to abort, abort!
508 + ExitFunction1(hr = S_OK);
509 + }
510 +
511 + if (pEngineState->userExperience.fSplashScreen && BOOTSTRAPPER_DISPLAY_NONE < pEngineState->command.display)
512 + {
513 + SplashScreenCreate(hInstance, NULL, &pEngineState->command.hwndSplashScreen);
514 + }
515 +
516 + // Create a top-level window to handle system messages.
517 + hr = UiCreateMessageWindow(hInstance, pEngineState);
518 + ExitOnFailure(hr, "Failed to create the message window.");
519 +
520 + // Query registration state.
521 + hr = CoreQueryRegistration(pEngineState);
522 + ExitOnFailure(hr, "Failed to query registration.");
523 +
524 + // Set some built-in variables before loading the BA.
525 + hr = PlanSetVariables(pEngineState->command.action, &pEngineState->variables);
526 + ExitOnFailure(hr, "Failed to set action variables.");
527 +
528 + hr = RegistrationSetVariables(&pEngineState->registration, &pEngineState->variables);
529 + ExitOnFailure(hr, "Failed to set registration variables.");
530 +
531 + // If a layout directory was specified on the command-line, set it as a well-known variable.
532 + if (pEngineState->command.wzLayoutDirectory && *pEngineState->command.wzLayoutDirectory)
533 + {
534 + hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_LAYOUT_DIRECTORY, pEngineState->command.wzLayoutDirectory, FALSE);
535 + ExitOnFailure(hr, "Failed to set layout directory variable to value provided from command-line.");
536 + }
537 +
538 + do
539 + {
540 + fReloadApp = FALSE;
541 +
542 + hr = RunApplication(pEngineState, &fReloadApp);
543 + ExitOnFailure(hr, "Failed while running ");
544 + } while (fReloadApp);
545 +
546 +LExit:
547 + // If the message window is still around, close it.
548 + UiCloseMessageWindow(pEngineState);
549 +
550 + VariablesDump(&pEngineState->variables);
551 +
552 + // end per-machine process if running
553 + if (INVALID_HANDLE_VALUE != pEngineState->companionConnection.hPipe)
554 + {
555 + PipeTerminateChildProcess(&pEngineState->companionConnection, pEngineState->userExperience.dwExitCode, FALSE);
556 + }
557 +
558 + // If the splash screen is still around, close it.
559 + if (::IsWindow(pEngineState->command.hwndSplashScreen))
560 + {
561 + ::PostMessageW(pEngineState->command.hwndSplashScreen, WM_CLOSE, 0, 0);
562 + }
563 +
564 + ReleaseHandle(hPipesCreatedEvent);
565 +
566 + return hr;
567 +}
568 +
569 +static HRESULT RunElevated(
570 + __in HINSTANCE hInstance,
571 + __in LPCWSTR /*wzCommandLine*/,
572 + __in BURN_ENGINE_STATE* pEngineState
573 + )
574 +{
575 + HRESULT hr = S_OK;
576 + HANDLE hLock = NULL;
577 + BOOL fDisabledAutomaticUpdates = FALSE;
578 +
579 + // connect to per-user process
580 + hr = PipeChildConnect(&pEngineState->companionConnection, TRUE);
581 + ExitOnFailure(hr, "Failed to connect to unelevated process.");
582 +
583 + // Set up the thread local storage to store the correct pipe to communicate logging then
584 + // override logging to write over the pipe.
585 + pEngineState->dwElevatedLoggingTlsId = ::TlsAlloc();
586 + if (TLS_OUT_OF_INDEXES == pEngineState->dwElevatedLoggingTlsId)
587 + {
588 + ExitWithLastError(hr, "Failed to allocate thread local storage for logging.");
589 + }
590 +
591 + if (!::TlsSetValue(pEngineState->dwElevatedLoggingTlsId, pEngineState->companionConnection.hPipe))
592 + {
593 + ExitWithLastError(hr, "Failed to set elevated pipe into thread local storage for logging.");
594 + }
595 +
596 + LogRedirect(RedirectLoggingOverPipe, pEngineState);
597 +
598 + // Create a top-level window to prevent shutting down the elevated process.
599 + hr = UiCreateMessageWindow(hInstance, pEngineState);
600 + ExitOnFailure(hr, "Failed to create the message window.");
601 +
602 + SrpInitialize(TRUE);
603 +
604 + // Pump messages from parent process.
605 + hr = ElevationChildPumpMessages(pEngineState->dwElevatedLoggingTlsId, pEngineState->companionConnection.hPipe, pEngineState->companionConnection.hCachePipe, &pEngineState->approvedExes, &pEngineState->containers, &pEngineState->packages, &pEngineState->payloads, &pEngineState->variables, &pEngineState->registration, &pEngineState->userExperience, &hLock, &fDisabledAutomaticUpdates, &pEngineState->userExperience.dwExitCode, &pEngineState->fRestart);
606 + LogRedirect(NULL, NULL); // reset logging so the next failure gets written to "log buffer" for the failure log.
607 + ExitOnFailure(hr, "Failed to pump messages from parent process.");
608 +
609 +LExit:
610 + LogRedirect(NULL, NULL); // we're done talking to the child so always reset logging now.
611 +
612 + // If the message window is still around, close it.
613 + UiCloseMessageWindow(pEngineState);
614 +
615 + if (fDisabledAutomaticUpdates)
616 + {
617 + ElevationChildResumeAutomaticUpdates();
618 + }
619 +
620 + if (hLock)
621 + {
622 + ::ReleaseMutex(hLock);
623 + ::CloseHandle(hLock);
624 + }
625 +
626 + return hr;
627 +}
628 +
629 +static HRESULT RunEmbedded(
630 + __in HINSTANCE hInstance,
631 + __in BURN_ENGINE_STATE* pEngineState
632 + )
633 +{
634 + HRESULT hr = S_OK;
635 +
636 + // Disable system restore since the parent bundle may have done it.
637 + pEngineState->fDisableSystemRestore = TRUE;
638 +
639 + // Connect to parent process.
640 + hr = PipeChildConnect(&pEngineState->embeddedConnection, FALSE);
641 + ExitOnFailure(hr, "Failed to connect to parent of embedded process.");
642 +
643 + // Do not register the bundle to automatically restart if embedded.
644 + if (BOOTSTRAPPER_DISPLAY_EMBEDDED == pEngineState->command.display)
645 + {
646 + pEngineState->registration.fDisableResume = TRUE;
647 + }
648 +
649 + // Now run the application like normal.
650 + hr = RunNormal(hInstance, pEngineState);
651 + ExitOnFailure(hr, "Failed to run bootstrapper application embedded.");
652 +
653 +LExit:
654 + return hr;
655 +}
656 +
657 +static HRESULT RunRunOnce(
658 + __in const BURN_REGISTRATION* pRegistration,
659 + __in int nCmdShow
660 + )
661 +{
662 + HRESULT hr = S_OK;
663 + LPWSTR sczNewCommandLine = NULL;
664 + LPWSTR sczBurnPath = NULL;
665 + HANDLE hProcess = NULL;
666 +
667 + hr = RegistrationGetResumeCommandLine(pRegistration, &sczNewCommandLine);
668 + ExitOnFailure(hr, "Unable to get resume command line from the registry");
669 +
670 + // and re-launch
671 + hr = PathForCurrentProcess(&sczBurnPath, NULL);
672 + ExitOnFailure(hr, "Failed to get current process path.");
673 +
674 + hr = ProcExec(sczBurnPath, 0 < sczNewCommandLine ? sczNewCommandLine : L"", nCmdShow, &hProcess);
675 + ExitOnFailure(hr, "Failed to re-launch bundle process after RunOnce: %ls", sczBurnPath);
676 +
677 +LExit:
678 + ReleaseHandle(hProcess);
679 + ReleaseStr(sczNewCommandLine);
680 + ReleaseStr(sczBurnPath);
681 +
682 + return hr;
683 +}
684 +
685 +static HRESULT RunApplication(
686 + __in BURN_ENGINE_STATE* pEngineState,
687 + __out BOOL* pfReloadApp
688 + )
689 +{
690 + HRESULT hr = S_OK;
691 + BOOTSTRAPPER_ENGINE_CONTEXT engineContext = { };
692 + BOOL fStartupCalled = FALSE;
693 + BOOL fRet = FALSE;
694 + MSG msg = { };
695 + BOOTSTRAPPER_SHUTDOWN_ACTION shutdownAction = BOOTSTRAPPER_SHUTDOWN_ACTION_NONE;
696 +
697 + ::PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
698 +
699 + // Setup the bootstrapper engine.
700 + engineContext.dwThreadId = ::GetCurrentThreadId();
701 + engineContext.pEngineState = pEngineState;
702 +
703 + // Load the bootstrapper application.
704 + hr = UserExperienceLoad(&pEngineState->userExperience, &engineContext, &pEngineState->command);
705 + ExitOnFailure(hr, "Failed to load BA.");
706 +
707 + fStartupCalled = TRUE;
708 + hr = UserExperienceOnStartup(&pEngineState->userExperience);
709 + ExitOnFailure(hr, "Failed to start bootstrapper application.");
710 +
711 + // Enter the message pump.
712 + while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0)))
713 + {
714 + if (-1 == fRet)
715 + {
716 + hr = E_UNEXPECTED;
717 + ExitOnRootFailure(hr, "Unexpected return value from message pump.");
718 + }
719 + else
720 + {
721 + ProcessMessage(pEngineState, &msg);
722 + }
723 + }
724 +
725 + // Get exit code.
726 + pEngineState->userExperience.dwExitCode = (DWORD)msg.wParam;
727 +
728 +LExit:
729 + if (fStartupCalled)
730 + {
731 + UserExperienceOnShutdown(&pEngineState->userExperience, &shutdownAction);
732 + if (BOOTSTRAPPER_SHUTDOWN_ACTION_RESTART == shutdownAction)
733 + {
734 + LogId(REPORT_STANDARD, MSG_BA_REQUESTED_RESTART, LoggingBoolToString(pEngineState->fRestart));
735 + pEngineState->fRestart = TRUE;
736 + }
737 + else if (BOOTSTRAPPER_SHUTDOWN_ACTION_RELOAD_BOOTSTRAPPER == shutdownAction)
738 + {
739 + LogId(REPORT_STANDARD, MSG_BA_REQUESTED_RELOAD);
740 + *pfReloadApp = TRUE;
741 + }
742 + }
743 +
744 + // Unload BA.
745 + UserExperienceUnload(&pEngineState->userExperience);
746 +
747 + return hr;
748 +}
749 +
750 +static HRESULT ProcessMessage(
751 + __in BURN_ENGINE_STATE* pEngineState,
752 + __in const MSG* pmsg
753 + )
754 +{
755 + HRESULT hr = S_OK;
756 +
757 + switch (pmsg->message)
758 + {
759 + case WM_BURN_DETECT:
760 + hr = CoreDetect(pEngineState, reinterpret_cast<HWND>(pmsg->lParam));
761 + break;
762 +
763 + case WM_BURN_PLAN:
764 + hr = CorePlan(pEngineState, static_cast<BOOTSTRAPPER_ACTION>(pmsg->lParam));
765 + break;
766 +
767 + case WM_BURN_ELEVATE:
768 + hr = CoreElevate(pEngineState, reinterpret_cast<HWND>(pmsg->lParam));
769 + break;
770 +
771 + case WM_BURN_APPLY:
772 + hr = CoreApply(pEngineState, reinterpret_cast<HWND>(pmsg->lParam));
773 + break;
774 +
775 + case WM_BURN_LAUNCH_APPROVED_EXE:
776 + hr = CoreLaunchApprovedExe(pEngineState, reinterpret_cast<BURN_LAUNCH_APPROVED_EXE*>(pmsg->lParam));
777 + break;
778 +
779 + case WM_BURN_QUIT:
780 + hr = CoreQuit(pEngineState, static_cast<int>(pmsg->wParam));
781 + break;
782 + }
783 +
784 + return hr;
785 +}
786 +
787 +static HRESULT DAPI RedirectLoggingOverPipe(
788 + __in_z LPCSTR szString,
789 + __in_opt LPVOID pvContext
790 + )
791 +{
792 + static BOOL s_fCurrentlyLoggingToPipe = FALSE;
793 +
794 + HRESULT hr = S_OK;
795 + BURN_ENGINE_STATE* pEngineState = static_cast<BURN_ENGINE_STATE*>(pvContext);
796 + BOOL fStartedLogging = FALSE;
797 + HANDLE hPipe = INVALID_HANDLE_VALUE;
798 + BYTE* pbData = NULL;
799 + SIZE_T cbData = 0;
800 + DWORD dwResult = 0;
801 +
802 + // Prevent this function from being called recursively.
803 + if (s_fCurrentlyLoggingToPipe)
804 + {
805 + ExitFunction();
806 + }
807 +
808 + s_fCurrentlyLoggingToPipe = TRUE;
809 + fStartedLogging = TRUE;
810 +
811 + // Make sure the current thread set the pipe in TLS.
812 + hPipe = ::TlsGetValue(pEngineState->dwElevatedLoggingTlsId);
813 + if (!hPipe || INVALID_HANDLE_VALUE == hPipe)
814 + {
815 + hr = HRESULT_FROM_WIN32(ERROR_PIPE_NOT_CONNECTED);
816 + ExitFunction();
817 + }
818 +
819 + // Do not log or use ExitOnFailure() macro here because they will be discarded
820 + // by the recursive block at the top of this function.
821 + hr = BuffWriteStringAnsi(&pbData, &cbData, szString);
822 + if (SUCCEEDED(hr))
823 + {
824 + hr = PipeSendMessage(hPipe, static_cast<DWORD>(BURN_PIPE_MESSAGE_TYPE_LOG), pbData, cbData, NULL, NULL, &dwResult);
825 + if (SUCCEEDED(hr))
826 + {
827 + hr = (HRESULT)dwResult;
828 + }
829 + }
830 +
831 +LExit:
832 + ReleaseBuffer(pbData);
833 +
834 + // We started logging so remember to say we are no longer logging.
835 + if (fStartedLogging)
836 + {
837 + s_fCurrentlyLoggingToPipe = FALSE;
838 + }
839 +
840 + return hr;
841 +}
842 +
843 +static HRESULT Restart()
844 +{
845 + HRESULT hr = S_OK;
846 + HANDLE hProcessToken = NULL;
847 + TOKEN_PRIVILEGES priv = { };
848 + DWORD dwRetries = 0;
849 +
850 + if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hProcessToken))
851 + {
852 + ExitWithLastError(hr, "Failed to get process token.");
853 + }
854 +
855 + priv.PrivilegeCount = 1;
856 + priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
857 + if (!::LookupPrivilegeValueW(NULL, L"SeShutdownPrivilege", &priv.Privileges[0].Luid))
858 + {
859 + ExitWithLastError(hr, "Failed to get shutdown privilege LUID.");
860 + }
861 +
862 + if (!::AdjustTokenPrivileges(hProcessToken, FALSE, &priv, sizeof(TOKEN_PRIVILEGES), NULL, 0))
863 + {
864 + ExitWithLastError(hr, "Failed to adjust token to add shutdown privileges.");
865 + }
866 +
867 + do
868 + {
869 + hr = S_OK;
870 +
871 + // Wait a second to let the companion process (assuming we did an elevated install) to get to the
872 + // point where it too is thinking about restarting the computer. Only one will schedule the restart
873 + // but both will have their log files closed and otherwise be ready to exit.
874 + //
875 + // On retry, we'll also wait a second to let the OS try to get to a place where the restart can
876 + // be initiated.
877 + ::Sleep(1000);
878 +
879 + if (!vpfnInitiateSystemShutdownExW(NULL, NULL, 0, FALSE, TRUE, SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_MINOR_INSTALLATION | SHTDN_REASON_FLAG_PLANNED))
880 + {
881 + hr = HRESULT_FROM_WIN32(::GetLastError());
882 + }
883 + } while (dwRetries++ < RESTART_RETRIES && (HRESULT_FROM_WIN32(ERROR_MACHINE_LOCKED) == hr || HRESULT_FROM_WIN32(ERROR_NOT_READY) == hr));
884 + ExitOnRootFailure(hr, "Failed to schedule restart.");
885 +
886 +LExit:
887 + ReleaseHandle(hProcessToken);
888 + return hr;
889 +}
src/engine/engine.mc new
+901
@@ -0,0 +1,901 @@
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 +
4 +MessageIdTypedef=DWORD
5 +
6 +LanguageNames=(English=0x409:MSG00409)
7 +
8 +
9 +; // message definitions
10 +
11 +; // MessageId=#
12 +; // Severity=Success
13 +; // SymbolicName=MSG_SUCCESS
14 +; // Language=English
15 +; // Success %1.
16 +; // .
17 +;
18 +; // MessageId=#
19 +; // Severity=Warning
20 +; // SymbolicName=MSG_WARNING
21 +; // Language=English
22 +; // Warning %1.
23 +; // .
24 +;
25 +; // MessageId=#
26 +; // Severity=Error
27 +; // SymbolicName=MSG_ERROR
28 +; // Language=English
29 +; // Error %1.
30 +; // .
31 +
32 +MessageId=1
33 +Severity=Success
34 +SymbolicName=MSG_BURN_INFO
35 +Language=English
36 +Burn v%1!hs!, Windows v%2!d!.%3!d! (Build %4!d!: Service Pack %5!d!), path: %6!ls!
37 +.
38 +
39 +MessageId=2
40 +Severity=Warning
41 +SymbolicName=MSG_BURN_UNKNOWN_PRIVATE_SWITCH
42 +Language=English
43 +Unknown burn internal command-line switch encountered: '%1!ls!'.
44 +.
45 +
46 +MessageId=3
47 +Severity=Success
48 +SymbolicName=MSG_BURN_RUN_BY_RELATED_BUNDLE
49 +Language=English
50 +This bundle is being run by a related bundle as type '%1!hs!'.
51 +.
52 +
53 +MessageId=4
54 +Severity=Success
55 +SymbolicName=MSG_BA_REQUESTED_RESTART
56 +Language=English
57 +Bootstrapper application requested restart at shutdown. Planned to restart already: %1!hs!.
58 +.
59 +
60 +MessageId=5
61 +Severity=Warning
62 +SymbolicName=MSG_RESTARTING
63 +Language=English
64 +Restarting computer...
65 +=======================================
66 +.
67 +
68 +MessageId=6
69 +Severity=Success
70 +SymbolicName=MSG_BA_REQUESTED_RELOAD
71 +Language=English
72 +Bootstrapper application requested to be reloaded.
73 +.
74 +
75 +MessageId=7
76 +Severity=Success
77 +SymbolicName=MSG_EXITING
78 +Language=English
79 +Exit code: 0x%1!x!, restarting: %2!hs!
80 +.
81 +
82 +MessageId=8
83 +Severity=Warning
84 +SymbolicName=MSG_RESTART_ABORTED
85 +Language=English
86 +Preventing requested restart because bundle is related: '%1!hs!'. Returning restart requested to parent bundle.
87 +.
88 +
89 +MessageId=9
90 +Severity=Success
91 +SymbolicName=MSG_BURN_COMMAND_LINE
92 +Language=English
93 +Command Line: '%1!ls!'
94 +.
95 +
96 +MessageId=10
97 +Severity=Success
98 +SymbolicName=MSG_LAUNCH_ELEVATED_ENGINE_STARTING
99 +Language=English
100 +Launching elevated engine process.
101 +.
102 +
103 +MessageId=11
104 +Severity=Success
105 +SymbolicName=MSG_LAUNCH_ELEVATED_ENGINE_SUCCESS
106 +Language=English
107 +Launched elevated engine process.
108 +.
109 +
110 +MessageId=12
111 +Severity=Success
112 +SymbolicName=MSG_CONNECT_TO_ELEVATED_ENGINE_SUCCESS
113 +Language=English
114 +Connected to elevated engine.
115 +.
116 +
117 +MessageId=51
118 +Severity=Error
119 +SymbolicName=MSG_FAILED_PARSE_CONDITION
120 +Language=English
121 +Error %1!hs!. Failed to parse condition %2!ls!. Unexpected symbol at position %3!hs!
122 +.
123 +
124 +MessageId=52
125 +Severity=Success
126 +SymbolicName=MSG_CONDITION_RESULT
127 +Language=English
128 +Condition '%1!ls!' evaluates to %2!hs!.
129 +.
130 +
131 +MessageId=53
132 +Severity=Error
133 +SymbolicName=MSG_FAILED_CONDITION_CHECK
134 +Language=English
135 +Bundle global condition check didn't succeed - aborting without loading application.
136 +.
137 +
138 +MessageId=54
139 +Severity=Error
140 +SymbolicName=MSG_PAYLOAD_FILE_NOT_PRESENT
141 +Language=English
142 +Failed to resolve source for file: %2!ls!, error: %1!ls!.
143 +.
144 +
145 +MessageId=55
146 +Severity=Warning
147 +SymbolicName=MSG_CANNOT_LOAD_STATE_FILE
148 +Language=English
149 +Could not load or read state file: %2!ls!, error: 0x%1!x!.
150 +.
151 +
152 +MessageId=56
153 +Severity=Error
154 +SymbolicName=MSG_USER_CANCELED
155 +Language=English
156 +Application canceled operation: %2!ls!, error: %1!ls!
157 +.
158 +
159 +MessageId=100
160 +Severity=Success
161 +SymbolicName=MSG_DETECT_BEGIN
162 +Language=English
163 +Detect begin, %1!u! packages
164 +.
165 +
166 +MessageId=101
167 +Severity=Success
168 +SymbolicName=MSG_DETECTED_PACKAGE
169 +Language=English
170 +Detected package: %1!ls!, state: %2!hs!, cached: %3!hs!
171 +.
172 +
173 +MessageId=102
174 +Severity=Success
175 +SymbolicName=MSG_DETECTED_RELATED_BUNDLE
176 +Language=English
177 +Detected related bundle: %1!ls!, type: %2!hs!, scope: %3!hs!, version: %4!hs!, operation: %5!hs!
178 +.
179 +
180 +MessageId=103
181 +Severity=Success
182 +SymbolicName=MSG_DETECTED_RELATED_PACKAGE
183 +Language=English
184 +Detected related package: %1!ls!, scope: %2!hs!, version: %3!hs!, language: %4!u! operation: %5!hs!
185 +.
186 +
187 +MessageId=104
188 +Severity=Success
189 +SymbolicName=MSG_DETECTED_MSI_FEATURE
190 +Language=English
191 +Detected package: %1!ls!, feature: %2!ls!, state: %3!hs!
192 +.
193 +
194 +MessageId=105
195 +Severity=Success
196 +SymbolicName=MSG_DETECTED_MSP_TARGET
197 +Language=English
198 +Detected package: %1!ls! target: %2!ls!, state: %3!hs!
199 +.
200 +
201 +MessageId=106
202 +Severity=Success
203 +SymbolicName=MSG_DETECT_CALCULATE_PATCH_APPLICABILITY
204 +Language=English
205 +Calculating patch applicability for target product code: %1!ls!, context: %2!hs!
206 +.
207 +
208 +MessageId=107
209 +Severity=Success
210 +SymbolicName=MSG_DETECTED_FORWARD_COMPATIBLE_BUNDLE
211 +Language=English
212 +Detected forward compatible bundle: %1!ls!, type: %2!hs!, scope: %3!hs!, version: %4!hs!, enabled: %5!hs!
213 +.
214 +
215 +MessageId=108
216 +Severity=Success
217 +SymbolicName=MSG_DETECTED_COMPATIBLE_PACKAGE_FROM_PROVIDER
218 +Language=English
219 +Detected compatible package: %1!ls!, provider: %2!ls!, installed: %3!ls!, version: %4!ls!, chained: %5!ls!
220 +.
221 +
222 +MessageId=120
223 +Severity=Warning
224 +SymbolicName=MSG_DETECT_PACKAGE_NOT_FULLY_CACHED
225 +Language=English
226 +Detected partially cached package: %1!ls!, invalid payload: %2!ls!, reason: 0x%3!x!
227 +.
228 +
229 +MessageId=121
230 +Severity=Warning
231 +SymbolicName=MSG_DETECT_FAILED_CALCULATE_PATCH_APPLICABILITY
232 +Language=English
233 +Could not calculate patch applicability for target product code: %1!ls!, context: %2!hs!, reason: 0x%3!x!
234 +.
235 +
236 +MessageId=151
237 +Severity=Error
238 +SymbolicName=MSG_FAILED_DETECT_PACKAGE
239 +Language=English
240 +Detect failed for package: %2!ls!, error: %1!ls!
241 +.
242 +
243 +MessageId=152
244 +Severity=Error
245 +SymbolicName=MSG_FAILED_READ_RELATED_PACKAGE_LANGUAGE
246 +Language=English
247 +Detected related package: %2!ls!, but failed to read language: %3!hs!, error: 0x%1!x!
248 +.
249 +
250 +MessageId=170
251 +Severity=Warning
252 +SymbolicName=MSG_DETECT_BAD_PRODUCT_CONFIGURATION
253 +Language=English
254 +Detected bad configuration for product: %1!ls!
255 +.
256 +
257 +MessageId=199
258 +Severity=Success
259 +SymbolicName=MSG_DETECT_COMPLETE
260 +Language=English
261 +Detect complete, result: 0x%1!x!
262 +.
263 +
264 +MessageId=200
265 +Severity=Success
266 +SymbolicName=MSG_PLAN_BEGIN
267 +Language=English
268 +Plan begin, %1!u! packages, action: %2!hs!
269 +.
270 +
271 +MessageId=201
272 +Severity=Success
273 +SymbolicName=MSG_PLANNED_PACKAGE
274 +Language=English
275 +Planned package: %1!ls!, state: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute: %5!hs!, rollback: %6!hs!, cache: %7!hs!, uncache: %8!hs!, dependency: %9!hs!
276 +.
277 +
278 +MessageId=202
279 +Severity=Success
280 +SymbolicName=MSG_PLANNED_BUNDLE_UX_CHANGED_REQUEST
281 +Language=English
282 +Planned bundle: %1!ls!, ba requested state: %2!hs! over default: %3!hs!
283 +.
284 +
285 +MessageId=203
286 +Severity=Success
287 +SymbolicName=MSG_PLANNED_MSI_FEATURE
288 +Language=English
289 +Planned feature: %1!ls!, state: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute action: %5!hs!, rollback action: %6!hs!
290 +.
291 +
292 +MessageId=204
293 +Severity=Success
294 +SymbolicName=MSG_PLAN_MSI_FEATURES
295 +Language=English
296 +Plan %1!u! msi features for package: %2!ls!
297 +.
298 +
299 +MessageId=205
300 +Severity=Warning
301 +SymbolicName=MSG_PLAN_SKIP_PATCH_ACTION
302 +Language=English
303 +Plan %5!hs! skipped patch: %1!ls!, action: %2!hs! because chained target package: %3!ls! being uninstalled
304 +.
305 +
306 +MessageId=206
307 +Severity=Warning
308 +SymbolicName=MSG_PLAN_SKIP_SLIPSTREAM_ACTION
309 +Language=English
310 +Plan %5!hs! skipped patch: %1!ls!, action: %2!hs! because slipstreamed into chained target package: %3!ls!, action: %4!hs!
311 +.
312 +
313 +MessageId=207
314 +Severity=Success
315 +SymbolicName=MSG_PLANNED_RELATED_BUNDLE
316 +Language=English
317 +Planned related bundle: %1!ls!, type: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute: %5!hs!, rollback: %6!hs!, dependency: %7!hs!
318 +.
319 +
320 +MessageId=208
321 +Severity=Warning
322 +SymbolicName=MSG_PLAN_DISABLING_ROLLBACK_NO_CACHE
323 +Language=English
324 +Plan disabled rollback for package: %1!ls!, due to incomplete cache: %2!hs!, original rollback action: %3!hs!
325 +.
326 +
327 +MessageId=209
328 +Severity=Warning
329 +SymbolicName=MSG_PLAN_SKIPPED_PROVIDER_KEY_REMOVAL
330 +Language=English
331 +Plan skipped removal of provider key: %1!ls! because it is registered to a different bundle: %2!ls!
332 +.
333 +
334 +MessageId=210
335 +Severity=Warning
336 +SymbolicName=MSG_PLAN_SKIPPED_DUE_TO_DEPENDENTS
337 +Language=English
338 +Plan skipped due to %1!u! remaining dependents
339 +.
340 +
341 +MessageId=211
342 +Severity=Success
343 +SymbolicName=MSG_PLANNED_UPGRADE_BUNDLE
344 +Language=English
345 +Planned upgrade bundle: %1!ls!, default requested: %2!hs!, ba requested: %3!hs!, execute: %4!hs!, rollback: %5!hs!, dependency: %6!hs!
346 +.
347 +
348 +MessageId=212
349 +Severity=Success
350 +SymbolicName=MSG_PLANNED_FORWARD_COMPATIBLE_BUNDLE
351 +Language=English
352 +Planned forward compatible bundle: %1!ls!, default requested: %2!hs!, ba requested: %3!hs!, execute: %4!hs!, rollback: %5!hs!, dependency: %6!hs!
353 +.
354 +
355 +MessageId=213
356 +Severity=Success
357 +SymbolicName=MSG_PLAN_SKIPPED_RELATED_BUNDLE_DEPENDENT
358 +Language=English
359 +Plan skipped related bundle: %1!ls!, type: %2!hs!, because it was dependent and the current bundle is being executed as type: %3!hs!.
360 +.
361 +
362 +MessageId=214
363 +Severity=Success
364 +SymbolicName=MSG_PLAN_SKIPPED_RELATED_BUNDLE_SCHEDULED
365 +Language=English
366 +Plan skipped related bundle: %1!ls!, type: %2!hs!, because it was previously scheduled.
367 +.
368 +
369 +MessageId=215
370 +Severity=Success
371 +SymbolicName=MSG_PLANNED_ORPHAN_PACKAGE_FROM_PROVIDER
372 +Language=English
373 +Will remove orphan package: %1!ls!, installed: %2!ls!, chained: %3!ls!
374 +.
375 +
376 +MessageId=216
377 +Severity=Success
378 +SymbolicName=MSG_PLAN_SKIPPED_RELATED_BUNDLE_EMBEDDED_BUNDLE_NEWER
379 +Language=English
380 +Plan skipped related bundle: %1!ls!, type: %2!hs!, provider key: %3!ls!, because an embedded bundle with the same provider key is being installed.
381 +.
382 +
383 +MessageId=217
384 +Severity=Success
385 +SymbolicName=MSG_PLAN_SKIPPED_DEPENDENT_BUNDLE_REPAIR
386 +Language=English
387 +Plan skipped dependent bundle repair: %1!ls!, type: %2!hs!, because no packages are being executed during this uninstall operation.
388 +.
389 +
390 +MessageId=299
391 +Severity=Success
392 +SymbolicName=MSG_PLAN_COMPLETE
393 +Language=English
394 +Plan complete, result: 0x%1!x!
395 +.
396 +
397 +MessageId=300
398 +Severity=Success
399 +SymbolicName=MSG_APPLY_BEGIN
400 +Language=English
401 +Apply begin
402 +.
403 +
404 +MessageId=301
405 +Severity=Success
406 +SymbolicName=MSG_APPLYING_PACKAGE
407 +Language=English
408 +Applying %1!hs! package: %2!ls!, action: %3!hs!, path: %4!ls!, arguments: '%5!ls!'
409 +.
410 +
411 +MessageId=302
412 +Severity=Success
413 +SymbolicName=MSG_ACQUIRED_PAYLOAD
414 +Language=English
415 +Acquired payload: %1!ls! to working path: %2!ls! from: %4!ls!.
416 +.
417 +
418 +MessageId=304
419 +Severity=Success
420 +SymbolicName=MSG_VERIFIED_EXISTING_PAYLOAD
421 +Language=English
422 +Verified existing payload: %1!ls! at path: %2!ls!.
423 +.
424 +
425 +MessageId=305
426 +Severity=Success
427 +SymbolicName=MSG_VERIFIED_ACQUIRED_PAYLOAD
428 +Language=English
429 +Verified acquired payload: %1!ls! at path: %2!ls!, %3!hs! to: %4!ls!.
430 +.
431 +
432 +MessageId=306
433 +Severity=Success
434 +SymbolicName=MSG_APPLYING_PATCH_PACKAGE
435 +Language=English
436 +Applying package: %1!ls!, target: %5!ls!, action: %2!hs!, path: %3!ls!, arguments: '%4!ls!'
437 +.
438 +
439 +MessageId=307
440 +Severity=Warning
441 +SymbolicName=MSG_ATTEMPTED_UNINSTALL_ABSENT_PACKAGE
442 +Language=English
443 +Attempted to uninstall absent package: %1!ls!. Continuing...
444 +.
445 +
446 +MessageId=308
447 +Severity=Warning
448 +SymbolicName=MSG_FAILED_PAUSE_AU
449 +Language=English
450 +Automatic updates could not be paused due to error: 0x%1!x!. Continuing...
451 +.
452 +
453 +MessageId=309
454 +Severity=Warning
455 +SymbolicName=MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE
456 +Language=English
457 +Skipping apply of package: %1!ls! due to cache error: 0x%2!x!. Continuing...
458 +.
459 +
460 +MessageId=310
461 +Severity=Error
462 +SymbolicName=MSG_FAILED_VERIFY_PAYLOAD
463 +Language=English
464 +Failed to verify payload: %2!ls! at path: %3!ls!, error: %1!ls!. Deleting file.
465 +.
466 +
467 +MessageId=311
468 +Severity=Error
469 +SymbolicName=MSG_FAILED_ACQUIRE_CONTAINER
470 +Language=English
471 +Failed to acquire container: %2!ls! to working path: %3!ls!, error: %1!ls!.
472 +.
473 +
474 +MessageId=312
475 +Severity=Error
476 +SymbolicName=MSG_FAILED_EXTRACT_CONTAINER
477 +Language=English
478 +Failed to extract payloads from container: %2!ls! to working path: %3!ls!, error: %1!ls!.
479 +.
480 +
481 +MessageId=313
482 +Severity=Error
483 +SymbolicName=MSG_FAILED_ACQUIRE_PAYLOAD
484 +Language=English
485 +Failed to acquire payload: %2!ls! to working path: %3!ls!, error: %1!ls!.
486 +.
487 +
488 +MessageId=314
489 +Severity=Error
490 +SymbolicName=MSG_FAILED_CACHE_PAYLOAD
491 +Language=English
492 +Failed to cache payload: %2!ls! from working path: %3!ls!, error: %1!ls!.
493 +.
494 +
495 +MessageId=315
496 +Severity=Error
497 +SymbolicName=MSG_FAILED_LAYOUT_BUNDLE
498 +Language=English
499 +Failed to layout bundle: %2!ls! to layout directory: %3!ls!, error: %1!ls!.
500 +.
501 +
502 +MessageId=316
503 +Severity=Error
504 +SymbolicName=MSG_FAILED_LAYOUT_CONTAINER
505 +Language=English
506 +Failed to layout container: %2!ls! to layout directory: %3!ls!, error: %1!ls!.
507 +.
508 +
509 +
510 +MessageId=317
511 +Severity=Error
512 +SymbolicName=MSG_FAILED_LAYOUT_PAYLOAD
513 +Language=English
514 +Failed to layout payload: %2!ls! to layout directory: %3!ls!, error: %1!ls!.
515 +.
516 +
517 +MessageId=318
518 +Severity=Success
519 +SymbolicName=MSG_ROLLBACK_PACKAGE_SKIPPED
520 +Language=English
521 +Skipped rollback of package: %1!ls!, action: %2!hs!, already: %3!hs!
522 +.
523 +
524 +MessageId=319
525 +Severity=Success
526 +SymbolicName=MSG_APPLY_COMPLETED_PACKAGE
527 +Language=English
528 +Applied %1!hs! package: %2!ls!, result: 0x%3!x!, restart: %4!hs!
529 +.
530 +
531 +MessageId=320
532 +Severity=Success
533 +SymbolicName=MSG_DEPENDENCY_BUNDLE_REGISTER
534 +Language=English
535 +Registering bundle dependency provider: %1!ls!, version: %2!ls!
536 +.
537 +
538 +MessageId=321
539 +Severity=Warning
540 +SymbolicName=MSG_DEPENDENCY_PACKAGE_SKIP_NOPROVIDERS
541 +Language=English
542 +Skipping dependency registration on package with no dependency providers: %1!ls!
543 +.
544 +
545 +MessageId=322
546 +Severity=Warning
547 +SymbolicName=MSG_DEPENDENCY_PACKAGE_SKIP_WRONGSCOPE
548 +Language=English
549 +Skipping cross-scope dependency registration on package: %1!ls!, bundle scope: %2!hs!, package scope: %3!hs!
550 +.
551 +
552 +MessageId=323
553 +Severity=Success
554 +SymbolicName=MSG_DEPENDENCY_PACKAGE_REGISTER
555 +Language=English
556 +Registering package dependency provider: %1!ls!, version: %2!ls!, package: %3!ls!
557 +.
558 +
559 +MessageId=324
560 +Severity=Warning
561 +SymbolicName=MSG_DEPENDENCY_PACKAGE_SKIP_MISSING
562 +Language=English
563 +Skipping dependency registration on missing package provider: %1!ls!, package: %2!ls!
564 +.
565 +
566 +MessageId=325
567 +Severity=Success
568 +SymbolicName=MSG_DEPENDENCY_PACKAGE_REGISTER_DEPENDENCY
569 +Language=English
570 +Registering dependency: %1!ls! on package provider: %2!ls!, package: %3!ls!
571 +.
572 +
573 +MessageId=326
574 +Severity=Success
575 +SymbolicName=MSG_DEPENDENCY_PACKAGE_UNREGISTERED_DEPENDENCY
576 +Language=English
577 +Removed dependency: %1!ls! on package provider: %2!ls!, package %3!ls!
578 +.
579 +
580 +MessageId=327
581 +Severity=Warning
582 +SymbolicName=MSG_DEPENDENCY_PACKAGE_HASDEPENDENTS
583 +Language=English
584 +Will not uninstall package: %1!ls!, found dependents: %2!d!
585 +.
586 +
587 +MessageId=328
588 +Severity=Warning
589 +SymbolicName=MSG_DEPENDENCY_PACKAGE_DEPENDENT
590 +Language=English
591 +Found dependent: %1!ls!, name: %2!ls!
592 +.
593 +
594 +MessageId=329
595 +Severity=Success
596 +SymbolicName=MSG_DEPENDENCY_PACKAGE_UNREGISTERED
597 +Language=English
598 +Removed package dependency provider: %1!ls!, package: %2!ls!
599 +.
600 +
601 +MessageId=330
602 +Severity=Success
603 +SymbolicName=MSG_DEPENDENCY_BUNDLE_UNREGISTERED
604 +Language=English
605 +Removed bundle dependency provider: %1!ls!
606 +.
607 +
608 +MessageId=331
609 +Severity=Warning
610 +SymbolicName=MSG_DEPENDENCY_PACKAGE_UNREGISTERED_DEPENDENCY_FAILED
611 +Language=English
612 +Could not remove dependency: %1!ls! on package provider: %2!ls!, package %3!ls!, error: 0x%4!x!
613 +.
614 +
615 +MessageId=332
616 +Severity=Warning
617 +SymbolicName=MSG_DEPENDENCY_PACKAGE_UNREGISTERED_FAILED
618 +Language=English
619 +Could not remove package dependency provider: %1!ls!, package: %2!ls!, error: 0x%3!x!
620 +.
621 +
622 +MessageId=333
623 +Severity=Warning
624 +SymbolicName=MSG_DEPENDENCY_BUNDLE_UNREGISTERED_FAILED
625 +Language=English
626 +Could not remove bundle dependency provider: %1!ls!, error: 0x%2!x!
627 +.
628 +
629 +MessageId=335
630 +Severity=Success
631 +SymbolicName=MSG_ACQUIRE_BUNDLE_PAYLOAD
632 +Language=English
633 +Acquiring bundle payload: %2!ls!, %3!hs! from: %4!ls!
634 +.
635 +
636 +MessageId=336
637 +Severity=Success
638 +SymbolicName=MSG_ACQUIRE_CONTAINER
639 +Language=English
640 +Acquiring container: %1!ls!, %3!hs! from: %4!ls!
641 +.
642 +
643 +MessageId=337
644 +Severity=Success
645 +SymbolicName=MSG_ACQUIRE_CONTAINER_PAYLOAD
646 +Language=English
647 +Acquiring container: %1!ls!, payload: %2!ls!, %3!hs! from: %4!ls!
648 +.
649 +
650 +MessageId=338
651 +Severity=Success
652 +SymbolicName=MSG_ACQUIRE_PACKAGE_PAYLOAD
653 +Language=English
654 +Acquiring package: %1!ls!, payload: %2!ls!, %3!hs! from: %4!ls!
655 +.
656 +
657 +MessageId=340
658 +Severity=Warning
659 +SymbolicName=MSG_PROMPT_BUNDLE_PAYLOAD_SOURCE
660 +Language=English
661 +Prompt for source of bundle payload: %2!ls!, path: %3!ls!
662 +.
663 +
664 +MessageId=341
665 +Severity=Warning
666 +SymbolicName=MSG_PROMPT_CONTAINER_SOURCE
667 +Language=English
668 +Prompt for source of container: %1!ls!, path: %3!ls!
669 +.
670 +
671 +MessageId=342
672 +Severity=Warning
673 +SymbolicName=MSG_PROMPT_CONTAINER_PAYLOAD_SOURCE
674 +Language=English
675 +Prompt for source of container: %1!ls!, payload: %2!ls!, path: %3!ls!
676 +.
677 +
678 +MessageId=343
679 +Severity=Warning
680 +SymbolicName=MSG_PROMPT_PACKAGE_PAYLOAD_SOURCE
681 +Language=English
682 +Prompt for source of package: %1!ls!, payload: %2!ls!, path: %3!ls!
683 +.
684 +
685 +MessageId=348
686 +Severity=Warning
687 +SymbolicName=MSG_APPLY_RETRYING_PACKAGE
688 +Language=English
689 +Application requested retry of package: %1!ls!, encountered error: 0x%2!x!. Retrying...
690 +.
691 +
692 +MessageId=349
693 +Severity=Warning
694 +SymbolicName=MSG_APPLY_RETRYING_PAYLOAD
695 +Language=English
696 +Application requested retry of payload: %2!ls!, encountered error: %1!ls!. Retrying...
697 +.
698 +
699 +MessageId=350
700 +Severity=Warning
701 +SymbolicName=MSG_APPLY_CONTINUING_NONVITAL_PACKAGE
702 +Language=English
703 +Applied non-vital package: %1!ls!, encountered error: 0x%2!x!. Continuing...
704 +.
705 +
706 +MessageId=351
707 +Severity=Success
708 +SymbolicName=MSG_UNCACHE_PACKAGE
709 +Language=English
710 +Removing cached package: %1!ls!, from path: %2!ls!
711 +.
712 +
713 +MessageId=352
714 +Severity=Success
715 +SymbolicName=MSG_UNCACHE_BUNDLE
716 +Language=English
717 +Removing cached bundle: %1!ls!, from path: %2!ls!
718 +.
719 +
720 +MessageId=353
721 +Severity=Warning
722 +SymbolicName=MSG_UNABLE_UNCACHE_PACKAGE
723 +Language=English
724 +Unable to remove cached package: %1!ls!, from path: %2!ls!, reason: 0x%3!x!. Continuing...
725 +.
726 +
727 +MessageId=354
728 +Severity=Warning
729 +SymbolicName=MSG_UNABLE_UNCACHE_BUNDLE
730 +Language=English
731 +Unable to remove cached bundle: %1!ls!, from path: %2!ls!, reason: 0x%3!x!. Continuing...
732 +.
733 +
734 +MessageId=355
735 +Severity=Warning
736 +SymbolicName=MSG_SOURCELIST_REGISTER
737 +Language=English
738 +Unable to register source directory: %1!ls!, product: %2!ls!, reason: 0x%3!x!. Continuing...
739 +.
740 +
741 +MessageId=358
742 +Severity=Success
743 +SymbolicName=MSG_PAUSE_AU_STARTING
744 +Language=English
745 +Pausing automatic updates.
746 +.
747 +
748 +MessageId=359
749 +Severity=Success
750 +SymbolicName=MSG_PAUSE_AU_SUCCEEDED
751 +Language=English
752 +Paused automatic updates.
753 +.
754 +
755 +MessageId=360
756 +Severity=Success
757 +SymbolicName=MSG_SYSTEM_RESTORE_POINT_STARTING
758 +Language=English
759 +Creating a system restore point.
760 +.
761 +
762 +MessageId=361
763 +Severity=Success
764 +SymbolicName=MSG_SYSTEM_RESTORE_POINT_SUCCEEDED
765 +Language=English
766 +Created a system restore point.
767 +.
768 +
769 +MessageId=362
770 +Severity=Success
771 +SymbolicName=MSG_SYSTEM_RESTORE_POINT_DISABLED
772 +Language=English
773 +System restore disabled, system restore point not created.
774 +.
775 +
776 +MessageId=363
777 +Severity=Warning
778 +SymbolicName=MSG_SYSTEM_RESTORE_POINT_FAILED
779 +Language=English
780 +Could not create system restore point, error: 0x%1!x!. Continuing...
781 +.
782 +
783 +MessageId=370
784 +Severity=Success
785 +SymbolicName=MSG_SESSION_BEGIN
786 +Language=English
787 +Session begin, registration key: %1!ls!, options: 0x%2!x!, disable resume: %3!hs!
788 +.
789 +
790 +MessageId=371
791 +Severity=Success
792 +SymbolicName=MSG_SESSION_UPDATE
793 +Language=English
794 +Updating session, registration key: %1!ls!, resume: %2!hs!, restart initiated: %3!hs!, disable resume: %4!hs!
795 +.
796 +
797 +MessageId=372
798 +Severity=Success
799 +SymbolicName=MSG_SESSION_END
800 +Language=English
801 +Session end, registration key: %1!ls!, resume: %2!hs!, restart: %3!hs!, disable resume: %4!hs!
802 +.
803 +
804 +MessageId=380
805 +Severity=Warning
806 +SymbolicName=MSG_APPLY_SKIPPED
807 +Language=English
808 +Apply skipped, no planned actions
809 +.
810 +
811 +MessageId=381
812 +Severity=Warning
813 +SymbolicName=MSG_APPLY_CANCEL_IGNORED_DURING_ROLLBACK
814 +Language=English
815 +Ignoring application request to cancel from %1!ls! during rollback.
816 +.
817 +
818 +MessageId=399
819 +Severity=Success
820 +SymbolicName=MSG_APPLY_COMPLETE
821 +Language=English
822 +Apply complete, result: 0x%1!x!, restart: %2!hs!, ba requested restart: %3!hs!
823 +.
824 +
825 +MessageId=400
826 +Severity=Success
827 +SymbolicName=MSG_SYSTEM_SHUTDOWN
828 +Language=English
829 +Received system request to shut down the process: critical: %1!hs!, elevated: %2!hs!, allowed: %3!hs!
830 +.
831 +
832 +MessageId=410
833 +Severity=Success
834 +SymbolicName=MSG_VARIABLE_DUMP
835 +Language=English
836 +Variable: %1!ls!
837 +.
838 +
839 +MessageId=420
840 +Severity=Success
841 +SymbolicName=MSG_RESUME_AU_STARTING
842 +Language=English
843 +Resuming automatic updates.
844 +.
845 +
846 +MessageId=421
847 +Severity=Success
848 +SymbolicName=MSG_RESUME_AU_SUCCEEDED
849 +Language=English
850 +Resumed automatic updates.
851 +.
852 +
853 +MessageId=500
854 +Severity=Success
855 +SymbolicName=MSG_QUIT
856 +Language=English
857 +Shutting down, exit code: 0x%1!x!
858 +.
859 +
860 +MessageId=501
861 +Severity=Warning
862 +SymbolicName=MSG_STATE_NOT_SAVED
863 +Language=English
864 +The state file could not be saved, error: 0x%1!x!. Continuing...
865 +.
866 +
867 +MessageId=600
868 +Severity=Success
869 +SymbolicName=MSG_LAUNCH_APPROVED_EXE_BEGIN
870 +Language=English
871 +LaunchApprovedExe begin, id: %1!ls!
872 +.
873 +
874 +MessageId=601
875 +Severity=Success
876 +SymbolicName=MSG_LAUNCH_APPROVED_EXE_SEARCH
877 +Language=English
878 +Searching registry for approved exe path, key: %1!ls!, value: '%2!ls!', win64: %3!ls!
879 +.
880 +
881 +MessageId=602
882 +Severity=Success
883 +SymbolicName=MSG_LAUNCHING_APPROVED_EXE
884 +Language=English
885 +Launching approved exe, path: '%1!ls!', 'command: %2!ls!'
886 +.
887 +
888 +MessageId=699
889 +Severity=Success
890 +SymbolicName=MSG_LAUNCH_APPROVED_EXE_COMPLETE
891 +Language=English
892 +LaunchApprovedExe complete, result: 0x%1!x!, processId: %2!lu!
893 +.
894 +
895 +MessageId=700
896 +Severity=Success
897 +SymbolicName=MSG_MSI_PROPERTY_CONDITION_FAILED
898 +Language=English
899 +Skipping MSI property '%1!ls!' because condition '%2!ls!' evaluates to %3!hs!.
900 +.
901 +
src/engine/exeengine.cpp new
+820
@@ -0,0 +1,820 @@
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 +// internal function declarations
7 +
8 +static HRESULT HandleExitCode(
9 + __in BURN_PACKAGE* pPackage,
10 + __in DWORD dwExitCode,
11 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart
12 + );
13 +static HRESULT ParseCommandLineArgumentsFromXml(
14 + __in IXMLDOMNode* pixnExePackage,
15 + __in BURN_PACKAGE* pPackage
16 + );
17 +static HRESULT ParseExitCodesFromXml(
18 + __in IXMLDOMNode* pixnExePackage,
19 + __in BURN_PACKAGE* pPackage
20 + );
21 +
22 +
23 +// function definitions
24 +
25 +extern "C" HRESULT ExeEngineParsePackageFromXml(
26 + __in IXMLDOMNode* pixnExePackage,
27 + __in BURN_PACKAGE* pPackage
28 + )
29 +{
30 + HRESULT hr = S_OK;
31 + IXMLDOMNodeList* pixnNodes = NULL;
32 + IXMLDOMNode* pixnNode = NULL;
33 + LPWSTR scz = NULL;
34 +
35 + // @DetectCondition
36 + hr = XmlGetAttributeEx(pixnExePackage, L"DetectCondition", &pPackage->Exe.sczDetectCondition);
37 + ExitOnFailure(hr, "Failed to get @DetectCondition.");
38 +
39 + // @InstallArguments
40 + hr = XmlGetAttributeEx(pixnExePackage, L"InstallArguments", &pPackage->Exe.sczInstallArguments);
41 + ExitOnFailure(hr, "Failed to get @InstallArguments.");
42 +
43 + // @UninstallArguments
44 + hr = XmlGetAttributeEx(pixnExePackage, L"UninstallArguments", &pPackage->Exe.sczUninstallArguments);
45 + ExitOnFailure(hr, "Failed to get @UninstallArguments.");
46 +
47 + // @RepairArguments
48 + hr = XmlGetAttributeEx(pixnExePackage, L"RepairArguments", &pPackage->Exe.sczRepairArguments);
49 + ExitOnFailure(hr, "Failed to get @RepairArguments.");
50 +
51 + // @Repairable
52 + hr = XmlGetYesNoAttribute(pixnExePackage, L"Repairable", &pPackage->Exe.fRepairable);
53 + if (E_NOTFOUND != hr)
54 + {
55 + ExitOnFailure(hr, "Failed to get @Repairable.");
56 + }
57 +
58 + // @Protocol
59 + hr = XmlGetAttributeEx(pixnExePackage, L"Protocol", &scz);
60 + if (SUCCEEDED(hr))
61 + {
62 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"burn", -1))
63 + {
64 + pPackage->Exe.protocol = BURN_EXE_PROTOCOL_TYPE_BURN;
65 + }
66 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"netfx4", -1))
67 + {
68 + pPackage->Exe.protocol = BURN_EXE_PROTOCOL_TYPE_NETFX4;
69 + }
70 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"none", -1))
71 + {
72 + pPackage->Exe.protocol = BURN_EXE_PROTOCOL_TYPE_NONE;
73 + }
74 + else
75 + {
76 + hr = E_UNEXPECTED;
77 + ExitOnFailure(hr, "Invalid protocol type: %ls", scz);
78 + }
79 + }
80 + else if (E_NOTFOUND != hr)
81 + {
82 + ExitOnFailure(hr, "Failed to get @Protocol.");
83 + }
84 +
85 + hr = ParseExitCodesFromXml(pixnExePackage, pPackage);
86 + ExitOnFailure(hr, "Failed to parse exit codes.");
87 +
88 + hr = ParseCommandLineArgumentsFromXml(pixnExePackage, pPackage);
89 + ExitOnFailure(hr, "Failed to parse command lines.");
90 +
91 +LExit:
92 + ReleaseObject(pixnNodes);
93 + ReleaseObject(pixnNode);
94 + ReleaseStr(scz);
95 +
96 + return hr;
97 +}
98 +
99 +extern "C" void ExeEnginePackageUninitialize(
100 + __in BURN_PACKAGE* pPackage
101 + )
102 +{
103 + ReleaseStr(pPackage->Exe.sczDetectCondition);
104 + ReleaseStr(pPackage->Exe.sczInstallArguments);
105 + ReleaseStr(pPackage->Exe.sczRepairArguments);
106 + ReleaseStr(pPackage->Exe.sczUninstallArguments);
107 + ReleaseStr(pPackage->Exe.sczIgnoreDependencies);
108 + ReleaseStr(pPackage->Exe.sczAncestors);
109 + //ReleaseStr(pPackage->Exe.sczProgressSwitch);
110 + ReleaseMem(pPackage->Exe.rgExitCodes);
111 +
112 + // free command-line arguments
113 + if (pPackage->Exe.rgCommandLineArguments)
114 + {
115 + for (DWORD i = 0; i < pPackage->Exe.cCommandLineArguments; ++i)
116 + {
117 + BURN_EXE_COMMAND_LINE_ARGUMENT* pCommandLineArgument = &pPackage->Exe.rgCommandLineArguments[i];
118 + ReleaseStr(pCommandLineArgument->sczInstallArgument);
119 + ReleaseStr(pCommandLineArgument->sczUninstallArgument);
120 + ReleaseStr(pCommandLineArgument->sczRepairArgument);
121 + ReleaseStr(pCommandLineArgument->sczCondition);
122 + }
123 + MemFree(pPackage->Exe.rgCommandLineArguments);
124 + }
125 +
126 + // clear struct
127 + memset(&pPackage->Exe, 0, sizeof(pPackage->Exe));
128 +}
129 +
130 +extern "C" HRESULT ExeEngineDetectPackage(
131 + __in BURN_PACKAGE* pPackage,
132 + __in BURN_VARIABLES* pVariables
133 + )
134 +{
135 + HRESULT hr = S_OK;
136 + BOOL fDetected = FALSE;
137 +
138 + // evaluate detect condition
139 + if (pPackage->Exe.sczDetectCondition && *pPackage->Exe.sczDetectCondition)
140 + {
141 + hr = ConditionEvaluate(pVariables, pPackage->Exe.sczDetectCondition, &fDetected);
142 + ExitOnFailure(hr, "Failed to evaluate executable package detect condition.");
143 + }
144 +
145 + // update detect state
146 + pPackage->currentState = fDetected ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
147 +
148 +LExit:
149 + return hr;
150 +}
151 +
152 +//
153 +// PlanCalculate - calculates the execute and rollback state for the requested package state.
154 +//
155 +extern "C" HRESULT ExeEnginePlanCalculatePackage(
156 + __in BURN_PACKAGE* pPackage,
157 + __out_opt BOOL* pfBARequestedCache
158 + )
159 +{
160 + HRESULT hr = S_OK;
161 + //BOOL fCondition = FALSE;
162 + //BOOTSTRAPPER_PACKAGE_STATE expected = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN;
163 + BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE;
164 + BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
165 + BOOL fBARequestedCache = FALSE;
166 +
167 + //// evaluate rollback install condition
168 + //if (pPackage->sczRollbackInstallCondition)
169 + //{
170 + // hr = ConditionEvaluate(pVariables, pPackage->sczRollbackInstallCondition, &fCondition);
171 + // ExitOnFailure(hr, "Failed to evaluate rollback install condition.");
172 +
173 + // expected = fCondition ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
174 + //}
175 +
176 + // execute action
177 + switch (pPackage->currentState)
178 + {
179 + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
180 + switch (pPackage->requested)
181 + {
182 + case BOOTSTRAPPER_REQUEST_STATE_PRESENT:
183 + execute = pPackage->Exe.fPseudoBundle ? BOOTSTRAPPER_ACTION_STATE_INSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
184 + break;
185 + case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
186 + execute = pPackage->Exe.fRepairable ? BOOTSTRAPPER_ACTION_STATE_REPAIR : BOOTSTRAPPER_ACTION_STATE_NONE;
187 + break;
188 + case BOOTSTRAPPER_REQUEST_STATE_ABSENT: __fallthrough;
189 + case BOOTSTRAPPER_REQUEST_STATE_CACHE:
190 + execute = pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
191 + break;
192 + case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT:
193 + execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL;
194 + break;
195 + default:
196 + execute = BOOTSTRAPPER_ACTION_STATE_NONE;
197 + break;
198 + }
199 + break;
200 +
201 + case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
202 + switch (pPackage->requested)
203 + {
204 + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
205 + case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
206 + execute = BOOTSTRAPPER_ACTION_STATE_INSTALL;
207 + break;
208 + case BOOTSTRAPPER_REQUEST_STATE_CACHE:
209 + execute = BOOTSTRAPPER_ACTION_STATE_NONE;
210 + fBARequestedCache = TRUE;
211 + break;
212 + default:
213 + execute = BOOTSTRAPPER_ACTION_STATE_NONE;
214 + break;
215 + }
216 + break;
217 +
218 + default:
219 + hr = E_INVALIDARG;
220 + ExitOnRootFailure(hr, "Invalid package current state: %d.", pPackage->currentState);
221 + }
222 +
223 + // Calculate the rollback action if there is an execute action.
224 + if (BOOTSTRAPPER_ACTION_STATE_NONE != execute)
225 + {
226 + switch (BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN != pPackage->expected ? pPackage->expected : pPackage->currentState)
227 + {
228 + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
229 + switch (pPackage->requested)
230 + {
231 + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
232 + case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
233 + rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
234 + break;
235 + case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough;
236 + case BOOTSTRAPPER_REQUEST_STATE_ABSENT:
237 + rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL;
238 + break;
239 + default:
240 + rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
241 + break;
242 + }
243 + break;
244 +
245 + case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
246 + switch (pPackage->requested)
247 + {
248 + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
249 + case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
250 + rollback = pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
251 + break;
252 + case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough;
253 + case BOOTSTRAPPER_REQUEST_STATE_ABSENT:
254 + rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
255 + break;
256 + default:
257 + rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
258 + break;
259 + }
260 + break;
261 +
262 + default:
263 + hr = E_INVALIDARG;
264 + ExitOnRootFailure(hr, "Invalid package expected state.");
265 + }
266 + }
267 +
268 + // return values
269 + pPackage->execute = execute;
270 + pPackage->rollback = rollback;
271 +
272 + if (pfBARequestedCache)
273 + {
274 + *pfBARequestedCache = fBARequestedCache;
275 + }
276 +
277 +LExit:
278 + return hr;
279 +}
280 +
281 +//
282 +// PlanAdd - adds the calculated execute and rollback actions for the package.
283 +//
284 +extern "C" HRESULT ExeEnginePlanAddPackage(
285 + __in_opt DWORD *pdwInsertSequence,
286 + __in BURN_PACKAGE* pPackage,
287 + __in BURN_PLAN* pPlan,
288 + __in BURN_LOGGING* pLog,
289 + __in BURN_VARIABLES* pVariables,
290 + __in_opt HANDLE hCacheEvent,
291 + __in BOOL fPlanPackageCacheRollback
292 + )
293 +{
294 + HRESULT hr = S_OK;
295 + BURN_EXECUTE_ACTION* pAction = NULL;
296 +
297 + // add wait for cache
298 + if (hCacheEvent)
299 + {
300 + hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent, fPlanPackageCacheRollback);
301 + ExitOnFailure(hr, "Failed to plan package cache syncpoint");
302 + }
303 +
304 + hr = DependencyPlanPackage(pdwInsertSequence, pPackage, pPlan);
305 + ExitOnFailure(hr, "Failed to plan package dependency actions.");
306 +
307 + // add execute action
308 + if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute)
309 + {
310 + if (NULL != pdwInsertSequence)
311 + {
312 + hr = PlanInsertExecuteAction(*pdwInsertSequence, pPlan, &pAction);
313 + ExitOnFailure(hr, "Failed to insert execute action.");
314 + }
315 + else
316 + {
317 + hr = PlanAppendExecuteAction(pPlan, &pAction);
318 + ExitOnFailure(hr, "Failed to append execute action.");
319 + }
320 +
321 + pAction->type = BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE;
322 + pAction->exePackage.pPackage = pPackage;
323 + pAction->exePackage.fFireAndForget = (BOOTSTRAPPER_ACTION_UPDATE_REPLACE == pPlan->action);
324 + pAction->exePackage.action = pPackage->execute;
325 +
326 + if (pPackage->Exe.sczIgnoreDependencies)
327 + {
328 + hr = StrAllocString(&pAction->exePackage.sczIgnoreDependencies, pPackage->Exe.sczIgnoreDependencies, 0);
329 + ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore.");
330 + }
331 +
332 + if (pPackage->Exe.sczAncestors)
333 + {
334 + hr = StrAllocString(&pAction->exePackage.sczAncestors, pPackage->Exe.sczAncestors, 0);
335 + ExitOnFailure(hr, "Failed to allocate the list of ancestors.");
336 + }
337 +
338 + LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, NULL); // ignore errors.
339 + }
340 +
341 + // add rollback action
342 + if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback)
343 + {
344 + hr = PlanAppendRollbackAction(pPlan, &pAction);
345 + ExitOnFailure(hr, "Failed to append rollback action.");
346 +
347 + pAction->type = BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE;
348 + pAction->exePackage.pPackage = pPackage;
349 + pAction->exePackage.action = pPackage->rollback;
350 +
351 + if (pPackage->Exe.sczIgnoreDependencies)
352 + {
353 + hr = StrAllocString(&pAction->exePackage.sczIgnoreDependencies, pPackage->Exe.sczIgnoreDependencies, 0);
354 + ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore.");
355 + }
356 +
357 + if (pPackage->Exe.sczAncestors)
358 + {
359 + hr = StrAllocString(&pAction->exePackage.sczAncestors, pPackage->Exe.sczAncestors, 0);
360 + ExitOnFailure(hr, "Failed to allocate the list of ancestors.");
361 + }
362 +
363 + LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, NULL); // ignore errors.
364 + }
365 +
366 +LExit:
367 + return hr;
368 +}
369 +
370 +extern "C" HRESULT ExeEngineExecutePackage(
371 + __in BURN_EXECUTE_ACTION* pExecuteAction,
372 + __in BURN_VARIABLES* pVariables,
373 + __in BOOL fRollback,
374 + __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
375 + __in LPVOID pvContext,
376 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart
377 + )
378 +{
379 + HRESULT hr = S_OK;
380 + WCHAR wzCurrentDirectory[MAX_PATH] = { };
381 + BOOL fChangedCurrentDirectory = FALSE;
382 + int nResult = IDNOACTION;
383 + LPCWSTR wzArguments = NULL;
384 + LPWSTR sczArguments = NULL;
385 + LPWSTR sczArgumentsFormatted = NULL;
386 + LPWSTR sczArgumentsObfuscated = NULL;
387 + LPWSTR sczCachedDirectory = NULL;
388 + LPWSTR sczExecutablePath = NULL;
389 + LPWSTR sczCommand = NULL;
390 + LPWSTR sczCommandObfuscated = NULL;
391 + HANDLE hExecutableFile = INVALID_HANDLE_VALUE;
392 + STARTUPINFOW si = { };
393 + PROCESS_INFORMATION pi = { };
394 + DWORD dwExitCode = 0;
395 + GENERIC_EXECUTE_MESSAGE message = { };
396 +
397 + // get cached executable path
398 + hr = CacheGetCompletedPath(pExecuteAction->exePackage.pPackage->fPerMachine, pExecuteAction->exePackage.pPackage->sczCacheId, &sczCachedDirectory);
399 + ExitOnFailure(hr, "Failed to get cached path for package: %ls", pExecuteAction->exePackage.pPackage->sczId);
400 +
401 + // Best effort to set the execute package cache folder and action variables.
402 + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE);
403 + VariableSetNumeric(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, pExecuteAction->exePackage.action, TRUE);
404 +
405 + hr = PathConcat(sczCachedDirectory, pExecuteAction->exePackage.pPackage->rgPayloads[0].pPayload->sczFilePath, &sczExecutablePath);
406 + ExitOnFailure(hr, "Failed to build executable path.");
407 +
408 + // pick arguments
409 + switch (pExecuteAction->exePackage.action)
410 + {
411 + case BOOTSTRAPPER_ACTION_STATE_INSTALL:
412 + wzArguments = pExecuteAction->exePackage.pPackage->Exe.sczInstallArguments;
413 + break;
414 +
415 + case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
416 + wzArguments = pExecuteAction->exePackage.pPackage->Exe.sczUninstallArguments;
417 + break;
418 +
419 + case BOOTSTRAPPER_ACTION_STATE_REPAIR:
420 + wzArguments = pExecuteAction->exePackage.pPackage->Exe.sczRepairArguments;
421 + break;
422 +
423 + default:
424 + hr = E_INVALIDARG;
425 + ExitOnFailure(hr, "Invalid Exe package action: %d.", pExecuteAction->exePackage.action);
426 + }
427 +
428 + // now add optional arguments
429 + hr = StrAllocString(&sczArguments, wzArguments && *wzArguments ? wzArguments : L"", 0);
430 + ExitOnFailure(hr, "Failed to copy package arguments.");
431 +
432 + for (DWORD i = 0; i < pExecuteAction->exePackage.pPackage->Exe.cCommandLineArguments; ++i)
433 + {
434 + BURN_EXE_COMMAND_LINE_ARGUMENT* commandLineArgument = &pExecuteAction->exePackage.pPackage->Exe.rgCommandLineArguments[i];
435 + BOOL fCondition = FALSE;
436 +
437 + hr = ConditionEvaluate(pVariables, commandLineArgument->sczCondition, &fCondition);
438 + ExitOnFailure(hr, "Failed to evaluate executable package command-line condition.");
439 +
440 + if (fCondition)
441 + {
442 + hr = StrAllocConcat(&sczArguments, L" ", 0);
443 + ExitOnFailure(hr, "Failed to separate command-line arguments.");
444 +
445 + switch (pExecuteAction->exePackage.action)
446 + {
447 + case BOOTSTRAPPER_ACTION_STATE_INSTALL:
448 + hr = StrAllocConcat(&sczArguments, commandLineArgument->sczInstallArgument, 0);
449 + ExitOnFailure(hr, "Failed to get command-line argument for install.");
450 + break;
451 +
452 + case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
453 + hr = StrAllocConcat(&sczArguments, commandLineArgument->sczUninstallArgument, 0);
454 + ExitOnFailure(hr, "Failed to get command-line argument for uninstall.");
455 + break;
456 +
457 + case BOOTSTRAPPER_ACTION_STATE_REPAIR:
458 + hr = StrAllocConcat(&sczArguments, commandLineArgument->sczRepairArgument, 0);
459 + ExitOnFailure(hr, "Failed to get command-line argument for repair.");
460 + break;
461 +
462 + default:
463 + hr = E_INVALIDARG;
464 + ExitOnFailure(hr, "Invalid Exe package action: %d.", pExecuteAction->exePackage.action);
465 + }
466 + }
467 + }
468 +
469 + // build command
470 + if (0 < lstrlenW(sczArguments))
471 + {
472 + hr = VariableFormatString(pVariables, sczArguments, &sczArgumentsFormatted, NULL);
473 + ExitOnFailure(hr, "Failed to format argument string.");
474 +
475 + hr = StrAllocFormattedSecure(&sczCommand, L"\"%ls\" %s", sczExecutablePath, sczArgumentsFormatted);
476 + ExitOnFailure(hr, "Failed to create executable command.");
477 +
478 + hr = VariableFormatStringObfuscated(pVariables, sczArguments, &sczArgumentsObfuscated, NULL);
479 + ExitOnFailure(hr, "Failed to format obfuscated argument string.");
480 +
481 + hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\" %s", sczExecutablePath, sczArgumentsObfuscated);
482 + }
483 + else
484 + {
485 + hr = StrAllocFormatted(&sczCommand, L"\"%ls\"", sczExecutablePath);
486 + ExitOnFailure(hr, "Failed to create executable command.");
487 +
488 + hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\"", sczExecutablePath);
489 + }
490 + ExitOnFailure(hr, "Failed to create obfuscated executable command.");
491 +
492 + if (pExecuteAction->exePackage.pPackage->Exe.fSupportsAncestors)
493 + {
494 + // Add the list of dependencies to ignore, if any, to the burn command line.
495 + if (pExecuteAction->exePackage.sczIgnoreDependencies && BURN_EXE_PROTOCOL_TYPE_BURN == pExecuteAction->exePackage.pPackage->Exe.protocol)
496 + {
497 + hr = StrAllocFormattedSecure(&sczCommand, L"%ls -%ls=%ls", sczCommand, BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, pExecuteAction->exePackage.sczIgnoreDependencies);
498 + ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the command line.");
499 +
500 + hr = StrAllocFormatted(&sczCommandObfuscated, L"%ls -%ls=%ls", sczCommandObfuscated, BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, pExecuteAction->exePackage.sczIgnoreDependencies);
501 + ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the obfuscated command line.");
502 + }
503 +
504 + // Add the list of ancestors, if any, to the burn command line.
505 + if (pExecuteAction->exePackage.sczAncestors)
506 + {
507 + hr = StrAllocFormattedSecure(&sczCommand, L"%ls -%ls=%ls", sczCommand, BURN_COMMANDLINE_SWITCH_ANCESTORS, pExecuteAction->exePackage.sczAncestors);
508 + ExitOnFailure(hr, "Failed to append the list of ancestors to the command line.");
509 +
510 + hr = StrAllocFormatted(&sczCommandObfuscated, L"%ls -%ls=%ls", sczCommandObfuscated, BURN_COMMANDLINE_SWITCH_ANCESTORS, pExecuteAction->exePackage.sczAncestors);
511 + ExitOnFailure(hr, "Failed to append the list of ancestors to the obfuscated command line.");
512 + }
513 + }
514 +
515 + if (BURN_EXE_PROTOCOL_TYPE_BURN == pExecuteAction->exePackage.pPackage->Exe.protocol)
516 + {
517 + hr = CoreAppendFileHandleSelfToCommandLine(sczExecutablePath, &hExecutableFile, &sczCommand, &sczCommandObfuscated);
518 + ExitOnFailure(hr, "Failed to append %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF);
519 + }
520 +
521 + // Log before we add the secret pipe name and client token for embedded processes.
522 + LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pExecuteAction->exePackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->exePackage.action), sczExecutablePath, sczCommandObfuscated);
523 +
524 + if (!pExecuteAction->exePackage.fFireAndForget && BURN_EXE_PROTOCOL_TYPE_BURN == pExecuteAction->exePackage.pPackage->Exe.protocol)
525 + {
526 + hr = EmbeddedRunBundle(sczExecutablePath, sczCommand, pfnGenericMessageHandler, pvContext, &dwExitCode);
527 + ExitOnFailure(hr, "Failed to run bundle as embedded from path: %ls", sczExecutablePath);
528 + }
529 + else if (!pExecuteAction->exePackage.fFireAndForget && BURN_EXE_PROTOCOL_TYPE_NETFX4 == pExecuteAction->exePackage.pPackage->Exe.protocol)
530 + {
531 + hr = NetFxRunChainer(sczExecutablePath, sczCommand, pfnGenericMessageHandler, pvContext, &dwExitCode);
532 + ExitOnFailure(hr, "Failed to run netfx chainer: %ls", sczExecutablePath);
533 + }
534 + else // create and wait for the executable process while sending fake progress to allow cancel.
535 + {
536 + // Make the cache location of the executable the current directory to help those executables
537 + // that expect stuff to be relative to them.
538 + if (::GetCurrentDirectoryW(countof(wzCurrentDirectory), wzCurrentDirectory))
539 + {
540 + fChangedCurrentDirectory = ::SetCurrentDirectoryW(sczCachedDirectory);
541 + }
542 +
543 + si.cb = sizeof(si); // TODO: hookup the stdin/stdout/stderr pipes for logging purposes?
544 + if (!::CreateProcessW(sczExecutablePath, sczCommand, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
545 + {
546 + ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", sczExecutablePath);
547 + }
548 +
549 + if (pExecuteAction->exePackage.fFireAndForget)
550 + {
551 + ::WaitForInputIdle(pi.hProcess, 5000);
552 + ExitFunction();
553 + }
554 +
555 + do
556 + {
557 + message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
558 + message.dwAllowedResults = MB_OKCANCEL;
559 + message.progress.dwPercentage = 50;
560 + nResult = pfnGenericMessageHandler(&message, pvContext);
561 + hr = (IDOK == nResult || IDNOACTION == nResult) ? S_OK : IDCANCEL == nResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE);
562 + ExitOnRootFailure(hr, "Bootstrapper application aborted during EXE progress.");
563 +
564 + hr = ProcWaitForCompletion(pi.hProcess, 500, &dwExitCode);
565 + if (HRESULT_FROM_WIN32(WAIT_TIMEOUT) != hr)
566 + {
567 + ExitOnFailure(hr, "Failed to wait for executable to complete: %ls", sczExecutablePath);
568 + }
569 + } while (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == hr);
570 + }
571 +
572 + hr = HandleExitCode(pExecuteAction->exePackage.pPackage, dwExitCode, pRestart);
573 + ExitOnRootFailure(hr, "Process returned error: 0x%x", dwExitCode);
574 +
575 +LExit:
576 + if (fChangedCurrentDirectory)
577 + {
578 + ::SetCurrentDirectoryW(wzCurrentDirectory);
579 + }
580 +
581 + StrSecureZeroFreeString(sczArguments);
582 + StrSecureZeroFreeString(sczArgumentsFormatted);
583 + ReleaseStr(sczArgumentsObfuscated);
584 + ReleaseStr(sczCachedDirectory);
585 + ReleaseStr(sczExecutablePath);
586 + StrSecureZeroFreeString(sczCommand);
587 + ReleaseStr(sczCommandObfuscated);
588 +
589 + ReleaseHandle(pi.hThread);
590 + ReleaseHandle(pi.hProcess);
591 + ReleaseFileHandle(hExecutableFile);
592 +
593 + // Best effort to clear the execute package cache folder and action variables.
594 + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE);
595 + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE);
596 +
597 + return hr;
598 +}
599 +
600 +
601 +// internal helper functions
602 +
603 +static HRESULT ParseExitCodesFromXml(
604 + __in IXMLDOMNode* pixnExePackage,
605 + __in BURN_PACKAGE* pPackage
606 + )
607 +{
608 + HRESULT hr = S_OK;
609 + IXMLDOMNodeList* pixnNodes = NULL;
610 + IXMLDOMNode* pixnNode = NULL;
611 + DWORD cNodes = 0;
612 + LPWSTR scz = NULL;
613 +
614 + // select exit code nodes
615 + hr = XmlSelectNodes(pixnExePackage, L"ExitCode", &pixnNodes);
616 + ExitOnFailure(hr, "Failed to select exit code nodes.");
617 +
618 + // get exit code node count
619 + hr = pixnNodes->get_length((long*) &cNodes);
620 + ExitOnFailure(hr, "Failed to get exit code node count.");
621 +
622 + if (cNodes)
623 + {
624 + // allocate memory for exit codes
625 + pPackage->Exe.rgExitCodes = (BURN_EXE_EXIT_CODE*) MemAlloc(sizeof(BURN_EXE_EXIT_CODE) * cNodes, TRUE);
626 + ExitOnNull(pPackage->Exe.rgExitCodes, hr, E_OUTOFMEMORY, "Failed to allocate memory for exit code structs.");
627 +
628 + pPackage->Exe.cExitCodes = cNodes;
629 +
630 + // parse package elements
631 + for (DWORD i = 0; i < cNodes; ++i)
632 + {
633 + BURN_EXE_EXIT_CODE* pExitCode = &pPackage->Exe.rgExitCodes[i];
634 +
635 + hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
636 + ExitOnFailure(hr, "Failed to get next node.");
637 +
638 + // @Type
639 + hr = XmlGetAttributeNumber(pixnNode, L"Type", (DWORD*)&pExitCode->type);
640 + ExitOnFailure(hr, "Failed to get @Type.");
641 +
642 + // @Code
643 + hr = XmlGetAttributeEx(pixnNode, L"Code", &scz);
644 + ExitOnFailure(hr, "Failed to get @Code.");
645 +
646 + if (L'*' == scz[0])
647 + {
648 + pExitCode->fWildcard = TRUE;
649 + }
650 + else
651 + {
652 + hr = StrStringToUInt32(scz, 0, (UINT*) &pExitCode->dwCode);
653 + ExitOnFailure(hr, "Failed to parse @Code value: %ls", scz);
654 + }
655 +
656 + // prepare next iteration
657 + ReleaseNullObject(pixnNode);
658 + }
659 + }
660 +
661 + hr = S_OK;
662 +
663 +LExit:
664 + ReleaseObject(pixnNodes);
665 + ReleaseObject(pixnNode);
666 + ReleaseStr(scz);
667 +
668 + return hr;
669 +}
670 +
671 +static HRESULT ParseCommandLineArgumentsFromXml(
672 + __in IXMLDOMNode* pixnExePackage,
673 + __in BURN_PACKAGE* pPackage
674 + )
675 +{
676 + HRESULT hr = S_OK;
677 + IXMLDOMNodeList* pixnNodes = NULL;
678 + IXMLDOMNode* pixnNode = NULL;
679 + DWORD cNodes = 0;
680 + LPWSTR scz = NULL;
681 +
682 + // Select command-line argument nodes.
683 + hr = XmlSelectNodes(pixnExePackage, L"CommandLine", &pixnNodes);
684 + ExitOnFailure(hr, "Failed to select command-line argument nodes.");
685 +
686 + // Get command-line argument node count.
687 + hr = pixnNodes->get_length((long*) &cNodes);
688 + ExitOnFailure(hr, "Failed to get command-line argument count.");
689 +
690 + if (cNodes)
691 + {
692 + pPackage->Exe.rgCommandLineArguments = (BURN_EXE_COMMAND_LINE_ARGUMENT*) MemAlloc(sizeof(BURN_EXE_COMMAND_LINE_ARGUMENT) * cNodes, TRUE);
693 + ExitOnNull(pPackage->Exe.rgCommandLineArguments, hr, E_OUTOFMEMORY, "Failed to allocate memory for command-line argument structs.");
694 +
695 + pPackage->Exe.cCommandLineArguments = cNodes;
696 +
697 + // Parse command-line argument elements.
698 + for (DWORD i = 0; i < cNodes; ++i)
699 + {
700 + BURN_EXE_COMMAND_LINE_ARGUMENT* pCommandLineArgument = &pPackage->Exe.rgCommandLineArguments[i];
701 +
702 + hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
703 + ExitOnFailure(hr, "Failed to get next command-line argument node.");
704 +
705 + // @InstallArgument
706 + hr = XmlGetAttributeEx(pixnNode, L"InstallArgument", &pCommandLineArgument->sczInstallArgument);
707 + ExitOnFailure(hr, "Failed to get @InstallArgument.");
708 +
709 + // @UninstallArgument
710 + hr = XmlGetAttributeEx(pixnNode, L"UninstallArgument", &pCommandLineArgument->sczUninstallArgument);
711 + ExitOnFailure(hr, "Failed to get @UninstallArgument.");
712 +
713 + // @RepairArgument
714 + hr = XmlGetAttributeEx(pixnNode, L"RepairArgument", &pCommandLineArgument->sczRepairArgument);
715 + ExitOnFailure(hr, "Failed to get @RepairArgument.");
716 +
717 + // @Condition
718 + hr = XmlGetAttributeEx(pixnNode, L"Condition", &pCommandLineArgument->sczCondition);
719 + ExitOnFailure(hr, "Failed to get @Condition.");
720 +
721 + // Prepare next iteration.
722 + ReleaseNullObject(pixnNode);
723 + }
724 + }
725 +
726 + hr = S_OK;
727 +
728 +LExit:
729 + ReleaseObject(pixnNodes);
730 + ReleaseObject(pixnNode);
731 + ReleaseStr(scz);
732 +
733 + return hr;
734 +}
735 +
736 +static HRESULT HandleExitCode(
737 + __in BURN_PACKAGE* pPackage,
738 + __in DWORD dwExitCode,
739 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart
740 + )
741 +{
742 + HRESULT hr = S_OK;
743 + BURN_EXE_EXIT_CODE_TYPE typeCode = BURN_EXE_EXIT_CODE_TYPE_NONE;
744 +
745 + for (DWORD i = 0; i < pPackage->Exe.cExitCodes; ++i)
746 + {
747 + BURN_EXE_EXIT_CODE* pExitCode = &pPackage->Exe.rgExitCodes[i];
748 +
749 + // If this is a wildcard, use the last one we come across.
750 + if (pExitCode->fWildcard)
751 + {
752 + typeCode = pExitCode->type;
753 + }
754 + else if (dwExitCode == pExitCode->dwCode) // If we have an exact match on the error code use that and stop looking.
755 + {
756 + typeCode = pExitCode->type;
757 + break;
758 + }
759 + }
760 +
761 + // If we didn't find a matching code then treat 0 as success, the standard restarts codes as restarts
762 + // and everything else as an error.
763 + if (BURN_EXE_EXIT_CODE_TYPE_NONE == typeCode)
764 + {
765 + if (0 == dwExitCode)
766 + {
767 + typeCode = BURN_EXE_EXIT_CODE_TYPE_SUCCESS;
768 + }
769 + else if (ERROR_SUCCESS_REBOOT_REQUIRED == dwExitCode ||
770 + HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED) == static_cast<HRESULT>(dwExitCode) ||
771 + ERROR_SUCCESS_RESTART_REQUIRED == dwExitCode ||
772 + HRESULT_FROM_WIN32(ERROR_SUCCESS_RESTART_REQUIRED) == static_cast<HRESULT>(dwExitCode))
773 + {
774 + typeCode = BURN_EXE_EXIT_CODE_TYPE_SCHEDULE_REBOOT;
775 + }
776 + else if (ERROR_SUCCESS_REBOOT_INITIATED == dwExitCode ||
777 + HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED) == static_cast<HRESULT>(dwExitCode))
778 + {
779 + typeCode = BURN_EXE_EXIT_CODE_TYPE_FORCE_REBOOT;
780 + }
781 + else
782 + {
783 + typeCode = BURN_EXE_EXIT_CODE_TYPE_ERROR;
784 + }
785 + }
786 +
787 + switch (typeCode)
788 + {
789 + case BURN_EXE_EXIT_CODE_TYPE_SUCCESS:
790 + *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
791 + hr = S_OK;
792 + break;
793 +
794 + case BURN_EXE_EXIT_CODE_TYPE_ERROR:
795 + *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
796 + hr = HRESULT_FROM_WIN32(dwExitCode);
797 + if (SUCCEEDED(hr))
798 + {
799 + hr = E_FAIL;
800 + }
801 + break;
802 +
803 + case BURN_EXE_EXIT_CODE_TYPE_SCHEDULE_REBOOT:
804 + *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED;
805 + hr = S_OK;
806 + break;
807 +
808 + case BURN_EXE_EXIT_CODE_TYPE_FORCE_REBOOT:
809 + *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED;
810 + hr = S_OK;
811 + break;
812 +
813 + default:
814 + hr = E_UNEXPECTED;
815 + break;
816 + }
817 +
818 +//LExit:
819 + return hr;
820 +}
src/engine/exeengine.h new
+48
@@ -0,0 +1,48 @@
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 +#if defined(__cplusplus)
6 +extern "C" {
7 +#endif
8 +
9 +
10 +// function declarations
11 +
12 +HRESULT ExeEngineParsePackageFromXml(
13 + __in IXMLDOMNode* pixnExePackage,
14 + __in BURN_PACKAGE* pPackage
15 + );
16 +void ExeEnginePackageUninitialize(
17 + __in BURN_PACKAGE* pPackage
18 + );
19 +HRESULT ExeEngineDetectPackage(
20 + __in BURN_PACKAGE* pPackage,
21 + __in BURN_VARIABLES* pVariables
22 + );
23 +HRESULT ExeEnginePlanCalculatePackage(
24 + __in BURN_PACKAGE* pPackage,
25 + __out_opt BOOL* pfBARequestedCache
26 + );
27 +HRESULT ExeEnginePlanAddPackage(
28 + __in_opt DWORD *pdwInsertSequence,
29 + __in BURN_PACKAGE* pPackage,
30 + __in BURN_PLAN* pPlan,
31 + __in BURN_LOGGING* pLog,
32 + __in BURN_VARIABLES* pVariables,
33 + __in_opt HANDLE hCacheEvent,
34 + __in BOOL fPlanPackageCacheRollback
35 + );
36 +HRESULT ExeEngineExecutePackage(
37 + __in BURN_EXECUTE_ACTION* pExecuteAction,
38 + __in BURN_VARIABLES* pVariables,
39 + __in BOOL fRollback,
40 + __in PFN_GENERICMESSAGEHANDLER pfnGenericExecuteProgress,
41 + __in LPVOID pvContext,
42 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart
43 + );
44 +
45 +
46 +#if defined(__cplusplus)
47 +}
48 +#endif
src/engine/inc/engine.h new
+27
@@ -0,0 +1,27 @@
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 +#if defined(__cplusplus)
6 +extern "C" {
7 +#endif
8 +
9 +
10 +// function declarations
11 +
12 +BOOL EngineInCleanRoom(
13 + __in_z_opt LPCWSTR wzCommandLine
14 + );
15 +
16 +HRESULT EngineRun(
17 + __in HINSTANCE hInstance,
18 + __in HANDLE hEngineFile,
19 + __in_z_opt LPCWSTR wzCommandLine,
20 + __in int nCmdShow,
21 + __out DWORD* pdwExitCode
22 + );
23 +
24 +
25 +#if defined(__cplusplus)
26 +}
27 +#endif
src/engine/logging.cpp new
+683
@@ -0,0 +1,683 @@
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 +static DWORD vdwPackageSequence = 0;
7 +static const DWORD LOG_OPEN_RETRY_COUNT = 3;
8 +static const DWORD LOG_OPEN_RETRY_WAIT = 2000;
9 +static CONST LPWSTR LOG_FAILED_EVENT_LOG_MESSAGE = L"Burn Engine Fatal Error: failed to open log file.";
10 +
11 +// structs
12 +
13 +
14 +
15 +// internal function declarations
16 +
17 +static void CheckLoggingPolicy(
18 + __out DWORD *pdwAttributes
19 + );
20 +static HRESULT GetNonSessionSpecificTempFolder(
21 + __deref_out_z LPWSTR* psczNonSessionTempFolder
22 + );
23 +
24 +
25 +// function definitions
26 +
27 +extern "C" HRESULT LoggingOpen(
28 + __in BURN_LOGGING* pLog,
29 + __in BURN_VARIABLES* pVariables,
30 + __in BOOTSTRAPPER_DISPLAY display,
31 + __in_z LPCWSTR wzBundleName
32 + )
33 +{
34 + HRESULT hr = S_OK;
35 + LPWSTR sczLoggingBaseFolder = NULL;
36 +
37 + // Check if the logging policy is set and configure the logging appropriately.
38 + CheckLoggingPolicy(&pLog->dwAttributes);
39 +
40 + if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_VERBOSE || pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_EXTRADEBUG)
41 + {
42 + if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_EXTRADEBUG)
43 + {
44 + LogSetLevel(REPORT_DEBUG, FALSE);
45 + }
46 + else if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_VERBOSE)
47 + {
48 + LogSetLevel(REPORT_VERBOSE, FALSE);
49 + }
50 +
51 + if ((!pLog->sczPath || !*pLog->sczPath) && (!pLog->sczPrefix || !*pLog->sczPrefix))
52 + {
53 + PathCreateTimeBasedTempFile(NULL, L"Setup", NULL, L"log", &pLog->sczPath, NULL);
54 + }
55 + }
56 +
57 + // Open the log approriately.
58 + if (pLog->sczPath && *pLog->sczPath)
59 + {
60 + DWORD cRetry = 0;
61 +
62 + hr = DirGetCurrent(&sczLoggingBaseFolder);
63 + ExitOnFailure(hr, "Failed to get current directory.");
64 +
65 + // Try pretty hard to open the log file when appending.
66 + do
67 + {
68 + if (0 < cRetry)
69 + {
70 + ::Sleep(LOG_OPEN_RETRY_WAIT);
71 + }
72 +
73 + hr = LogOpen(sczLoggingBaseFolder, pLog->sczPath, NULL, NULL, pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_APPEND, FALSE, &pLog->sczPath);
74 + if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_APPEND && HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION) == hr)
75 + {
76 + ++cRetry;
77 + }
78 + } while (cRetry > 0 && cRetry <= LOG_OPEN_RETRY_COUNT);
79 +
80 + if (FAILED(hr))
81 + {
82 + // Log is not open, so note that.
83 + LogDisable();
84 + pLog->state = BURN_LOGGING_STATE_DISABLED;
85 +
86 + if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_APPEND)
87 + {
88 + // If appending, ignore the failure and continue.
89 + hr = S_OK;
90 + }
91 + else // specifically tried to create a log file so show an error if appropriate and bail.
92 + {
93 + HRESULT hrOriginal = hr;
94 +
95 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_LOG_FAILURE);
96 + SplashScreenDisplayError(display, wzBundleName, hr);
97 +
98 + ExitOnFailure(hrOriginal, "Failed to open log: %ls", pLog->sczPath);
99 + }
100 + }
101 + else
102 + {
103 + pLog->state = BURN_LOGGING_STATE_OPEN;
104 + }
105 + }
106 + else if (pLog->sczPrefix && *pLog->sczPrefix)
107 + {
108 + hr = GetNonSessionSpecificTempFolder(&sczLoggingBaseFolder);
109 + ExitOnFailure(hr, "Failed to get non-session specific TEMP folder.");
110 +
111 + // Best effort to open default logging.
112 + hr = LogOpen(sczLoggingBaseFolder, pLog->sczPrefix, NULL, pLog->sczExtension, FALSE, FALSE, &pLog->sczPath);
113 + if (FAILED(hr))
114 + {
115 + LogDisable();
116 + pLog->state = BURN_LOGGING_STATE_DISABLED;
117 +
118 + hr = S_OK;
119 + }
120 + else
121 + {
122 + pLog->state = BURN_LOGGING_STATE_OPEN;
123 + }
124 + }
125 + else // no logging enabled.
126 + {
127 + LogDisable();
128 + pLog->state = BURN_LOGGING_STATE_DISABLED;
129 + }
130 +
131 + // If the log was opened, write the header info and update the prefix and extension to match
132 + // the log name so future logs are opened with the same pattern.
133 + if (BURN_LOGGING_STATE_OPEN == pLog->state)
134 + {
135 + LPCWSTR wzExtension = PathExtension(pLog->sczPath);
136 + if (wzExtension && *wzExtension)
137 + {
138 + hr = StrAllocString(&pLog->sczPrefix, pLog->sczPath, wzExtension - pLog->sczPath);
139 + ExitOnFailure(hr, "Failed to copy log path to prefix.");
140 +
141 + hr = StrAllocString(&pLog->sczExtension, wzExtension + 1, 0);
142 + ExitOnFailure(hr, "Failed to copy log extension to extension.");
143 + }
144 + else
145 + {
146 + hr = StrAllocString(&pLog->sczPrefix, pLog->sczPath, 0);
147 + ExitOnFailure(hr, "Failed to copy full log path to prefix.");
148 + }
149 +
150 + if (pLog->sczPathVariable && *pLog->sczPathVariable)
151 + {
152 + VariableSetString(pVariables, pLog->sczPathVariable, pLog->sczPath, FALSE); // Ignore failure.
153 + }
154 + }
155 +
156 +LExit:
157 + ReleaseStr(sczLoggingBaseFolder);
158 +
159 + return hr;
160 +}
161 +
162 +extern "C" void LoggingOpenFailed()
163 +{
164 + HRESULT hr = S_OK;
165 + HANDLE hEventLog = NULL;
166 + LPCWSTR* lpStrings = const_cast<LPCWSTR*>(&LOG_FAILED_EVENT_LOG_MESSAGE);
167 + WORD wNumStrings = 1;
168 +
169 + hr = LogOpen(NULL, L"Setup", L"_Failed", L"txt", FALSE, FALSE, NULL);
170 + if (SUCCEEDED(hr))
171 + {
172 + ExitFunction();
173 + }
174 +
175 + // If opening the "failure" log failed, then attempt to record that in the Application event log.
176 + hEventLog = ::OpenEventLogW(NULL, L"Application");
177 + ExitOnNullWithLastError(hEventLog, hr, "Failed to open Application event log");
178 +
179 + hr = ::ReportEventW(hEventLog, EVENTLOG_ERROR_TYPE, 1, 1, NULL, wNumStrings, 0, lpStrings, NULL);
180 + ExitOnNullWithLastError(hEventLog, hr, "Failed to write event log entry");
181 +
182 +LExit:
183 + if (hEventLog)
184 + {
185 + ::CloseEventLog(hEventLog);
186 + }
187 +}
188 +
189 +extern "C" void LoggingIncrementPackageSequence()
190 +{
191 + ++vdwPackageSequence;
192 +}
193 +
194 +extern "C" HRESULT LoggingSetPackageVariable(
195 + __in BURN_PACKAGE* pPackage,
196 + __in_z_opt LPCWSTR wzSuffix,
197 + __in BOOL fRollback,
198 + __in BURN_LOGGING* pLog,
199 + __in BURN_VARIABLES* pVariables,
200 + __out_opt LPWSTR* psczLogPath
201 + )
202 +{
203 + HRESULT hr = S_OK;
204 + LPWSTR sczLogPath = NULL;
205 +
206 + // Make sure that no package log files are created when logging has been disabled via Log element.
207 + if (BURN_LOGGING_STATE_DISABLED == pLog->state)
208 + {
209 + if (psczLogPath)
210 + {
211 + *psczLogPath = NULL;
212 + }
213 +
214 + ExitFunction();
215 + }
216 +
217 + if ((!fRollback && pPackage->sczLogPathVariable && *pPackage->sczLogPathVariable) ||
218 + (fRollback && pPackage->sczRollbackLogPathVariable && *pPackage->sczRollbackLogPathVariable))
219 + {
220 + hr = StrAllocFormatted(&sczLogPath, L"%ls%hs%ls_%03u_%ls%ls.%ls", pLog->sczPrefix, wzSuffix && *wzSuffix ? "_" : "", wzSuffix && *wzSuffix ? wzSuffix : L"", vdwPackageSequence, pPackage->sczId, fRollback ? L"_rollback" : L"", pLog->sczExtension);
221 + ExitOnFailure(hr, "Failed to allocate path for package log.");
222 +
223 + hr = VariableSetString(pVariables, fRollback ? pPackage->sczRollbackLogPathVariable : pPackage->sczLogPathVariable, sczLogPath, FALSE);
224 + ExitOnFailure(hr, "Failed to set log path into variable.");
225 +
226 + if (psczLogPath)
227 + {
228 + hr = StrAllocString(psczLogPath, sczLogPath, 0);
229 + ExitOnFailure(hr, "Failed to copy package log path.");
230 + }
231 + }
232 +
233 +LExit:
234 + ReleaseStr(sczLogPath);
235 +
236 + return hr;
237 +}
238 +
239 +extern "C" LPCSTR LoggingBurnActionToString(
240 + __in BOOTSTRAPPER_ACTION action
241 + )
242 +{
243 + switch (action)
244 + {
245 + case BOOTSTRAPPER_ACTION_UNKNOWN:
246 + return "Unknown";
247 + case BOOTSTRAPPER_ACTION_HELP:
248 + return "Help";
249 + case BOOTSTRAPPER_ACTION_LAYOUT:
250 + return "Layout";
251 + case BOOTSTRAPPER_ACTION_CACHE:
252 + return "Cache";
253 + case BOOTSTRAPPER_ACTION_UNINSTALL:
254 + return "Uninstall";
255 + case BOOTSTRAPPER_ACTION_INSTALL:
256 + return "Install";
257 + case BOOTSTRAPPER_ACTION_MODIFY:
258 + return "Modify";
259 + case BOOTSTRAPPER_ACTION_REPAIR:
260 + return "Repair";
261 + case BOOTSTRAPPER_ACTION_UPDATE_REPLACE:
262 + return "UpdateReplace";
263 + case BOOTSTRAPPER_ACTION_UPDATE_REPLACE_EMBEDDED:
264 + return "UpdateReplaceEmbedded";
265 + default:
266 + return "Invalid";
267 + }
268 +}
269 +
270 +extern "C" LPCSTR LoggingActionStateToString(
271 + __in BOOTSTRAPPER_ACTION_STATE actionState
272 + )
273 +{
274 + switch (actionState)
275 + {
276 + case BOOTSTRAPPER_ACTION_STATE_NONE:
277 + return "None";
278 + case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
279 + return "Uninstall";
280 + case BOOTSTRAPPER_ACTION_STATE_INSTALL:
281 + return "Install";
282 + case BOOTSTRAPPER_ACTION_STATE_ADMIN_INSTALL:
283 + return "AdminInstall";
284 + case BOOTSTRAPPER_ACTION_STATE_MODIFY:
285 + return "Modify";
286 + case BOOTSTRAPPER_ACTION_STATE_REPAIR:
287 + return "Repair";
288 + case BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE:
289 + return "MinorUpgrade";
290 + case BOOTSTRAPPER_ACTION_STATE_MAJOR_UPGRADE:
291 + return "MajorUpgrade";
292 + case BOOTSTRAPPER_ACTION_STATE_PATCH:
293 + return "Patch";
294 + default:
295 + return "Invalid";
296 + }
297 +}
298 +
299 +extern "C" LPCSTR LoggingDependencyActionToString(
300 + BURN_DEPENDENCY_ACTION action
301 + )
302 +{
303 + switch (action)
304 + {
305 + case BURN_DEPENDENCY_ACTION_NONE:
306 + return "None";
307 + case BURN_DEPENDENCY_ACTION_REGISTER:
308 + return "Register";
309 + case BURN_DEPENDENCY_ACTION_UNREGISTER:
310 + return "Unregister";
311 + default:
312 + return "Invalid";
313 + }
314 +}
315 +
316 +extern "C" LPCSTR LoggingBoolToString(
317 + __in BOOL f
318 + )
319 +{
320 + if (f)
321 + {
322 + return "Yes";
323 + }
324 +
325 + return "No";
326 +}
327 +
328 +extern "C" LPCSTR LoggingTrueFalseToString(
329 + __in BOOL f
330 + )
331 +{
332 + if (f)
333 + {
334 + return "true";
335 + }
336 +
337 + return "false";
338 +}
339 +
340 +extern "C" LPCSTR LoggingPackageStateToString(
341 + __in BOOTSTRAPPER_PACKAGE_STATE packageState
342 + )
343 +{
344 + switch (packageState)
345 + {
346 + case BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN:
347 + return "Unknown";
348 + case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE:
349 + return "Obsolete";
350 + case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
351 + return "Absent";
352 + case BOOTSTRAPPER_PACKAGE_STATE_CACHED:
353 + return "Cached";
354 + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
355 + return "Present";
356 + case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED:
357 + return "Superseded";
358 + default:
359 + return "Invalid";
360 + }
361 +}
362 +
363 +extern "C" LPCSTR LoggingCacheStateToString(
364 + __in BURN_CACHE_STATE cacheState
365 + )
366 +{
367 + switch (cacheState)
368 + {
369 + case BURN_CACHE_STATE_NONE:
370 + return "None";
371 + case BURN_CACHE_STATE_PARTIAL:
372 + return "Partial";
373 + case BURN_CACHE_STATE_COMPLETE:
374 + return "Complete";
375 + default:
376 + return "Invalid";
377 + }
378 +}
379 +
380 +extern "C" LPCSTR LoggingMsiFeatureStateToString(
381 + __in BOOTSTRAPPER_FEATURE_STATE featureState
382 + )
383 +{
384 + switch (featureState)
385 + {
386 + case BOOTSTRAPPER_FEATURE_STATE_UNKNOWN:
387 + return "Unknown";
388 + case BOOTSTRAPPER_FEATURE_STATE_ABSENT:
389 + return "Absent";
390 + case BOOTSTRAPPER_FEATURE_STATE_ADVERTISED:
391 + return "Advertised";
392 + case BOOTSTRAPPER_FEATURE_STATE_LOCAL:
393 + return "Local";
394 + case BOOTSTRAPPER_FEATURE_STATE_SOURCE:
395 + return "Source";
396 + default:
397 + return "Invalid";
398 + }
399 +}
400 +
401 +extern "C" LPCSTR LoggingMsiFeatureActionToString(
402 + __in BOOTSTRAPPER_FEATURE_ACTION featureAction
403 + )
404 +{
405 + switch (featureAction)
406 + {
407 + case BOOTSTRAPPER_FEATURE_ACTION_NONE:
408 + return "None";
409 + case BOOTSTRAPPER_FEATURE_ACTION_ADDLOCAL:
410 + return "AddLocal";
411 + case BOOTSTRAPPER_FEATURE_ACTION_ADDSOURCE:
412 + return "AddSource";
413 + case BOOTSTRAPPER_FEATURE_ACTION_ADDDEFAULT:
414 + return "AddDefault";
415 + case BOOTSTRAPPER_FEATURE_ACTION_REINSTALL:
416 + return "Reinstall";
417 + case BOOTSTRAPPER_FEATURE_ACTION_ADVERTISE:
418 + return "Advertise";
419 + case BOOTSTRAPPER_FEATURE_ACTION_REMOVE:
420 + return "Remove";
421 + default:
422 + return "Invalid";
423 + }
424 +}
425 +
426 +extern "C" LPCSTR LoggingMsiInstallContext(
427 + __in MSIINSTALLCONTEXT context
428 + )
429 +{
430 + switch (context)
431 + {
432 + case MSIINSTALLCONTEXT_ALL:
433 + return "All";
434 + case MSIINSTALLCONTEXT_ALLUSERMANAGED:
435 + return "AllUserManaged";
436 + case MSIINSTALLCONTEXT_MACHINE:
437 + return "Machine";
438 + case MSIINSTALLCONTEXT_NONE:
439 + return "None";
440 + case MSIINSTALLCONTEXT_USERMANAGED:
441 + return "UserManaged";
442 + case MSIINSTALLCONTEXT_USERUNMANAGED:
443 + return "UserUnmanaged";
444 + default:
445 + return "Invalid";
446 + }
447 +}
448 +
449 +extern "C" LPCSTR LoggingPerMachineToString(
450 + __in BOOL fPerMachine
451 + )
452 +{
453 + if (fPerMachine)
454 + {
455 + return "PerMachine";
456 + }
457 +
458 + return "PerUser";
459 +}
460 +
461 +extern "C" LPCSTR LoggingRestartToString(
462 + __in BOOTSTRAPPER_APPLY_RESTART restart
463 + )
464 +{
465 + switch (restart)
466 + {
467 + case BOOTSTRAPPER_APPLY_RESTART_NONE:
468 + return "None";
469 + case BOOTSTRAPPER_APPLY_RESTART_REQUIRED:
470 + return "Required";
471 + case BOOTSTRAPPER_APPLY_RESTART_INITIATED:
472 + return "Initiated";
473 + default:
474 + return "Invalid";
475 + }
476 +}
477 +
478 +extern "C" LPCSTR LoggingResumeModeToString(
479 + __in BURN_RESUME_MODE resumeMode
480 + )
481 +{
482 + switch (resumeMode)
483 + {
484 + case BURN_RESUME_MODE_NONE:
485 + return "None";
486 + case BURN_RESUME_MODE_ACTIVE:
487 + return "Active";
488 + case BURN_RESUME_MODE_SUSPEND:
489 + return "Suspend";
490 + case BURN_RESUME_MODE_ARP:
491 + return "ARP";
492 + case BURN_RESUME_MODE_REBOOT_PENDING:
493 + return "Reboot Pending";
494 + default:
495 + return "Invalid";
496 + }
497 +}
498 +
499 +extern "C" LPCSTR LoggingRelationTypeToString(
500 + __in BOOTSTRAPPER_RELATION_TYPE type
501 + )
502 +{
503 + switch (type)
504 + {
505 + case BOOTSTRAPPER_RELATION_NONE:
506 + return "None";
507 + case BOOTSTRAPPER_RELATION_DETECT:
508 + return "Detect";
509 + case BOOTSTRAPPER_RELATION_UPGRADE:
510 + return "Upgrade";
511 + case BOOTSTRAPPER_RELATION_ADDON:
512 + return "Addon";
513 + case BOOTSTRAPPER_RELATION_PATCH:
514 + return "Patch";
515 + case BOOTSTRAPPER_RELATION_DEPENDENT:
516 + return "Dependent";
517 + case BOOTSTRAPPER_RELATION_UPDATE:
518 + return "Update";
519 + default:
520 + return "Invalid";
521 + }
522 +}
523 +
524 +extern "C" LPCSTR LoggingRelatedOperationToString(
525 + __in BOOTSTRAPPER_RELATED_OPERATION operation
526 + )
527 +{
528 + switch (operation)
529 + {
530 + case BOOTSTRAPPER_RELATED_OPERATION_NONE:
531 + return "None";
532 + case BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE:
533 + return "Downgrade";
534 + case BOOTSTRAPPER_RELATED_OPERATION_MINOR_UPDATE:
535 + return "MinorUpdate";
536 + case BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE:
537 + return "MajorUpgrade";
538 + case BOOTSTRAPPER_RELATED_OPERATION_REMOVE:
539 + return "Remove";
540 + case BOOTSTRAPPER_RELATED_OPERATION_INSTALL:
541 + return "Install";
542 + case BOOTSTRAPPER_RELATED_OPERATION_REPAIR:
543 + return "Repair";
544 + default:
545 + return "Invalid";
546 + }
547 +}
548 +
549 +extern "C" LPCSTR LoggingRequestStateToString(
550 + __in BOOTSTRAPPER_REQUEST_STATE requestState
551 + )
552 +{
553 + switch (requestState)
554 + {
555 + case BOOTSTRAPPER_REQUEST_STATE_NONE:
556 + return "None";
557 + case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT:
558 + return "ForceAbsent";
559 + case BOOTSTRAPPER_REQUEST_STATE_ABSENT:
560 + return "Absent";
561 + case BOOTSTRAPPER_REQUEST_STATE_CACHE:
562 + return "Cache";
563 + case BOOTSTRAPPER_REQUEST_STATE_PRESENT:
564 + return "Present";
565 + case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
566 + return "Repair";
567 + default:
568 + return "Invalid";
569 + }
570 +}
571 +
572 +extern "C" LPCSTR LoggingRollbackOrExecute(
573 + __in BOOL fRollback
574 + )
575 +{
576 + return fRollback ? "rollback" : "execute";
577 +}
578 +
579 +extern "C" LPWSTR LoggingStringOrUnknownIfNull(
580 + __in LPCWSTR wz
581 + )
582 +{
583 + return wz ? wz : L"Unknown";
584 +}
585 +
586 +// Note: this function is not thread safe.
587 +extern "C" LPCSTR LoggingVersionToString(
588 + __in DWORD64 dw64Version
589 + )
590 +{
591 + static CHAR szVersion[40] = { 0 };
592 + HRESULT hr = S_OK;
593 +
594 + hr = ::StringCchPrintfA(szVersion, countof(szVersion), "%I64u.%I64u.%I64u.%I64u", dw64Version >> 48 & 0xFFFF, dw64Version >> 32 & 0xFFFF, dw64Version >> 16 & 0xFFFF, dw64Version & 0xFFFF);
595 + if (FAILED(hr))
596 + {
597 + memset(szVersion, 0, sizeof(szVersion));
598 + }
599 +
600 + return szVersion;
601 +}
602 +
603 +
604 +// internal function declarations
605 +
606 +static void CheckLoggingPolicy(
607 + __out DWORD *pdwAttributes
608 + )
609 +{
610 + HRESULT hr = S_OK;
611 + HKEY hk = NULL;
612 + LPWSTR sczLoggingPolicy = NULL;
613 +
614 + hr = RegOpen(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Policies\\Microsoft\\Windows\\Installer", KEY_READ, &hk);
615 + if (SUCCEEDED(hr))
616 + {
617 + hr = RegReadString(hk, L"Logging", &sczLoggingPolicy);
618 + if (SUCCEEDED(hr))
619 + {
620 + LPCWSTR wz = sczLoggingPolicy;
621 + while (*wz)
622 + {
623 + if (L'v' == *wz || L'V' == *wz)
624 + {
625 + *pdwAttributes |= BURN_LOGGING_ATTRIBUTE_VERBOSE;
626 + }
627 + else if (L'x' == *wz || L'X' == *wz)
628 + {
629 + *pdwAttributes |= BURN_LOGGING_ATTRIBUTE_EXTRADEBUG;
630 + }
631 +
632 + ++wz;
633 + }
634 + }
635 + }
636 +
637 + ReleaseStr(sczLoggingPolicy);
638 + ReleaseRegKey(hk);
639 +}
640 +
641 +static HRESULT GetNonSessionSpecificTempFolder(
642 + __deref_out_z LPWSTR* psczNonSessionTempFolder
643 + )
644 +{
645 + HRESULT hr = S_OK;
646 + WCHAR wzTempFolder[MAX_PATH] = { };
647 + DWORD cchTempFolder = 0;
648 + DWORD dwSessionId = 0;
649 + LPWSTR sczSessionId = 0;
650 + DWORD cchSessionId = 0;
651 +
652 + if (!::GetTempPathW(countof(wzTempFolder), wzTempFolder))
653 + {
654 + ExitWithLastError(hr, "Failed to get temp folder.");
655 + }
656 +
657 + hr = ::StringCchLengthW(wzTempFolder, countof(wzTempFolder), reinterpret_cast<size_t*>(&cchTempFolder));
658 + ExitOnFailure(hr, "Failed to get length of temp folder.");
659 +
660 + // If our session id is in the TEMP path then remove that part so we get the non-session
661 + // specific temporary folder.
662 + if (::ProcessIdToSessionId(::GetCurrentProcessId(), &dwSessionId))
663 + {
664 + hr = StrAllocFormatted(&sczSessionId, L"%u\\", dwSessionId);
665 + ExitOnFailure(hr, "Failed to format session id as a string.");
666 +
667 + hr = ::StringCchLengthW(sczSessionId, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cchSessionId));
668 + ExitOnFailure(hr, "Failed to get length of session id string.");
669 +
670 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzTempFolder + cchTempFolder - cchSessionId, cchSessionId, sczSessionId, cchSessionId))
671 + {
672 + cchTempFolder -= cchSessionId;
673 + }
674 + }
675 +
676 + hr = StrAllocString(psczNonSessionTempFolder, wzTempFolder, cchTempFolder);
677 + ExitOnFailure(hr, "Failed to copy temp folder.");
678 +
679 +LExit:
680 + ReleaseStr(sczSessionId);
681 +
682 + return hr;
683 +}
src/engine/logging.h new
+144
@@ -0,0 +1,144 @@
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 +#if defined(__cplusplus)
6 +extern "C" {
7 +#endif
8 +
9 +
10 +// constants
11 +
12 +enum BURN_LOGGING_STATE
13 +{
14 + BURN_LOGGING_STATE_CLOSED,
15 + BURN_LOGGING_STATE_OPEN,
16 + BURN_LOGGING_STATE_DISABLED,
17 +};
18 +
19 +enum BURN_LOGGING_ATTRIBUTE
20 +{
21 + BURN_LOGGING_ATTRIBUTE_APPEND = 0x1,
22 + BURN_LOGGING_ATTRIBUTE_VERBOSE = 0x2,
23 + BURN_LOGGING_ATTRIBUTE_EXTRADEBUG = 0x4,
24 +};
25 +
26 +
27 +// structs
28 +
29 +typedef struct _BURN_LOGGING
30 +{
31 + BURN_LOGGING_STATE state;
32 + LPWSTR sczPathVariable;
33 +
34 + DWORD dwAttributes;
35 + LPWSTR sczPath;
36 + LPWSTR sczPrefix;
37 + LPWSTR sczExtension;
38 +} BURN_LOGGING;
39 +
40 +
41 +
42 +// function declarations
43 +
44 +HRESULT LoggingOpen(
45 + __in BURN_LOGGING* pLog,
46 + __in BURN_VARIABLES* pVariables,
47 + __in BOOTSTRAPPER_DISPLAY display,
48 + __in_z LPCWSTR wzBundleName
49 + );
50 +
51 +void LoggingOpenFailed();
52 +
53 +void LoggingIncrementPackageSequence();
54 +
55 +HRESULT LoggingSetPackageVariable(
56 + __in BURN_PACKAGE* pPackage,
57 + __in_z_opt LPCWSTR wzSuffix,
58 + __in BOOL fRollback,
59 + __in BURN_LOGGING* pLog,
60 + __in BURN_VARIABLES* pVariables,
61 + __out_opt LPWSTR* psczLogPath
62 + );
63 +
64 +LPCSTR LoggingBurnActionToString(
65 + __in BOOTSTRAPPER_ACTION action
66 + );
67 +
68 +LPCSTR LoggingActionStateToString(
69 + __in BOOTSTRAPPER_ACTION_STATE actionState
70 + );
71 +
72 +LPCSTR LoggingDependencyActionToString(
73 + BURN_DEPENDENCY_ACTION action
74 + );
75 +
76 +LPCSTR LoggingBoolToString(
77 + __in BOOL f
78 + );
79 +
80 +LPCSTR LoggingTrueFalseToString(
81 + __in BOOL f
82 + );
83 +
84 +LPCSTR LoggingPackageStateToString(
85 + __in BOOTSTRAPPER_PACKAGE_STATE packageState
86 + );
87 +
88 +LPCSTR LoggingCacheStateToString(
89 + __in BURN_CACHE_STATE cacheState
90 + );
91 +
92 +LPCSTR LoggingMsiFeatureStateToString(
93 + __in BOOTSTRAPPER_FEATURE_STATE featureState
94 + );
95 +
96 +LPCSTR LoggingMsiFeatureActionToString(
97 + __in BOOTSTRAPPER_FEATURE_ACTION featureAction
98 + );
99 +
100 +LPCSTR LoggingMsiInstallContext(
101 + __in MSIINSTALLCONTEXT context
102 + );
103 +
104 +LPCSTR LoggingPerMachineToString(
105 + __in BOOL fPerMachine
106 + );
107 +
108 +LPCSTR LoggingRestartToString(
109 + __in BOOTSTRAPPER_APPLY_RESTART restart
110 + );
111 +
112 +LPCSTR LoggingResumeModeToString(
113 + __in BURN_RESUME_MODE resumeMode
114 + );
115 +
116 +LPCSTR LoggingRelationTypeToString(
117 + __in BOOTSTRAPPER_RELATION_TYPE type
118 + );
119 +
120 +LPCSTR LoggingRelatedOperationToString(
121 + __in BOOTSTRAPPER_RELATED_OPERATION operation
122 + );
123 +
124 +LPCSTR LoggingRequestStateToString(
125 + __in BOOTSTRAPPER_REQUEST_STATE requestState
126 + );
127 +
128 +LPCSTR LoggingRollbackOrExecute(
129 + __in BOOL fRollback
130 + );
131 +
132 +LPWSTR LoggingStringOrUnknownIfNull(
133 + __in LPCWSTR wz
134 + );
135 +
136 +// Note: this function is not thread safe.
137 +LPCSTR LoggingVersionToString(
138 + __in DWORD64 dw64Version
139 + );
140 +
141 +
142 +#if defined(__cplusplus)
143 +}
144 +#endif
src/engine/manifest.cpp new
+125
@@ -0,0 +1,125 @@
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 +// function definitions
7 +
8 +extern "C" HRESULT ManifestLoadXmlFromBuffer(
9 + __in_bcount(cbBuffer) BYTE* pbBuffer,
10 + __in SIZE_T cbBuffer,
11 + __in BURN_ENGINE_STATE* pEngineState
12 + )
13 +{
14 + HRESULT hr = S_OK;
15 + IXMLDOMDocument* pixdDocument = NULL;
16 + IXMLDOMElement* pixeBundle = NULL;
17 + IXMLDOMNode* pixnLog = NULL;
18 + IXMLDOMNode* pixnChain = NULL;
19 +
20 + // load xml document
21 + hr = XmlLoadDocumentFromBuffer(pbBuffer, cbBuffer, &pixdDocument);
22 + ExitOnFailure(hr, "Failed to load manifest as XML document.");
23 +
24 + // get bundle element
25 + hr = pixdDocument->get_documentElement(&pixeBundle);
26 + ExitOnFailure(hr, "Failed to get bundle element.");
27 +
28 + // parse the log element, if present.
29 + hr = XmlSelectSingleNode(pixeBundle, L"Log", &pixnLog);
30 + ExitOnFailure(hr, "Failed to get Log element.");
31 +
32 + if (S_OK == hr)
33 + {
34 + hr = XmlGetAttributeEx(pixnLog, L"PathVariable", &pEngineState->log.sczPathVariable);
35 + if (E_NOTFOUND != hr)
36 + {
37 + ExitOnFailure(hr, "Failed to get Log/@PathVariable.");
38 + }
39 +
40 + hr = XmlGetAttributeEx(pixnLog, L"Prefix", &pEngineState->log.sczPrefix);
41 + ExitOnFailure(hr, "Failed to get Log/@Prefix attribute.");
42 +
43 + hr = XmlGetAttributeEx(pixnLog, L"Extension", &pEngineState->log.sczExtension);
44 + ExitOnFailure(hr, "Failed to get Log/@Extension attribute.");
45 + }
46 +
47 + // get the chain element
48 + hr = XmlSelectSingleNode(pixeBundle, L"Chain", &pixnChain);
49 + ExitOnFailure(hr, "Failed to get chain element.");
50 +
51 + if (S_OK == hr)
52 + {
53 + // parse disable rollback
54 + hr = XmlGetYesNoAttribute(pixnChain, L"DisableRollback", &pEngineState->fDisableRollback);
55 + if (E_NOTFOUND != hr)
56 + {
57 + ExitOnFailure(hr, "Failed to get Chain/@DisableRollback");
58 + }
59 +
60 + // parse disable system restore
61 + hr = XmlGetYesNoAttribute(pixnChain, L"DisableSystemRestore", &pEngineState->fDisableSystemRestore);
62 + if (E_NOTFOUND != hr)
63 + {
64 + ExitOnFailure(hr, "Failed to get Chain/@DisableSystemRestore");
65 + }
66 +
67 + // parse parallel cache
68 + hr = XmlGetYesNoAttribute(pixnChain, L"ParallelCache", &pEngineState->fParallelCacheAndExecute);
69 + if (E_NOTFOUND != hr)
70 + {
71 + ExitOnFailure(hr, "Failed to get Chain/@ParallelCache");
72 + }
73 + }
74 +
75 + // parse built-in condition
76 + hr = ConditionGlobalParseFromXml(&pEngineState->condition, pixeBundle);
77 + ExitOnFailure(hr, "Failed to parse global condition.");
78 +
79 + // parse variables
80 + hr = VariablesParseFromXml(&pEngineState->variables, pixeBundle);
81 + ExitOnFailure(hr, "Failed to parse variables.");
82 +
83 + // parse searches
84 + hr = SearchesParseFromXml(&pEngineState->searches, pixeBundle); // TODO: Modularization
85 + ExitOnFailure(hr, "Failed to parse searches.");
86 +
87 + // parse user experience
88 + hr = UserExperienceParseFromXml(&pEngineState->userExperience, pixeBundle);
89 + ExitOnFailure(hr, "Failed to parse user experience.");
90 +
91 + // parse catalog files
92 + hr = CatalogsParseFromXml(&pEngineState->catalogs, pixeBundle);
93 + ExitOnFailure(hr, "Failed to parse catalog files.");
94 +
95 + // parse registration
96 + hr = RegistrationParseFromXml(&pEngineState->registration, pixeBundle);
97 + ExitOnFailure(hr, "Failed to parse registration.");
98 +
99 + // parse update
100 + hr = UpdateParseFromXml(&pEngineState->update, pixeBundle);
101 + ExitOnFailure(hr, "Failed to parse update.");
102 +
103 + // parse containers
104 + hr = ContainersParseFromXml(&pEngineState->section, &pEngineState->containers, pixeBundle);
105 + ExitOnFailure(hr, "Failed to parse containers.");
106 +
107 + // parse payloads
108 + hr = PayloadsParseFromXml(&pEngineState->payloads, &pEngineState->containers, &pEngineState->catalogs, pixeBundle);
109 + ExitOnFailure(hr, "Failed to parse payloads.");
110 +
111 + // parse packages
112 + hr = PackagesParseFromXml(&pEngineState->packages, &pEngineState->payloads, pixeBundle);
113 + ExitOnFailure(hr, "Failed to parse packages.");
114 +
115 + // parse approved exes for elevation
116 + hr = ApprovedExesParseFromXml(&pEngineState->approvedExes, pixeBundle);
117 + ExitOnFailure(hr, "Failed to parse approved exes.");
118 +
119 +LExit:
120 + ReleaseObject(pixnChain);
121 + ReleaseObject(pixnLog);
122 + ReleaseObject(pixeBundle);
123 + ReleaseObject(pixdDocument);
124 + return hr;
125 +}
src/engine/manifest.h new
+23
@@ -0,0 +1,23 @@
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 +interface IBurnPayload; // forward declare.
6 +
7 +#if defined(__cplusplus)
8 +extern "C" {
9 +#endif
10 +
11 +
12 +// function declarations
13 +
14 +HRESULT ManifestLoadXmlFromBuffer(
15 + __in_bcount(cbBuffer) BYTE* pbBuffer,
16 + __in SIZE_T cbBuffer,
17 + __in BURN_ENGINE_STATE* pEngineState
18 + );
19 +
20 +
21 +#if defined(__cplusplus)
22 +}
23 +#endif
src/engine/msiengine.cpp new
+1910
@@ -0,0 +1,1910 @@
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 +// constants
7 +
8 +
9 +// structs
10 +
11 +
12 +
13 +// internal function declarations
14 +
15 +static HRESULT ParseRelatedMsiFromXml(
16 + __in IXMLDOMNode* pixnRelatedMsi,
17 + __in BURN_RELATED_MSI* pRelatedMsi
18 + );
19 +static HRESULT EvaluateActionStateConditions(
20 + __in BURN_VARIABLES* pVariables,
21 + __in_z_opt LPCWSTR sczAddLocalCondition,
22 + __in_z_opt LPCWSTR sczAddSourceCondition,
23 + __in_z_opt LPCWSTR sczAdvertiseCondition,
24 + __out BOOTSTRAPPER_FEATURE_STATE* pState
25 + );
26 +static HRESULT CalculateFeatureAction(
27 + __in BOOTSTRAPPER_FEATURE_STATE currentState,
28 + __in BOOTSTRAPPER_FEATURE_STATE requestedState,
29 + __in BOOL fRepair,
30 + __out BOOTSTRAPPER_FEATURE_ACTION* pFeatureAction,
31 + __inout BOOL* pfDelta
32 + );
33 +static HRESULT EscapePropertyArgumentString(
34 + __in LPCWSTR wzProperty,
35 + __inout_z LPWSTR* psczEscapedValue,
36 + __in BOOL fZeroOnRealloc
37 + );
38 +static HRESULT ConcatFeatureActionProperties(
39 + __in BURN_PACKAGE* pPackage,
40 + __in BOOTSTRAPPER_FEATURE_ACTION* rgFeatureActions,
41 + __inout_z LPWSTR* psczArguments
42 + );
43 +static HRESULT ConcatPatchProperty(
44 + __in BURN_PACKAGE* pPackage,
45 + __in_opt BOOTSTRAPPER_ACTION_STATE* rgSlipstreamPatchActions,
46 + __inout_z LPWSTR* psczArguments
47 + );
48 +static void RegisterSourceDirectory(
49 + __in BURN_PACKAGE* pPackage,
50 + __in_z LPCWSTR wzCacheDirectory
51 + );
52 +
53 +
54 +// function definitions
55 +
56 +extern "C" HRESULT MsiEngineParsePackageFromXml(
57 + __in IXMLDOMNode* pixnMsiPackage,
58 + __in BURN_PACKAGE* pPackage
59 + )
60 +{
61 + HRESULT hr = S_OK;
62 + IXMLDOMNodeList* pixnNodes = NULL;
63 + IXMLDOMNode* pixnNode = NULL;
64 + DWORD cNodes = 0;
65 + LPWSTR scz = NULL;
66 +
67 + // @ProductCode
68 + hr = XmlGetAttributeEx(pixnMsiPackage, L"ProductCode", &pPackage->Msi.sczProductCode);
69 + ExitOnFailure(hr, "Failed to get @ProductCode.");
70 +
71 + // @Language
72 + hr = XmlGetAttributeNumber(pixnMsiPackage, L"Language", &pPackage->Msi.dwLanguage);
73 + ExitOnFailure(hr, "Failed to get @Language.");
74 +
75 + // @Version
76 + hr = XmlGetAttributeEx(pixnMsiPackage, L"Version", &scz);
77 + ExitOnFailure(hr, "Failed to get @Version.");
78 +
79 + hr = FileVersionFromStringEx(scz, 0, &pPackage->Msi.qwVersion);
80 + ExitOnFailure(hr, "Failed to parse @Version: %ls", scz);
81 +
82 + // @DisplayInternalUI
83 + hr = XmlGetYesNoAttribute(pixnMsiPackage, L"DisplayInternalUI", &pPackage->Msi.fDisplayInternalUI);
84 + ExitOnFailure(hr, "Failed to get @DisplayInternalUI.");
85 +
86 + // @UpgradeCode
87 + hr = XmlGetAttributeEx(pixnMsiPackage, L"UpgradeCode", &pPackage->Msi.sczUpgradeCode);
88 + if (E_NOTFOUND != hr)
89 + {
90 + ExitOnFailure(hr, "Failed to get @UpgradeCode.");
91 + }
92 +
93 + // select feature nodes
94 + hr = XmlSelectNodes(pixnMsiPackage, L"MsiFeature", &pixnNodes);
95 + ExitOnFailure(hr, "Failed to select feature nodes.");
96 +
97 + // get feature node count
98 + hr = pixnNodes->get_length((long*)&cNodes);
99 + ExitOnFailure(hr, "Failed to get feature node count.");
100 +
101 + if (cNodes)
102 + {
103 + // allocate memory for features
104 + pPackage->Msi.rgFeatures = (BURN_MSIFEATURE*)MemAlloc(sizeof(BURN_MSIFEATURE) * cNodes, TRUE);
105 + ExitOnNull(pPackage->Msi.rgFeatures, hr, E_OUTOFMEMORY, "Failed to allocate memory for MSI feature structs.");
106 +
107 + pPackage->Msi.cFeatures = cNodes;
108 +
109 + // parse feature elements
110 + for (DWORD i = 0; i < cNodes; ++i)
111 + {
112 + BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i];
113 +
114 + hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
115 + ExitOnFailure(hr, "Failed to get next node.");
116 +
117 + // @Id
118 + hr = XmlGetAttributeEx(pixnNode, L"Id", &pFeature->sczId);
119 + ExitOnFailure(hr, "Failed to get @Id.");
120 +
121 + // @AddLocalCondition
122 + hr = XmlGetAttributeEx(pixnNode, L"AddLocalCondition", &pFeature->sczAddLocalCondition);
123 + if (E_NOTFOUND != hr)
124 + {
125 + ExitOnFailure(hr, "Failed to get @AddLocalCondition.");
126 + }
127 +
128 + // @AddSourceCondition
129 + hr = XmlGetAttributeEx(pixnNode, L"AddSourceCondition", &pFeature->sczAddSourceCondition);
130 + if (E_NOTFOUND != hr)
131 + {
132 + ExitOnFailure(hr, "Failed to get @AddSourceCondition.");
133 + }
134 +
135 + // @AdvertiseCondition
136 + hr = XmlGetAttributeEx(pixnNode, L"AdvertiseCondition", &pFeature->sczAdvertiseCondition);
137 + if (E_NOTFOUND != hr)
138 + {
139 + ExitOnFailure(hr, "Failed to get @AdvertiseCondition.");
140 + }
141 +
142 + // @RollbackAddLocalCondition
143 + hr = XmlGetAttributeEx(pixnNode, L"RollbackAddLocalCondition", &pFeature->sczRollbackAddLocalCondition);
144 + if (E_NOTFOUND != hr)
145 + {
146 + ExitOnFailure(hr, "Failed to get @RollbackAddLocalCondition.");
147 + }
148 +
149 + // @RollbackAddSourceCondition
150 + hr = XmlGetAttributeEx(pixnNode, L"RollbackAddSourceCondition", &pFeature->sczRollbackAddSourceCondition);
151 + if (E_NOTFOUND != hr)
152 + {
153 + ExitOnFailure(hr, "Failed to get @RollbackAddSourceCondition.");
154 + }
155 +
156 + // @RollbackAdvertiseCondition
157 + hr = XmlGetAttributeEx(pixnNode, L"RollbackAdvertiseCondition", &pFeature->sczRollbackAdvertiseCondition);
158 + if (E_NOTFOUND != hr)
159 + {
160 + ExitOnFailure(hr, "Failed to get @RollbackAdvertiseCondition.");
161 + }
162 +
163 + // prepare next iteration
164 + ReleaseNullObject(pixnNode);
165 + }
166 + }
167 +
168 + ReleaseNullObject(pixnNodes); // done with the MsiFeature elements.
169 +
170 + hr = MsiEngineParsePropertiesFromXml(pixnMsiPackage, &pPackage->Msi.rgProperties, &pPackage->Msi.cProperties);
171 + ExitOnFailure(hr, "Failed to parse properties from XML.");
172 +
173 + // select related MSI nodes
174 + hr = XmlSelectNodes(pixnMsiPackage, L"RelatedPackage", &pixnNodes);
175 + ExitOnFailure(hr, "Failed to select related MSI nodes.");
176 +
177 + // get related MSI node count
178 + hr = pixnNodes->get_length((long*)&cNodes);
179 + ExitOnFailure(hr, "Failed to get related MSI node count.");
180 +
181 + if (cNodes)
182 + {
183 + // allocate memory for related MSIs
184 + pPackage->Msi.rgRelatedMsis = (BURN_RELATED_MSI*)MemAlloc(sizeof(BURN_RELATED_MSI) * cNodes, TRUE);
185 + ExitOnNull(pPackage->Msi.rgRelatedMsis, hr, E_OUTOFMEMORY, "Failed to allocate memory for related MSI structs.");
186 +
187 + pPackage->Msi.cRelatedMsis = cNodes;
188 +
189 + // parse related MSI elements
190 + for (DWORD i = 0; i < cNodes; ++i)
191 + {
192 + hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
193 + ExitOnFailure(hr, "Failed to get next node.");
194 +
195 + // parse related MSI element
196 + hr = ParseRelatedMsiFromXml(pixnNode, &pPackage->Msi.rgRelatedMsis[i]);
197 + ExitOnFailure(hr, "Failed to parse related MSI element.");
198 +
199 + // prepare next iteration
200 + ReleaseNullObject(pixnNode);
201 + }
202 + }
203 +
204 + ReleaseNullObject(pixnNodes); // done with the RelatedPackage elements.
205 +
206 + // Select slipstream MSP nodes.
207 + hr = XmlSelectNodes(pixnMsiPackage, L"SlipstreamMsp", &pixnNodes);
208 + ExitOnFailure(hr, "Failed to select related MSI nodes.");
209 +
210 + hr = pixnNodes->get_length((long*)&cNodes);
211 + ExitOnFailure(hr, "Failed to get related MSI node count.");
212 +
213 + if (cNodes)
214 + {
215 + pPackage->Msi.rgpSlipstreamMspPackages = reinterpret_cast<BURN_PACKAGE**>(MemAlloc(sizeof(BURN_PACKAGE*) * cNodes, TRUE));
216 + ExitOnNull(pPackage->Msi.rgpSlipstreamMspPackages, hr, E_OUTOFMEMORY, "Failed to allocate memory for slipstream MSP packages.");
217 +
218 + pPackage->Msi.rgsczSlipstreamMspPackageIds = reinterpret_cast<LPWSTR*>(MemAlloc(sizeof(LPWSTR*) * cNodes, TRUE));
219 + ExitOnNull(pPackage->Msi.rgsczSlipstreamMspPackageIds, hr, E_OUTOFMEMORY, "Failed to allocate memory for slipstream MSP ids.");
220 +
221 + pPackage->Msi.cSlipstreamMspPackages = cNodes;
222 +
223 + // Parse slipstream MSP Ids.
224 + for (DWORD i = 0; i < cNodes; ++i)
225 + {
226 + hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
227 + ExitOnFailure(hr, "Failed to get next slipstream MSP node.");
228 +
229 + hr = XmlGetAttributeEx(pixnNode, L"Id", pPackage->Msi.rgsczSlipstreamMspPackageIds + i);
230 + ExitOnFailure(hr, "Failed to parse slipstream MSP ids.");
231 +
232 + ReleaseNullObject(pixnNode);
233 + }
234 + }
235 +
236 + hr = S_OK;
237 +
238 +LExit:
239 + ReleaseObject(pixnNodes);
240 + ReleaseObject(pixnNode);
241 + ReleaseStr(scz);
242 +
243 + return hr;
244 +}
245 +
246 +extern "C" HRESULT MsiEngineParsePropertiesFromXml(
247 + __in IXMLDOMNode* pixnPackage,
248 + __out BURN_MSIPROPERTY** prgProperties,
249 + __out DWORD* pcProperties
250 + )
251 +{
252 + HRESULT hr = S_OK;
253 + IXMLDOMNodeList* pixnNodes = NULL;
254 + IXMLDOMNode* pixnNode = NULL;
255 + DWORD cNodes = 0;
256 +
257 + BURN_MSIPROPERTY* pProperties = NULL;
258 +
259 + // select property nodes
260 + hr = XmlSelectNodes(pixnPackage, L"MsiProperty", &pixnNodes);
261 + ExitOnFailure(hr, "Failed to select property nodes.");
262 +
263 + // get property node count
264 + hr = pixnNodes->get_length((long*)&cNodes);
265 + ExitOnFailure(hr, "Failed to get property node count.");
266 +
267 + if (cNodes)
268 + {
269 + // allocate memory for properties
270 + pProperties = (BURN_MSIPROPERTY*)MemAlloc(sizeof(BURN_MSIPROPERTY) * cNodes, TRUE);
271 + ExitOnNull(pProperties, hr, E_OUTOFMEMORY, "Failed to allocate memory for MSI property structs.");
272 +
273 + // parse property elements
274 + for (DWORD i = 0; i < cNodes; ++i)
275 + {
276 + BURN_MSIPROPERTY* pProperty = &pProperties[i];
277 +
278 + hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
279 + ExitOnFailure(hr, "Failed to get next node.");
280 +
281 + // @Id
282 + hr = XmlGetAttributeEx(pixnNode, L"Id", &pProperty->sczId);
283 + ExitOnFailure(hr, "Failed to get @Id.");
284 +
285 + // @Value
286 + hr = XmlGetAttributeEx(pixnNode, L"Value", &pProperty->sczValue);
287 + ExitOnFailure(hr, "Failed to get @Value.");
288 +
289 + // @RollbackValue
290 + hr = XmlGetAttributeEx(pixnNode, L"RollbackValue", &pProperty->sczRollbackValue);
291 + if (E_NOTFOUND != hr)
292 + {
293 + ExitOnFailure(hr, "Failed to get @RollbackValue.");
294 + }
295 +
296 + // @Condition
297 + hr = XmlGetAttributeEx(pixnNode, L"Condition", &pProperty->sczCondition);
298 + if (E_NOTFOUND != hr)
299 + {
300 + ExitOnFailure(hr, "Failed to get @Condition.");
301 + }
302 +
303 + // prepare next iteration
304 + ReleaseNullObject(pixnNode);
305 + }
306 + }
307 +
308 + *pcProperties = cNodes;
309 + *prgProperties = pProperties;
310 + pProperties = NULL;
311 +
312 + hr = S_OK;
313 +
314 +LExit:
315 + ReleaseNullObject(pixnNodes);
316 + ReleaseMem(pProperties);
317 +
318 + return hr;
319 +}
320 +
321 +extern "C" void MsiEnginePackageUninitialize(
322 + __in BURN_PACKAGE* pPackage
323 + )
324 +{
325 + ReleaseStr(pPackage->Msi.sczProductCode);
326 + ReleaseStr(pPackage->Msi.sczUpgradeCode);
327 + ReleaseStr(pPackage->Msi.sczInstalledProductCode);
328 +
329 + // free features
330 + if (pPackage->Msi.rgFeatures)
331 + {
332 + for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i)
333 + {
334 + BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i];
335 +
336 + ReleaseStr(pFeature->sczId);
337 + ReleaseStr(pFeature->sczAddLocalCondition);
338 + ReleaseStr(pFeature->sczAddSourceCondition);
339 + ReleaseStr(pFeature->sczAdvertiseCondition);
340 + ReleaseStr(pFeature->sczRollbackAddLocalCondition);
341 + ReleaseStr(pFeature->sczRollbackAddSourceCondition);
342 + ReleaseStr(pFeature->sczRollbackAdvertiseCondition);
343 + }
344 + MemFree(pPackage->Msi.rgFeatures);
345 + }
346 +
347 + // free properties
348 + if (pPackage->Msi.rgProperties)
349 + {
350 + for (DWORD i = 0; i < pPackage->Msi.cProperties; ++i)
351 + {
352 + BURN_MSIPROPERTY* pProperty = &pPackage->Msi.rgProperties[i];
353 +
354 + ReleaseStr(pProperty->sczId);
355 + ReleaseStr(pProperty->sczValue);
356 + ReleaseStr(pProperty->sczRollbackValue);
357 + ReleaseStr(pProperty->sczCondition);
358 + }
359 + MemFree(pPackage->Msi.rgProperties);
360 + }
361 +
362 + // free related MSIs
363 + if (pPackage->Msi.rgRelatedMsis)
364 + {
365 + for (DWORD i = 0; i < pPackage->Msi.cRelatedMsis; ++i)
366 + {
367 + BURN_RELATED_MSI* pRelatedMsi = &pPackage->Msi.rgRelatedMsis[i];
368 +
369 + ReleaseStr(pRelatedMsi->sczUpgradeCode);
370 + ReleaseMem(pRelatedMsi->rgdwLanguages);
371 + }
372 + MemFree(pPackage->Msi.rgRelatedMsis);
373 + }
374 +
375 + // free slipstream MSPs
376 + if (pPackage->Msi.rgsczSlipstreamMspPackageIds)
377 + {
378 + for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i)
379 + {
380 + ReleaseStr(pPackage->Msi.rgsczSlipstreamMspPackageIds[i]);
381 + }
382 +
383 + MemFree(pPackage->Msi.rgsczSlipstreamMspPackageIds);
384 + }
385 +
386 + if (pPackage->Msi.rgpSlipstreamMspPackages)
387 + {
388 + MemFree(pPackage->Msi.rgpSlipstreamMspPackages);
389 + }
390 +
391 + // clear struct
392 + memset(&pPackage->Msi, 0, sizeof(pPackage->Msi));
393 +}
394 +
395 +extern "C" HRESULT MsiEngineDetectPackage(
396 + __in BURN_PACKAGE* pPackage,
397 + __in BURN_USER_EXPERIENCE* pUserExperience
398 + )
399 +{
400 + Trace(REPORT_STANDARD, "Detecting MSI package 0x%p", pPackage);
401 +
402 + HRESULT hr = S_OK;
403 + LPWSTR sczInstalledVersion = NULL;
404 + LPWSTR sczInstalledLanguage = NULL;
405 + LPWSTR sczInstalledProductCode = NULL;
406 + LPWSTR sczInstalledProviderKey = NULL;
407 + INSTALLSTATE installState = INSTALLSTATE_UNKNOWN;
408 + BOOTSTRAPPER_RELATED_OPERATION operation = BOOTSTRAPPER_RELATED_OPERATION_NONE;
409 + BOOTSTRAPPER_RELATED_OPERATION relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_NONE;
410 + WCHAR wzProductCode[MAX_GUID_CHARS + 1] = { };
411 + DWORD64 qwVersion = 0;
412 + UINT uLcid = 0;
413 + BOOL fPerMachine = FALSE;
414 +
415 + // detect self by product code
416 + // TODO: what to do about MSIINSTALLCONTEXT_USERMANAGED?
417 + hr = WiuGetProductInfoEx(pPackage->Msi.sczProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion);
418 + if (SUCCEEDED(hr))
419 + {
420 + hr = FileVersionFromStringEx(sczInstalledVersion, 0, &pPackage->Msi.qwInstalledVersion);
421 + ExitOnFailure(hr, "Failed to convert version: %ls to DWORD64 for ProductCode: %ls", sczInstalledVersion, pPackage->Msi.sczProductCode);
422 +
423 + // compare versions
424 + if (pPackage->Msi.qwVersion < pPackage->Msi.qwInstalledVersion)
425 + {
426 + operation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE;
427 + pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED;
428 + }
429 + else
430 + {
431 + if (pPackage->Msi.qwVersion > pPackage->Msi.qwInstalledVersion)
432 + {
433 + operation = BOOTSTRAPPER_RELATED_OPERATION_MINOR_UPDATE;
434 + }
435 +
436 + pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT;
437 + }
438 +
439 + // Report related MSI package to BA.
440 + if (BOOTSTRAPPER_RELATED_OPERATION_NONE != operation)
441 + {
442 + LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_PACKAGE, pPackage->Msi.sczProductCode, LoggingPerMachineToString(pPackage->fPerMachine), LoggingVersionToString(pPackage->Msi.qwInstalledVersion), pPackage->Msi.dwLanguage, LoggingRelatedOperationToString(operation));
443 +
444 + hr = UserExperienceOnDetectRelatedMsiPackage(pUserExperience, pPackage->sczId, pPackage->Msi.sczUpgradeCode, pPackage->Msi.sczProductCode, pPackage->fPerMachine, pPackage->Msi.qwInstalledVersion, operation);
445 + ExitOnRootFailure(hr, "BA aborted detect related MSI package.");
446 + }
447 + }
448 + else if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr || HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) == hr) // package not present.
449 + {
450 + // Check for newer, compatible packages based on a fixed provider key.
451 + hr = DependencyDetectProviderKeyPackageId(pPackage, &sczInstalledProviderKey, &sczInstalledProductCode);
452 + if (SUCCEEDED(hr))
453 + {
454 + hr = WiuGetProductInfoEx(sczInstalledProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion);
455 + if (SUCCEEDED(hr))
456 + {
457 + hr = FileVersionFromStringEx(sczInstalledVersion, 0, &qwVersion);
458 + ExitOnFailure(hr, "Failed to convert version: %ls to DWORD64 for ProductCode: %ls", sczInstalledVersion, sczInstalledProductCode);
459 +
460 + if (pPackage->Msi.qwVersion < qwVersion)
461 + {
462 + LogId(REPORT_STANDARD, MSG_DETECTED_COMPATIBLE_PACKAGE_FROM_PROVIDER, pPackage->sczId, sczInstalledProviderKey, sczInstalledProductCode, sczInstalledVersion, pPackage->Msi.sczProductCode);
463 +
464 + hr = UserExperienceOnDetectCompatibleMsiPackage(pUserExperience, pPackage->sczId, sczInstalledProductCode, qwVersion);
465 + ExitOnRootFailure(hr, "BA aborted detect compatible MSI package.");
466 +
467 + hr = StrAllocString(&pPackage->Msi.sczInstalledProductCode, sczInstalledProductCode, 0);
468 + ExitOnFailure(hr, "Failed to copy the installed ProductCode to the package.");
469 +
470 + pPackage->Msi.qwInstalledVersion = qwVersion;
471 + pPackage->Msi.fCompatibleInstalled = TRUE;
472 + }
473 + }
474 + }
475 +
476 + pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
477 + hr = S_OK;
478 + }
479 + else
480 + {
481 + ExitOnFailure(hr, "Failed to get product information for ProductCode: %ls", pPackage->Msi.sczProductCode);
482 + }
483 +
484 + // detect related packages by upgrade code
485 + for (DWORD i = 0; i < pPackage->Msi.cRelatedMsis; ++i)
486 + {
487 + BURN_RELATED_MSI* pRelatedMsi = &pPackage->Msi.rgRelatedMsis[i];
488 +
489 + for (DWORD iProduct = 0; ; ++iProduct)
490 + {
491 + // get product
492 + hr = WiuEnumRelatedProducts(pRelatedMsi->sczUpgradeCode, iProduct, wzProductCode);
493 + if (E_NOMOREITEMS == hr)
494 + {
495 + hr = S_OK;
496 + break;
497 + }
498 + ExitOnFailure(hr, "Failed to enum related products.");
499 +
500 + // If we found ourselves, skip because saying that a package is related to itself is nonsensical.
501 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pPackage->Msi.sczProductCode, -1, wzProductCode, -1))
502 + {
503 + continue;
504 + }
505 +
506 + // get product version
507 + hr = WiuGetProductInfoEx(wzProductCode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion);
508 + if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) != hr && HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) != hr)
509 + {
510 + ExitOnFailure(hr, "Failed to get version for product in user unmanaged context: %ls", wzProductCode);
511 + fPerMachine = FALSE;
512 + }
513 + else
514 + {
515 + hr = WiuGetProductInfoEx(wzProductCode, NULL, MSIINSTALLCONTEXT_MACHINE, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion);
516 + if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) != hr && HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) != hr)
517 + {
518 + ExitOnFailure(hr, "Failed to get version for product in machine context: %ls", wzProductCode);
519 + fPerMachine = TRUE;
520 + }
521 + else
522 + {
523 + hr = S_OK;
524 + continue;
525 + }
526 + }
527 +
528 + hr = FileVersionFromStringEx(sczInstalledVersion, 0, &qwVersion);
529 + ExitOnFailure(hr, "Failed to convert version: %ls to DWORD64 for ProductCode: %ls", sczInstalledVersion, wzProductCode);
530 +
531 + // compare versions
532 + if (pRelatedMsi->fMinProvided && (pRelatedMsi->fMinInclusive ? (qwVersion < pRelatedMsi->qwMinVersion) : (qwVersion <= pRelatedMsi->qwMinVersion)))
533 + {
534 + continue;
535 + }
536 +
537 + if (pRelatedMsi->fMaxProvided && (pRelatedMsi->fMaxInclusive ? (qwVersion > pRelatedMsi->qwMaxVersion) : (qwVersion >= pRelatedMsi->qwMaxVersion)))
538 + {
539 + continue;
540 + }
541 +
542 + // Filter by language if necessary.
543 + uLcid = 0; // always reset the found language.
544 + if (pRelatedMsi->cLanguages)
545 + {
546 + // If there is a language to get, convert it into an LCID.
547 + hr = WiuGetProductInfoEx(wzProductCode, NULL, fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_LANGUAGE, &sczInstalledLanguage);
548 + if (SUCCEEDED(hr))
549 + {
550 + hr = StrStringToUInt32(sczInstalledLanguage, 0, &uLcid);
551 + }
552 +
553 + // Ignore related product where we can't read the language.
554 + if (FAILED(hr))
555 + {
556 + LogErrorId(hr, MSG_FAILED_READ_RELATED_PACKAGE_LANGUAGE, wzProductCode, sczInstalledLanguage, NULL);
557 +
558 + hr = S_OK;
559 + continue;
560 + }
561 +
562 + BOOL fMatchedLcid = FALSE;
563 + for (DWORD iLanguage = 0; iLanguage < pRelatedMsi->cLanguages; ++iLanguage)
564 + {
565 + if (uLcid == pRelatedMsi->rgdwLanguages[iLanguage])
566 + {
567 + fMatchedLcid = TRUE;
568 + break;
569 + }
570 + }
571 +
572 + // Skip the product if the language did not meet the inclusive/exclusive criteria.
573 + if ((pRelatedMsi->fLangInclusive && !fMatchedLcid) || (!pRelatedMsi->fLangInclusive && fMatchedLcid))
574 + {
575 + continue;
576 + }
577 + }
578 +
579 + // If this is a detect-only related package and we're not installed yet, then we'll assume a downgrade
580 + // would take place since that is the overwhelmingly common use of detect-only related packages. If
581 + // not detect-only then it's easy; we're clearly doing a major upgrade.
582 + if (pRelatedMsi->fOnlyDetect)
583 + {
584 + // If we've already detected a major upgrade that trumps any guesses that the detect is a downgrade
585 + // or even something else.
586 + if (BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE == operation)
587 + {
588 + relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_NONE;
589 + }
590 + // It can't be a downgrade if the upgrade codes aren't the same.
591 + else if (BOOTSTRAPPER_PACKAGE_STATE_ABSENT == pPackage->currentState &&
592 + pPackage->Msi.sczUpgradeCode && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pPackage->Msi.sczUpgradeCode, -1, pRelatedMsi->sczUpgradeCode, -1))
593 + {
594 + relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE;
595 + operation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE;
596 + pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE;
597 + }
598 + else // we're already on the machine so the detect-only *must* be for detection purposes only.
599 + {
600 + relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_NONE;
601 + }
602 + }
603 + else
604 + {
605 + relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE;
606 + operation = BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE;
607 + }
608 +
609 + LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_PACKAGE, wzProductCode, LoggingPerMachineToString(fPerMachine), LoggingVersionToString(qwVersion), uLcid, LoggingRelatedOperationToString(relatedMsiOperation));
610 +
611 + // Pass to BA.
612 + hr = UserExperienceOnDetectRelatedMsiPackage(pUserExperience, pPackage->sczId, pRelatedMsi->sczUpgradeCode, wzProductCode, fPerMachine, qwVersion, relatedMsiOperation);
613 + ExitOnRootFailure(hr, "BA aborted detect related MSI package.");
614 + }
615 + }
616 +
617 + // detect features
618 + if (pPackage->Msi.cFeatures)
619 + {
620 + for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i)
621 + {
622 + BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i];
623 +
624 + // Try to detect features state if the product is present on the machine.
625 + if (BOOTSTRAPPER_PACKAGE_STATE_PRESENT <= pPackage->currentState)
626 + {
627 + hr = WiuQueryFeatureState(pPackage->Msi.sczProductCode, pFeature->sczId, &installState);
628 + ExitOnFailure(hr, "Failed to query feature state.");
629 +
630 + if (INSTALLSTATE_UNKNOWN == installState) // in case of an upgrade a feature could be removed.
631 + {
632 + installState = INSTALLSTATE_ABSENT;
633 + }
634 + }
635 + else // MSI not installed then the features can't be either.
636 + {
637 + installState = INSTALLSTATE_ABSENT;
638 + }
639 +
640 + // set current state
641 + switch (installState)
642 + {
643 + case INSTALLSTATE_ABSENT:
644 + pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_ABSENT;
645 + break;
646 + case INSTALLSTATE_ADVERTISED:
647 + pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_ADVERTISED;
648 + break;
649 + case INSTALLSTATE_LOCAL:
650 + pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_LOCAL;
651 + break;
652 + case INSTALLSTATE_SOURCE:
653 + pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_SOURCE;
654 + break;
655 + default:
656 + hr = E_UNEXPECTED;
657 + ExitOnRootFailure(hr, "Invalid state value.");
658 + }
659 +
660 + // Pass to BA.
661 + hr = UserExperienceOnDetectMsiFeature(pUserExperience, pPackage->sczId, pFeature->sczId, pFeature->currentState);
662 + ExitOnRootFailure(hr, "BA aborted detect MSI feature.");
663 + }
664 + }
665 +
666 +LExit:
667 + ReleaseStr(sczInstalledProviderKey);
668 + ReleaseStr(sczInstalledProductCode);
669 + ReleaseStr(sczInstalledLanguage);
670 + ReleaseStr(sczInstalledVersion);
671 +
672 + return hr;
673 +}
674 +
675 +//
676 +// PlanCalculate - calculates the execute and rollback state for the requested package state.
677 +//
678 +extern "C" HRESULT MsiEnginePlanCalculatePackage(
679 + __in BURN_PACKAGE* pPackage,
680 + __in BURN_VARIABLES* pVariables,
681 + __in BURN_USER_EXPERIENCE* pUserExperience,
682 + __out BOOL* pfBARequestedCache
683 + )
684 +{
685 + Trace(REPORT_STANDARD, "Planning MSI package 0x%p", pPackage);
686 +
687 + HRESULT hr = S_OK;
688 + DWORD64 qwVersion = pPackage->Msi.qwVersion;
689 + DWORD64 qwInstalledVersion = pPackage->Msi.qwInstalledVersion;
690 + BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE;
691 + BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
692 + BOOL fFeatureActionDelta = FALSE;
693 + BOOL fRollbackFeatureActionDelta = FALSE;
694 + BOOL fBARequestedCache = FALSE;
695 +
696 + if (pPackage->Msi.cFeatures)
697 + {
698 + // If the package is present and we're repairing it.
699 + BOOL fRepairingPackage = (BOOTSTRAPPER_PACKAGE_STATE_CACHED < pPackage->currentState && BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested);
700 +
701 + LogId(REPORT_STANDARD, MSG_PLAN_MSI_FEATURES, pPackage->Msi.cFeatures, pPackage->sczId);
702 +
703 + // plan features
704 + for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i)
705 + {
706 + BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i];
707 + BOOTSTRAPPER_FEATURE_STATE defaultFeatureRequestedState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN;
708 + BOOTSTRAPPER_FEATURE_STATE featureRequestedState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN;
709 + BOOTSTRAPPER_FEATURE_STATE featureExpectedState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN;
710 +
711 + // Evaluate feature conditions.
712 + hr = EvaluateActionStateConditions(pVariables, pFeature->sczAddLocalCondition, pFeature->sczAddSourceCondition, pFeature->sczAdvertiseCondition, &defaultFeatureRequestedState);
713 + ExitOnFailure(hr, "Failed to evaluate requested state conditions.");
714 +
715 + hr = EvaluateActionStateConditions(pVariables, pFeature->sczRollbackAddLocalCondition, pFeature->sczRollbackAddSourceCondition, pFeature->sczRollbackAdvertiseCondition, &featureExpectedState);
716 + ExitOnFailure(hr, "Failed to evaluate expected state conditions.");
717 +
718 + // Remember the default feature requested state so the engine doesn't get blamed for planning the wrong thing if the BA changes it.
719 + featureRequestedState = defaultFeatureRequestedState;
720 +
721 + // Send plan MSI feature message to BA.
722 + hr = UserExperienceOnPlanMsiFeature(pUserExperience, pPackage->sczId, pFeature->sczId, &featureRequestedState);
723 + ExitOnRootFailure(hr, "BA aborted plan MSI feature.");
724 +
725 + // Calculate feature actions.
726 + hr = CalculateFeatureAction(pFeature->currentState, featureRequestedState, fRepairingPackage, &pFeature->execute, &fFeatureActionDelta);
727 + ExitOnFailure(hr, "Failed to calculate execute feature state.");
728 +
729 + hr = CalculateFeatureAction(featureRequestedState, BOOTSTRAPPER_FEATURE_ACTION_NONE == pFeature->execute ? featureExpectedState : pFeature->currentState, FALSE, &pFeature->rollback, &fRollbackFeatureActionDelta);
730 + ExitOnFailure(hr, "Failed to calculate rollback feature state.");
731 +
732 + LogId(REPORT_STANDARD, MSG_PLANNED_MSI_FEATURE, pFeature->sczId, LoggingMsiFeatureStateToString(pFeature->currentState), LoggingMsiFeatureStateToString(defaultFeatureRequestedState), LoggingMsiFeatureStateToString(featureRequestedState), LoggingMsiFeatureActionToString(pFeature->execute), LoggingMsiFeatureActionToString(pFeature->rollback));
733 + }
734 + }
735 +
736 + // execute action
737 + switch (pPackage->currentState)
738 + {
739 + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough;
740 + case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED:
741 + if (BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested || BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested)
742 + {
743 + // Take a look at the version and determine if this is a potential
744 + // minor upgrade (same ProductCode newer ProductVersion), otherwise,
745 + // there is a newer version so no work necessary.
746 + if (qwVersion > qwInstalledVersion)
747 + {
748 + execute = BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE;
749 + }
750 + else if (BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested)
751 + {
752 + execute = BOOTSTRAPPER_ACTION_STATE_REPAIR;
753 + }
754 + else
755 + {
756 + execute = fFeatureActionDelta ? BOOTSTRAPPER_ACTION_STATE_MODIFY : BOOTSTRAPPER_ACTION_STATE_NONE;
757 + }
758 + }
759 + else if ((BOOTSTRAPPER_REQUEST_STATE_ABSENT == pPackage->requested || BOOTSTRAPPER_REQUEST_STATE_CACHE == pPackage->requested) &&
760 + pPackage->fUninstallable) // removing a package that can be removed.
761 + {
762 + execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL;
763 + }
764 + else if (BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT == pPackage->requested)
765 + {
766 + execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL;
767 + }
768 + else
769 + {
770 + execute = BOOTSTRAPPER_ACTION_STATE_NONE;
771 + }
772 + break;
773 +
774 + case BOOTSTRAPPER_PACKAGE_STATE_CACHED:
775 + switch (pPackage->requested)
776 + {
777 + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
778 + case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
779 + execute = BOOTSTRAPPER_ACTION_STATE_INSTALL;
780 + break;
781 +
782 + default:
783 + execute = BOOTSTRAPPER_ACTION_STATE_NONE;
784 + break;
785 + }
786 + break;
787 +
788 + case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough;
789 + case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
790 + switch (pPackage->requested)
791 + {
792 + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
793 + case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
794 + execute = BOOTSTRAPPER_ACTION_STATE_INSTALL;
795 + break;
796 +
797 + case BOOTSTRAPPER_REQUEST_STATE_CACHE:
798 + execute = BOOTSTRAPPER_ACTION_STATE_NONE;
799 + fBARequestedCache = TRUE;
800 + break;
801 +
802 + default:
803 + execute = BOOTSTRAPPER_ACTION_STATE_NONE;
804 + break;
805 + }
806 + break;
807 +
808 + default:
809 + hr = E_INVALIDARG;
810 + ExitOnRootFailure(hr, "Invalid package current state result encountered during plan: %d", pPackage->currentState);
811 + }
812 +
813 + // Calculate the rollback action if there is an execute action.
814 + if (BOOTSTRAPPER_ACTION_STATE_NONE != execute)
815 + {
816 + switch (BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN != pPackage->expected ? pPackage->expected : pPackage->currentState)
817 + {
818 + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough;
819 + case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED:
820 + switch (pPackage->requested)
821 + {
822 + case BOOTSTRAPPER_REQUEST_STATE_PRESENT:
823 + rollback = fRollbackFeatureActionDelta ? BOOTSTRAPPER_ACTION_STATE_MODIFY : BOOTSTRAPPER_ACTION_STATE_NONE;
824 + break;
825 + case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
826 + rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
827 + break;
828 + case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough;
829 + case BOOTSTRAPPER_REQUEST_STATE_ABSENT:
830 + rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL;
831 + break;
832 + default:
833 + rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
834 + break;
835 + }
836 + break;
837 +
838 + case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough;
839 + case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: __fallthrough;
840 + case BOOTSTRAPPER_PACKAGE_STATE_CACHED:
841 + // If we requested to put the package on the machine then remove the package during rollback
842 + // if the package is uninstallable.
843 + if ((BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested || BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested) &&
844 + pPackage->fUninstallable)
845 + {
846 + rollback = BOOTSTRAPPER_ACTION_STATE_UNINSTALL;
847 + }
848 + else
849 + {
850 + rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
851 + }
852 + break;
853 +
854 + default:
855 + hr = E_INVALIDARG;
856 + ExitOnRootFailure(hr, "Invalid package detection result encountered.");
857 + }
858 + }
859 +
860 + // return values
861 + pPackage->execute = execute;
862 + pPackage->rollback = rollback;
863 +
864 + if (pfBARequestedCache)
865 + {
866 + *pfBARequestedCache = fBARequestedCache;
867 + }
868 +
869 +LExit:
870 + return hr;
871 +}
872 +
873 +//
874 +// PlanAdd - adds the calculated execute and rollback actions for the package.
875 +//
876 +extern "C" HRESULT MsiEnginePlanAddPackage(
877 + __in BOOTSTRAPPER_DISPLAY display,
878 + __in BURN_PACKAGE* pPackage,
879 + __in BURN_PLAN* pPlan,
880 + __in BURN_LOGGING* pLog,
881 + __in BURN_VARIABLES* pVariables,
882 + __in_opt HANDLE hCacheEvent,
883 + __in BOOL fPlanPackageCacheRollback
884 + )
885 +{
886 + HRESULT hr = S_OK;
887 + BURN_EXECUTE_ACTION* pAction = NULL;
888 + BOOTSTRAPPER_FEATURE_ACTION* rgFeatureActions = NULL;
889 + BOOTSTRAPPER_FEATURE_ACTION* rgRollbackFeatureActions = NULL;
890 +
891 + if (pPackage->Msi.cFeatures)
892 + {
893 + // Allocate and populate array for feature actions.
894 + rgFeatureActions = (BOOTSTRAPPER_FEATURE_ACTION*)MemAlloc(sizeof(BOOTSTRAPPER_FEATURE_ACTION) * pPackage->Msi.cFeatures, TRUE);
895 + ExitOnNull(rgFeatureActions, hr, E_OUTOFMEMORY, "Failed to allocate memory for feature actions.");
896 +
897 + rgRollbackFeatureActions = (BOOTSTRAPPER_FEATURE_ACTION*)MemAlloc(sizeof(BOOTSTRAPPER_FEATURE_ACTION) * pPackage->Msi.cFeatures, TRUE);
898 + ExitOnNull(rgRollbackFeatureActions, hr, E_OUTOFMEMORY, "Failed to allocate memory for rollback feature actions.");
899 +
900 + for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i)
901 + {
902 + BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i];
903 +
904 + // calculate feature actions
905 + rgFeatureActions[i] = pFeature->execute;
906 + rgRollbackFeatureActions[i] = pFeature->rollback;
907 + }
908 + }
909 +
910 + // add wait for cache
911 + if (hCacheEvent)
912 + {
913 + hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent, fPlanPackageCacheRollback);
914 + ExitOnFailure(hr, "Failed to plan package cache syncpoint");
915 + }
916 +
917 + hr = DependencyPlanPackage(NULL, pPackage, pPlan);
918 + ExitOnFailure(hr, "Failed to plan package dependency actions.");
919 +
920 + // add rollback action
921 + if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback)
922 + {
923 + hr = PlanAppendRollbackAction(pPlan, &pAction);
924 + ExitOnFailure(hr, "Failed to append rollback action.");
925 +
926 + pAction->type = BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE;
927 + pAction->msiPackage.pPackage = pPackage;
928 + pAction->msiPackage.action = pPackage->rollback;
929 + pAction->msiPackage.uiLevel = MsiEngineCalculateInstallUiLevel(pPackage->Msi.fDisplayInternalUI, display, pAction->msiPackage.action);
930 + pAction->msiPackage.rgFeatures = rgRollbackFeatureActions;
931 + rgRollbackFeatureActions = NULL;
932 +
933 + LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, &pAction->msiPackage.sczLogPath); // ignore errors.
934 + pAction->msiPackage.dwLoggingAttributes = pLog->dwAttributes;
935 +
936 + // Plan a checkpoint between rollback and execute so that we always attempt
937 + // rollback in the case that the MSI was not able to rollback itself (e.g.
938 + // user pushes cancel after InstallFinalize).
939 + hr = PlanExecuteCheckpoint(pPlan);
940 + ExitOnFailure(hr, "Failed to append execute checkpoint.");
941 + }
942 +
943 + // add execute action
944 + if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute)
945 + {
946 + hr = PlanAppendExecuteAction(pPlan, &pAction);
947 + ExitOnFailure(hr, "Failed to append execute action.");
948 +
949 + pAction->type = BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE;
950 + pAction->msiPackage.pPackage = pPackage;
951 + pAction->msiPackage.action = pPackage->execute;
952 + pAction->msiPackage.uiLevel = MsiEngineCalculateInstallUiLevel(pPackage->Msi.fDisplayInternalUI, display, pAction->msiPackage.action);
953 + pAction->msiPackage.rgFeatures = rgFeatureActions;
954 + rgFeatureActions = NULL;
955 +
956 + LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, &pAction->msiPackage.sczLogPath); // ignore errors.
957 + pAction->msiPackage.dwLoggingAttributes = pLog->dwAttributes;
958 + }
959 +
960 + // Update any slipstream patches' state.
961 + for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i)
962 + {
963 + BURN_PACKAGE* pMspPackage = pPackage->Msi.rgpSlipstreamMspPackages[i];
964 + AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Only MSP packages can be slipstream patches.");
965 +
966 + MspEngineSlipstreamUpdateState(pMspPackage, pPackage->execute, pPackage->rollback);
967 + }
968 +
969 +LExit:
970 + ReleaseMem(rgFeatureActions);
971 + ReleaseMem(rgRollbackFeatureActions);
972 +
973 + return hr;
974 +}
975 +
976 +extern "C" HRESULT MsiEngineAddCompatiblePackage(
977 + __in BURN_PACKAGES* pPackages,
978 + __in const BURN_PACKAGE* pPackage,
979 + __out_opt BURN_PACKAGE** ppCompatiblePackage
980 + )
981 +{
982 + Assert(BURN_PACKAGE_TYPE_MSI == pPackage->type);
983 +
984 + HRESULT hr = S_OK;
985 + BURN_PACKAGE* pCompatiblePackage = NULL;
986 + LPWSTR sczInstalledVersion = NULL;
987 +
988 + // Allocate enough memory all at once so pointers to packages within
989 + // aren't invalidated if we otherwise reallocated.
990 + hr = PackageEnsureCompatiblePackagesArray(pPackages);
991 + ExitOnFailure(hr, "Failed to allocate memory for compatible MSI package.");
992 +
993 + pCompatiblePackage = pPackages->rgCompatiblePackages + pPackages->cCompatiblePackages;
994 + ++pPackages->cCompatiblePackages;
995 +
996 + pCompatiblePackage->type = BURN_PACKAGE_TYPE_MSI;
997 +
998 + // Read in the compatible ProductCode if not already available.
999 + if (pPackage->Msi.sczInstalledProductCode)
1000 + {
1001 + hr = StrAllocString(&pCompatiblePackage->Msi.sczProductCode, pPackage->Msi.sczInstalledProductCode, 0);
1002 + ExitOnFailure(hr, "Failed to copy installed ProductCode to compatible package.");
1003 + }
1004 + else
1005 + {
1006 + hr = DependencyDetectProviderKeyPackageId(pPackage, NULL, &pCompatiblePackage->Msi.sczProductCode);
1007 + ExitOnFailure(hr, "Failed to detect compatible package from provider key.");
1008 + }
1009 +
1010 + // Read in the compatible ProductVersion if not already available.
1011 + if (pPackage->Msi.qwInstalledVersion)
1012 + {
1013 + pCompatiblePackage->Msi.qwVersion = pPackage->Msi.qwInstalledVersion;
1014 +
1015 + hr = FileVersionToStringEx(pCompatiblePackage->Msi.qwVersion, &sczInstalledVersion);
1016 + ExitOnFailure(hr, "Failed to format version number string.");
1017 + }
1018 + else
1019 + {
1020 + hr = WiuGetProductInfoEx(pCompatiblePackage->Msi.sczProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion);
1021 + ExitOnFailure(hr, "Failed to read version from compatible package.");
1022 +
1023 + hr = FileVersionFromStringEx(sczInstalledVersion, 0, &pCompatiblePackage->Msi.qwVersion);
1024 + ExitOnFailure(hr, "Failed to convert version: %ls to DWORD64 for ProductCode: %ls", sczInstalledVersion, pCompatiblePackage->Msi.sczProductCode);
1025 + }
1026 +
1027 + // For now, copy enough information to support uninstalling the newer, compatible package.
1028 + hr = StrAllocString(&pCompatiblePackage->sczId, pCompatiblePackage->Msi.sczProductCode, 0);
1029 + ExitOnFailure(hr, "Failed to copy installed ProductCode as compatible package ID.");
1030 +
1031 + pCompatiblePackage->fPerMachine = pPackage->fPerMachine;
1032 + pCompatiblePackage->fUninstallable = pPackage->fUninstallable;
1033 + pCompatiblePackage->cacheType = pPackage->cacheType;
1034 +
1035 + // Removing compatible packages is best effort.
1036 + pCompatiblePackage->fVital = FALSE;
1037 +
1038 + // Format a suitable log path variable from the original package.
1039 + hr = StrAllocFormatted(&pCompatiblePackage->sczLogPathVariable, L"%ls_Compatible", pPackage->sczLogPathVariable);
1040 + ExitOnFailure(hr, "Failed to format log path variable for compatible package.");
1041 +
1042 + // Use the default cache ID generation from the binder.
1043 + hr = StrAllocFormatted(&pCompatiblePackage->sczCacheId, L"%lsv%ls", pCompatiblePackage->sczId, sczInstalledVersion);
1044 + ExitOnFailure(hr, "Failed to format cache ID for compatible package.");
1045 +
1046 + pCompatiblePackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT;
1047 + pCompatiblePackage->cache = BURN_CACHE_STATE_PARTIAL; // Cannot know if it's complete or not.
1048 +
1049 + // Copy all the providers to ensure no dependents.
1050 + if (pPackage->cDependencyProviders)
1051 + {
1052 + pCompatiblePackage->rgDependencyProviders = (BURN_DEPENDENCY_PROVIDER*)MemAlloc(sizeof(BURN_DEPENDENCY_PROVIDER) * pPackage->cDependencyProviders, TRUE);
1053 + ExitOnNull(pCompatiblePackage->rgDependencyProviders, hr, E_OUTOFMEMORY, "Failed to allocate for compatible package providers.");
1054 +
1055 + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
1056 + {
1057 + BURN_DEPENDENCY_PROVIDER* pProvider = pPackage->rgDependencyProviders + i;
1058 + BURN_DEPENDENCY_PROVIDER* pCompatibleProvider = pCompatiblePackage->rgDependencyProviders + i;
1059 +
1060 + // Only need to copy the key for uninstall.
1061 + hr = StrAllocString(&pCompatibleProvider->sczKey, pProvider->sczKey, 0);
1062 + ExitOnFailure(hr, "Failed to copy the compatible provider key.");
1063 +
1064 + // Assume the package version is the same as the provider version.
1065 + hr = StrAllocString(&pCompatibleProvider->sczVersion, sczInstalledVersion, 0);
1066 + ExitOnFailure(hr, "Failed to copy the compatible provider version.");
1067 +
1068 + // Assume provider keys are similarly authored for this package.
1069 + pCompatibleProvider->fImported = pProvider->fImported;
1070 + }
1071 +
1072 + pCompatiblePackage->cDependencyProviders = pPackage->cDependencyProviders;
1073 + }
1074 +
1075 + pCompatiblePackage->type = BURN_PACKAGE_TYPE_MSI;
1076 + pCompatiblePackage->Msi.fDisplayInternalUI = pPackage->Msi.fDisplayInternalUI;
1077 +
1078 + if (ppCompatiblePackage)
1079 + {
1080 + *ppCompatiblePackage = pCompatiblePackage;
1081 + }
1082 +
1083 +LExit:
1084 + ReleaseStr(sczInstalledVersion);
1085 +
1086 + return hr;
1087 +}
1088 +
1089 +extern "C" HRESULT MsiEngineExecutePackage(
1090 + __in_opt HWND hwndParent,
1091 + __in BURN_EXECUTE_ACTION* pExecuteAction,
1092 + __in BURN_VARIABLES* pVariables,
1093 + __in BOOL fRollback,
1094 + __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
1095 + __in LPVOID pvContext,
1096 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart
1097 + )
1098 +{
1099 + HRESULT hr = S_OK;
1100 + WIU_MSI_EXECUTE_CONTEXT context = { };
1101 + WIU_RESTART restart = WIU_RESTART_NONE;
1102 +
1103 + LPWSTR sczInstalledVersion = NULL;
1104 + LPWSTR sczCachedDirectory = NULL;
1105 + LPWSTR sczMsiPath = NULL;
1106 + LPWSTR sczProperties = NULL;
1107 + LPWSTR sczObfuscatedProperties = NULL;
1108 +
1109 + // During rollback, if the package is already in the rollback state we expect don't
1110 + // touch it again.
1111 + if (fRollback)
1112 + {
1113 + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pExecuteAction->msiPackage.action)
1114 + {
1115 + hr = WiuGetProductInfoEx(pExecuteAction->msiPackage.pPackage->Msi.sczProductCode, NULL, pExecuteAction->msiPackage.pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion);
1116 + if (FAILED(hr)) // package not present.
1117 + {
1118 + LogId(REPORT_STANDARD, MSG_ROLLBACK_PACKAGE_SKIPPED, pExecuteAction->msiPackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->msiPackage.action), LoggingPackageStateToString(BOOTSTRAPPER_PACKAGE_STATE_ABSENT));
1119 +
1120 + hr = S_OK;
1121 + ExitFunction();
1122 + }
1123 + }
1124 + else if (BOOTSTRAPPER_ACTION_STATE_INSTALL == pExecuteAction->msiPackage.action)
1125 + {
1126 + hr = WiuGetProductInfoEx(pExecuteAction->msiPackage.pPackage->Msi.sczProductCode, NULL, pExecuteAction->msiPackage.pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion);
1127 + if (SUCCEEDED(hr)) // package present.
1128 + {
1129 + LogId(REPORT_STANDARD, MSG_ROLLBACK_PACKAGE_SKIPPED, pExecuteAction->msiPackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->msiPackage.action), LoggingPackageStateToString(BOOTSTRAPPER_PACKAGE_STATE_PRESENT));
1130 +
1131 + hr = S_OK;
1132 + ExitFunction();
1133 + }
1134 +
1135 + hr = S_OK;
1136 + }
1137 + }
1138 +
1139 + // Default to "verbose" logging and set extra debug mode only if explicitly required.
1140 + DWORD dwLogMode = WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE;
1141 +
1142 + if (pExecuteAction->msiPackage.dwLoggingAttributes & BURN_LOGGING_ATTRIBUTE_EXTRADEBUG)
1143 + {
1144 + dwLogMode |= INSTALLLOGMODE_EXTRADEBUG;
1145 + }
1146 +
1147 + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL != pExecuteAction->msiPackage.action)
1148 + {
1149 + // get cached MSI path
1150 + hr = CacheGetCompletedPath(pExecuteAction->msiPackage.pPackage->fPerMachine, pExecuteAction->msiPackage.pPackage->sczCacheId, &sczCachedDirectory);
1151 + ExitOnFailure(hr, "Failed to get cached path for package: %ls", pExecuteAction->msiPackage.pPackage->sczId);
1152 +
1153 + // Best effort to set the execute package cache folder variable.
1154 + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE);
1155 +
1156 + hr = PathConcat(sczCachedDirectory, pExecuteAction->msiPackage.pPackage->rgPayloads[0].pPayload->sczFilePath, &sczMsiPath);
1157 + ExitOnFailure(hr, "Failed to build MSI path.");
1158 + }
1159 +
1160 + // Best effort to set the execute package action variable.
1161 + VariableSetNumeric(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, pExecuteAction->msiPackage.action, TRUE);
1162 +
1163 + // Wire up the external UI handler and logging.
1164 + hr = WiuInitializeExternalUI(pfnMessageHandler, pExecuteAction->msiPackage.uiLevel, hwndParent, pvContext, fRollback, &context);
1165 + ExitOnFailure(hr, "Failed to initialize external UI handler.");
1166 +
1167 + if (pExecuteAction->msiPackage.sczLogPath && *pExecuteAction->msiPackage.sczLogPath)
1168 + {
1169 + hr = WiuEnableLog(dwLogMode, pExecuteAction->msiPackage.sczLogPath, 0);
1170 + ExitOnFailure(hr, "Failed to enable logging for package: %ls to: %ls", pExecuteAction->msiPackage.pPackage->sczId, pExecuteAction->msiPackage.sczLogPath);
1171 + }
1172 +
1173 + // set up properties
1174 + hr = MsiEngineConcatProperties(pExecuteAction->msiPackage.pPackage->Msi.rgProperties, pExecuteAction->msiPackage.pPackage->Msi.cProperties, pVariables, fRollback, &sczProperties, FALSE);
1175 + ExitOnFailure(hr, "Failed to add properties to argument string.");
1176 +
1177 + hr = MsiEngineConcatProperties(pExecuteAction->msiPackage.pPackage->Msi.rgProperties, pExecuteAction->msiPackage.pPackage->Msi.cProperties, pVariables, fRollback, &sczObfuscatedProperties, TRUE);
1178 + ExitOnFailure(hr, "Failed to add obfuscated properties to argument string.");
1179 +
1180 + // add feature action properties
1181 + hr = ConcatFeatureActionProperties(pExecuteAction->msiPackage.pPackage, pExecuteAction->msiPackage.rgFeatures, &sczProperties);
1182 + ExitOnFailure(hr, "Failed to add feature action properties to argument string.");
1183 +
1184 + hr = ConcatFeatureActionProperties(pExecuteAction->msiPackage.pPackage, pExecuteAction->msiPackage.rgFeatures, &sczObfuscatedProperties);
1185 + ExitOnFailure(hr, "Failed to add feature action properties to obfuscated argument string.");
1186 +
1187 + // add slipstream patch properties
1188 + hr = ConcatPatchProperty(pExecuteAction->msiPackage.pPackage, pExecuteAction->msiPackage.rgSlipstreamPatches, &sczProperties);
1189 + ExitOnFailure(hr, "Failed to add patch properties to argument string.");
1190 +
1191 + hr = ConcatPatchProperty(pExecuteAction->msiPackage.pPackage, pExecuteAction->msiPackage.rgSlipstreamPatches, &sczObfuscatedProperties);
1192 + ExitOnFailure(hr, "Failed to add patch properties to obfuscated argument string.");
1193 +
1194 + LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pExecuteAction->msiPackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->msiPackage.action), sczMsiPath, sczObfuscatedProperties ? sczObfuscatedProperties : L"");
1195 +
1196 + //
1197 + // Do the actual action.
1198 + //
1199 + switch (pExecuteAction->msiPackage.action)
1200 + {
1201 + case BOOTSTRAPPER_ACTION_STATE_ADMIN_INSTALL:
1202 + hr = StrAllocConcatSecure(&sczProperties, L" ACTION=ADMIN", 0);
1203 + ExitOnFailure(hr, "Failed to add ADMIN property on admin install.");
1204 + __fallthrough;
1205 +
1206 + case BOOTSTRAPPER_ACTION_STATE_MAJOR_UPGRADE: __fallthrough;
1207 + case BOOTSTRAPPER_ACTION_STATE_INSTALL:
1208 + hr = StrAllocConcatSecure(&sczProperties, L" REBOOT=ReallySuppress", 0);
1209 + ExitOnFailure(hr, "Failed to add reboot suppression property on install.");
1210 +
1211 + hr = WiuInstallProduct(sczMsiPath, sczProperties, &restart);
1212 + ExitOnFailure(hr, "Failed to install MSI package.");
1213 +
1214 + RegisterSourceDirectory(pExecuteAction->msiPackage.pPackage, sczMsiPath);
1215 + break;
1216 +
1217 + case BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE:
1218 + // If feature selection is not enabled, then reinstall the existing features to ensure they get
1219 + // updated.
1220 + if (0 == pExecuteAction->msiPackage.pPackage->Msi.cFeatures)
1221 + {
1222 + hr = StrAllocConcatSecure(&sczProperties, L" REINSTALL=ALL", 0);
1223 + ExitOnFailure(hr, "Failed to add reinstall all property on minor upgrade.");
1224 + }
1225 +
1226 + hr = StrAllocConcatSecure(&sczProperties, L" REINSTALLMODE=\"vomus\" REBOOT=ReallySuppress", 0);
1227 + ExitOnFailure(hr, "Failed to add reinstall mode and reboot suppression properties on minor upgrade.");
1228 +
1229 + hr = WiuInstallProduct(sczMsiPath, sczProperties, &restart);
1230 + ExitOnFailure(hr, "Failed to perform minor upgrade of MSI package.");
1231 +
1232 + RegisterSourceDirectory(pExecuteAction->msiPackage.pPackage, sczMsiPath);
1233 + break;
1234 +
1235 + case BOOTSTRAPPER_ACTION_STATE_MODIFY: __fallthrough;
1236 + case BOOTSTRAPPER_ACTION_STATE_REPAIR:
1237 + {
1238 + LPCWSTR wzReinstallAll = (BOOTSTRAPPER_ACTION_STATE_MODIFY == pExecuteAction->msiPackage.action ||
1239 + pExecuteAction->msiPackage.pPackage->Msi.cFeatures) ? L"" : L" REINSTALL=ALL";
1240 + LPCWSTR wzReinstallMode = (BOOTSTRAPPER_ACTION_STATE_MODIFY == pExecuteAction->msiPackage.action) ? L"o" : L"e";
1241 +
1242 + hr = StrAllocFormattedSecure(&sczProperties, L"%ls%ls REINSTALLMODE=\"cmus%ls\" REBOOT=ReallySuppress", sczProperties ? sczProperties : L"", wzReinstallAll, wzReinstallMode);
1243 + ExitOnFailure(hr, "Failed to add reinstall mode and reboot suppression properties on repair.");
1244 + }
1245 +
1246 + // Ignore all dependencies, since the Burn engine already performed the check.
1247 + hr = StrAllocFormattedSecure(&sczProperties, L"%ls %ls=ALL", sczProperties, DEPENDENCY_IGNOREDEPENDENCIES);
1248 + ExitOnFailure(hr, "Failed to add the list of dependencies to ignore to the properties.");
1249 +
1250 + hr = WiuInstallProduct(sczMsiPath, sczProperties, &restart);
1251 + ExitOnFailure(hr, "Failed to run maintenance mode for MSI package.");
1252 + break;
1253 +
1254 + case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
1255 + hr = StrAllocConcatSecure(&sczProperties, L" REBOOT=ReallySuppress", 0);
1256 + ExitOnFailure(hr, "Failed to add reboot suppression property on uninstall.");
1257 +
1258 + // Ignore all dependencies, since the Burn engine already performed the check.
1259 + hr = StrAllocFormattedSecure(&sczProperties, L"%ls %ls=ALL", sczProperties, DEPENDENCY_IGNOREDEPENDENCIES);
1260 + ExitOnFailure(hr, "Failed to add the list of dependencies to ignore to the properties.");
1261 +
1262 + hr = WiuConfigureProductEx(pExecuteAction->msiPackage.pPackage->Msi.sczProductCode, INSTALLLEVEL_DEFAULT, INSTALLSTATE_ABSENT, sczProperties, &restart);
1263 + if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr)
1264 + {
1265 + LogId(REPORT_STANDARD, MSG_ATTEMPTED_UNINSTALL_ABSENT_PACKAGE, pExecuteAction->msiPackage.pPackage->sczId);
1266 + hr = S_OK;
1267 + }
1268 + ExitOnFailure(hr, "Failed to uninstall MSI package.");
1269 + break;
1270 + }
1271 +
1272 +LExit:
1273 + WiuUninitializeExternalUI(&context);
1274 +
1275 + StrSecureZeroFreeString(sczProperties);
1276 + ReleaseStr(sczObfuscatedProperties);
1277 + ReleaseStr(sczMsiPath);
1278 + ReleaseStr(sczCachedDirectory);
1279 + ReleaseStr(sczInstalledVersion);
1280 +
1281 + switch (restart)
1282 + {
1283 + case WIU_RESTART_NONE:
1284 + *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
1285 + break;
1286 +
1287 + case WIU_RESTART_REQUIRED:
1288 + *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED;
1289 + break;
1290 +
1291 + case WIU_RESTART_INITIATED:
1292 + *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED;
1293 + break;
1294 + }
1295 +
1296 + // Best effort to clear the execute package cache folder and action variables.
1297 + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE);
1298 + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE);
1299 +
1300 + return hr;
1301 +}
1302 +
1303 +// The contents of psczProperties may be sensitive, should keep encrypted and SecureZeroFree.
1304 +extern "C" HRESULT MsiEngineConcatProperties(
1305 + __in_ecount(cProperties) BURN_MSIPROPERTY* rgProperties,
1306 + __in DWORD cProperties,
1307 + __in BURN_VARIABLES* pVariables,
1308 + __in BOOL fRollback,
1309 + __deref_out_z LPWSTR* psczProperties,
1310 + __in BOOL fObfuscateHiddenVariables
1311 + )
1312 +{
1313 + HRESULT hr = S_OK;
1314 + LPWSTR sczValue = NULL;
1315 + LPWSTR sczEscapedValue = NULL;
1316 + LPWSTR sczProperty = NULL;
1317 +
1318 + for (DWORD i = 0; i < cProperties; ++i)
1319 + {
1320 + BURN_MSIPROPERTY* pProperty = &rgProperties[i];
1321 +
1322 + if (pProperty->sczCondition && *pProperty->sczCondition)
1323 + {
1324 + BOOL fCondition = FALSE;
1325 +
1326 + hr = ConditionEvaluate(pVariables, pProperty->sczCondition, &fCondition);
1327 + if (FAILED(hr) || !fCondition)
1328 + {
1329 + LogId(REPORT_VERBOSE, MSG_MSI_PROPERTY_CONDITION_FAILED, pProperty->sczId, pProperty->sczCondition, LoggingTrueFalseToString(fCondition));
1330 + continue;
1331 + }
1332 + }
1333 +
1334 + // format property value
1335 + if (fObfuscateHiddenVariables)
1336 + {
1337 + hr = VariableFormatStringObfuscated(pVariables, (fRollback && pProperty->sczRollbackValue) ? pProperty->sczRollbackValue : pProperty->sczValue, &sczValue, NULL);
1338 + }
1339 + else
1340 + {
1341 + hr = VariableFormatString(pVariables, (fRollback && pProperty->sczRollbackValue) ? pProperty->sczRollbackValue : pProperty->sczValue, &sczValue, NULL);
1342 + ExitOnFailure(hr, "Failed to format property value.");
1343 + }
1344 + ExitOnFailure(hr, "Failed to format property value.");
1345 +
1346 + // escape property value
1347 + hr = EscapePropertyArgumentString(sczValue, &sczEscapedValue, !fObfuscateHiddenVariables);
1348 + ExitOnFailure(hr, "Failed to escape string.");
1349 +
1350 + // build part
1351 + hr = VariableStrAllocFormatted(!fObfuscateHiddenVariables, &sczProperty, L" %s%=\"%s\"", pProperty->sczId, sczEscapedValue);
1352 + ExitOnFailure(hr, "Failed to format property string part.");
1353 +
1354 + // append to property string
1355 + hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, psczProperties, sczProperty, 0);
1356 + ExitOnFailure(hr, "Failed to append property string part.");
1357 + }
1358 +
1359 +LExit:
1360 + StrSecureZeroFreeString(sczValue);
1361 + StrSecureZeroFreeString(sczEscapedValue);
1362 + StrSecureZeroFreeString(sczProperty);
1363 + return hr;
1364 +}
1365 +
1366 +extern "C" INSTALLUILEVEL MsiEngineCalculateInstallUiLevel(
1367 + __in BOOL fDisplayInternalUI,
1368 + __in BOOTSTRAPPER_DISPLAY display,
1369 + __in BOOTSTRAPPER_ACTION_STATE actionState
1370 + )
1371 +{
1372 + // Assume there will be no internal UI displayed.
1373 + INSTALLUILEVEL uiLevel = static_cast<INSTALLUILEVEL>(INSTALLUILEVEL_NONE | INSTALLUILEVEL_SOURCERESONLY);
1374 +
1375 + // suppress internal UI during uninstall to mimic ARP and "msiexec /x" behavior
1376 + if (fDisplayInternalUI && BOOTSTRAPPER_ACTION_STATE_UNINSTALL != actionState && BOOTSTRAPPER_ACTION_STATE_REPAIR != actionState)
1377 + {
1378 + switch (display)
1379 + {
1380 + case BOOTSTRAPPER_DISPLAY_FULL:
1381 + uiLevel = INSTALLUILEVEL_FULL;
1382 + break;
1383 +
1384 + case BOOTSTRAPPER_DISPLAY_PASSIVE:
1385 + uiLevel = INSTALLUILEVEL_REDUCED;
1386 + break;
1387 + }
1388 + }
1389 +
1390 + return uiLevel;
1391 +}
1392 +
1393 +
1394 +// internal helper functions
1395 +
1396 +static HRESULT ParseRelatedMsiFromXml(
1397 + __in IXMLDOMNode* pixnRelatedMsi,
1398 + __in BURN_RELATED_MSI* pRelatedMsi
1399 + )
1400 +{
1401 + HRESULT hr = S_OK;
1402 + IXMLDOMNodeList* pixnNodes = NULL;
1403 + IXMLDOMNode* pixnNode = NULL;
1404 + DWORD cNodes = 0;
1405 + LPWSTR scz = NULL;
1406 +
1407 + // @Id
1408 + hr = XmlGetAttributeEx(pixnRelatedMsi, L"Id", &pRelatedMsi->sczUpgradeCode);
1409 + ExitOnFailure(hr, "Failed to get @Id.");
1410 +
1411 + // @MinVersion
1412 + hr = XmlGetAttributeEx(pixnRelatedMsi, L"MinVersion", &scz);
1413 + if (E_NOTFOUND != hr)
1414 + {
1415 + ExitOnFailure(hr, "Failed to get @MinVersion.");
1416 +
1417 + hr = FileVersionFromStringEx(scz, 0, &pRelatedMsi->qwMinVersion);
1418 + ExitOnFailure(hr, "Failed to parse @MinVersion: %ls", scz);
1419 +
1420 + // flag that we have a min version
1421 + pRelatedMsi->fMinProvided = TRUE;
1422 +
1423 + // @MinInclusive
1424 + hr = XmlGetYesNoAttribute(pixnRelatedMsi, L"MinInclusive", &pRelatedMsi->fMinInclusive);
1425 + ExitOnFailure(hr, "Failed to get @MinInclusive.");
1426 + }
1427 +
1428 + // @MaxVersion
1429 + hr = XmlGetAttributeEx(pixnRelatedMsi, L"MaxVersion", &scz);
1430 + if (E_NOTFOUND != hr)
1431 + {
1432 + ExitOnFailure(hr, "Failed to get @MaxVersion.");
1433 +
1434 + hr = FileVersionFromStringEx(scz, 0, &pRelatedMsi->qwMaxVersion);
1435 + ExitOnFailure(hr, "Failed to parse @MaxVersion: %ls", scz);
1436 +
1437 + // flag that we have a max version
1438 + pRelatedMsi->fMaxProvided = TRUE;
1439 +
1440 + // @MaxInclusive
1441 + hr = XmlGetYesNoAttribute(pixnRelatedMsi, L"MaxInclusive", &pRelatedMsi->fMaxInclusive);
1442 + ExitOnFailure(hr, "Failed to get @MaxInclusive.");
1443 + }
1444 +
1445 + // @OnlyDetect
1446 + hr = XmlGetYesNoAttribute(pixnRelatedMsi, L"OnlyDetect", &pRelatedMsi->fOnlyDetect);
1447 + ExitOnFailure(hr, "Failed to get @OnlyDetect.");
1448 +
1449 + // select language nodes
1450 + hr = XmlSelectNodes(pixnRelatedMsi, L"Language", &pixnNodes);
1451 + ExitOnFailure(hr, "Failed to select language nodes.");
1452 +
1453 + // get language node count
1454 + hr = pixnNodes->get_length((long*)&cNodes);
1455 + ExitOnFailure(hr, "Failed to get language node count.");
1456 +
1457 + if (cNodes)
1458 + {
1459 + // @LangInclusive
1460 + hr = XmlGetYesNoAttribute(pixnRelatedMsi, L"LangInclusive", &pRelatedMsi->fLangInclusive);
1461 + ExitOnFailure(hr, "Failed to get @LangInclusive.");
1462 +
1463 + // allocate memory for language IDs
1464 + pRelatedMsi->rgdwLanguages = (DWORD*)MemAlloc(sizeof(DWORD) * cNodes, TRUE);
1465 + ExitOnNull(pRelatedMsi->rgdwLanguages, hr, E_OUTOFMEMORY, "Failed to allocate memory for language IDs.");
1466 +
1467 + pRelatedMsi->cLanguages = cNodes;
1468 +
1469 + // parse language elements
1470 + for (DWORD i = 0; i < cNodes; ++i)
1471 + {
1472 + hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
1473 + ExitOnFailure(hr, "Failed to get next node.");
1474 +
1475 + // @Id
1476 + hr = XmlGetAttributeNumber(pixnNode, L"Id", &pRelatedMsi->rgdwLanguages[i]);
1477 + ExitOnFailure(hr, "Failed to get Language/@Id.");
1478 +
1479 + // prepare next iteration
1480 + ReleaseNullObject(pixnNode);
1481 + }
1482 + }
1483 +
1484 + hr = S_OK;
1485 +
1486 +LExit:
1487 + ReleaseObject(pixnNodes);
1488 + ReleaseObject(pixnNode);
1489 + ReleaseStr(scz);
1490 +
1491 + return hr;
1492 +}
1493 +
1494 +static HRESULT EvaluateActionStateConditions(
1495 + __in BURN_VARIABLES* pVariables,
1496 + __in_z_opt LPCWSTR sczAddLocalCondition,
1497 + __in_z_opt LPCWSTR sczAddSourceCondition,
1498 + __in_z_opt LPCWSTR sczAdvertiseCondition,
1499 + __out BOOTSTRAPPER_FEATURE_STATE* pState
1500 + )
1501 +{
1502 + HRESULT hr = S_OK;
1503 + BOOL fCondition = FALSE;
1504 +
1505 + // if no condition was set, return no feature state
1506 + if (!sczAddLocalCondition && !sczAddSourceCondition && !sczAdvertiseCondition)
1507 + {
1508 + *pState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN;
1509 + ExitFunction();
1510 + }
1511 +
1512 + if (sczAddLocalCondition)
1513 + {
1514 + hr = ConditionEvaluate(pVariables, sczAddLocalCondition, &fCondition);
1515 + ExitOnFailure(hr, "Failed to evaluate add local condition.");
1516 +
1517 + if (fCondition)
1518 + {
1519 + *pState = BOOTSTRAPPER_FEATURE_STATE_LOCAL;
1520 + ExitFunction();
1521 + }
1522 + }
1523 +
1524 + if (sczAddSourceCondition)
1525 + {
1526 + hr = ConditionEvaluate(pVariables, sczAddSourceCondition, &fCondition);
1527 + ExitOnFailure(hr, "Failed to evaluate add source condition.");
1528 +
1529 + if (fCondition)
1530 + {
1531 + *pState = BOOTSTRAPPER_FEATURE_STATE_SOURCE;
1532 + ExitFunction();
1533 + }
1534 + }
1535 +
1536 + if (sczAdvertiseCondition)
1537 + {
1538 + hr = ConditionEvaluate(pVariables, sczAdvertiseCondition, &fCondition);
1539 + ExitOnFailure(hr, "Failed to evaluate advertise condition.");
1540 +
1541 + if (fCondition)
1542 + {
1543 + *pState = BOOTSTRAPPER_FEATURE_STATE_ADVERTISED;
1544 + ExitFunction();
1545 + }
1546 + }
1547 +
1548 + // if no condition was true, set to absent
1549 + *pState = BOOTSTRAPPER_FEATURE_STATE_ABSENT;
1550 +
1551 +LExit:
1552 + return hr;
1553 +}
1554 +
1555 +static HRESULT CalculateFeatureAction(
1556 + __in BOOTSTRAPPER_FEATURE_STATE currentState,
1557 + __in BOOTSTRAPPER_FEATURE_STATE requestedState,
1558 + __in BOOL fRepair,
1559 + __out BOOTSTRAPPER_FEATURE_ACTION* pFeatureAction,
1560 + __inout BOOL* pfDelta
1561 + )
1562 +{
1563 + HRESULT hr = S_OK;
1564 +
1565 + *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_NONE;
1566 + switch (requestedState)
1567 + {
1568 + case BOOTSTRAPPER_FEATURE_STATE_UNKNOWN:
1569 + *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_NONE;
1570 + break;
1571 +
1572 + case BOOTSTRAPPER_FEATURE_STATE_ABSENT:
1573 + if (BOOTSTRAPPER_FEATURE_STATE_ABSENT != currentState)
1574 + {
1575 + *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_REMOVE;
1576 + }
1577 + break;
1578 +
1579 + case BOOTSTRAPPER_FEATURE_STATE_ADVERTISED:
1580 + if (BOOTSTRAPPER_FEATURE_STATE_ADVERTISED != currentState)
1581 + {
1582 + *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_ADVERTISE;
1583 + }
1584 + else if (fRepair)
1585 + {
1586 + *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_REINSTALL;
1587 + }
1588 + break;
1589 +
1590 + case BOOTSTRAPPER_FEATURE_STATE_LOCAL:
1591 + if (BOOTSTRAPPER_FEATURE_STATE_LOCAL != currentState)
1592 + {
1593 + *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_ADDLOCAL;
1594 + }
1595 + else if (fRepair)
1596 + {
1597 + *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_REINSTALL;
1598 + }
1599 + break;
1600 +
1601 + case BOOTSTRAPPER_FEATURE_STATE_SOURCE:
1602 + if (BOOTSTRAPPER_FEATURE_STATE_SOURCE != currentState)
1603 + {
1604 + *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_ADDSOURCE;
1605 + }
1606 + else if (fRepair)
1607 + {
1608 + *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_REINSTALL;
1609 + }
1610 + break;
1611 +
1612 + default:
1613 + hr = E_UNEXPECTED;
1614 + ExitOnRootFailure(hr, "Invalid state value.");
1615 + }
1616 +
1617 + if (BOOTSTRAPPER_FEATURE_ACTION_NONE != *pFeatureAction)
1618 + {
1619 + *pfDelta = TRUE;
1620 + }
1621 +
1622 +LExit:
1623 + return hr;
1624 +}
1625 +
1626 +static HRESULT EscapePropertyArgumentString(
1627 + __in LPCWSTR wzProperty,
1628 + __inout_z LPWSTR* psczEscapedValue,
1629 + __in BOOL fZeroOnRealloc
1630 + )
1631 +{
1632 + HRESULT hr = S_OK;
1633 + DWORD cch = 0;
1634 + DWORD cchEscape = 0;
1635 + LPCWSTR wzSource = NULL;
1636 + LPWSTR wzTarget = NULL;
1637 +
1638 + // count characters to escape
1639 + wzSource = wzProperty;
1640 + while (*wzSource)
1641 + {
1642 + ++cch;
1643 + if (L'\"' == *wzSource)
1644 + {
1645 + ++cchEscape;
1646 + }
1647 + ++wzSource;
1648 + }
1649 +
1650 + // allocate target buffer
1651 + hr = VariableStrAlloc(fZeroOnRealloc, psczEscapedValue, cch + cchEscape + 1); // character count, plus escape character count, plus null terminator
1652 + ExitOnFailure(hr, "Failed to allocate string buffer.");
1653 +
1654 + // write to target buffer
1655 + wzSource = wzProperty;
1656 + wzTarget = *psczEscapedValue;
1657 + while (*wzSource)
1658 + {
1659 + *wzTarget = *wzSource;
1660 + if (L'\"' == *wzTarget)
1661 + {
1662 + ++wzTarget;
1663 + *wzTarget = L'\"';
1664 + }
1665 +
1666 + ++wzSource;
1667 + ++wzTarget;
1668 + }
1669 +
1670 + *wzTarget = L'\0'; // add null terminator
1671 +
1672 +LExit:
1673 + return hr;
1674 +}
1675 +
1676 +static HRESULT ConcatFeatureActionProperties(
1677 + __in BURN_PACKAGE* pPackage,
1678 + __in BOOTSTRAPPER_FEATURE_ACTION* rgFeatureActions,
1679 + __inout_z LPWSTR* psczArguments
1680 + )
1681 +{
1682 + HRESULT hr = S_OK;
1683 + LPWSTR scz = NULL;
1684 + LPWSTR sczAddLocal = NULL;
1685 + LPWSTR sczAddSource = NULL;
1686 + LPWSTR sczAddDefault = NULL;
1687 + LPWSTR sczReinstall = NULL;
1688 + LPWSTR sczAdvertise = NULL;
1689 + LPWSTR sczRemove = NULL;
1690 +
1691 + // features
1692 + for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i)
1693 + {
1694 + BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i];
1695 +
1696 + switch (rgFeatureActions[i])
1697 + {
1698 + case BOOTSTRAPPER_FEATURE_ACTION_ADDLOCAL:
1699 + if (sczAddLocal)
1700 + {
1701 + hr = StrAllocConcat(&sczAddLocal, L",", 0);
1702 + ExitOnFailure(hr, "Failed to concat separator.");
1703 + }
1704 + hr = StrAllocConcat(&sczAddLocal, pFeature->sczId, 0);
1705 + ExitOnFailure(hr, "Failed to concat feature.");
1706 + break;
1707 +
1708 + case BOOTSTRAPPER_FEATURE_ACTION_ADDSOURCE:
1709 + if (sczAddSource)
1710 + {
1711 + hr = StrAllocConcat(&sczAddSource, L",", 0);
1712 + ExitOnFailure(hr, "Failed to concat separator.");
1713 + }
1714 + hr = StrAllocConcat(&sczAddSource, pFeature->sczId, 0);
1715 + ExitOnFailure(hr, "Failed to concat feature.");
1716 + break;
1717 +
1718 + case BOOTSTRAPPER_FEATURE_ACTION_ADDDEFAULT:
1719 + if (sczAddDefault)
1720 + {
1721 + hr = StrAllocConcat(&sczAddDefault, L",", 0);
1722 + ExitOnFailure(hr, "Failed to concat separator.");
1723 + }
1724 + hr = StrAllocConcat(&sczAddDefault, pFeature->sczId, 0);
1725 + ExitOnFailure(hr, "Failed to concat feature.");
1726 + break;
1727 +
1728 + case BOOTSTRAPPER_FEATURE_ACTION_REINSTALL:
1729 + if (sczReinstall)
1730 + {
1731 + hr = StrAllocConcat(&sczReinstall, L",", 0);
1732 + ExitOnFailure(hr, "Failed to concat separator.");
1733 + }
1734 + hr = StrAllocConcat(&sczReinstall, pFeature->sczId, 0);
1735 + ExitOnFailure(hr, "Failed to concat feature.");
1736 + break;
1737 +
1738 + case BOOTSTRAPPER_FEATURE_ACTION_ADVERTISE:
1739 + if (sczAdvertise)
1740 + {
1741 + hr = StrAllocConcat(&sczAdvertise, L",", 0);
1742 + ExitOnFailure(hr, "Failed to concat separator.");
1743 + }
1744 + hr = StrAllocConcat(&sczAdvertise, pFeature->sczId, 0);
1745 + ExitOnFailure(hr, "Failed to concat feature.");
1746 + break;
1747 +
1748 + case BOOTSTRAPPER_FEATURE_ACTION_REMOVE:
1749 + if (sczRemove)
1750 + {
1751 + hr = StrAllocConcat(&sczRemove, L",", 0);
1752 + ExitOnFailure(hr, "Failed to concat separator.");
1753 + }
1754 + hr = StrAllocConcat(&sczRemove, pFeature->sczId, 0);
1755 + ExitOnFailure(hr, "Failed to concat feature.");
1756 + break;
1757 + }
1758 + }
1759 +
1760 + if (sczAddLocal)
1761 + {
1762 + hr = StrAllocFormatted(&scz, L" ADDLOCAL=\"%s\"", sczAddLocal, 0);
1763 + ExitOnFailure(hr, "Failed to format ADDLOCAL string.");
1764 +
1765 + hr = StrAllocConcatSecure(psczArguments, scz, 0);
1766 + ExitOnFailure(hr, "Failed to concat argument string.");
1767 + }
1768 +
1769 + if (sczAddSource)
1770 + {
1771 + hr = StrAllocFormatted(&scz, L" ADDSOURCE=\"%s\"", sczAddSource, 0);
1772 + ExitOnFailure(hr, "Failed to format ADDSOURCE string.");
1773 +
1774 + hr = StrAllocConcatSecure(psczArguments, scz, 0);
1775 + ExitOnFailure(hr, "Failed to concat argument string.");
1776 + }
1777 +
1778 + if (sczAddDefault)
1779 + {
1780 + hr = StrAllocFormatted(&scz, L" ADDDEFAULT=\"%s\"", sczAddDefault, 0);
1781 + ExitOnFailure(hr, "Failed to format ADDDEFAULT string.");
1782 +
1783 + hr = StrAllocConcatSecure(psczArguments, scz, 0);
1784 + ExitOnFailure(hr, "Failed to concat argument string.");
1785 + }
1786 +
1787 + if (sczReinstall)
1788 + {
1789 + hr = StrAllocFormatted(&scz, L" REINSTALL=\"%s\"", sczReinstall, 0);
1790 + ExitOnFailure(hr, "Failed to format REINSTALL string.");
1791 +
1792 + hr = StrAllocConcatSecure(psczArguments, scz, 0);
1793 + ExitOnFailure(hr, "Failed to concat argument string.");
1794 + }
1795 +
1796 + if (sczAdvertise)
1797 + {
1798 + hr = StrAllocFormatted(&scz, L" ADVERTISE=\"%s\"", sczAdvertise, 0);
1799 + ExitOnFailure(hr, "Failed to format ADVERTISE string.");
1800 +
1801 + hr = StrAllocConcatSecure(psczArguments, scz, 0);
1802 + ExitOnFailure(hr, "Failed to concat argument string.");
1803 + }
1804 +
1805 + if (sczRemove)
1806 + {
1807 + hr = StrAllocFormatted(&scz, L" REMOVE=\"%s\"", sczRemove, 0);
1808 + ExitOnFailure(hr, "Failed to format REMOVE string.");
1809 +
1810 + hr = StrAllocConcatSecure(psczArguments, scz, 0);
1811 + ExitOnFailure(hr, "Failed to concat argument string.");
1812 + }
1813 +
1814 +LExit:
1815 + ReleaseStr(scz);
1816 + ReleaseStr(sczAddLocal);
1817 + ReleaseStr(sczAddSource);
1818 + ReleaseStr(sczAddDefault);
1819 + ReleaseStr(sczReinstall);
1820 + ReleaseStr(sczAdvertise);
1821 + ReleaseStr(sczRemove);
1822 +
1823 + return hr;
1824 +}
1825 +
1826 +static HRESULT ConcatPatchProperty(
1827 + __in BURN_PACKAGE* pPackage,
1828 + __in_opt BOOTSTRAPPER_ACTION_STATE* rgSlipstreamPatchActions,
1829 + __inout_z LPWSTR* psczArguments
1830 + )
1831 +{
1832 + HRESULT hr = S_OK;
1833 + LPWSTR sczCachedDirectory = NULL;
1834 + LPWSTR sczMspPath = NULL;
1835 + LPWSTR sczPatches = NULL;
1836 +
1837 + // If there are slipstream patch actions, build up their patch action.
1838 + if (rgSlipstreamPatchActions)
1839 + {
1840 + for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i)
1841 + {
1842 + BURN_PACKAGE* pMspPackage = pPackage->Msi.rgpSlipstreamMspPackages[i];
1843 + AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Only MSP packages can be slipstream patches.");
1844 +
1845 + BOOTSTRAPPER_ACTION_STATE patchExecuteAction = rgSlipstreamPatchActions[i];
1846 + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL < patchExecuteAction)
1847 + {
1848 + hr = CacheGetCompletedPath(pMspPackage->fPerMachine, pMspPackage->sczCacheId, &sczCachedDirectory);
1849 + ExitOnFailure(hr, "Failed to get cached path for MSP package: %ls", pMspPackage->sczId);
1850 +
1851 + hr = PathConcat(sczCachedDirectory, pMspPackage->rgPayloads[0].pPayload->sczFilePath, &sczMspPath);
1852 + ExitOnFailure(hr, "Failed to build MSP path.");
1853 +
1854 + if (!sczPatches)
1855 + {
1856 + hr = StrAllocConcat(&sczPatches, L" PATCH=\"", 0);
1857 + ExitOnFailure(hr, "Failed to prefix with PATCH property.");
1858 + }
1859 + else
1860 + {
1861 + hr = StrAllocConcat(&sczPatches, L";", 0);
1862 + ExitOnFailure(hr, "Failed to semi-colon delimit patches.");
1863 + }
1864 +
1865 + hr = StrAllocConcat(&sczPatches, sczMspPath, 0);
1866 + ExitOnFailure(hr, "Failed to append patch path.");
1867 + }
1868 + }
1869 +
1870 + if (sczPatches)
1871 + {
1872 + hr = StrAllocConcat(&sczPatches, L"\"", 0);
1873 + ExitOnFailure(hr, "Failed to close the quoted PATCH property.");
1874 +
1875 + hr = StrAllocConcatSecure(psczArguments, sczPatches, 0);
1876 + ExitOnFailure(hr, "Failed to append PATCH property.");
1877 + }
1878 + }
1879 +
1880 +LExit:
1881 + ReleaseStr(sczMspPath);
1882 + ReleaseStr(sczCachedDirectory);
1883 + ReleaseStr(sczPatches);
1884 + return hr;
1885 +}
1886 +
1887 +static void RegisterSourceDirectory(
1888 + __in BURN_PACKAGE* pPackage,
1889 + __in_z LPCWSTR wzMsiPath
1890 + )
1891 +{
1892 + HRESULT hr = S_OK;
1893 + LPWSTR sczMsiDirectory = NULL;
1894 + MSIINSTALLCONTEXT dwContext = pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED;
1895 +
1896 + hr = PathGetDirectory(wzMsiPath, &sczMsiDirectory);
1897 + ExitOnFailure(hr, "Failed to get directory for path: %ls", wzMsiPath);
1898 +
1899 + hr = WiuSourceListAddSourceEx(pPackage->Msi.sczProductCode, NULL, dwContext, MSICODE_PRODUCT, sczMsiDirectory, 1);
1900 + if (FAILED(hr))
1901 + {
1902 + LogId(REPORT_VERBOSE, MSG_SOURCELIST_REGISTER, sczMsiDirectory, pPackage->Msi.sczProductCode, hr);
1903 + ExitFunction();
1904 + }
1905 +
1906 +LExit:
1907 + ReleaseStr(sczMsiDirectory);
1908 +
1909 + return;
1910 +}
src/engine/msiengine.h new
+73
@@ -0,0 +1,73 @@
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 +#if defined(__cplusplus)
6 +extern "C" {
7 +#endif
8 +
9 +
10 +// function declarations
11 +
12 +HRESULT MsiEngineParsePackageFromXml(
13 + __in IXMLDOMNode* pixnBundle,
14 + __in BURN_PACKAGE* pPackage
15 + );
16 +HRESULT MsiEngineParsePropertiesFromXml(
17 + __in IXMLDOMNode* pixnPackage,
18 + __out BURN_MSIPROPERTY** prgProperties,
19 + __out DWORD* pcProperties
20 + );
21 +void MsiEnginePackageUninitialize(
22 + __in BURN_PACKAGE* pPackage
23 + );
24 +HRESULT MsiEngineDetectPackage(
25 + __in BURN_PACKAGE* pPackage,
26 + __in BURN_USER_EXPERIENCE* pUserExperience
27 + );
28 +HRESULT MsiEnginePlanCalculatePackage(
29 + __in BURN_PACKAGE* pPackage,
30 + __in BURN_VARIABLES* pVariables,
31 + __in BURN_USER_EXPERIENCE* pUserExperience,
32 + __out_opt BOOL* pfBARequestedCache
33 + );
34 +HRESULT MsiEnginePlanAddPackage(
35 + __in BOOTSTRAPPER_DISPLAY display,
36 + __in BURN_PACKAGE* pPackage,
37 + __in BURN_PLAN* pPlan,
38 + __in BURN_LOGGING* pLog,
39 + __in BURN_VARIABLES* pVariables,
40 + __in_opt HANDLE hCacheEvent,
41 + __in BOOL fPlanPackageCacheRollback
42 + );
43 +HRESULT MsiEngineAddCompatiblePackage(
44 + __in BURN_PACKAGES* pPackages,
45 + __in const BURN_PACKAGE* pPackage,
46 + __out_opt BURN_PACKAGE** ppCompatiblePackage
47 + );
48 +HRESULT MsiEngineExecutePackage(
49 + __in_opt HWND hwndParent,
50 + __in BURN_EXECUTE_ACTION* pExecuteAction,
51 + __in BURN_VARIABLES* pVariables,
52 + __in BOOL fRollback,
53 + __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
54 + __in LPVOID pvContext,
55 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart
56 + );
57 +HRESULT MsiEngineConcatProperties(
58 + __in_ecount(cProperties) BURN_MSIPROPERTY* rgProperties,
59 + __in DWORD cProperties,
60 + __in BURN_VARIABLES* pVariables,
61 + __in BOOL fRollback,
62 + __deref_out_z LPWSTR* psczProperties,
63 + __in BOOL fObfuscateHiddenVariables
64 + );
65 +INSTALLUILEVEL MsiEngineCalculateInstallUiLevel(
66 + __in BOOL fDisplayInternalUI,
67 + __in BOOTSTRAPPER_DISPLAY display,
68 + __in BOOTSTRAPPER_ACTION_STATE actionState
69 + );
70 +
71 +#if defined(__cplusplus)
72 +}
73 +#endif
src/engine/mspengine.cpp new
+980
@@ -0,0 +1,980 @@
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 +// constants
7 +
8 +
9 +// structs
10 +
11 +struct POSSIBLE_TARGETPRODUCT
12 +{
13 + WCHAR wzProductCode[39];
14 + LPWSTR pszLocalPackage;
15 + MSIINSTALLCONTEXT context;
16 +};
17 +
18 +// internal function declarations
19 +
20 +static HRESULT GetPossibleTargetProductCodes(
21 + __in BURN_PACKAGES* pPackages,
22 + __deref_inout_ecount_opt(*pcPossibleTargetProductCodes) POSSIBLE_TARGETPRODUCT** prgPossibleTargetProductCodes,
23 + __inout DWORD* pcPossibleTargetProductCodes
24 + );
25 +static HRESULT AddPossibleTargetProduct(
26 + __in STRINGDICT_HANDLE sdUniquePossibleTargetProductCodes,
27 + __in_z LPCWSTR wzPossibleTargetProductCode,
28 + __in MSIINSTALLCONTEXT context,
29 + __deref_inout_ecount_opt(*pcPossibleTargetProducts) POSSIBLE_TARGETPRODUCT** prgPossibleTargetProducts,
30 + __inout DWORD* pcPossibleTargetProducts
31 + );
32 +static HRESULT AddDetectedTargetProduct(
33 + __in BURN_PACKAGES* pPackages,
34 + __in BURN_PACKAGE* pPackage,
35 + __in DWORD dwOrder,
36 + __in_z LPCWSTR wzProductCode,
37 + __in MSIINSTALLCONTEXT context
38 + );
39 +static void DeterminePatchChainedTarget(
40 + __in BURN_PACKAGES* pPackages,
41 + __in BURN_PACKAGE* pMspPackage,
42 + __in LPCWSTR wzTargetProductCode,
43 + __out BURN_PACKAGE** ppChainedTargetPackage,
44 + __out BOOL* pfSlipstreamed
45 + );
46 +static HRESULT PlanTargetProduct(
47 + __in BOOTSTRAPPER_DISPLAY display,
48 + __in BOOL fRollback,
49 + __in BURN_PLAN* pPlan,
50 + __in BURN_LOGGING* pLog,
51 + __in BURN_VARIABLES* pVariables,
52 + __in BOOTSTRAPPER_ACTION_STATE actionState,
53 + __in BURN_PACKAGE* pPackage,
54 + __in BURN_MSPTARGETPRODUCT* pTargetProduct,
55 + __in_opt HANDLE hCacheEvent
56 + );
57 +
58 +
59 +// function definitions
60 +
61 +extern "C" HRESULT MspEngineParsePackageFromXml(
62 + __in IXMLDOMNode* pixnMspPackage,
63 + __in BURN_PACKAGE* pPackage
64 + )
65 +{
66 + HRESULT hr = S_OK;
67 +
68 + // @PatchCode
69 + hr = XmlGetAttributeEx(pixnMspPackage, L"PatchCode", &pPackage->Msp.sczPatchCode);
70 + ExitOnFailure(hr, "Failed to get @PatchCode.");
71 +
72 + // @PatchXml
73 + hr = XmlGetAttributeEx(pixnMspPackage, L"PatchXml", &pPackage->Msp.sczApplicabilityXml);
74 + ExitOnFailure(hr, "Failed to get @PatchXml.");
75 +
76 + // @DisplayInternalUI
77 + hr = XmlGetYesNoAttribute(pixnMspPackage, L"DisplayInternalUI", &pPackage->Msp.fDisplayInternalUI);
78 + ExitOnFailure(hr, "Failed to get @DisplayInternalUI.");
79 +
80 + // Read properties.
81 + hr = MsiEngineParsePropertiesFromXml(pixnMspPackage, &pPackage->Msp.rgProperties, &pPackage->Msp.cProperties);
82 + ExitOnFailure(hr, "Failed to parse properties from XML.");
83 +
84 +LExit:
85 +
86 + return hr;
87 +}
88 +
89 +extern "C" void MspEnginePackageUninitialize(
90 + __in BURN_PACKAGE* pPackage
91 + )
92 +{
93 + ReleaseStr(pPackage->Msp.sczPatchCode);
94 + ReleaseStr(pPackage->Msp.sczApplicabilityXml);
95 +
96 + // free properties
97 + if (pPackage->Msp.rgProperties)
98 + {
99 + for (DWORD i = 0; i < pPackage->Msp.cProperties; ++i)
100 + {
101 + BURN_MSIPROPERTY* pProperty = &pPackage->Msp.rgProperties[i];
102 +
103 + ReleaseStr(pProperty->sczId);
104 + ReleaseStr(pProperty->sczValue);
105 + ReleaseStr(pProperty->sczRollbackValue);
106 + }
107 + MemFree(pPackage->Msp.rgProperties);
108 + }
109 +
110 + // free target products
111 + ReleaseMem(pPackage->Msp.rgTargetProducts);
112 +
113 + // clear struct
114 + memset(&pPackage->Msp, 0, sizeof(pPackage->Msp));
115 +}
116 +
117 +extern "C" HRESULT MspEngineDetectInitialize(
118 + __in BURN_PACKAGES* pPackages
119 + )
120 +{
121 + AssertSz(pPackages->cPatchInfo, "MspEngineDetectInitialize() should only be called if there are MSP packages.");
122 +
123 + HRESULT hr = S_OK;
124 + POSSIBLE_TARGETPRODUCT* rgPossibleTargetProducts = NULL;
125 + DWORD cPossibleTargetProducts = 0;
126 +
127 +#ifdef DEBUG
128 + // All patch info should be initialized to zero.
129 + for (DWORD i = 0; i < pPackages->cPatchInfo; ++i)
130 + {
131 + BURN_PACKAGE* pPackage = pPackages->rgPatchInfoToPackage[i];
132 + Assert(!pPackage->Msp.cTargetProductCodes);
133 + Assert(!pPackage->Msp.rgTargetProducts);
134 + }
135 +#endif
136 +
137 + // Figure out which product codes to target on the machine. In the worst case all products on the machine
138 + // will be returned.
139 + hr = GetPossibleTargetProductCodes(pPackages, &rgPossibleTargetProducts, &cPossibleTargetProducts);
140 + ExitOnFailure(hr, "Failed to get possible target product codes.");
141 +
142 + // Loop through possible target products, testing the collective patch applicability against each product in
143 + // the appropriate context. Store the result with the appropriate patch package.
144 + for (DWORD iSearch = 0; iSearch < cPossibleTargetProducts; ++iSearch)
145 + {
146 + const POSSIBLE_TARGETPRODUCT* pPossibleTargetProduct = rgPossibleTargetProducts + iSearch;
147 +
148 + LogId(REPORT_STANDARD, MSG_DETECT_CALCULATE_PATCH_APPLICABILITY, pPossibleTargetProduct->wzProductCode, LoggingMsiInstallContext(pPossibleTargetProduct->context));
149 +
150 + if (pPossibleTargetProduct->pszLocalPackage)
151 + {
152 + // Ignores current machine state to determine just patch applicability.
153 + // Superseded and obsolesced patches will be planned separately.
154 + hr = WiuDetermineApplicablePatches(pPossibleTargetProduct->pszLocalPackage, pPackages->rgPatchInfo, pPackages->cPatchInfo);
155 + }
156 + else
157 + {
158 + hr = WiuDeterminePatchSequence(pPossibleTargetProduct->wzProductCode, NULL, pPossibleTargetProduct->context, pPackages->rgPatchInfo, pPackages->cPatchInfo);
159 + }
160 +
161 + if (SUCCEEDED(hr))
162 + {
163 + for (DWORD iPatchInfo = 0; iPatchInfo < pPackages->cPatchInfo; ++iPatchInfo)
164 + {
165 + if (ERROR_SUCCESS == pPackages->rgPatchInfo[iPatchInfo].uStatus)
166 + {
167 + BURN_PACKAGE* pMspPackage = pPackages->rgPatchInfoToPackage[iPatchInfo];
168 + Assert(BURN_PACKAGE_TYPE_MSP == pMspPackage->type);
169 +
170 + // Note that we do add superseded and obsolete MSP packages. Package Detect and Plan will sort them out later.
171 + hr = AddDetectedTargetProduct(pPackages, pMspPackage, pPackages->rgPatchInfo[iPatchInfo].dwOrder, pPossibleTargetProduct->wzProductCode, pPossibleTargetProduct->context);
172 + ExitOnFailure(hr, "Failed to add target product code to package: %ls", pMspPackage->sczId);
173 + }
174 + // TODO: should we log something for this error case?
175 + }
176 + }
177 + else
178 + {
179 + LogId(REPORT_STANDARD, MSG_DETECT_FAILED_CALCULATE_PATCH_APPLICABILITY, pPossibleTargetProduct->wzProductCode, LoggingMsiInstallContext(pPossibleTargetProduct->context), hr);
180 + }
181 +
182 + hr = S_OK; // always reset so we test all possible target products.
183 + }
184 +
185 +LExit:
186 + if (rgPossibleTargetProducts)
187 + {
188 + for (DWORD i = 0; i < cPossibleTargetProducts; ++i)
189 + {
190 + ReleaseStr(rgPossibleTargetProducts[i].pszLocalPackage);
191 + }
192 + MemFree(rgPossibleTargetProducts);
193 + }
194 +
195 + return hr;
196 +}
197 +
198 +extern "C" HRESULT MspEngineDetectPackage(
199 + __in BURN_PACKAGE* pPackage,
200 + __in BURN_USER_EXPERIENCE* pUserExperience
201 + )
202 +{
203 + HRESULT hr = S_OK;
204 + LPWSTR sczState = NULL;
205 +
206 + if (0 == pPackage->Msp.cTargetProductCodes)
207 + {
208 + pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
209 + }
210 + else
211 + {
212 + // Start the package state at the the highest state then loop through all the
213 + // target product codes and end up setting the current state to the lowest
214 + // package state applied to the the target product codes.
215 + pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED;
216 +
217 + for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i)
218 + {
219 + BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i;
220 +
221 + hr = WiuGetPatchInfoEx(pPackage->Msp.sczPatchCode, pTargetProduct->wzTargetProductCode, NULL, pTargetProduct->context, INSTALLPROPERTY_PATCHSTATE, &sczState);
222 + if (SUCCEEDED(hr))
223 + {
224 + switch (*sczState)
225 + {
226 + case '1':
227 + pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT;
228 + break;
229 +
230 + case '2':
231 + pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED;
232 + break;
233 +
234 + case '4':
235 + pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE;
236 + break;
237 +
238 + default:
239 + pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
240 + break;
241 + }
242 + }
243 + else if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PATCH) == hr)
244 + {
245 + pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
246 + hr = S_OK;
247 + }
248 + ExitOnFailure(hr, "Failed to get patch information for patch code: %ls, target product code: %ls", pPackage->Msp.sczPatchCode, pTargetProduct->wzTargetProductCode);
249 +
250 + if (pPackage->currentState > pTargetProduct->patchPackageState)
251 + {
252 + pPackage->currentState = pTargetProduct->patchPackageState;
253 + }
254 +
255 + hr = UserExperienceOnDetectTargetMsiPackage(pUserExperience, pPackage->sczId, pTargetProduct->wzTargetProductCode, pTargetProduct->patchPackageState);
256 + ExitOnRootFailure(hr, "BA aborted detect target MSI package.");
257 + }
258 + }
259 +
260 +LExit:
261 + ReleaseStr(sczState);
262 +
263 + return hr;
264 +}
265 +
266 +//
267 +// PlanCalculate - calculates the execute and rollback state for the requested package state.
268 +//
269 +extern "C" HRESULT MspEnginePlanCalculatePackage(
270 + __in BURN_PACKAGE* pPackage,
271 + __in BURN_USER_EXPERIENCE* pUserExperience,
272 + __out BOOL* pfBARequestedCache
273 + )
274 +{
275 + HRESULT hr = S_OK;
276 + BOOL fBARequestedCache = FALSE;
277 +
278 + for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i)
279 + {
280 + BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i;
281 +
282 + BOOTSTRAPPER_REQUEST_STATE requested = pPackage->requested;
283 + BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE;
284 + BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
285 +
286 + hr = UserExperienceOnPlanTargetMsiPackage(pUserExperience, pPackage->sczId, pTargetProduct->wzTargetProductCode, &requested);
287 + ExitOnRootFailure(hr, "BA aborted plan target MSI package.");
288 +
289 + // Calculate the execute action.
290 + switch (pTargetProduct->patchPackageState)
291 + {
292 + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
293 + switch (requested)
294 + {
295 + case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
296 + execute = BOOTSTRAPPER_ACTION_STATE_REPAIR;
297 + break;
298 +
299 + case BOOTSTRAPPER_REQUEST_STATE_ABSENT: __fallthrough;
300 + case BOOTSTRAPPER_REQUEST_STATE_CACHE:
301 + execute = pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
302 + break;
303 +
304 + case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT:
305 + execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL;
306 + break;
307 +
308 + default:
309 + execute = BOOTSTRAPPER_ACTION_STATE_NONE;
310 + break;
311 + }
312 + break;
313 +
314 + case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
315 + switch (requested)
316 + {
317 + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
318 + case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
319 + execute = BOOTSTRAPPER_ACTION_STATE_INSTALL;
320 + break;
321 +
322 + case BOOTSTRAPPER_REQUEST_STATE_CACHE:
323 + execute = BOOTSTRAPPER_ACTION_STATE_NONE;
324 + fBARequestedCache = TRUE;
325 + break;
326 +
327 + default:
328 + execute = BOOTSTRAPPER_ACTION_STATE_NONE;
329 + break;
330 + }
331 + break;
332 + }
333 +
334 + // Calculate the rollback action if there is an execute action.
335 + if (BOOTSTRAPPER_ACTION_STATE_NONE != execute)
336 + {
337 + switch (BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN != pPackage->expected ? pPackage->expected : pPackage->currentState)
338 + {
339 + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
340 + switch (requested)
341 + {
342 + case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough;
343 + case BOOTSTRAPPER_REQUEST_STATE_ABSENT:
344 + rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL;
345 + break;
346 +
347 + default:
348 + rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
349 + break;
350 + }
351 + break;
352 +
353 + case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: __fallthrough;
354 + case BOOTSTRAPPER_PACKAGE_STATE_CACHED:
355 + switch (requested)
356 + {
357 + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
358 + case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
359 + rollback = pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
360 + break;
361 +
362 + default:
363 + rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
364 + break;
365 + }
366 + break;
367 +
368 + default:
369 + rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
370 + break;
371 + }
372 + }
373 +
374 + pTargetProduct->execute = execute;
375 + pTargetProduct->rollback = rollback;
376 +
377 + // The highest aggregate action state found will be returned.
378 + if (pPackage->execute < execute)
379 + {
380 + pPackage->execute = execute;
381 + }
382 +
383 + if (pPackage->rollback < rollback)
384 + {
385 + pPackage->rollback = rollback;
386 + }
387 + }
388 +
389 + if (pfBARequestedCache)
390 + {
391 + *pfBARequestedCache = fBARequestedCache;
392 + }
393 +
394 +LExit:
395 +
396 + return hr;
397 +}
398 +
399 +//
400 +// PlanAdd - adds the calculated execute and rollback actions for the package.
401 +//
402 +extern "C" HRESULT MspEnginePlanAddPackage(
403 + __in BOOTSTRAPPER_DISPLAY display,
404 + __in BURN_PACKAGE* pPackage,
405 + __in BURN_PLAN* pPlan,
406 + __in BURN_LOGGING* pLog,
407 + __in BURN_VARIABLES* pVariables,
408 + __in_opt HANDLE hCacheEvent,
409 + __in BOOL fPlanPackageCacheRollback
410 + )
411 +{
412 + HRESULT hr = S_OK;
413 +
414 + // TODO: need to handle the case where this patch adds itself to an earlier patch's list of target products. That would
415 + // essentially bump this patch earlier in the plan and we need to make sure this patch is downloaded.
416 + // add wait for cache
417 + if (hCacheEvent)
418 + {
419 + hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent, fPlanPackageCacheRollback);
420 + ExitOnFailure(hr, "Failed to plan package cache syncpoint");
421 + }
422 +
423 + hr = DependencyPlanPackage(NULL, pPackage, pPlan);
424 + ExitOnFailure(hr, "Failed to plan package dependency actions.");
425 +
426 + // Plan the actions for each target product code.
427 + for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i)
428 + {
429 + BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i;
430 +
431 + // If the dependency manager changed the action state for the patch, change the target product actions.
432 + if (pPackage->fDependencyManagerWasHere)
433 + {
434 + pTargetProduct->execute = pPackage->execute;
435 + pTargetProduct->rollback = pPackage->rollback;
436 + }
437 +
438 + if (BOOTSTRAPPER_ACTION_STATE_NONE != pTargetProduct->execute)
439 + {
440 + hr = PlanTargetProduct(display, FALSE, pPlan, pLog, pVariables, pTargetProduct->execute, pPackage, pTargetProduct, hCacheEvent);
441 + ExitOnFailure(hr, "Failed to plan target product.");
442 + }
443 +
444 + if (BOOTSTRAPPER_ACTION_STATE_NONE != pTargetProduct->rollback)
445 + {
446 + hr = PlanTargetProduct(display, TRUE, pPlan, pLog, pVariables, pTargetProduct->rollback, pPackage, pTargetProduct, hCacheEvent);
447 + ExitOnFailure(hr, "Failed to plan rollack target product.");
448 + }
449 + }
450 +
451 +LExit:
452 +
453 + return hr;
454 +}
455 +
456 +extern "C" HRESULT MspEngineExecutePackage(
457 + __in_opt HWND hwndParent,
458 + __in BURN_EXECUTE_ACTION* pExecuteAction,
459 + __in BURN_VARIABLES* pVariables,
460 + __in BOOL fRollback,
461 + __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
462 + __in LPVOID pvContext,
463 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart
464 + )
465 +{
466 + HRESULT hr = S_OK;
467 + INSTALLUILEVEL uiLevel = pExecuteAction->mspTarget.pPackage->Msp.fDisplayInternalUI ? INSTALLUILEVEL_DEFAULT : static_cast<INSTALLUILEVEL>(INSTALLUILEVEL_NONE | INSTALLUILEVEL_SOURCERESONLY);
468 + WIU_MSI_EXECUTE_CONTEXT context = { };
469 + WIU_RESTART restart = WIU_RESTART_NONE;
470 +
471 + LPWSTR sczCachedDirectory = NULL;
472 + LPWSTR sczMspPath = NULL;
473 + LPWSTR sczPatches = NULL;
474 + LPWSTR sczProperties = NULL;
475 + LPWSTR sczObfuscatedProperties = NULL;
476 +
477 + // default to "verbose" logging
478 + DWORD dwLogMode = WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE;
479 +
480 + // get cached MSP paths
481 + for (DWORD i = 0; i < pExecuteAction->mspTarget.cOrderedPatches; ++i)
482 + {
483 + LPCWSTR wzAppend = NULL;
484 + BURN_PACKAGE* pMspPackage = pExecuteAction->mspTarget.rgOrderedPatches[i].pPackage;
485 + AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Invalid package type added to ordered patches.");
486 +
487 + if (BOOTSTRAPPER_ACTION_STATE_INSTALL == pExecuteAction->mspTarget.action)
488 + {
489 + hr = CacheGetCompletedPath(pMspPackage->fPerMachine, pMspPackage->sczCacheId, &sczCachedDirectory);
490 + ExitOnFailure(hr, "Failed to get cached path for MSP package: %ls", pMspPackage->sczId);
491 +
492 + // TODO: Figure out if this makes sense -- the variable is set to the last patch's path only
493 + // Best effort to set the execute package cache folder variable.
494 + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE);
495 +
496 + hr = PathConcat(sczCachedDirectory, pMspPackage->rgPayloads[0].pPayload->sczFilePath, &sczMspPath);
497 + ExitOnFailure(hr, "Failed to build MSP path.");
498 +
499 + wzAppend = sczMspPath;
500 + }
501 + else // uninstall
502 + {
503 + wzAppend = pMspPackage->Msp.sczPatchCode;
504 + }
505 +
506 + if (NULL != sczPatches)
507 + {
508 + hr = StrAllocConcat(&sczPatches, L";", 0);
509 + ExitOnFailure(hr, "Failed to semi-colon delimit patches.");
510 + }
511 +
512 + hr = StrAllocConcat(&sczPatches, wzAppend, 0);
513 + ExitOnFailure(hr, "Failed to append patch.");
514 + }
515 +
516 + // Best effort to set the execute package action variable.
517 + VariableSetNumeric(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, pExecuteAction->mspTarget.action, TRUE);
518 +
519 + // Wire up the external UI handler and logging.
520 + hr = WiuInitializeExternalUI(pfnMessageHandler, uiLevel, hwndParent, pvContext, fRollback, &context);
521 + ExitOnFailure(hr, "Failed to initialize external UI handler.");
522 +
523 + //if (BURN_LOGGING_LEVEL_DEBUG == logLevel)
524 + //{
525 + // dwLogMode | INSTALLLOGMODE_EXTRADEBUG;
526 + //}
527 +
528 + if (pExecuteAction->mspTarget.sczLogPath && *pExecuteAction->mspTarget.sczLogPath)
529 + {
530 + hr = WiuEnableLog(dwLogMode, pExecuteAction->mspTarget.sczLogPath, 0);
531 + ExitOnFailure(hr, "Failed to enable logging for package: %ls to: %ls", pExecuteAction->mspTarget.pPackage->sczId, pExecuteAction->mspTarget.sczLogPath);
532 + }
533 +
534 + // set up properties
535 + hr = MsiEngineConcatProperties(pExecuteAction->mspTarget.pPackage->Msp.rgProperties, pExecuteAction->mspTarget.pPackage->Msp.cProperties, pVariables, fRollback, &sczProperties, FALSE);
536 + ExitOnFailure(hr, "Failed to add properties to argument string.");
537 +
538 + hr = MsiEngineConcatProperties(pExecuteAction->mspTarget.pPackage->Msp.rgProperties, pExecuteAction->mspTarget.pPackage->Msp.cProperties, pVariables, fRollback, &sczObfuscatedProperties, TRUE);
539 + ExitOnFailure(hr, "Failed to add properties to obfuscated argument string.");
540 +
541 + LogId(REPORT_STANDARD, MSG_APPLYING_PATCH_PACKAGE, pExecuteAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pExecuteAction->mspTarget.action), sczPatches, sczObfuscatedProperties, pExecuteAction->mspTarget.sczTargetProductCode);
542 +
543 + //
544 + // Do the actual action.
545 + //
546 + switch (pExecuteAction->mspTarget.action)
547 + {
548 + case BOOTSTRAPPER_ACTION_STATE_INSTALL: __fallthrough;
549 + case BOOTSTRAPPER_ACTION_STATE_REPAIR:
550 + hr = StrAllocConcatSecure(&sczProperties, L" PATCH=\"", 0);
551 + ExitOnFailure(hr, "Failed to add PATCH property on install.");
552 +
553 + hr = StrAllocConcatSecure(&sczProperties, sczPatches, 0);
554 + ExitOnFailure(hr, "Failed to add patches to PATCH property on install.");
555 +
556 + hr = StrAllocConcatSecure(&sczProperties, L"\" REBOOT=ReallySuppress", 0);
557 + ExitOnFailure(hr, "Failed to add reboot suppression property on install.");
558 +
559 + hr = WiuConfigureProductEx(pExecuteAction->mspTarget.sczTargetProductCode, INSTALLLEVEL_DEFAULT, INSTALLSTATE_DEFAULT, sczProperties, &restart);
560 + ExitOnFailure(hr, "Failed to install MSP package.");
561 + break;
562 +
563 + case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
564 + hr = StrAllocConcatSecure(&sczProperties, L" REBOOT=ReallySuppress", 0);
565 + ExitOnFailure(hr, "Failed to add reboot suppression property on uninstall.");
566 +
567 + // Ignore all dependencies, since the Burn engine already performed the check.
568 + hr = StrAllocFormattedSecure(&sczProperties, L"%ls %ls=ALL", sczProperties, DEPENDENCY_IGNOREDEPENDENCIES);
569 + ExitOnFailure(hr, "Failed to add the list of dependencies to ignore to the properties.");
570 +
571 + hr = WiuRemovePatches(sczPatches, pExecuteAction->mspTarget.sczTargetProductCode, sczProperties, &restart);
572 + ExitOnFailure(hr, "Failed to uninstall MSP package.");
573 + break;
574 + }
575 +
576 +LExit:
577 + WiuUninitializeExternalUI(&context);
578 +
579 + ReleaseStr(sczCachedDirectory);
580 + ReleaseStr(sczMspPath);
581 + StrSecureZeroFreeString(sczProperties);
582 + ReleaseStr(sczObfuscatedProperties);
583 + ReleaseStr(sczPatches);
584 +
585 + switch (restart)
586 + {
587 + case WIU_RESTART_NONE:
588 + *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
589 + break;
590 +
591 + case WIU_RESTART_REQUIRED:
592 + *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED;
593 + break;
594 +
595 + case WIU_RESTART_INITIATED:
596 + *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED;
597 + break;
598 + }
599 +
600 + // Best effort to clear the execute package cache folder and action variables.
601 + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE);
602 + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE);
603 +
604 + return hr;
605 +}
606 +
607 +extern "C" void MspEngineSlipstreamUpdateState(
608 + __in BURN_PACKAGE* pPackage,
609 + __in BOOTSTRAPPER_ACTION_STATE execute,
610 + __in BOOTSTRAPPER_ACTION_STATE rollback
611 + )
612 +{
613 + Assert(BURN_PACKAGE_TYPE_MSP == pPackage->type);
614 +
615 + // If the dependency manager set our state then that means something else
616 + // is dependent on our package. That trumps whatever the slipstream update
617 + // state might set.
618 + if (!pPackage->fDependencyManagerWasHere)
619 + {
620 + // The highest aggregate action state found will be returned.
621 + if (pPackage->execute < execute)
622 + {
623 + pPackage->execute = execute;
624 + }
625 +
626 + if (pPackage->rollback < rollback)
627 + {
628 + pPackage->rollback = rollback;
629 + }
630 + }
631 +}
632 +
633 +
634 +// internal helper functions
635 +
636 +static HRESULT GetPossibleTargetProductCodes(
637 + __in BURN_PACKAGES* pPackages,
638 + __deref_inout_ecount_opt(*pcPossibleTargetProducts) POSSIBLE_TARGETPRODUCT** prgPossibleTargetProducts,
639 + __inout DWORD* pcPossibleTargetProducts
640 + )
641 +{
642 + HRESULT hr = S_OK;
643 + STRINGDICT_HANDLE sdUniquePossibleTargetProductCodes = NULL;
644 + BOOL fCheckAll = FALSE;
645 + WCHAR wzPossibleTargetProductCode[MAX_GUID_CHARS + 1];
646 +
647 + // Use a dictionary to ensure we capture unique product codes. Otherwise, we could end up
648 + // doing patch applicability for the same product code multiple times and that would confuse
649 + // everything down stream.
650 + hr = DictCreateStringList(&sdUniquePossibleTargetProductCodes, 5, DICT_FLAG_NONE);
651 + ExitOnFailure(hr, "Failed to create unique target product codes.");
652 +
653 + // If the patches target a specific set of product/upgrade codes, search only those. This
654 + // should be much faster than searching all packages on the machine.
655 + if (pPackages->rgPatchTargetCodes)
656 + {
657 + for (DWORD i = 0; i < pPackages->cPatchTargetCodes; ++i)
658 + {
659 + BURN_PATCH_TARGETCODE* pTargetCode = pPackages->rgPatchTargetCodes + i;
660 +
661 + // If targeting a product, add the unique product code to the list.
662 + if (BURN_PATCH_TARGETCODE_TYPE_PRODUCT == pTargetCode->type)
663 + {
664 + hr = AddPossibleTargetProduct(sdUniquePossibleTargetProductCodes, pTargetCode->sczTargetCode, MSIINSTALLCONTEXT_NONE, prgPossibleTargetProducts, pcPossibleTargetProducts);
665 + ExitOnFailure(hr, "Failed to add product code to possible target product codes.");
666 + }
667 + else if (BURN_PATCH_TARGETCODE_TYPE_UPGRADE == pTargetCode->type)
668 + {
669 + // Enumerate all unique related products to the target upgrade code.
670 + for (DWORD iProduct = 0; SUCCEEDED(hr); ++iProduct)
671 + {
672 + hr = WiuEnumRelatedProducts(pTargetCode->sczTargetCode, iProduct, wzPossibleTargetProductCode);
673 + if (SUCCEEDED(hr))
674 + {
675 + hr = AddPossibleTargetProduct(sdUniquePossibleTargetProductCodes, wzPossibleTargetProductCode, MSIINSTALLCONTEXT_NONE, prgPossibleTargetProducts, pcPossibleTargetProducts);
676 + ExitOnFailure(hr, "Failed to add upgrade product code to possible target product codes.");
677 + }
678 + else if (E_BADCONFIGURATION == hr)
679 + {
680 + // Skip product's with bad configuration and continue.
681 + LogId(REPORT_STANDARD, MSG_DETECT_BAD_PRODUCT_CONFIGURATION, wzPossibleTargetProductCode);
682 +
683 + hr = S_OK;
684 + }
685 + }
686 +
687 + if (E_NOMOREITEMS == hr)
688 + {
689 + hr = S_OK;
690 + }
691 + ExitOnFailure(hr, "Failed to enumerate all products to patch related to upgrade code: %ls", pTargetCode->sczTargetCode);
692 + }
693 + else
694 + {
695 + // The element does not target a specific product.
696 + fCheckAll = TRUE;
697 +
698 + break;
699 + }
700 + }
701 + }
702 + else
703 + {
704 + fCheckAll = TRUE;
705 + }
706 +
707 + // One or more of the patches do not target a specific product so search everything on the machine.
708 + if (fCheckAll)
709 + {
710 + for (DWORD iProduct = 0; SUCCEEDED(hr); ++iProduct)
711 + {
712 + MSIINSTALLCONTEXT context = MSIINSTALLCONTEXT_NONE;
713 +
714 + hr = WiuEnumProductsEx(NULL, NULL, MSIINSTALLCONTEXT_ALL, iProduct, wzPossibleTargetProductCode, &context, NULL, NULL);
715 + if (SUCCEEDED(hr))
716 + {
717 + hr = AddPossibleTargetProduct(sdUniquePossibleTargetProductCodes, wzPossibleTargetProductCode, context, prgPossibleTargetProducts, pcPossibleTargetProducts);
718 + ExitOnFailure(hr, "Failed to add product code to search product codes.");
719 + }
720 + else if (E_BADCONFIGURATION == hr)
721 + {
722 + // Skip products with bad configuration and continue.
723 + LogId(REPORT_STANDARD, MSG_DETECT_BAD_PRODUCT_CONFIGURATION, wzPossibleTargetProductCode);
724 +
725 + hr = S_OK;
726 + }
727 + }
728 +
729 + if (E_NOMOREITEMS == hr)
730 + {
731 + hr = S_OK;
732 + }
733 + ExitOnFailure(hr, "Failed to enumerate all products on the machine for patches applicability.");
734 + }
735 +
736 +LExit:
737 + ReleaseDict(sdUniquePossibleTargetProductCodes);
738 +
739 + return hr;
740 +}
741 +
742 +static HRESULT AddPossibleTargetProduct(
743 + __in STRINGDICT_HANDLE sdUniquePossibleTargetProductCodes,
744 + __in_z LPCWSTR wzPossibleTargetProductCode,
745 + __in MSIINSTALLCONTEXT context,
746 + __deref_inout_ecount_opt(*pcPossibleTargetProducts) POSSIBLE_TARGETPRODUCT** prgPossibleTargetProducts,
747 + __inout DWORD* pcPossibleTargetProducts
748 + )
749 +{
750 + HRESULT hr = S_OK;
751 + LPWSTR pszLocalPackage = NULL;
752 +
753 + // Only add this possible target code if we haven't queried for it already.
754 + if (E_NOTFOUND == DictKeyExists(sdUniquePossibleTargetProductCodes, wzPossibleTargetProductCode))
755 + {
756 + // If the install context is not known, ask the Windows Installer for it. If we can't get the context
757 + // then bail.
758 + if (MSIINSTALLCONTEXT_NONE == context)
759 + {
760 + hr = WiuEnumProductsEx(wzPossibleTargetProductCode, NULL, MSIINSTALLCONTEXT_ALL, 0, NULL, &context, NULL, NULL);
761 + if (FAILED(hr))
762 + {
763 + ExitFunction1(hr = S_OK);
764 + }
765 + }
766 +
767 + hr = DictAddKey(sdUniquePossibleTargetProductCodes, wzPossibleTargetProductCode);
768 + ExitOnFailure(hr, "Failed to add possible target code to unique product codes.");
769 +
770 + hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgPossibleTargetProducts), *pcPossibleTargetProducts + 1, sizeof(POSSIBLE_TARGETPRODUCT), 3);
771 + ExitOnFailure(hr, "Failed to grow array of possible target products.");
772 +
773 + POSSIBLE_TARGETPRODUCT *const pPossibleTargetProduct = *prgPossibleTargetProducts + *pcPossibleTargetProducts;
774 +
775 + hr = ::StringCchCopyW(pPossibleTargetProduct->wzProductCode, countof(pPossibleTargetProduct->wzProductCode), wzPossibleTargetProductCode);
776 + ExitOnFailure(hr, "Failed to copy possible target product code.");
777 +
778 + // Attempt to get the local package path so we can more quickly determine patch applicability later.
779 + hr = WiuGetProductInfoEx(wzPossibleTargetProductCode, NULL, context, INSTALLPROPERTY_LOCALPACKAGE, &pszLocalPackage);
780 + if (SUCCEEDED(hr))
781 + {
782 + pPossibleTargetProduct->pszLocalPackage = pszLocalPackage;
783 + pszLocalPackage = NULL;
784 + }
785 + else
786 + {
787 + // Will instead call MsiDeterminePatchSequence later.
788 + hr = S_OK;
789 + }
790 +
791 + pPossibleTargetProduct->context = context;
792 +
793 + ++(*pcPossibleTargetProducts);
794 + }
795 +
796 +LExit:
797 + ReleaseStr(pszLocalPackage);
798 +
799 + return hr;
800 +}
801 +
802 +static HRESULT AddDetectedTargetProduct(
803 + __in BURN_PACKAGES* pPackages,
804 + __in BURN_PACKAGE* pPackage,
805 + __in DWORD dwOrder,
806 + __in_z LPCWSTR wzProductCode,
807 + __in MSIINSTALLCONTEXT context
808 + )
809 +{
810 + HRESULT hr = S_OK;
811 +
812 + hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPackage->Msp.rgTargetProducts), pPackage->Msp.cTargetProductCodes + 1, sizeof(BURN_MSPTARGETPRODUCT), 5);
813 + ExitOnFailure(hr, "Failed to ensure enough target product codes were allocated.");
814 +
815 + hr = ::StringCchCopyW(pPackage->Msp.rgTargetProducts[pPackage->Msp.cTargetProductCodes].wzTargetProductCode, countof(pPackage->Msp.rgTargetProducts[pPackage->Msp.cTargetProductCodes].wzTargetProductCode), wzProductCode);
816 + ExitOnFailure(hr, "Failed to copy target product code.");
817 +
818 + DeterminePatchChainedTarget(pPackages, pPackage, wzProductCode,
819 + &pPackage->Msp.rgTargetProducts[pPackage->Msp.cTargetProductCodes].pChainedTargetPackage,
820 + &pPackage->Msp.rgTargetProducts[pPackage->Msp.cTargetProductCodes].fSlipstream);
821 +
822 + pPackage->Msp.rgTargetProducts[pPackage->Msp.cTargetProductCodes].context = context;
823 + pPackage->Msp.rgTargetProducts[pPackage->Msp.cTargetProductCodes].dwOrder = dwOrder;
824 + ++pPackage->Msp.cTargetProductCodes;
825 +
826 +LExit:
827 + return hr;
828 +}
829 +
830 +static void DeterminePatchChainedTarget(
831 + __in BURN_PACKAGES* pPackages,
832 + __in BURN_PACKAGE* pMspPackage,
833 + __in LPCWSTR wzTargetProductCode,
834 + __out BURN_PACKAGE** ppChainedTargetPackage,
835 + __out BOOL* pfSlipstreamed
836 + )
837 +{
838 + BURN_PACKAGE* pTargetMsiPackage = NULL;
839 + BOOL fSlipstreamed = FALSE;
840 +
841 + for (DWORD iPackage = 0; iPackage < pPackages->cPackages; ++iPackage)
842 + {
843 + BURN_PACKAGE* pPackage = pPackages->rgPackages + iPackage;
844 +
845 + if (BURN_PACKAGE_TYPE_MSI == pPackage->type && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzTargetProductCode, -1, pPackage->Msi.sczProductCode, -1))
846 + {
847 + pTargetMsiPackage = pPackage;
848 +
849 + for (DWORD j = 0; j < pPackage->Msi.cSlipstreamMspPackages; ++j)
850 + {
851 + BURN_PACKAGE* pSlipstreamMsp = pPackage->Msi.rgpSlipstreamMspPackages[j];
852 + if (pSlipstreamMsp == pMspPackage)
853 + {
854 + AssertSz(!fSlipstreamed, "An MSP should only show up as a slipstreamed patch in an MSI once.");
855 + fSlipstreamed = TRUE;
856 + break;
857 + }
858 + }
859 +
860 + break;
861 + }
862 + }
863 +
864 + *ppChainedTargetPackage = pTargetMsiPackage;
865 + *pfSlipstreamed = fSlipstreamed;
866 +
867 + return;
868 +}
869 +
870 +static HRESULT PlanTargetProduct(
871 + __in BOOTSTRAPPER_DISPLAY display,
872 + __in BOOL fRollback,
873 + __in BURN_PLAN* pPlan,
874 + __in BURN_LOGGING* pLog,
875 + __in BURN_VARIABLES* pVariables,
876 + __in BOOTSTRAPPER_ACTION_STATE actionState,
877 + __in BURN_PACKAGE* pPackage,
878 + __in BURN_MSPTARGETPRODUCT* pTargetProduct,
879 + __in_opt HANDLE hCacheEvent
880 + )
881 +{
882 + HRESULT hr = S_OK;
883 + BURN_EXECUTE_ACTION* rgActions = fRollback ? pPlan->rgRollbackActions : pPlan->rgExecuteActions;
884 + DWORD cActions = fRollback ? pPlan->cRollbackActions : pPlan->cExecuteActions;
885 + BURN_EXECUTE_ACTION* pAction = NULL;
886 + DWORD dwInsertSequence = 0;
887 +
888 + // Try to find another MSP action with the exact same action (install or uninstall) targeting
889 + // the same product in the same machine context (per-user or per-machine).
890 + for (DWORD i = 0; i < cActions; ++i)
891 + {
892 + pAction = rgActions + i;
893 +
894 + if (BURN_EXECUTE_ACTION_TYPE_MSP_TARGET == pAction->type &&
895 + pAction->mspTarget.action == actionState &&
896 + pAction->mspTarget.fPerMachineTarget == (MSIINSTALLCONTEXT_MACHINE == pTargetProduct->context) &&
897 + CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pAction->mspTarget.sczTargetProductCode, -1, pTargetProduct->wzTargetProductCode, -1))
898 + {
899 + dwInsertSequence = i;
900 + break;
901 + }
902 +
903 + pAction = NULL;
904 + }
905 +
906 + // If we didn't find an MSP target action already updating the product, create a new action.
907 + if (!pAction)
908 + {
909 + if (fRollback)
910 + {
911 + hr = PlanAppendRollbackAction(pPlan, &pAction);
912 + }
913 + else
914 + {
915 + hr = PlanAppendExecuteAction(pPlan, &pAction);
916 + }
917 + ExitOnFailure(hr, "Failed to plan action for target product.");
918 +
919 + pAction->type = BURN_EXECUTE_ACTION_TYPE_MSP_TARGET;
920 + pAction->mspTarget.action = actionState;
921 + pAction->mspTarget.pPackage = pPackage;
922 + pAction->mspTarget.fPerMachineTarget = (MSIINSTALLCONTEXT_MACHINE == pTargetProduct->context);
923 + pAction->mspTarget.uiLevel = MsiEngineCalculateInstallUiLevel(pPackage->Msp.fDisplayInternalUI, display, pAction->mspTarget.action);
924 + pAction->mspTarget.pChainedTargetPackage = pTargetProduct->pChainedTargetPackage;
925 + pAction->mspTarget.fSlipstream = pTargetProduct->fSlipstream;
926 + hr = StrAllocString(&pAction->mspTarget.sczTargetProductCode, pTargetProduct->wzTargetProductCode, 0);
927 + ExitOnFailure(hr, "Failed to copy target product code.");
928 +
929 + // If this is a per-machine target product, then the plan needs to be per-machine as well.
930 + if (pAction->mspTarget.fPerMachineTarget)
931 + {
932 + pPlan->fPerMachine = TRUE;
933 + }
934 +
935 + LoggingSetPackageVariable(pPackage, pAction->mspTarget.sczTargetProductCode, fRollback, pLog, pVariables, &pAction->mspTarget.sczLogPath); // ignore errors.
936 + }
937 + else
938 + {
939 + if (!fRollback && hCacheEvent)
940 + {
941 + // Since a previouse MSP target action is being updated with the new MSP,
942 + // insert a wait syncpoint to before this action since we need to cache the current MSI before using it.
943 + BURN_EXECUTE_ACTION* pWaitSyncPointAction = NULL;
944 + hr = PlanInsertExecuteAction(dwInsertSequence, pPlan, &pWaitSyncPointAction);
945 + ExitOnFailure(hr, "Failed to insert execute action.");
946 +
947 + pWaitSyncPointAction->type = BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT;
948 + pWaitSyncPointAction->syncpoint.hEvent = hCacheEvent;
949 +
950 + // Since we inserted an action before the MSP target action that we will be updating, need to update the pointer.
951 + pAction = pPlan->rgExecuteActions + (dwInsertSequence + 1);
952 + }
953 + }
954 +
955 + // Add our target product to the array and sort based on their order determined during detection.
956 + hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pAction->mspTarget.rgOrderedPatches), pAction->mspTarget.cOrderedPatches + 1, sizeof(BURN_ORDERED_PATCHES), 2);
957 + ExitOnFailure(hr, "Failed grow array of ordered patches.");
958 +
959 + pAction->mspTarget.rgOrderedPatches[pAction->mspTarget.cOrderedPatches].dwOrder = pTargetProduct->dwOrder;
960 + pAction->mspTarget.rgOrderedPatches[pAction->mspTarget.cOrderedPatches].pPackage = pPackage;
961 + ++pAction->mspTarget.cOrderedPatches;
962 +
963 + // Insertion sort to keep the patches ordered.
964 + for (DWORD i = pAction->mspTarget.cOrderedPatches - 1; i > 0; --i)
965 + {
966 + if (pAction->mspTarget.rgOrderedPatches[i].dwOrder < pAction->mspTarget.rgOrderedPatches[i - 1].dwOrder)
967 + {
968 + BURN_ORDERED_PATCHES temp = pAction->mspTarget.rgOrderedPatches[i - 1];
969 + pAction->mspTarget.rgOrderedPatches[i - 1] = pAction->mspTarget.rgOrderedPatches[i];
970 + pAction->mspTarget.rgOrderedPatches[i] = temp;
971 + }
972 + else // no swap necessary, we're done.
973 + {
974 + break;
975 + }
976 + }
977 +
978 +LExit:
979 + return hr;
980 +}
src/engine/mspengine.h new
+67
@@ -0,0 +1,67 @@
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 +#if defined(__cplusplus)
6 +extern "C" {
7 +#endif
8 +
9 +
10 +// constants
11 +
12 +
13 +// structures
14 +
15 +
16 +// typedefs
17 +
18 +
19 +// function declarations
20 +
21 +HRESULT MspEngineParsePackageFromXml(
22 + __in IXMLDOMNode* pixnBundle,
23 + __in BURN_PACKAGE* pPackage
24 + );
25 +void MspEnginePackageUninitialize(
26 + __in BURN_PACKAGE* pPackage
27 + );
28 +HRESULT MspEngineDetectInitialize(
29 + __in BURN_PACKAGES* pPackages
30 + );
31 +HRESULT MspEngineDetectPackage(
32 + __in BURN_PACKAGE* pPackage,
33 + __in BURN_USER_EXPERIENCE* pUserExperience
34 + );
35 +HRESULT MspEnginePlanCalculatePackage(
36 + __in BURN_PACKAGE* pPackage,
37 + __in BURN_USER_EXPERIENCE* pUserExperience,
38 + __out_opt BOOL* pfBARequestedCache
39 + );
40 +HRESULT MspEnginePlanAddPackage(
41 + __in BOOTSTRAPPER_DISPLAY display,
42 + __in BURN_PACKAGE* pPackage,
43 + __in BURN_PLAN* pPlan,
44 + __in BURN_LOGGING* pLog,
45 + __in BURN_VARIABLES* pVariables,
46 + __in_opt HANDLE hCacheEvent,
47 + __in BOOL fPlanPackageCacheRollback
48 + );
49 +HRESULT MspEngineExecutePackage(
50 + __in_opt HWND hwndParent,
51 + __in BURN_EXECUTE_ACTION* pExecuteAction,
52 + __in BURN_VARIABLES* pVariables,
53 + __in BOOL fRollback,
54 + __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
55 + __in LPVOID pvContext,
56 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart
57 + );
58 +void MspEngineSlipstreamUpdateState(
59 + __in BURN_PACKAGE* pMspPackage,
60 + __in BOOTSTRAPPER_ACTION_STATE execute,
61 + __in BOOTSTRAPPER_ACTION_STATE rollback
62 + );
63 +
64 +
65 +#if defined(__cplusplus)
66 +}
67 +#endif
src/engine/msuengine.cpp new
+513
@@ -0,0 +1,513 @@
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 +// constants
7 +
8 +#define WU_S_REBOOT_REQUIRED 0x00240005L
9 +#define WU_S_ALREADY_INSTALLED 0x00240006L
10 +
11 +
12 +// function definitions
13 +static HRESULT EnsureWUServiceEnabled(
14 + __in BOOL fStopWusaService,
15 + __out SC_HANDLE* pschWu,
16 + __out BOOL* pfPreviouslyDisabled
17 + );
18 +static HRESULT SetServiceStartType(
19 + __in SC_HANDLE sch,
20 + __in DWORD stratType
21 + );
22 +static HRESULT StopWUService(
23 + __in SC_HANDLE schWu
24 + );
25 +
26 +
27 +extern "C" HRESULT MsuEngineParsePackageFromXml(
28 + __in IXMLDOMNode* pixnMsuPackage,
29 + __in BURN_PACKAGE* pPackage
30 + )
31 +{
32 + HRESULT hr = S_OK;
33 +
34 + // @KB
35 + hr = XmlGetAttributeEx(pixnMsuPackage, L"KB", &pPackage->Msu.sczKB);
36 + ExitOnFailure(hr, "Failed to get @KB.");
37 +
38 + // @DetectCondition
39 + hr = XmlGetAttributeEx(pixnMsuPackage, L"DetectCondition", &pPackage->Msu.sczDetectCondition);
40 + ExitOnFailure(hr, "Failed to get @DetectCondition.");
41 +
42 +LExit:
43 + return hr;
44 +}
45 +
46 +extern "C" void MsuEnginePackageUninitialize(
47 + __in BURN_PACKAGE* pPackage
48 + )
49 +{
50 + ReleaseNullStr(pPackage->Msu.sczKB);
51 + ReleaseNullStr(pPackage->Msu.sczDetectCondition);
52 +}
53 +
54 +extern "C" HRESULT MsuEngineDetectPackage(
55 + __in BURN_PACKAGE* pPackage,
56 + __in BURN_VARIABLES* pVariables
57 + )
58 +{
59 + HRESULT hr = S_OK;
60 + BOOL fDetected = FALSE;
61 +
62 + // evaluate detect condition
63 + if (pPackage->Msu.sczDetectCondition && *pPackage->Msu.sczDetectCondition)
64 + {
65 + hr = ConditionEvaluate(pVariables, pPackage->Msu.sczDetectCondition, &fDetected);
66 + ExitOnFailure(hr, "Failed to evaluate MSU package detect condition.");
67 + }
68 +
69 + // update detect state
70 + pPackage->currentState = fDetected ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
71 +
72 +LExit:
73 + return hr;
74 +}
75 +
76 +//
77 +// PlanCalculate - calculates the execute and rollback state for the requested package state.
78 +//
79 +extern "C" HRESULT MsuEnginePlanCalculatePackage(
80 + __in BURN_PACKAGE* pPackage,
81 + __out_opt BOOL* pfBARequestedCache
82 + )
83 +{
84 + HRESULT hr = S_OK;
85 + BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE;
86 + BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
87 + BOOL fBARequestedCache = FALSE;
88 +
89 + BOOL fAllowUninstall = FALSE;
90 + OS_VERSION osVersion = OS_VERSION_UNKNOWN;
91 + DWORD dwServicePack = 0;
92 +
93 + // We can only uninstall MSU packages if they have a KB and we are on Win7 or newer.
94 + OsGetVersion(&osVersion, &dwServicePack);
95 + fAllowUninstall = (pPackage->Msu.sczKB && *pPackage->Msu.sczKB) && OS_VERSION_WIN7 <= osVersion;
96 +
97 + // execute action
98 + switch (pPackage->currentState)
99 + {
100 + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
101 + switch (pPackage->requested)
102 + {
103 + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
104 + case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
105 + execute = BOOTSTRAPPER_ACTION_STATE_NONE;
106 + break;
107 +
108 + case BOOTSTRAPPER_REQUEST_STATE_ABSENT: __fallthrough;
109 + case BOOTSTRAPPER_REQUEST_STATE_CACHE:
110 + execute = fAllowUninstall && pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
111 + break;
112 +
113 + case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT:
114 + execute = fAllowUninstall ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
115 + break;
116 +
117 + default:
118 + execute = BOOTSTRAPPER_ACTION_STATE_NONE;
119 + break;
120 + }
121 + break;
122 +
123 + case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
124 + switch (pPackage->requested)
125 + {
126 + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
127 + case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
128 + execute = BOOTSTRAPPER_ACTION_STATE_INSTALL;
129 + break;
130 +
131 + case BOOTSTRAPPER_REQUEST_STATE_CACHE:
132 + execute = BOOTSTRAPPER_ACTION_STATE_NONE;
133 + fBARequestedCache = TRUE;
134 +
135 + default:
136 + execute = BOOTSTRAPPER_ACTION_STATE_NONE;
137 + break;
138 + }
139 + break;
140 +
141 + default:
142 + hr = E_INVALIDARG;
143 + ExitOnRootFailure(hr, "Invalid package state.");
144 + }
145 +
146 + // Calculate the rollback action if there is an execute action.
147 + if (BOOTSTRAPPER_ACTION_STATE_NONE != execute)
148 + {
149 + switch (BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN != pPackage->expected ? pPackage->expected : pPackage->currentState)
150 + {
151 + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
152 + switch (pPackage->requested)
153 + {
154 + case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough;
155 + case BOOTSTRAPPER_REQUEST_STATE_ABSENT:
156 + rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL;
157 + break;
158 +
159 + default:
160 + rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
161 + break;
162 + }
163 + break;
164 +
165 + case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
166 + switch (pPackage->requested)
167 + {
168 + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
169 + case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
170 + rollback = fAllowUninstall ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
171 + break;
172 +
173 + default:
174 + rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
175 + break;
176 + }
177 + break;
178 +
179 + default:
180 + hr = E_INVALIDARG;
181 + ExitOnRootFailure(hr, "Invalid package expected state.");
182 + }
183 + }
184 +
185 + // return values
186 + pPackage->execute = execute;
187 + pPackage->rollback = rollback;
188 +
189 + if (pfBARequestedCache)
190 + {
191 + *pfBARequestedCache = fBARequestedCache;
192 + }
193 +
194 +LExit:
195 + return hr;
196 +}
197 +
198 +//
199 +// PlanAdd - adds the calculated execute and rollback actions for the package.
200 +//
201 +extern "C" HRESULT MsuEnginePlanAddPackage(
202 + __in BURN_PACKAGE* pPackage,
203 + __in BURN_PLAN* pPlan,
204 + __in BURN_LOGGING* pLog,
205 + __in BURN_VARIABLES* pVariables,
206 + __in HANDLE hCacheEvent,
207 + __in BOOL fPlanPackageCacheRollback
208 + )
209 +{
210 + HRESULT hr = S_OK;
211 + BURN_EXECUTE_ACTION* pAction = NULL;
212 +
213 + // add wait for cache
214 + if (hCacheEvent)
215 + {
216 + hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent, fPlanPackageCacheRollback);
217 + ExitOnFailure(hr, "Failed to plan package cache syncpoint");
218 + }
219 +
220 + hr = DependencyPlanPackage(NULL, pPackage, pPlan);
221 + ExitOnFailure(hr, "Failed to plan package dependency actions.");
222 +
223 + // add execute action
224 + if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute)
225 + {
226 + hr = PlanAppendExecuteAction(pPlan, &pAction);
227 + ExitOnFailure(hr, "Failed to append execute action.");
228 +
229 + pAction->type = BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE;
230 + pAction->msuPackage.pPackage = pPackage;
231 + pAction->msuPackage.action = pPackage->execute;
232 +
233 + LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, &pAction->msuPackage.sczLogPath); // ignore errors.
234 + }
235 +
236 + // add rollback action
237 + if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback)
238 + {
239 + hr = PlanAppendRollbackAction(pPlan, &pAction);
240 + ExitOnFailure(hr, "Failed to append rollback action.");
241 +
242 + pAction->type = BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE;
243 + pAction->msuPackage.pPackage = pPackage;
244 + pAction->msuPackage.action = pPackage->rollback;
245 +
246 + LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, &pAction->msuPackage.sczLogPath); // ignore errors.
247 + }
248 +
249 +LExit:
250 + return hr;
251 +}
252 +
253 +extern "C" HRESULT MsuEngineExecutePackage(
254 + __in BURN_EXECUTE_ACTION* pExecuteAction,
255 + __in BURN_VARIABLES* pVariables,
256 + __in BOOL fRollback,
257 + __in BOOL fStopWusaService,
258 + __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
259 + __in LPVOID pvContext,
260 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart
261 + )
262 +{
263 + HRESULT hr = S_OK;
264 + int nResult = IDNOACTION;
265 + LPWSTR sczCachedDirectory = NULL;
266 + LPWSTR sczMsuPath = NULL;
267 + LPWSTR sczWindowsPath = NULL;
268 + LPWSTR sczSystemPath = NULL;
269 + LPWSTR sczWusaPath = NULL;
270 + LPWSTR sczCommand = NULL;
271 + SC_HANDLE schWu = NULL;
272 + BOOL fWuWasDisabled = FALSE;
273 + STARTUPINFOW si = { };
274 + PROCESS_INFORMATION pi = { };
275 + GENERIC_EXECUTE_MESSAGE message = { };
276 + DWORD dwExitCode = 0;
277 + BOOL fUseSysNativePath = FALSE;
278 +
279 +#if !defined(_WIN64)
280 + hr = ProcWow64(::GetCurrentProcess(), &fUseSysNativePath);
281 + ExitOnFailure(hr, "Failed to determine WOW64 status.");
282 +#endif
283 +
284 + *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
285 +
286 + // get wusa.exe path
287 + if (fUseSysNativePath)
288 + {
289 + hr = PathGetKnownFolder(CSIDL_WINDOWS, &sczWindowsPath);
290 + ExitOnFailure(hr, "Failed to find Windows directory.");
291 +
292 + hr = PathConcat(sczWindowsPath, L"SysNative\\", &sczSystemPath);
293 + ExitOnFailure(hr, "Failed to append SysNative directory.");
294 + }
295 + else
296 + {
297 + hr = PathGetKnownFolder(CSIDL_SYSTEM, &sczSystemPath);
298 + ExitOnFailure(hr, "Failed to find System32 directory.");
299 + }
300 +
301 + hr = PathConcat(sczSystemPath, L"wusa.exe", &sczWusaPath);
302 + ExitOnFailure(hr, "Failed to allocate WUSA.exe path.");
303 +
304 + // build command
305 + switch (pExecuteAction->msuPackage.action)
306 + {
307 + case BOOTSTRAPPER_ACTION_STATE_INSTALL:
308 + // get cached MSU path
309 + hr = CacheGetCompletedPath(TRUE, pExecuteAction->msuPackage.pPackage->sczCacheId, &sczCachedDirectory);
310 + ExitOnFailure(hr, "Failed to get cached path for package: %ls", pExecuteAction->msuPackage.pPackage->sczId);
311 +
312 + // Best effort to set the execute package cache folder variable.
313 + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE);
314 +
315 + hr = PathConcat(sczCachedDirectory, pExecuteAction->msuPackage.pPackage->rgPayloads[0].pPayload->sczFilePath, &sczMsuPath);
316 + ExitOnFailure(hr, "Failed to build MSU path.");
317 +
318 + // format command
319 + hr = StrAllocFormatted(&sczCommand, L"\"%ls\" \"%ls\" /quiet /norestart", sczWusaPath, sczMsuPath);
320 + ExitOnFailure(hr, "Failed to format MSU install command.");
321 + break;
322 +
323 + case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
324 + // format command
325 + hr = StrAllocFormatted(&sczCommand, L"\"%ls\" /uninstall /kb:%ls /quiet /norestart", sczWusaPath, pExecuteAction->msuPackage.pPackage->Msu.sczKB);
326 + ExitOnFailure(hr, "Failed to format MSU uninstall command.");
327 + break;
328 +
329 + default:
330 + hr = E_UNEXPECTED;
331 + ExitOnFailure(hr, "Failed to get action arguments for MSU package.");
332 + }
333 +
334 + if (pExecuteAction->msuPackage.sczLogPath && *pExecuteAction->msuPackage.sczLogPath)
335 + {
336 + hr = StrAllocConcat(&sczCommand, L" /log:", 0);
337 + ExitOnFailure(hr, "Failed to append log switch to MSU command-line.");
338 +
339 + hr = StrAllocConcat(&sczCommand, pExecuteAction->msuPackage.sczLogPath, 0);
340 + ExitOnFailure(hr, "Failed to append log path to MSU command-line.");
341 + }
342 +
343 + LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pExecuteAction->msuPackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->msuPackage.action), sczMsuPath ? sczMsuPath : pExecuteAction->msuPackage.pPackage->Msu.sczKB, sczCommand);
344 +
345 + hr = EnsureWUServiceEnabled(fStopWusaService, &schWu, &fWuWasDisabled);
346 + ExitOnFailure(hr, "Failed to ensure WU service was enabled to install MSU package.");
347 +
348 + // create process
349 + si.cb = sizeof(si);
350 + if (!::CreateProcessW(sczWusaPath, sczCommand, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
351 + {
352 + ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", sczWusaPath);
353 + }
354 +
355 + do
356 + {
357 + message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
358 + message.dwAllowedResults = MB_OKCANCEL;
359 + message.progress.dwPercentage = 50;
360 + nResult = pfnGenericMessageHandler(&message, pvContext);
361 + hr = (IDOK == nResult || IDNOACTION == nResult) ? S_OK : IDCANCEL == nResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE);
362 + ExitOnRootFailure(hr, "Bootstrapper application aborted during MSU progress.");
363 +
364 + // wait for process to terminate
365 + hr = ProcWaitForCompletion(pi.hProcess, 500, &dwExitCode);
366 + if (HRESULT_FROM_WIN32(WAIT_TIMEOUT) != hr)
367 + {
368 + ExitOnFailure(hr, "Failed to wait for executable to complete: %ls", sczWusaPath);
369 + }
370 + } while (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == hr);
371 +
372 + // get process exit code
373 + if (!::GetExitCodeProcess(pi.hProcess, &dwExitCode))
374 + {
375 + ExitWithLastError(hr, "Failed to get process exit code.");
376 + }
377 +
378 + // We'll normalize the restart required error code from wusa.exe just in case. Most likely
379 + // that on reboot we'll actually get WU_S_REBOOT_REQUIRED.
380 + if (HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED) == static_cast<HRESULT>(dwExitCode))
381 + {
382 + dwExitCode = ERROR_SUCCESS_REBOOT_REQUIRED;
383 + }
384 +
385 + // handle exit code
386 + switch (dwExitCode)
387 + {
388 + case S_OK: __fallthrough;
389 + case S_FALSE: __fallthrough;
390 + case WU_S_ALREADY_INSTALLED:
391 + hr = S_OK;
392 + break;
393 +
394 + case ERROR_SUCCESS_REBOOT_REQUIRED: __fallthrough;
395 + case WU_S_REBOOT_REQUIRED:
396 + *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED;
397 + hr = S_OK;
398 + break;
399 +
400 + default:
401 + hr = static_cast<HRESULT>(dwExitCode);
402 + break;
403 + }
404 +
405 +LExit:
406 + ReleaseStr(sczCachedDirectory);
407 + ReleaseStr(sczMsuPath);
408 + ReleaseStr(sczSystemPath);
409 + ReleaseStr(sczWindowsPath);
410 + ReleaseStr(sczWusaPath);
411 + ReleaseStr(sczCommand);
412 +
413 + ReleaseHandle(pi.hProcess);
414 + ReleaseHandle(pi.hThread);
415 +
416 + if (fWuWasDisabled)
417 + {
418 + SetServiceStartType(schWu, SERVICE_DISABLED);
419 + }
420 +
421 + // Best effort to clear the execute package cache folder variable.
422 + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE);
423 +
424 + return hr;
425 +}
426 +
427 +static HRESULT EnsureWUServiceEnabled(
428 + __in BOOL fStopWusaService,
429 + __out SC_HANDLE* pschWu,
430 + __out BOOL* pfPreviouslyDisabled
431 + )
432 +{
433 + HRESULT hr = S_OK;
434 + SC_HANDLE schSCM = NULL;
435 + SC_HANDLE schWu = NULL;
436 + SERVICE_STATUS serviceStatus = { };
437 + QUERY_SERVICE_CONFIGW* pConfig = NULL;
438 +
439 + schSCM = ::OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
440 + ExitOnNullWithLastError(schSCM, hr, "Failed to open service control manager.");
441 +
442 + schWu = ::OpenServiceW(schSCM, L"wuauserv", SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS | SERVICE_STOP );
443 + ExitOnNullWithLastError(schWu, hr, "Failed to open WU service.");
444 +
445 + if (!::QueryServiceStatus(schWu, &serviceStatus) )
446 + {
447 + ExitWithLastError(hr, "Failed to query status of WU service.");
448 + }
449 +
450 + // Stop service if requested to.
451 + if (SERVICE_STOPPED != serviceStatus.dwCurrentState && fStopWusaService)
452 + {
453 + hr = StopWUService(schWu);
454 + }
455 +
456 + // If the service is not running then it might be disabled so let's check.
457 + if (SERVICE_RUNNING != serviceStatus.dwCurrentState)
458 + {
459 + hr = SvcQueryConfig(schWu, &pConfig);
460 + ExitOnFailure(hr, "Failed to read configuration for WU service.");
461 +
462 + // If WU is disabled, change it to a demand start service (but touch nothing else).
463 + if (SERVICE_DISABLED == pConfig->dwStartType)
464 + {
465 + hr = SetServiceStartType(schWu, SERVICE_DEMAND_START);
466 + ExitOnFailure(hr, "Failed to mark WU service to start on demand.");
467 +
468 + *pfPreviouslyDisabled = TRUE;
469 + }
470 + }
471 +
472 + *pschWu = schWu;
473 + schWu = NULL;
474 +
475 +LExit:
476 + ReleaseMem(pConfig);
477 + ReleaseServiceHandle(schWu);
478 + ReleaseServiceHandle(schSCM);
479 +
480 + return hr;
481 +}
482 +
483 +static HRESULT SetServiceStartType(
484 + __in SC_HANDLE sch,
485 + __in DWORD startType
486 + )
487 +{
488 + HRESULT hr = S_OK;
489 +
490 + if (!::ChangeServiceConfigW(sch, SERVICE_NO_CHANGE, startType, SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL, NULL, NULL, NULL))
491 + {
492 + ExitWithLastError(hr, "Failed to set service start type.");
493 + }
494 +
495 +LExit:
496 + return hr;
497 +}
498 +
499 +static HRESULT StopWUService(
500 + __in SC_HANDLE schWu
501 + )
502 +{
503 + HRESULT hr = S_OK;
504 + SERVICE_STATUS serviceStatus = { };
505 +
506 + if(!::ControlService(schWu, SERVICE_CONTROL_STOP, &serviceStatus))
507 + {
508 + ExitWithLastError(hr, "Failed to stop wusa service.");
509 + }
510 +
511 +LExit:
512 + return hr;
513 +}
src/engine/msuengine.h new
+48
@@ -0,0 +1,48 @@
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 +#if defined(__cplusplus)
6 +extern "C" {
7 +#endif
8 +
9 +
10 +// function declarations
11 +
12 +HRESULT MsuEngineParsePackageFromXml(
13 + __in IXMLDOMNode* pixnMsiPackage,
14 + __in BURN_PACKAGE* pPackage
15 + );
16 +void MsuEnginePackageUninitialize(
17 + __in BURN_PACKAGE* pPackage
18 + );
19 +HRESULT MsuEngineDetectPackage(
20 + __in BURN_PACKAGE* pPackage,
21 + __in BURN_VARIABLES* pVariables
22 + );
23 +HRESULT MsuEnginePlanCalculatePackage(
24 + __in BURN_PACKAGE* pPackage,
25 + __out_opt BOOL* pfBARequestedCache
26 + );
27 +HRESULT MsuEnginePlanAddPackage(
28 + __in BURN_PACKAGE* pPackage,
29 + __in BURN_PLAN* pPlan,
30 + __in BURN_LOGGING* pLog,
31 + __in BURN_VARIABLES* pVariables,
32 + __in HANDLE hCacheEvent,
33 + __in BOOL fPlanPackageCacheRollback
34 + );
35 +HRESULT MsuEngineExecutePackage(
36 + __in BURN_EXECUTE_ACTION* pExecuteAction,
37 + __in BURN_VARIABLES* pVariables,
38 + __in BOOL fRollback,
39 + __in BOOL fStopWusaService,
40 + __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
41 + __in LPVOID pvContext,
42 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart
43 + );
44 +
45 +
46 +#if defined(__cplusplus)
47 +}
48 +#endif
src/engine/netfxchainer.cpp new
+418
@@ -0,0 +1,418 @@
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 +static VOID DestroyNetFxChainer(
6 + __in NetFxChainer* pChainer
7 + )
8 +{
9 + if (pChainer)
10 + {
11 + ReleaseHandle(pChainer->hSection);
12 + ReleaseHandle(pChainer->hEventChaineeSend);
13 + ReleaseHandle(pChainer->hEventChainerSend);
14 + ReleaseHandle(pChainer->hMutex);
15 +
16 + if (pChainer->pData)
17 + {
18 + ::UnmapViewOfFile(pChainer->pData);
19 + }
20 +
21 + MemFree(pChainer);
22 + }
23 +}
24 +
25 +static HRESULT CreateNetFxChainer(
26 + __in LPCWSTR wzSectionName,
27 + __in LPCWSTR wzEventName,
28 + __out NetFxChainer** ppChainer
29 + )
30 +{
31 + HRESULT hr = S_OK;
32 + LPWSTR sczName = NULL;
33 + NetFxChainer* pChainer = NULL;
34 +
35 + pChainer = (NetFxChainer*)MemAlloc(sizeof(NetFxChainer), TRUE);
36 + ExitOnNull(pChainer, hr, E_OUTOFMEMORY, "Failed to allocate memory for NetFxChainer struct.");
37 +
38 + pChainer->hEventChaineeSend = ::CreateEvent(NULL, FALSE, FALSE, wzEventName);
39 + ExitOnNullWithLastError(pChainer->hEventChaineeSend, hr, "Failed to create event: %ls", wzEventName);
40 +
41 + hr = StrAllocFormatted(&sczName, L"%ls_send", wzEventName);
42 + ExitOnFailure(hr, "failed to allocate memory for event name");
43 +
44 + pChainer->hEventChainerSend = ::CreateEvent(NULL, FALSE, FALSE, sczName);
45 + ExitOnNullWithLastError(pChainer->hEventChainerSend, hr, "Failed to create event: %ls", sczName);
46 +
47 + hr = StrAllocFormatted(&sczName, L"%ls_mutex", wzEventName);
48 + ExitOnFailure(hr, "failed to allocate memory for mutex name");
49 +
50 + // Create the mutex, we initially own
51 + pChainer->hMutex = ::CreateMutex(NULL, TRUE, sczName);
52 + ExitOnNullWithLastError(pChainer->hMutex, hr, "Failed to create mutex: %ls", sczName);
53 +
54 + pChainer->hSection = ::CreateFileMapping(INVALID_HANDLE_VALUE,
55 + NULL, // security attributes
56 + PAGE_READWRITE,
57 + 0, // high-order DWORD of maximum size
58 + NETFXDATA_SIZE, // low-order DWORD of maximum size
59 + wzSectionName);
60 + ExitOnNullWithLastError(pChainer->hSection, hr, "Failed to memory map cabinet file: %ls", wzSectionName);
61 +
62 + pChainer->pData = reinterpret_cast<NetFxDataStructure*>(::MapViewOfFile(pChainer->hSection,
63 + FILE_MAP_WRITE,
64 + 0, 0, // offsets
65 + 0 // map entire file
66 + ));
67 + ExitOnNullWithLastError(pChainer->pData, hr, "Failed to MapViewOfFile for %ls.", wzSectionName);
68 +
69 + // Initialize the shared memory
70 + hr = ::StringCchCopyW(pChainer->pData->szEventName, countof(pChainer->pData->szEventName), wzEventName);
71 + ExitOnFailure(hr, "failed to copy event name to shared memory structure.");
72 + pChainer->pData->downloadFinished = false;
73 + pChainer->pData->downloadSoFar = 0;
74 + pChainer->pData->hrDownloadFinished = E_PENDING;
75 + pChainer->pData->downloadAbort = false;
76 + pChainer->pData->installFinished = false;
77 + pChainer->pData->installSoFar = 0;
78 + pChainer->pData->hrInstallFinished = E_PENDING;
79 + pChainer->pData->installAbort = false;
80 + pChainer->pData->hrInternalError = S_OK;
81 + pChainer->pData->version = NETFXDATA_VERSION;
82 + pChainer->pData->messageCode = 0;
83 + pChainer->pData->messageResponse = 0;
84 + pChainer->pData->messageDataLength = 0;
85 +
86 + // Done with initialization, allow others to access.
87 + ::ReleaseMutex(pChainer->hMutex);
88 +
89 + *ppChainer = pChainer;
90 + pChainer = NULL;
91 +
92 +LExit:
93 + ReleaseStr(sczName);
94 +
95 + if (pChainer)
96 + {
97 + // Something failed, release the mutex and destroy the object
98 + if (pChainer->hMutex)
99 + {
100 + ::ReleaseMutex(pChainer->hMutex);
101 + }
102 +
103 + DestroyNetFxChainer(pChainer);
104 + }
105 +
106 + return hr;
107 +}
108 +
109 +
110 +static VOID NetFxAbort(
111 + __in NetFxChainer* pChainer
112 + )
113 +{
114 + ::WaitForSingleObject(pChainer->hMutex, INFINITE);
115 +
116 + pChainer->pData->downloadAbort = true;
117 + pChainer->pData->installAbort = true;
118 +
119 + ::ReleaseMutex(pChainer->hMutex);
120 +
121 + ::SetEvent(pChainer->hEventChainerSend);
122 +}
123 +
124 +static BYTE NetFxGetProgress(
125 + __in NetFxChainer* pChainer
126 + )
127 +{
128 + BYTE bProgress = 0;
129 + ::WaitForSingleObject(pChainer->hMutex, INFINITE);
130 +
131 + bProgress = (pChainer->pData->installSoFar + pChainer->pData->downloadSoFar) / 2;
132 +
133 + ::ReleaseMutex(pChainer->hMutex);
134 +
135 + return bProgress;
136 +}
137 +
138 +static HRESULT NetFxGetMessage(
139 + __in NetFxChainer* pChainer,
140 + __out DWORD* pdwMessage,
141 + __out LPVOID* ppBuffer,
142 + __out DWORD* pdwBufferSize
143 + )
144 +{
145 + HRESULT hr = S_OK;
146 + ::WaitForSingleObject(pChainer->hMutex, INFINITE);
147 +
148 + *pdwMessage = pChainer->pData->messageCode;
149 + *ppBuffer = NULL;
150 + *pdwBufferSize = 0;
151 +
152 + if (NETFX_NO_MESSAGE != *pdwMessage)
153 + {
154 + *ppBuffer = MemAlloc(pChainer->pData->messageDataLength, TRUE);
155 + ExitOnNull(*ppBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for message data");
156 +
157 + memcpy(*ppBuffer, pChainer->pData->messageData, pChainer->pData->messageDataLength);
158 + *pdwBufferSize = pChainer->pData->messageDataLength;
159 + }
160 +
161 +LExit:
162 + ::ReleaseMutex(pChainer->hMutex);
163 +
164 + return hr;
165 +}
166 +
167 +static void NetFxRespond(
168 + __in NetFxChainer* pChainer,
169 + __in DWORD dwResponse
170 + )
171 +{
172 + ::WaitForSingleObject(pChainer->hMutex, INFINITE);
173 +
174 + pChainer->pData->messageCode = NETFX_NO_MESSAGE;
175 + pChainer->pData->messageResponse = dwResponse;
176 + if (IDCANCEL == dwResponse)
177 + {
178 + pChainer->pData->downloadAbort = true;
179 + pChainer->pData->installAbort = true;
180 + }
181 +
182 + ::ReleaseMutex(pChainer->hMutex);
183 +
184 + ::SetEvent(pChainer->hEventChainerSend);
185 +}
186 +
187 +static HRESULT NetFxGetResult(
188 + __in NetFxChainer* pChainer,
189 + __out HRESULT* phrInternalError
190 + )
191 +{
192 + HRESULT hr = S_OK;
193 + ::WaitForSingleObject(pChainer->hMutex, INFINITE);
194 +
195 + hr = pChainer->pData->hrInstallFinished;
196 +
197 + if (FAILED(pChainer->pData->hrDownloadFinished) && // Download failed
198 + (S_OK == hr || E_ABORT == hr)) // Install succeeded or was aborted
199 + {
200 + hr = pChainer->pData->hrDownloadFinished;
201 + }
202 +
203 + if (phrInternalError)
204 + {
205 + *phrInternalError = pChainer->pData->hrInternalError;
206 + }
207 +
208 + ::ReleaseMutex(pChainer->hMutex);
209 +
210 + return hr;
211 +}
212 +
213 +static HRESULT OnNetFxFilesInUse(
214 + __in NetFxChainer* pNetfxChainer,
215 + __in NetFxCloseApplications* pCloseApps,
216 + __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler,
217 + __in LPVOID pvContext
218 + )
219 +{
220 + HRESULT hr = S_OK;
221 + DWORD cFiles = 0;
222 + LPWSTR* rgwzFiles = NULL;
223 + GENERIC_EXECUTE_MESSAGE message = { };
224 + DWORD dwResponse = 0;
225 +
226 + cFiles = pCloseApps->dwApplicationsSize;
227 + rgwzFiles = (LPWSTR*)MemAlloc(sizeof(LPWSTR*) * cFiles, TRUE);
228 + ExitOnNull(rgwzFiles, hr, E_OUTOFMEMORY, "Failed to allocate buffer.");
229 +
230 + for (DWORD i = 0; i < pCloseApps->dwApplicationsSize; ++i)
231 + {
232 + rgwzFiles[i] = pCloseApps->applications[i].szName;
233 + }
234 +
235 + // send message
236 + message.type = GENERIC_EXECUTE_MESSAGE_FILES_IN_USE;
237 + message.dwAllowedResults = MB_ABORTRETRYIGNORE;
238 + message.filesInUse.cFiles = cFiles;
239 + message.filesInUse.rgwzFiles = (LPCWSTR*)rgwzFiles;
240 + dwResponse = (DWORD)pfnMessageHandler(&message, pvContext);
241 +
242 + NetFxRespond(pNetfxChainer, dwResponse);
243 +
244 +LExit:
245 + ReleaseMem(rgwzFiles);
246 +
247 + return hr;
248 +}
249 +
250 +static HRESULT OnNetFxProgress(
251 + __in NetFxChainer* pNetfxChainer,
252 + __in BYTE bProgress,
253 + __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler,
254 + __in LPVOID pvContext
255 + )
256 +{
257 + GENERIC_EXECUTE_MESSAGE message = { };
258 + DWORD dwResponse = 0;
259 +
260 + // send message
261 + message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
262 + message.dwAllowedResults = MB_OKCANCEL;
263 + message.progress.dwPercentage = 100 * (DWORD)bProgress / BYTE_MAX;
264 + dwResponse = (DWORD)pfnMessageHandler(&message, pvContext);
265 +
266 + if (IDCANCEL == dwResponse)
267 + {
268 + NetFxAbort(pNetfxChainer);
269 + }
270 +
271 + return S_OK;
272 +}
273 +
274 +static HRESULT OnNetFxError(
275 + __in NetFxChainer* /*pNetfxChainer*/,
276 + __in HRESULT hrError,
277 + __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler,
278 + __in LPVOID pvContext
279 + )
280 +{
281 + GENERIC_EXECUTE_MESSAGE message = { };
282 + DWORD dwResponse = 0;
283 +
284 + // send message
285 + message.type = GENERIC_EXECUTE_MESSAGE_ERROR;
286 + message.dwAllowedResults = MB_OK;
287 + message.error.dwErrorCode = hrError;
288 + message.error.wzMessage = NULL;
289 + dwResponse = (DWORD)pfnMessageHandler(&message, pvContext);
290 +
291 + return S_OK;
292 +}
293 +
294 +static HRESULT ProcessNetFxMessage(
295 + __in NetFxChainer* pNetfxChainer,
296 + __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
297 + __in LPVOID pvContext
298 + )
299 +{
300 + HRESULT hr = S_OK;
301 + DWORD dwMessage = NETFX_NO_MESSAGE;
302 + DWORD dwBufferSize = 0;
303 + LPVOID pBuffer = NULL;
304 +
305 + // send progress
306 + hr = OnNetFxProgress(pNetfxChainer, NetFxGetProgress(pNetfxChainer), pfnGenericMessageHandler, pvContext);
307 + ExitOnFailure(hr, "Failed to send progress from netfx chainer.");
308 +
309 + // Check for message
310 + hr = NetFxGetMessage(pNetfxChainer, &dwMessage, &pBuffer, &dwBufferSize);
311 + ExitOnFailure(hr, "Failed to get message from netfx chainer.");
312 +
313 + switch(dwMessage)
314 + {
315 + case NETFX_CLOSE_APPS:
316 + hr = OnNetFxFilesInUse(pNetfxChainer, (NetFxCloseApplications*)pBuffer, pfnGenericMessageHandler, pvContext);
317 + ExitOnFailure(hr, "Failed to send files in use message from netfx chainer.");
318 + break;
319 +
320 + default:
321 + // No message we understand.
322 + break;
323 + }
324 +
325 +LExit:
326 + ReleaseMem(pBuffer);
327 +
328 + return hr;
329 +}
330 +
331 +extern "C" HRESULT NetFxRunChainer(
332 + __in LPCWSTR wzExecutablePath,
333 + __in LPCWSTR wzArguments,
334 + __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
335 + __in LPVOID pvContext,
336 + __out DWORD* pdwExitCode
337 + )
338 +{
339 + HRESULT hr = S_OK;
340 + DWORD er = 0;
341 + WCHAR wzGuid[GUID_STRING_LENGTH];
342 + LPWSTR sczEventName = NULL;
343 + LPWSTR sczSectionName = NULL;
344 + LPWSTR sczCommand = NULL;
345 + NetFxChainer* pNetfxChainer = NULL;
346 + STARTUPINFOW si = { };
347 + PROCESS_INFORMATION pi = { };
348 + HRESULT hrInternalError = 0;
349 +
350 + // Create the unique name suffix.
351 + hr = GuidFixedCreate(wzGuid);
352 + ExitOnRootFailure(hr, "Failed to create netfx chainer guid.");
353 +
354 + hr = StrAllocFormatted(&sczSectionName, L"NetFxSection.%ls", wzGuid);
355 + ExitOnFailure(hr, "Failed to allocate section name.");
356 +
357 + hr = StrAllocFormatted(&sczEventName, L"NetFxEvent.%ls", wzGuid);
358 + ExitOnFailure(hr, "Failed to allocate event name.");
359 +
360 + hr = CreateNetFxChainer(sczSectionName, sczEventName, &pNetfxChainer);
361 + ExitOnFailure(hr, "Failed to create netfx chainer.");
362 +
363 + hr = StrAllocFormattedSecure(&sczCommand, L"%ls /pipe %ls", wzArguments, sczSectionName);
364 + ExitOnFailure(hr, "Failed to allocate netfx chainer arguments.");
365 +
366 + si.cb = sizeof(si);
367 + if (!::CreateProcessW(wzExecutablePath, sczCommand, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
368 + {
369 + ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", wzExecutablePath);
370 + }
371 +
372 + HANDLE handles[2] = { pi.hProcess, pNetfxChainer->hEventChaineeSend };
373 +
374 + for (;;)
375 + {
376 + er = ::WaitForMultipleObjects(2, handles, FALSE, 100);
377 + if (WAIT_OBJECT_0 == er)
378 + {
379 + // Process has exited
380 + *pdwExitCode = NetFxGetResult(pNetfxChainer, &hrInternalError);
381 + if (E_PENDING == *pdwExitCode)
382 + {
383 + if (!::GetExitCodeProcess(pi.hProcess, pdwExitCode))
384 + {
385 + ExitWithLastError(hr, "Failed to get netfx return code.");
386 + }
387 + }
388 + else if (FAILED(hrInternalError))
389 + {
390 + // push internal error message
391 + OnNetFxError(pNetfxChainer, hrInternalError, pfnGenericMessageHandler, pvContext);
392 + ExitOnFailure(hr, "Failed to send internal error message from netfx chainer.");
393 + }
394 +
395 + break;
396 + }
397 + else if (WAIT_OBJECT_0 + 1 == er)
398 + {
399 + // Chainee has notified us of a change.
400 + hr = ProcessNetFxMessage(pNetfxChainer, pfnGenericMessageHandler, pvContext);
401 + ExitOnFailure(hr, "Failed to process netfx chainer message.");
402 + }
403 + else if (WAIT_FAILED == er)
404 + {
405 + ExitWithLastError(hr, "Failed to wait for netfx chainer process to complete");
406 + }
407 + }
408 +
409 +LExit:
410 + ReleaseStr(sczSectionName);
411 + ReleaseStr(sczEventName);
412 + StrSecureZeroFreeString(sczCommand);
413 + DestroyNetFxChainer(pNetfxChainer);
414 + ReleaseHandle(pi.hThread);
415 + ReleaseHandle(pi.hProcess);
416 +
417 + return hr;
418 +}
src/engine/netfxchainer.h new
+98
@@ -0,0 +1,98 @@
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 +#if defined(__cplusplus)
6 +extern "C" {
7 +#endif
8 +
9 +struct NetFxDataStructure
10 +{
11 + bool downloadFinished; // download done yet?
12 + bool installFinished; // install done yet?
13 + bool downloadAbort; // set downloader to abort
14 + bool installAbort; // set installer to abort
15 + HRESULT hrDownloadFinished; // resultant HRESULT for download
16 + HRESULT hrInstallFinished; // resultant HRESULT for install
17 + HRESULT hrInternalError;
18 + WCHAR szCurrentItemStep[MAX_PATH];
19 + BYTE downloadSoFar; // download progress 0 - 255 (0 to 100% done)
20 + BYTE installSoFar; // install progress 0 - 255 (0 to 100% done)
21 + WCHAR szEventName[MAX_PATH]; // event that chainer 'creates' and chainee 'opens'to sync communications
22 +
23 + BYTE version; // version of the data structure, set by chainer.
24 +
25 + DWORD messageCode; // current message being sent by the chainee, 0 if no message is active
26 + DWORD messageResponse; // chainer's response to current message, 0 if not yet handled
27 + DWORD messageDataLength; // length of the m_messageData field in bytes
28 + BYTE messageData[1]; // variable length buffer, content depends on m_messageCode
29 +};
30 +
31 +struct NetFxChainer
32 +{
33 + HANDLE hSection;
34 +
35 + HANDLE hEventChaineeSend;
36 + HANDLE hEventChainerSend;
37 + HANDLE hMutex;
38 +
39 + NetFxDataStructure* pData;
40 + DWORD dwDataSize;
41 +};
42 +
43 +#define NETFXDATA_SIZE 65536
44 +
45 +#define NETFXDATA_VERSION 1
46 +
47 +#define NETFX_MESSAGE(version, defaultResponse, messageCode) \
48 + ((((DWORD)version & 0xFF) << 24) | (((DWORD)defaultResponse & 0xFF) << 16) | ((DWORD)messageCode & 0xFFFF))
49 +#define NETFX_MESSAGE_CODE(messageId) \
50 + (messageId & 0xFFFF)
51 +#define NETFX_MESSAGE_DEFAULT_RESPONSE(messageId) \
52 + ((messageId >> 16) & 0xFF)
53 +#define NETFX_MESSAGE_VERSION(messageId) \
54 + ((messageId >>24) & 0xFF)
55 +
56 +#define NETFX_NO_MESSAGE 0
57 +
58 +
59 +//------------------------------------------------------------------------------
60 +// NETFX_CLOSE_APPS
61 +//
62 +// Sent by the chainee when it detects that applications are holding files in
63 +// use. Respond to this message in order to tell the chainee to close the
64 +// applications to prevent a reboot.
65 +//
66 +// pData : NetFxCloseApplications : The list of applications
67 +// Acceptable responses:
68 +// IDYES : Indicates that the chainee should attempt to shutdown the apps.
69 +// If all apps do not successfully close the message may be sent again.
70 +// IDNO : Indicates that the chainee should not attempt to close apps.
71 +// IDRETRY : Indicates that the chainee should refresh the list of apps.
72 +// Another NETFX_CLOSE_APPS message will be sent asynchronously with
73 +// the new list of apps.
74 +//------------------------------------------------------------------------------
75 +#define NETFX_CLOSE_APPS NETFX_MESSAGE(NETFXDATA_VERSION, IDNO, 1)
76 +
77 +struct NetFxApplication
78 +{
79 + WCHAR szName[MAX_PATH];
80 + DWORD dwPid;
81 +};
82 +
83 +struct NetFxCloseApplications
84 +{
85 + DWORD dwApplicationsSize;
86 + NetFxApplication applications[1];
87 +};
88 +
89 +HRESULT NetFxRunChainer(
90 + __in LPCWSTR wzExecutablePath,
91 + __in LPCWSTR wzArguments,
92 + __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
93 + __in LPVOID pvContext,
94 + __out DWORD* pdwExitCode
95 + );
96 +#if defined(__cplusplus)
97 +}
98 +#endif
src/engine/package.cpp new
+678
@@ -0,0 +1,678 @@
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 +// internal function declarations
7 +
8 +static HRESULT ParsePayloadRefsFromXml(
9 + __in BURN_PACKAGE* pPackage,
10 + __in BURN_PAYLOADS* pPayloads,
11 + __in IXMLDOMNode* pixnPackage
12 + );
13 +static HRESULT ParsePatchTargetCode(
14 + __in BURN_PACKAGES* pPackages,
15 + __in IXMLDOMNode* pixnBundle
16 + );
17 +static HRESULT FindRollbackBoundaryById(
18 + __in BURN_PACKAGES* pPackages,
19 + __in_z LPCWSTR wzId,
20 + __out BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary
21 + );
22 +
23 +
24 +// function definitions
25 +
26 +extern "C" HRESULT PackagesParseFromXml(
27 + __in BURN_PACKAGES* pPackages,
28 + __in BURN_PAYLOADS* pPayloads,
29 + __in IXMLDOMNode* pixnBundle
30 + )
31 +{
32 + HRESULT hr = S_OK;
33 + IXMLDOMNodeList* pixnNodes = NULL;
34 + IXMLDOMNode* pixnNode = NULL;
35 + DWORD cNodes = 0;
36 + BSTR bstrNodeName = NULL;
37 + DWORD cMspPackages = 0;
38 + LPWSTR scz = NULL;
39 +
40 + // select rollback boundary nodes
41 + hr = XmlSelectNodes(pixnBundle, L"RollbackBoundary", &pixnNodes);
42 + ExitOnFailure(hr, "Failed to select rollback boundary nodes.");
43 +
44 + // get rollback boundary node count
45 + hr = pixnNodes->get_length((long*)&cNodes);
46 + ExitOnFailure(hr, "Failed to get rollback bundary node count.");
47 +
48 + if (cNodes)
49 + {
50 + // allocate memory for rollback boundaries
51 + pPackages->rgRollbackBoundaries = (BURN_ROLLBACK_BOUNDARY*)MemAlloc(sizeof(BURN_ROLLBACK_BOUNDARY) * cNodes, TRUE);
52 + ExitOnNull(pPackages->rgRollbackBoundaries, hr, E_OUTOFMEMORY, "Failed to allocate memory for rollback boundary structs.");
53 +
54 + pPackages->cRollbackBoundaries = cNodes;
55 +
56 + // parse rollback boundary elements
57 + for (DWORD i = 0; i < cNodes; ++i)
58 + {
59 + BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = &pPackages->rgRollbackBoundaries[i];
60 +
61 + hr = XmlNextElement(pixnNodes, &pixnNode, &bstrNodeName);
62 + ExitOnFailure(hr, "Failed to get next node.");
63 +
64 + // @Id
65 + hr = XmlGetAttributeEx(pixnNode, L"Id", &pRollbackBoundary->sczId);
66 + ExitOnFailure(hr, "Failed to get @Id.");
67 +
68 + // @Vital
69 + hr = XmlGetYesNoAttribute(pixnNode, L"Vital", &pRollbackBoundary->fVital);
70 + ExitOnFailure(hr, "Failed to get @Vital.");
71 +
72 + // @Transaction
73 + hr = XmlGetYesNoAttribute(pixnNode, L"Transaction", &pRollbackBoundary->fTransaction);
74 + ExitOnFailure(hr, "Failed to get @Transaction.");
75 +
76 + // prepare next iteration
77 + ReleaseNullObject(pixnNode);
78 + ReleaseNullBSTR(bstrNodeName);
79 + }
80 + }
81 +
82 + ReleaseNullObject(pixnNodes); // done with the RollbackBoundary elements.
83 +
84 + // select package nodes
85 + hr = XmlSelectNodes(pixnBundle, L"Chain/ExePackage|Chain/MsiPackage|Chain/MspPackage|Chain/MsuPackage", &pixnNodes);
86 + ExitOnFailure(hr, "Failed to select package nodes.");
87 +
88 + // get package node count
89 + hr = pixnNodes->get_length((long*)&cNodes);
90 + ExitOnFailure(hr, "Failed to get package node count.");
91 +
92 + if (!cNodes)
93 + {
94 + ExitFunction1(hr = S_OK);
95 + }
96 +
97 + // allocate memory for packages
98 + pPackages->rgPackages = (BURN_PACKAGE*)MemAlloc(sizeof(BURN_PACKAGE) * cNodes, TRUE);
99 + ExitOnNull(pPackages->rgPackages, hr, E_OUTOFMEMORY, "Failed to allocate memory for package structs.");
100 +
101 + pPackages->cPackages = cNodes;
102 +
103 + // parse package elements
104 + for (DWORD i = 0; i < cNodes; ++i)
105 + {
106 + BURN_PACKAGE* pPackage = &pPackages->rgPackages[i];
107 +
108 + hr = XmlNextElement(pixnNodes, &pixnNode, &bstrNodeName);
109 + ExitOnFailure(hr, "Failed to get next node.");
110 +
111 + // @Id
112 + hr = XmlGetAttributeEx(pixnNode, L"Id", &pPackage->sczId);
113 + ExitOnFailure(hr, "Failed to get @Id.");
114 +
115 + // @Cache
116 + hr = XmlGetAttributeEx(pixnNode, L"Cache", &scz);
117 + if (SUCCEEDED(hr))
118 + {
119 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"no", -1))
120 + {
121 + pPackage->cacheType = BURN_CACHE_TYPE_NO;
122 + }
123 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"yes", -1))
124 + {
125 + pPackage->cacheType = BURN_CACHE_TYPE_YES;
126 + }
127 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"always", -1))
128 + {
129 + pPackage->cacheType = BURN_CACHE_TYPE_ALWAYS;
130 + }
131 + else
132 + {
133 + hr = E_UNEXPECTED;
134 + ExitOnFailure(hr, "Invalid cache type: %ls", scz);
135 + }
136 + }
137 + ExitOnFailure(hr, "Failed to get @Cache.");
138 +
139 + // @CacheId
140 + hr = XmlGetAttributeEx(pixnNode, L"CacheId", &pPackage->sczCacheId);
141 + ExitOnFailure(hr, "Failed to get @CacheId.");
142 +
143 + // @Size
144 + hr = XmlGetAttributeLargeNumber(pixnNode, L"Size", &pPackage->qwSize);
145 + ExitOnFailure(hr, "Failed to get @Size.");
146 +
147 + // @InstallSize
148 + hr = XmlGetAttributeLargeNumber(pixnNode, L"InstallSize", &pPackage->qwInstallSize);
149 + ExitOnFailure(hr, "Failed to get @InstallSize.");
150 +
151 + // @PerMachine
152 + hr = XmlGetYesNoAttribute(pixnNode, L"PerMachine", &pPackage->fPerMachine);
153 + ExitOnFailure(hr, "Failed to get @PerMachine.");
154 +
155 + // @Permanent
156 + hr = XmlGetYesNoAttribute(pixnNode, L"Permanent", &pPackage->fUninstallable);
157 + ExitOnFailure(hr, "Failed to get @Permanent.");
158 + pPackage->fUninstallable = !pPackage->fUninstallable; // TODO: change "Uninstallable" variable name to permanent, until then Uninstallable is the opposite of Permanent so fix the variable.
159 +
160 + // @Vital
161 + hr = XmlGetYesNoAttribute(pixnNode, L"Vital", &pPackage->fVital);
162 + ExitOnFailure(hr, "Failed to get @Vital.");
163 +
164 + // @LogPathVariable
165 + hr = XmlGetAttributeEx(pixnNode, L"LogPathVariable", &pPackage->sczLogPathVariable);
166 + if (E_NOTFOUND != hr)
167 + {
168 + ExitOnFailure(hr, "Failed to get @LogPathVariable.");
169 + }
170 +
171 + // @RollbackLogPathVariable
172 + hr = XmlGetAttributeEx(pixnNode, L"RollbackLogPathVariable", &pPackage->sczRollbackLogPathVariable);
173 + if (E_NOTFOUND != hr)
174 + {
175 + ExitOnFailure(hr, "Failed to get @RollbackLogPathVariable.");
176 + }
177 +
178 + // @InstallCondition
179 + hr = XmlGetAttributeEx(pixnNode, L"InstallCondition", &pPackage->sczInstallCondition);
180 + if (E_NOTFOUND != hr)
181 + {
182 + ExitOnFailure(hr, "Failed to get @InstallCondition.");
183 + }
184 +
185 + // @RollbackBoundaryForward
186 + hr = XmlGetAttributeEx(pixnNode, L"RollbackBoundaryForward", &scz);
187 + if (E_NOTFOUND != hr)
188 + {
189 + ExitOnFailure(hr, "Failed to get @RollbackBoundaryForward.");
190 +
191 + hr = FindRollbackBoundaryById(pPackages, scz, &pPackage->pRollbackBoundaryForward);
192 + ExitOnFailure(hr, "Failed to find forward transaction boundary: %ls", scz);
193 + }
194 +
195 + // @RollbackBoundaryBackward
196 + hr = XmlGetAttributeEx(pixnNode, L"RollbackBoundaryBackward", &scz);
197 + if (E_NOTFOUND != hr)
198 + {
199 + ExitOnFailure(hr, "Failed to get @RollbackBoundaryBackward.");
200 +
201 + hr = FindRollbackBoundaryById(pPackages, scz, &pPackage->pRollbackBoundaryBackward);
202 + ExitOnFailure(hr, "Failed to find backward transaction boundary: %ls", scz);
203 + }
204 +
205 + // read type specific attributes
206 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"ExePackage", -1))
207 + {
208 + pPackage->type = BURN_PACKAGE_TYPE_EXE;
209 +
210 + hr = ExeEngineParsePackageFromXml(pixnNode, pPackage); // TODO: Modularization
211 + ExitOnFailure(hr, "Failed to parse EXE package.");
212 + }
213 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsiPackage", -1))
214 + {
215 + pPackage->type = BURN_PACKAGE_TYPE_MSI;
216 +
217 + hr = MsiEngineParsePackageFromXml(pixnNode, pPackage); // TODO: Modularization
218 + ExitOnFailure(hr, "Failed to parse MSI package.");
219 + }
220 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MspPackage", -1))
221 + {
222 + pPackage->type = BURN_PACKAGE_TYPE_MSP;
223 +
224 + hr = MspEngineParsePackageFromXml(pixnNode, pPackage); // TODO: Modularization
225 + ExitOnFailure(hr, "Failed to parse MSP package.");
226 +
227 + ++cMspPackages;
228 + }
229 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsuPackage", -1))
230 + {
231 + pPackage->type = BURN_PACKAGE_TYPE_MSU;
232 +
233 + hr = MsuEngineParsePackageFromXml(pixnNode, pPackage); // TODO: Modularization
234 + ExitOnFailure(hr, "Failed to parse MSU package.");
235 + }
236 + else
237 + {
238 + // ignore other package types for now
239 + }
240 +
241 + // parse payload references
242 + hr = ParsePayloadRefsFromXml(pPackage, pPayloads, pixnNode);
243 + ExitOnFailure(hr, "Failed to parse payload references.");
244 +
245 + // parse dependency providers
246 + hr = DependencyParseProvidersFromXml(pPackage, pixnNode);
247 + ExitOnFailure(hr, "Failed to parse dependency providers.");
248 +
249 + // prepare next iteration
250 + ReleaseNullObject(pixnNode);
251 + ReleaseNullBSTR(bstrNodeName);
252 + }
253 +
254 + if (cMspPackages)
255 + {
256 + pPackages->rgPatchInfo = static_cast<MSIPATCHSEQUENCEINFOW*>(MemAlloc(sizeof(MSIPATCHSEQUENCEINFOW) * cMspPackages, TRUE));
257 + ExitOnNull(pPackages->rgPatchInfo, hr, E_OUTOFMEMORY, "Failed to allocate memory for MSP patch sequence information.");
258 +
259 + pPackages->rgPatchInfoToPackage = static_cast<BURN_PACKAGE**>(MemAlloc(sizeof(BURN_PACKAGE*) * cMspPackages, TRUE));
260 + ExitOnNull(pPackages->rgPatchInfoToPackage, hr, E_OUTOFMEMORY, "Failed to allocate memory for patch sequence information to package lookup.");
261 +
262 + for (DWORD i = 0; i < pPackages->cPackages; ++i)
263 + {
264 + BURN_PACKAGE* pPackage = &pPackages->rgPackages[i];
265 +
266 + if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
267 + {
268 + pPackages->rgPatchInfo[pPackages->cPatchInfo].szPatchData = pPackage->Msp.sczApplicabilityXml;
269 + pPackages->rgPatchInfo[pPackages->cPatchInfo].ePatchDataType = MSIPATCH_DATATYPE_XMLBLOB;
270 + pPackages->rgPatchInfoToPackage[pPackages->cPatchInfo] = pPackage;
271 + ++pPackages->cPatchInfo;
272 +
273 + // Loop through all MSI packages seeing if any of them slipstream this MSP.
274 + for (DWORD j = 0; j < pPackages->cPackages; ++j)
275 + {
276 + BURN_PACKAGE* pMsiPackage = &pPackages->rgPackages[j];
277 +
278 + if (BURN_PACKAGE_TYPE_MSI == pMsiPackage->type)
279 + {
280 + for (DWORD k = 0; k < pMsiPackage->Msi.cSlipstreamMspPackages; ++k)
281 + {
282 + if (pMsiPackage->Msi.rgsczSlipstreamMspPackageIds[k] && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPackage->sczId, -1, pMsiPackage->Msi.rgsczSlipstreamMspPackageIds[k], -1))
283 + {
284 + pMsiPackage->Msi.rgpSlipstreamMspPackages[k] = pPackage;
285 +
286 + ReleaseNullStr(pMsiPackage->Msi.rgsczSlipstreamMspPackageIds[k]); // we don't need the slipstream package id any longer so free it.
287 + }
288 + }
289 + }
290 + }
291 + }
292 + }
293 + }
294 +
295 + AssertSz(pPackages->cPatchInfo == cMspPackages, "Count of packages patch info should be equal to the number of MSP packages.");
296 +
297 + hr = ParsePatchTargetCode(pPackages, pixnBundle);
298 + ExitOnFailure(hr, "Failed to parse target product codes.");
299 +
300 + hr = S_OK;
301 +
302 +LExit:
303 + ReleaseObject(pixnNodes);
304 + ReleaseObject(pixnNode);
305 + ReleaseBSTR(bstrNodeName);
306 + ReleaseStr(scz);
307 +
308 + return hr;
309 +}
310 +
311 +extern "C" void PackageUninitialize(
312 + __in BURN_PACKAGE* pPackage
313 + )
314 +{
315 + ReleaseStr(pPackage->sczId);
316 + ReleaseStr(pPackage->sczLogPathVariable);
317 + ReleaseStr(pPackage->sczRollbackLogPathVariable);
318 + ReleaseStr(pPackage->sczInstallCondition);
319 + ReleaseStr(pPackage->sczRollbackInstallCondition);
320 + ReleaseStr(pPackage->sczCacheId);
321 +
322 + if (pPackage->rgDependencyProviders)
323 + {
324 + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
325 + {
326 + DependencyUninitialize(pPackage->rgDependencyProviders + i);
327 + }
328 + MemFree(pPackage->rgDependencyProviders);
329 + }
330 +
331 + ReleaseMem(pPackage->rgPayloads);
332 +
333 + switch (pPackage->type)
334 + {
335 + case BURN_PACKAGE_TYPE_EXE:
336 + ExeEnginePackageUninitialize(pPackage); // TODO: Modularization
337 + break;
338 + case BURN_PACKAGE_TYPE_MSI:
339 + MsiEnginePackageUninitialize(pPackage); // TODO: Modularization
340 + break;
341 + case BURN_PACKAGE_TYPE_MSP:
342 + MspEnginePackageUninitialize(pPackage); // TODO: Modularization
343 + break;
344 + case BURN_PACKAGE_TYPE_MSU:
345 + MsuEnginePackageUninitialize(pPackage); // TODO: Modularization
346 + break;
347 + }
348 +}
349 +
350 +extern "C" void PackagesUninitialize(
351 + __in BURN_PACKAGES* pPackages
352 + )
353 +{
354 + if (pPackages->rgRollbackBoundaries)
355 + {
356 + for (DWORD i = 0; i < pPackages->cRollbackBoundaries; ++i)
357 + {
358 + ReleaseStr(pPackages->rgRollbackBoundaries[i].sczId);
359 + }
360 + MemFree(pPackages->rgRollbackBoundaries);
361 + }
362 +
363 + if (pPackages->rgPackages)
364 + {
365 + for (DWORD i = 0; i < pPackages->cPackages; ++i)
366 + {
367 + PackageUninitialize(pPackages->rgPackages + i);
368 + }
369 + MemFree(pPackages->rgPackages);
370 + }
371 +
372 + if (pPackages->rgCompatiblePackages)
373 + {
374 + for (DWORD i = 0; i < pPackages->cCompatiblePackages; ++i)
375 + {
376 + PackageUninitialize(pPackages->rgCompatiblePackages + i);
377 + }
378 + MemFree(pPackages->rgCompatiblePackages);
379 + }
380 +
381 + if (pPackages->rgPatchTargetCodes)
382 + {
383 + for (DWORD i = 0; i < pPackages->cPatchTargetCodes; ++i)
384 + {
385 + ReleaseStr(pPackages->rgPatchTargetCodes[i].sczTargetCode);
386 + }
387 + MemFree(pPackages->rgPatchTargetCodes);
388 + }
389 +
390 + ReleaseMem(pPackages->rgPatchInfo);
391 + ReleaseMem(pPackages->rgPatchInfoToPackage);
392 +
393 + // clear struct
394 + memset(pPackages, 0, sizeof(BURN_PACKAGES));
395 +}
396 +
397 +extern "C" HRESULT PackageFindById(
398 + __in BURN_PACKAGES* pPackages,
399 + __in_z LPCWSTR wzId,
400 + __out BURN_PACKAGE** ppPackage
401 + )
402 +{
403 + HRESULT hr = S_OK;
404 + BURN_PACKAGE* pPackage = NULL;
405 +
406 + for (DWORD i = 0; i < pPackages->cPackages; ++i)
407 + {
408 + pPackage = &pPackages->rgPackages[i];
409 +
410 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPackage->sczId, -1, wzId, -1))
411 + {
412 + *ppPackage = pPackage;
413 + ExitFunction1(hr = S_OK);
414 + }
415 + }
416 +
417 + for (DWORD i = 0; i < pPackages->cCompatiblePackages; ++i)
418 + {
419 + pPackage = &pPackages->rgCompatiblePackages[i];
420 +
421 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPackage->sczId, -1, wzId, -1))
422 + {
423 + *ppPackage = pPackage;
424 + ExitFunction1(hr = S_OK);
425 + }
426 + }
427 +
428 + hr = E_NOTFOUND;
429 +
430 +LExit:
431 + return hr;
432 +}
433 +
434 +
435 +extern "C" HRESULT PackageFindRelatedById(
436 + __in BURN_RELATED_BUNDLES* pRelatedBundles,
437 + __in_z LPCWSTR wzId,
438 + __out BURN_PACKAGE** ppPackage
439 + )
440 +{
441 + HRESULT hr = S_OK;
442 + BURN_PACKAGE* pPackage = NULL;
443 +
444 + for (DWORD i = 0; i < pRelatedBundles->cRelatedBundles; ++i)
445 + {
446 + pPackage = &pRelatedBundles->rgRelatedBundles[i].package;
447 +
448 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPackage->sczId, -1, wzId, -1))
449 + {
450 + *ppPackage = pPackage;
451 + ExitFunction1(hr = S_OK);
452 + }
453 + }
454 +
455 + hr = E_NOTFOUND;
456 +
457 +LExit:
458 + return hr;
459 +}
460 +
461 +/********************************************************************
462 + PackageGetProperty - Determines if the property is defined
463 + and optionally copies the property value.
464 +
465 + Note: The caller must free psczValue if requested.
466 +
467 + Note: Returns E_NOTFOUND if the property was not defined or if the
468 + package does not support properties.
469 +
470 +*********************************************************************/
471 +extern "C" HRESULT PackageGetProperty(
472 + __in const BURN_PACKAGE* pPackage,
473 + __in_z LPCWSTR wzProperty,
474 + __out_z_opt LPWSTR* psczValue
475 + )
476 +{
477 + HRESULT hr = E_NOTFOUND;
478 + BURN_MSIPROPERTY* rgProperties = NULL;
479 + DWORD cProperties = 0;
480 +
481 + // For MSIs and MSPs, enumerate the properties looking for wzProperty.
482 + if (BURN_PACKAGE_TYPE_MSI == pPackage->type)
483 + {
484 + rgProperties = pPackage->Msi.rgProperties;
485 + cProperties = pPackage->Msi.cProperties;
486 + }
487 + else if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
488 + {
489 + rgProperties = pPackage->Msp.rgProperties;
490 + cProperties = pPackage->Msp.cProperties;
491 + }
492 +
493 + for (DWORD i = 0; i < cProperties; ++i)
494 + {
495 + const BURN_MSIPROPERTY* pProperty = &rgProperties[i];
496 +
497 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pProperty->sczId, -1, wzProperty, -1))
498 + {
499 + if (psczValue)
500 + {
501 + hr = StrAllocString(psczValue, pProperty->sczValue, 0);
502 + ExitOnFailure(hr, "Failed to copy the property value.");
503 + }
504 +
505 + ExitFunction1(hr = S_OK);
506 + }
507 + }
508 +
509 +LExit:
510 + return hr;
511 +}
512 +
513 +HRESULT PackageEnsureCompatiblePackagesArray(
514 + __in BURN_PACKAGES* pPackages
515 + )
516 +{
517 + HRESULT hr = S_OK;
518 +
519 + if (!pPackages->rgCompatiblePackages)
520 + {
521 + pPackages->rgCompatiblePackages = (BURN_PACKAGE*)MemAlloc(sizeof(BURN_PACKAGE) * pPackages->cPackages, TRUE);
522 + ExitOnNull(pPackages->rgCompatiblePackages, hr, E_OUTOFMEMORY, "Failed to allocate memory for compatible packages.");
523 + }
524 +
525 +LExit:
526 + return hr;
527 +}
528 +
529 +
530 +// internal function declarations
531 +
532 +static HRESULT ParsePayloadRefsFromXml(
533 + __in BURN_PACKAGE* pPackage,
534 + __in BURN_PAYLOADS* pPayloads,
535 + __in IXMLDOMNode* pixnPackage
536 + )
537 +{
538 + HRESULT hr = S_OK;
539 + IXMLDOMNodeList* pixnNodes = NULL;
540 + IXMLDOMNode* pixnNode = NULL;
541 + DWORD cNodes = 0;
542 + LPWSTR sczId = NULL;
543 +
544 + // select package nodes
545 + hr = XmlSelectNodes(pixnPackage, L"PayloadRef", &pixnNodes);
546 + ExitOnFailure(hr, "Failed to select package nodes.");
547 +
548 + // get package node count
549 + hr = pixnNodes->get_length((long*)&cNodes);
550 + ExitOnFailure(hr, "Failed to get package node count.");
551 +
552 + if (!cNodes)
553 + {
554 + ExitFunction1(hr = S_OK);
555 + }
556 +
557 + // allocate memory for payload pointers
558 + pPackage->rgPayloads = (BURN_PACKAGE_PAYLOAD*)MemAlloc(sizeof(BURN_PACKAGE_PAYLOAD) * cNodes, TRUE);
559 + ExitOnNull(pPackage->rgPayloads, hr, E_OUTOFMEMORY, "Failed to allocate memory for package payloads.");
560 +
561 + pPackage->cPayloads = cNodes;
562 +
563 + // parse package elements
564 + for (DWORD i = 0; i < cNodes; ++i)
565 + {
566 + BURN_PACKAGE_PAYLOAD* pPackagePayload = &pPackage->rgPayloads[i];
567 +
568 + hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
569 + ExitOnFailure(hr, "Failed to get next node.");
570 +
571 + // @Id
572 + hr = XmlGetAttributeEx(pixnNode, L"Id", &sczId);
573 + ExitOnFailure(hr, "Failed to get Id attribute.");
574 +
575 + // find payload
576 + hr = PayloadFindById(pPayloads, sczId, &pPackagePayload->pPayload);
577 + ExitOnFailure(hr, "Failed to find payload.");
578 +
579 + // prepare next iteration
580 + ReleaseNullObject(pixnNode);
581 + }
582 +
583 + hr = S_OK;
584 +
585 +LExit:
586 + ReleaseObject(pixnNodes);
587 + ReleaseObject(pixnNode);
588 + ReleaseStr(sczId);
589 +
590 + return hr;
591 +}
592 +
593 +static HRESULT ParsePatchTargetCode(
594 + __in BURN_PACKAGES* pPackages,
595 + __in IXMLDOMNode* pixnBundle
596 + )
597 +{
598 + HRESULT hr = S_OK;
599 + IXMLDOMNodeList* pixnNodes = NULL;
600 + IXMLDOMNode* pixnNode = NULL;
601 + DWORD cNodes = 0;
602 + BSTR bstrNodeText = NULL;
603 + BOOL fProduct;
604 +
605 + hr = XmlSelectNodes(pixnBundle, L"PatchTargetCode", &pixnNodes);
606 + ExitOnFailure(hr, "Failed to select PatchTargetCode nodes.");
607 +
608 + hr = pixnNodes->get_length((long*)&cNodes);
609 + ExitOnFailure(hr, "Failed to get PatchTargetCode node count.");
610 +
611 + if (!cNodes)
612 + {
613 + ExitFunction1(hr = S_OK);
614 + }
615 +
616 + pPackages->rgPatchTargetCodes = (BURN_PATCH_TARGETCODE*)MemAlloc(sizeof(BURN_PATCH_TARGETCODE) * cNodes, TRUE);
617 + ExitOnNull(pPackages->rgPatchTargetCodes, hr, E_OUTOFMEMORY, "Failed to allocate memory for patch targetcodes.");
618 +
619 + pPackages->cPatchTargetCodes = cNodes;
620 +
621 + for (DWORD i = 0; i < cNodes; ++i)
622 + {
623 + BURN_PATCH_TARGETCODE* pTargetCode = pPackages->rgPatchTargetCodes + i;
624 +
625 + hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
626 + ExitOnFailure(hr, "Failed to get next node.");
627 +
628 + hr = XmlGetAttributeEx(pixnNode, L"TargetCode", &pTargetCode->sczTargetCode);
629 + ExitOnFailure(hr, "Failed to get @TargetCode attribute.");
630 +
631 + hr = XmlGetYesNoAttribute(pixnNode, L"Product", &fProduct);
632 + if (E_NOTFOUND == hr)
633 + {
634 + fProduct = FALSE;
635 + hr = S_OK;
636 + }
637 + ExitOnFailure(hr, "Failed to get @Product.");
638 +
639 + pTargetCode->type = fProduct ? BURN_PATCH_TARGETCODE_TYPE_PRODUCT : BURN_PATCH_TARGETCODE_TYPE_UPGRADE;
640 +
641 + // prepare next iteration
642 + ReleaseNullBSTR(bstrNodeText);
643 + ReleaseNullObject(pixnNode);
644 + }
645 +
646 +LExit:
647 + ReleaseBSTR(bstrNodeText);
648 + ReleaseObject(pixnNode);
649 + ReleaseObject(pixnNodes);
650 +
651 + return hr;
652 +}
653 +
654 +static HRESULT FindRollbackBoundaryById(
655 + __in BURN_PACKAGES* pPackages,
656 + __in_z LPCWSTR wzId,
657 + __out BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary
658 + )
659 +{
660 + HRESULT hr = S_OK;
661 + BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL;
662 +
663 + for (DWORD i = 0; i < pPackages->cRollbackBoundaries; ++i)
664 + {
665 + pRollbackBoundary = &pPackages->rgRollbackBoundaries[i];
666 +
667 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pRollbackBoundary->sczId, -1, wzId, -1))
668 + {
669 + *ppRollbackBoundary = pRollbackBoundary;
670 + ExitFunction1(hr = S_OK);
671 + }
672 + }
673 +
674 + hr = E_NOTFOUND;
675 +
676 +LExit:
677 + return hr;
678 +}
src/engine/package.h new
+336
@@ -0,0 +1,336 @@
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 +#if defined(__cplusplus)
6 +extern "C" {
7 +#endif
8 +
9 +struct _BURN_RELATED_BUNDLES;
10 +typedef _BURN_RELATED_BUNDLES BURN_RELATED_BUNDLES;
11 +
12 +struct _BURN_PACKAGE;
13 +typedef _BURN_PACKAGE BURN_PACKAGE;
14 +
15 +// constants
16 +
17 +enum BURN_EXE_EXIT_CODE_TYPE
18 +{
19 + BURN_EXE_EXIT_CODE_TYPE_NONE,
20 + BURN_EXE_EXIT_CODE_TYPE_SUCCESS,
21 + BURN_EXE_EXIT_CODE_TYPE_ERROR,
22 + BURN_EXE_EXIT_CODE_TYPE_SCHEDULE_REBOOT,
23 + BURN_EXE_EXIT_CODE_TYPE_FORCE_REBOOT,
24 +};
25 +
26 +enum BURN_EXE_PROTOCOL_TYPE
27 +{
28 + BURN_EXE_PROTOCOL_TYPE_NONE,
29 + BURN_EXE_PROTOCOL_TYPE_BURN,
30 + BURN_EXE_PROTOCOL_TYPE_NETFX4,
31 +};
32 +
33 +enum BURN_PACKAGE_TYPE
34 +{
35 + BURN_PACKAGE_TYPE_NONE,
36 + BURN_PACKAGE_TYPE_EXE,
37 + BURN_PACKAGE_TYPE_MSI,
38 + BURN_PACKAGE_TYPE_MSP,
39 + BURN_PACKAGE_TYPE_MSU,
40 +};
41 +
42 +enum BURN_CACHE_STATE
43 +{
44 + BURN_CACHE_STATE_NONE,
45 + BURN_CACHE_STATE_PARTIAL,
46 + BURN_CACHE_STATE_COMPLETE,
47 +};
48 +
49 +enum BURN_CACHE_TYPE
50 +{
51 + BURN_CACHE_TYPE_NO,
52 + BURN_CACHE_TYPE_YES,
53 + BURN_CACHE_TYPE_ALWAYS,
54 +};
55 +
56 +enum BURN_DEPENDENCY_ACTION
57 +{
58 + BURN_DEPENDENCY_ACTION_NONE,
59 + BURN_DEPENDENCY_ACTION_REGISTER,
60 + BURN_DEPENDENCY_ACTION_UNREGISTER,
61 +};
62 +
63 +enum BURN_PATCH_TARGETCODE_TYPE
64 +{
65 + BURN_PATCH_TARGETCODE_TYPE_UNKNOWN,
66 + BURN_PATCH_TARGETCODE_TYPE_PRODUCT,
67 + BURN_PATCH_TARGETCODE_TYPE_UPGRADE,
68 +};
69 +
70 +// structs
71 +
72 +typedef struct _BURN_EXE_EXIT_CODE
73 +{
74 + BURN_EXE_EXIT_CODE_TYPE type;
75 + DWORD dwCode;
76 + BOOL fWildcard;
77 +} BURN_EXE_EXIT_CODE;
78 +
79 +typedef struct _BURN_EXE_COMMAND_LINE_ARGUMENT
80 +{
81 + LPWSTR sczInstallArgument;
82 + LPWSTR sczUninstallArgument;
83 + LPWSTR sczRepairArgument;
84 + LPWSTR sczCondition;
85 +} BURN_EXE_COMMAND_LINE_ARGUMENT;
86 +
87 +typedef struct _BURN_MSPTARGETPRODUCT
88 +{
89 + MSIINSTALLCONTEXT context;
90 + DWORD dwOrder;
91 + WCHAR wzTargetProductCode[39];
92 + BURN_PACKAGE* pChainedTargetPackage;
93 + BOOL fSlipstream;
94 +
95 + BOOTSTRAPPER_PACKAGE_STATE patchPackageState; // only valid after Detect.
96 + BOOTSTRAPPER_ACTION_STATE execute; // only valid during Plan.
97 + BOOTSTRAPPER_ACTION_STATE rollback; // only valid during Plan.
98 +} BURN_MSPTARGETPRODUCT;
99 +
100 +typedef struct _BURN_MSIPROPERTY
101 +{
102 + LPWSTR sczId;
103 + LPWSTR sczValue; // used during forward execution
104 + LPWSTR sczRollbackValue; // used during rollback
105 + LPWSTR sczCondition;
106 +} BURN_MSIPROPERTY;
107 +
108 +typedef struct _BURN_MSIFEATURE
109 +{
110 + LPWSTR sczId;
111 + LPWSTR sczAddLocalCondition;
112 + LPWSTR sczAddSourceCondition;
113 + LPWSTR sczAdvertiseCondition;
114 + LPWSTR sczRollbackAddLocalCondition;
115 + LPWSTR sczRollbackAddSourceCondition;
116 + LPWSTR sczRollbackAdvertiseCondition;
117 +
118 + BOOTSTRAPPER_FEATURE_STATE currentState; // only valid after Detect.
119 + BOOTSTRAPPER_FEATURE_ACTION execute; // only valid during Plan.
120 + BOOTSTRAPPER_FEATURE_ACTION rollback; // only valid during Plan.
121 +} BURN_MSIFEATURE;
122 +
123 +typedef struct _BURN_RELATED_MSI
124 +{
125 + LPWSTR sczUpgradeCode;
126 + DWORD64 qwMinVersion;
127 + DWORD64 qwMaxVersion;
128 + BOOL fMinProvided;
129 + BOOL fMaxProvided;
130 + BOOL fMinInclusive;
131 + BOOL fMaxInclusive;
132 + BOOL fOnlyDetect;
133 + BOOL fLangInclusive;
134 +
135 + DWORD* rgdwLanguages;
136 + DWORD cLanguages;
137 +} BURN_RELATED_MSI;
138 +
139 +typedef struct _BURN_PACKAGE_PAYLOAD
140 +{
141 + BURN_PAYLOAD* pPayload;
142 + BOOL fCached;
143 +} BURN_PACKAGE_PAYLOAD;
144 +
145 +typedef struct _BURN_DEPENDENCY_PROVIDER
146 +{
147 + LPWSTR sczKey;
148 + LPWSTR sczVersion;
149 + LPWSTR sczDisplayName;
150 + BOOL fImported;
151 +} BURN_DEPENDENCY_PROVIDER;
152 +
153 +typedef struct _BURN_ROLLBACK_BOUNDARY
154 +{
155 + LPWSTR sczId;
156 + BOOL fVital;
157 + BOOL fTransaction;
158 +} BURN_ROLLBACK_BOUNDARY;
159 +
160 +typedef struct _BURN_PATCH_TARGETCODE
161 +{
162 + LPWSTR sczTargetCode;
163 + BURN_PATCH_TARGETCODE_TYPE type;
164 +} BURN_PATCH_TARGETCODE;
165 +
166 +typedef struct _BURN_PACKAGE
167 +{
168 + LPWSTR sczId;
169 +
170 + LPWSTR sczLogPathVariable; // name of the variable that will be set to the log path.
171 + LPWSTR sczRollbackLogPathVariable; // name of the variable that will be set to the rollback path.
172 +
173 + LPWSTR sczInstallCondition;
174 + LPWSTR sczRollbackInstallCondition;
175 + BOOL fPerMachine;
176 + BOOL fUninstallable;
177 + BOOL fVital;
178 +
179 + BURN_CACHE_TYPE cacheType;
180 + LPWSTR sczCacheId;
181 +
182 + DWORD64 qwInstallSize;
183 + DWORD64 qwSize;
184 +
185 + BURN_ROLLBACK_BOUNDARY* pRollbackBoundaryForward; // used during install and repair.
186 + BURN_ROLLBACK_BOUNDARY* pRollbackBoundaryBackward; // used during uninstall.
187 +
188 + BOOTSTRAPPER_PACKAGE_STATE currentState; // only valid after Detect.
189 + BURN_CACHE_STATE cache; // only valid after Detect.
190 + BOOTSTRAPPER_PACKAGE_STATE expected; // only valid during Plan.
191 + BOOTSTRAPPER_REQUEST_STATE defaultRequested;// only valid during Plan.
192 + BOOTSTRAPPER_REQUEST_STATE requested; // only valid during Plan.
193 + BOOL fAcquire; // only valid during Plan.
194 + BOOL fUncache; // only valid during Plan.
195 + BOOTSTRAPPER_ACTION_STATE execute; // only valid during Plan.
196 + BOOTSTRAPPER_ACTION_STATE rollback; // only valid during Plan.
197 + BURN_DEPENDENCY_ACTION providerExecute; // only valid during Plan.
198 + BURN_DEPENDENCY_ACTION providerRollback; // only valid during Plan.
199 + BURN_DEPENDENCY_ACTION dependencyExecute; // only valid during Plan.
200 + BURN_DEPENDENCY_ACTION dependencyRollback; // only valid during Plan.
201 + BOOL fDependencyManagerWasHere; // only valid during Plan.
202 + HRESULT hrCacheResult; // only valid during Apply.
203 +
204 + BURN_PACKAGE_PAYLOAD* rgPayloads;
205 + DWORD cPayloads;
206 +
207 + BURN_DEPENDENCY_PROVIDER* rgDependencyProviders;
208 + DWORD cDependencyProviders;
209 +
210 + BURN_PACKAGE_TYPE type;
211 + union
212 + {
213 + struct
214 + {
215 + LPWSTR sczDetectCondition;
216 + LPWSTR sczInstallArguments;
217 + LPWSTR sczRepairArguments;
218 + LPWSTR sczUninstallArguments;
219 + LPWSTR sczIgnoreDependencies;
220 + LPWSTR sczAncestors;
221 +
222 + BOOL fPseudoBundle;
223 +
224 + BOOL fRepairable;
225 + BURN_EXE_PROTOCOL_TYPE protocol;
226 +
227 + BOOL fSupportsAncestors;
228 +
229 + BURN_EXE_EXIT_CODE* rgExitCodes;
230 + DWORD cExitCodes;
231 +
232 + BURN_EXE_COMMAND_LINE_ARGUMENT* rgCommandLineArguments;
233 + DWORD cCommandLineArguments;
234 + } Exe;
235 + struct
236 + {
237 + LPWSTR sczProductCode;
238 + DWORD dwLanguage;
239 + DWORD64 qwVersion;
240 + LPWSTR sczInstalledProductCode;
241 + DWORD64 qwInstalledVersion;
242 + BOOL fDisplayInternalUI;
243 + LPWSTR sczUpgradeCode;
244 +
245 + BURN_MSIPROPERTY* rgProperties;
246 + DWORD cProperties;
247 +
248 + BURN_MSIFEATURE* rgFeatures;
249 + DWORD cFeatures;
250 +
251 + BURN_RELATED_MSI* rgRelatedMsis;
252 + DWORD cRelatedMsis;
253 +
254 + _BURN_PACKAGE** rgpSlipstreamMspPackages;
255 + LPWSTR* rgsczSlipstreamMspPackageIds;
256 + DWORD cSlipstreamMspPackages;
257 +
258 + BOOL fCompatibleInstalled;
259 + } Msi;
260 + struct
261 + {
262 + LPWSTR sczPatchCode;
263 + LPWSTR sczApplicabilityXml;
264 + BOOL fDisplayInternalUI;
265 +
266 + BURN_MSIPROPERTY* rgProperties;
267 + DWORD cProperties;
268 +
269 + BURN_MSPTARGETPRODUCT* rgTargetProducts;
270 + DWORD cTargetProductCodes;
271 + } Msp;
272 + struct
273 + {
274 + LPWSTR sczDetectCondition;
275 + LPWSTR sczKB;
276 + } Msu;
277 + };
278 +} BURN_PACKAGE;
279 +
280 +typedef struct _BURN_PACKAGES
281 +{
282 + BURN_ROLLBACK_BOUNDARY* rgRollbackBoundaries;
283 + DWORD cRollbackBoundaries;
284 +
285 + BURN_PACKAGE* rgPackages;
286 + DWORD cPackages;
287 +
288 + BURN_PACKAGE* rgCompatiblePackages;
289 + DWORD cCompatiblePackages;
290 +
291 + BURN_PATCH_TARGETCODE* rgPatchTargetCodes;
292 + DWORD cPatchTargetCodes;
293 +
294 + MSIPATCHSEQUENCEINFOW* rgPatchInfo;
295 + BURN_PACKAGE** rgPatchInfoToPackage; // direct lookup from patch information to the (MSP) package it describes.
296 + // Thus this array is the exact same size as rgPatchInfo.
297 + DWORD cPatchInfo;
298 +} BURN_PACKAGES;
299 +
300 +
301 +// function declarations
302 +
303 +HRESULT PackagesParseFromXml(
304 + __in BURN_PACKAGES* pPackages,
305 + __in BURN_PAYLOADS* pPayloads,
306 + __in IXMLDOMNode* pixnBundle
307 + );
308 +void PackageUninitialize(
309 + __in BURN_PACKAGE* pPackage
310 + );
311 +void PackagesUninitialize(
312 + __in BURN_PACKAGES* pPackages
313 + );
314 +HRESULT PackageFindById(
315 + __in BURN_PACKAGES* pPackages,
316 + __in_z LPCWSTR wzId,
317 + __out BURN_PACKAGE** ppPackage
318 + );
319 +HRESULT PackageFindRelatedById(
320 + __in BURN_RELATED_BUNDLES* pRelatedBundles,
321 + __in_z LPCWSTR wzId,
322 + __out BURN_PACKAGE** ppPackage
323 + );
324 +HRESULT PackageGetProperty(
325 + __in const BURN_PACKAGE* pPackage,
326 + __in_z LPCWSTR wzProperty,
327 + __out_z_opt LPWSTR* psczValue
328 + );
329 +HRESULT PackageEnsureCompatiblePackagesArray(
330 + __in BURN_PACKAGES* pPackages
331 + );
332 +
333 +
334 +#if defined(__cplusplus)
335 +}
336 +#endif
src/engine/payload.cpp new
+367
@@ -0,0 +1,367 @@
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 +// internal function declarations
7 +
8 +static HRESULT FindEmbeddedBySourcePath(
9 + __in BURN_PAYLOADS* pPayloads,
10 + __in_opt BURN_CONTAINER* pContainer,
11 + __in_z LPCWSTR wzStreamName,
12 + __out BURN_PAYLOAD** ppPayload
13 + );
14 +
15 +
16 +// function definitions
17 +
18 +extern "C" HRESULT PayloadsParseFromXml(
19 + __in BURN_PAYLOADS* pPayloads,
20 + __in_opt BURN_CONTAINERS* pContainers,
21 + __in_opt BURN_CATALOGS* pCatalogs,
22 + __in IXMLDOMNode* pixnBundle
23 + )
24 +{
25 + HRESULT hr = S_OK;
26 + IXMLDOMNodeList* pixnNodes = NULL;
27 + IXMLDOMNode* pixnNode = NULL;
28 + DWORD cNodes = 0;
29 + LPWSTR scz = NULL;
30 +
31 + // select payload nodes
32 + hr = XmlSelectNodes(pixnBundle, L"Payload", &pixnNodes);
33 + ExitOnFailure(hr, "Failed to select payload nodes.");
34 +
35 + // get payload node count
36 + hr = pixnNodes->get_length((long*)&cNodes);
37 + ExitOnFailure(hr, "Failed to get payload node count.");
38 +
39 + if (!cNodes)
40 + {
41 + ExitFunction();
42 + }
43 +
44 + // allocate memory for payloads
45 + pPayloads->rgPayloads = (BURN_PAYLOAD*)MemAlloc(sizeof(BURN_PAYLOAD) * cNodes, TRUE);
46 + ExitOnNull(pPayloads->rgPayloads, hr, E_OUTOFMEMORY, "Failed to allocate memory for payload structs.");
47 +
48 + pPayloads->cPayloads = cNodes;
49 +
50 + // parse search elements
51 + for (DWORD i = 0; i < cNodes; ++i)
52 + {
53 + BURN_PAYLOAD* pPayload = &pPayloads->rgPayloads[i];
54 +
55 + hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
56 + ExitOnFailure(hr, "Failed to get next node.");
57 +
58 + // @Id
59 + hr = XmlGetAttributeEx(pixnNode, L"Id", &pPayload->sczKey);
60 + ExitOnFailure(hr, "Failed to get @Id.");
61 +
62 + // @FilePath
63 + hr = XmlGetAttributeEx(pixnNode, L"FilePath", &pPayload->sczFilePath);
64 + ExitOnFailure(hr, "Failed to get @FilePath.");
65 +
66 + // @Packaging
67 + hr = XmlGetAttributeEx(pixnNode, L"Packaging", &scz);
68 + ExitOnFailure(hr, "Failed to get @Packaging.");
69 +
70 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"download", -1))
71 + {
72 + pPayload->packaging = BURN_PAYLOAD_PACKAGING_DOWNLOAD;
73 + }
74 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"embedded", -1))
75 + {
76 + pPayload->packaging = BURN_PAYLOAD_PACKAGING_EMBEDDED;
77 + }
78 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"external", -1))
79 + {
80 + pPayload->packaging = BURN_PAYLOAD_PACKAGING_EXTERNAL;
81 + }
82 + else
83 + {
84 + hr = E_INVALIDARG;
85 + ExitOnFailure(hr, "Invalid value for @Packaging: %ls", scz);
86 + }
87 +
88 + // @Container
89 + if (pContainers)
90 + {
91 + hr = XmlGetAttributeEx(pixnNode, L"Container", &scz);
92 + if (E_NOTFOUND != hr || BURN_PAYLOAD_PACKAGING_EMBEDDED == pPayload->packaging)
93 + {
94 + ExitOnFailure(hr, "Failed to get @Container.");
95 +
96 + // find container
97 + hr = ContainerFindById(pContainers, scz, &pPayload->pContainer);
98 + ExitOnFailure(hr, "Failed to to find container: %ls", scz);
99 + }
100 + }
101 +
102 + // @LayoutOnly
103 + hr = XmlGetYesNoAttribute(pixnNode, L"LayoutOnly", &pPayload->fLayoutOnly);
104 + if (E_NOTFOUND != hr)
105 + {
106 + ExitOnFailure(hr, "Failed to get @LayoutOnly.");
107 + }
108 +
109 + // @SourcePath
110 + hr = XmlGetAttributeEx(pixnNode, L"SourcePath", &pPayload->sczSourcePath);
111 + if (E_NOTFOUND != hr || BURN_PAYLOAD_PACKAGING_DOWNLOAD != pPayload->packaging)
112 + {
113 + ExitOnFailure(hr, "Failed to get @SourcePath.");
114 + }
115 +
116 + // @DownloadUrl
117 + hr = XmlGetAttributeEx(pixnNode, L"DownloadUrl", &pPayload->downloadSource.sczUrl);
118 + if (E_NOTFOUND != hr || BURN_PAYLOAD_PACKAGING_DOWNLOAD == pPayload->packaging)
119 + {
120 + ExitOnFailure(hr, "Failed to get @DownloadUrl.");
121 + }
122 +
123 + // @FileSize
124 + hr = XmlGetAttributeEx(pixnNode, L"FileSize", &scz);
125 + if (E_NOTFOUND != hr)
126 + {
127 + ExitOnFailure(hr, "Failed to get @FileSize.");
128 +
129 + hr = StrStringToUInt64(scz, 0, &pPayload->qwFileSize);
130 + ExitOnFailure(hr, "Failed to parse @FileSize.");
131 + }
132 +
133 + // @CertificateAuthorityKeyIdentifier
134 + hr = XmlGetAttributeEx(pixnNode, L"CertificateRootPublicKeyIdentifier", &scz);
135 + if (E_NOTFOUND != hr)
136 + {
137 + ExitOnFailure(hr, "Failed to get @CertificateRootPublicKeyIdentifier.");
138 +
139 + hr = StrAllocHexDecode(scz, &pPayload->pbCertificateRootPublicKeyIdentifier, &pPayload->cbCertificateRootPublicKeyIdentifier);
140 + ExitOnFailure(hr, "Failed to hex decode @CertificateRootPublicKeyIdentifier.");
141 + }
142 +
143 + // @CertificateThumbprint
144 + hr = XmlGetAttributeEx(pixnNode, L"CertificateRootThumbprint", &scz);
145 + if (E_NOTFOUND != hr)
146 + {
147 + ExitOnFailure(hr, "Failed to get @CertificateRootThumbprint.");
148 +
149 + hr = StrAllocHexDecode(scz, &pPayload->pbCertificateRootThumbprint, &pPayload->cbCertificateRootThumbprint);
150 + ExitOnFailure(hr, "Failed to hex decode @CertificateRootThumbprint.");
151 + }
152 +
153 + // @Hash
154 + hr = XmlGetAttributeEx(pixnNode, L"Hash", &scz);
155 + ExitOnFailure(hr, "Failed to get @Hash.");
156 +
157 + hr = StrAllocHexDecode(scz, &pPayload->pbHash, &pPayload->cbHash);
158 + ExitOnFailure(hr, "Failed to hex decode the Payload/@Hash.");
159 +
160 + // @Catalog
161 + hr = XmlGetAttributeEx(pixnNode, L"Catalog", &scz);
162 + if (E_NOTFOUND != hr)
163 + {
164 + ExitOnFailure(hr, "Failed to get @Catalog.");
165 +
166 + hr = CatalogFindById(pCatalogs, scz, &pPayload->pCatalog);
167 + ExitOnFailure(hr, "Failed to find catalog.");
168 + }
169 +
170 + // prepare next iteration
171 + ReleaseNullObject(pixnNode);
172 + }
173 +
174 + hr = S_OK;
175 +
176 +LExit:
177 + ReleaseObject(pixnNodes);
178 + ReleaseObject(pixnNode);
179 + ReleaseStr(scz);
180 +
181 + return hr;
182 +}
183 +
184 +extern "C" void PayloadsUninitialize(
185 + __in BURN_PAYLOADS* pPayloads
186 + )
187 +{
188 + if (pPayloads->rgPayloads)
189 + {
190 + for (DWORD i = 0; i < pPayloads->cPayloads; ++i)
191 + {
192 + BURN_PAYLOAD* pPayload = &pPayloads->rgPayloads[i];
193 +
194 + ReleaseStr(pPayload->sczKey);
195 + ReleaseStr(pPayload->sczFilePath);
196 + ReleaseMem(pPayload->pbHash);
197 + ReleaseMem(pPayload->pbCertificateRootThumbprint);
198 + ReleaseMem(pPayload->pbCertificateRootPublicKeyIdentifier);
199 + ReleaseStr(pPayload->sczSourcePath);
200 + ReleaseStr(pPayload->sczLocalFilePath);
201 + ReleaseStr(pPayload->downloadSource.sczUrl);
202 + ReleaseStr(pPayload->downloadSource.sczUser);
203 + ReleaseStr(pPayload->downloadSource.sczPassword);
204 + }
205 + MemFree(pPayloads->rgPayloads);
206 + }
207 +
208 + // clear struct
209 + memset(pPayloads, 0, sizeof(BURN_PAYLOADS));
210 +}
211 +
212 +extern "C" HRESULT PayloadExtractFromContainer(
213 + __in BURN_PAYLOADS* pPayloads,
214 + __in_opt BURN_CONTAINER* pContainer,
215 + __in BURN_CONTAINER_CONTEXT* pContainerContext,
216 + __in_z LPCWSTR wzTargetDir
217 + )
218 +{
219 + HRESULT hr = S_OK;
220 + LPWSTR sczStreamName = NULL;
221 + LPWSTR sczDirectory = NULL;
222 + BURN_PAYLOAD* pPayload = NULL;
223 +
224 + // extract all payloads
225 + for (;;)
226 + {
227 + // get next stream
228 + hr = ContainerNextStream(pContainerContext, &sczStreamName);
229 + if (E_NOMOREITEMS == hr)
230 + {
231 + hr = S_OK;
232 + break;
233 + }
234 + ExitOnFailure(hr, "Failed to get next stream.");
235 +
236 + // find payload by stream name
237 + hr = FindEmbeddedBySourcePath(pPayloads, pContainer, sczStreamName, &pPayload);
238 + ExitOnFailure(hr, "Failed to find embedded payload: %ls", sczStreamName);
239 +
240 + // make file path
241 + hr = PathConcat(wzTargetDir, pPayload->sczFilePath, &pPayload->sczLocalFilePath);
242 + ExitOnFailure(hr, "Failed to concat file paths.");
243 +
244 + // extract file
245 + hr = PathGetDirectory(pPayload->sczLocalFilePath, &sczDirectory);
246 + ExitOnFailure(hr, "Failed to get directory portion of local file path");
247 +
248 + hr = DirEnsureExists(sczDirectory, NULL);
249 + ExitOnFailure(hr, "Failed to ensure directory exists");
250 +
251 + hr = ContainerStreamToFile(pContainerContext, pPayload->sczLocalFilePath);
252 + ExitOnFailure(hr, "Failed to extract file.");
253 +
254 + // flag that the payload has been acquired
255 + pPayload->state = BURN_PAYLOAD_STATE_ACQUIRED;
256 + }
257 +
258 + // locate any payloads that were not extracted
259 + for (DWORD i = 0; i < pPayloads->cPayloads; ++i)
260 + {
261 + pPayload = &pPayloads->rgPayloads[i];
262 +
263 + // if the payload is part of the container
264 + if (!pContainer || pPayload->pContainer == pContainer)
265 + {
266 + // if the payload has not been acquired
267 + if (BURN_PAYLOAD_STATE_ACQUIRED > pPayload->state)
268 + {
269 + hr = E_INVALIDDATA;
270 + ExitOnRootFailure(hr, "Payload was not found in container: %ls", pPayload->sczKey);
271 + }
272 + }
273 + }
274 +
275 +LExit:
276 + ReleaseStr(sczStreamName);
277 + ReleaseStr(sczDirectory);
278 +
279 + return hr;
280 +}
281 +
282 +extern "C" HRESULT PayloadFindById(
283 + __in BURN_PAYLOADS* pPayloads,
284 + __in_z LPCWSTR wzId,
285 + __out BURN_PAYLOAD** ppPayload
286 + )
287 +{
288 + HRESULT hr = S_OK;
289 + BURN_PAYLOAD* pPayload = NULL;
290 +
291 + for (DWORD i = 0; i < pPayloads->cPayloads; ++i)
292 + {
293 + pPayload = &pPayloads->rgPayloads[i];
294 +
295 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPayload->sczKey, -1, wzId, -1))
296 + {
297 + *ppPayload = pPayload;
298 + ExitFunction1(hr = S_OK);
299 + }
300 + }
301 +
302 + hr = E_NOTFOUND;
303 +
304 +LExit:
305 + return hr;
306 +}
307 +
308 +extern "C" HRESULT PayloadFindEmbeddedBySourcePath(
309 + __in BURN_PAYLOADS* pPayloads,
310 + __in_z LPCWSTR wzStreamName,
311 + __out BURN_PAYLOAD** ppPayload
312 + )
313 +{
314 + HRESULT hr = S_OK;
315 + BURN_PAYLOAD* pPayload = NULL;
316 +
317 + for (DWORD i = 0; i < pPayloads->cPayloads; ++i)
318 + {
319 + pPayload = &pPayloads->rgPayloads[i];
320 +
321 + if (BURN_PAYLOAD_PACKAGING_EMBEDDED == pPayload->packaging)
322 + {
323 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPayload->sczSourcePath, -1, wzStreamName, -1))
324 + {
325 + *ppPayload = pPayload;
326 + ExitFunction1(hr = S_OK);
327 + }
328 + }
329 + }
330 +
331 + hr = E_NOTFOUND;
332 +
333 +LExit:
334 + return hr;
335 +}
336 +
337 +
338 +// internal function definitions
339 +
340 +static HRESULT FindEmbeddedBySourcePath(
341 + __in BURN_PAYLOADS* pPayloads,
342 + __in_opt BURN_CONTAINER* pContainer,
343 + __in_z LPCWSTR wzStreamName,
344 + __out BURN_PAYLOAD** ppPayload
345 + )
346 +{
347 + HRESULT hr = S_OK;
348 +
349 + for (DWORD i = 0; i < pPayloads->cPayloads; ++i)
350 + {
351 + BURN_PAYLOAD* pPayload = &pPayloads->rgPayloads[i];
352 +
353 + if (BURN_PAYLOAD_PACKAGING_EMBEDDED == pPayload->packaging && (!pContainer || pPayload->pContainer == pContainer))
354 + {
355 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPayload->sczSourcePath, -1, wzStreamName, -1))
356 + {
357 + *ppPayload = pPayload;
358 + ExitFunction1(hr = S_OK);
359 + }
360 + }
361 + }
362 +
363 + hr = E_NOTFOUND;
364 +
365 +LExit:
366 + return hr;
367 +}
src/engine/payload.h new
+93
@@ -0,0 +1,93 @@
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 +#if defined(__cplusplus)
6 +extern "C" {
7 +#endif
8 +
9 +
10 +// constants
11 +
12 +enum BURN_PAYLOAD_PACKAGING
13 +{
14 + BURN_PAYLOAD_PACKAGING_NONE,
15 + BURN_PAYLOAD_PACKAGING_DOWNLOAD,
16 + BURN_PAYLOAD_PACKAGING_EMBEDDED,
17 + BURN_PAYLOAD_PACKAGING_EXTERNAL,
18 +};
19 +
20 +enum BURN_PAYLOAD_STATE
21 +{
22 + BURN_PAYLOAD_STATE_NONE,
23 + BURN_PAYLOAD_STATE_ACQUIRED,
24 + BURN_PAYLOAD_STATE_CACHED,
25 +};
26 +
27 +
28 +// structs
29 +
30 +typedef struct _BURN_PAYLOAD
31 +{
32 + LPWSTR sczKey;
33 + BURN_PAYLOAD_PACKAGING packaging;
34 + BOOL fLayoutOnly;
35 + DWORD64 qwFileSize;
36 + LPWSTR sczFilePath; // file path relative to the execute location
37 +
38 + BURN_CATALOG *pCatalog; // used to verify this payload
39 + BYTE* pbCertificateRootPublicKeyIdentifier;
40 + DWORD cbCertificateRootPublicKeyIdentifier;
41 + BYTE* pbCertificateRootThumbprint;
42 + DWORD cbCertificateRootThumbprint;
43 + BYTE* pbHash;
44 + DWORD cbHash;
45 +
46 + LPWSTR sczSourcePath;
47 + BURN_CONTAINER* pContainer;
48 + DOWNLOAD_SOURCE downloadSource;
49 +
50 + // mutable members
51 + BURN_PAYLOAD_STATE state;
52 + LPWSTR sczLocalFilePath; // location of extracted or downloaded copy
53 +} BURN_PAYLOAD;
54 +
55 +typedef struct _BURN_PAYLOADS
56 +{
57 + BURN_PAYLOAD* rgPayloads;
58 + DWORD cPayloads;
59 +} BURN_PAYLOADS;
60 +
61 +
62 +// functions
63 +
64 +HRESULT PayloadsParseFromXml(
65 + __in BURN_PAYLOADS* pPayloads,
66 + __in_opt BURN_CONTAINERS* pContainers,
67 + __in_opt BURN_CATALOGS* pCatalogs,
68 + __in IXMLDOMNode* pixnBundle
69 + );
70 +void PayloadsUninitialize(
71 + __in BURN_PAYLOADS* pPayloads
72 + );
73 +HRESULT PayloadExtractFromContainer(
74 + __in BURN_PAYLOADS* pPayloads,
75 + __in_opt BURN_CONTAINER* pContainer,
76 + __in BURN_CONTAINER_CONTEXT* pContainerContext,
77 + __in_z LPCWSTR wzTargetDir
78 + );
79 +HRESULT PayloadFindById(
80 + __in BURN_PAYLOADS* pPayloads,
81 + __in_z LPCWSTR wzId,
82 + __out BURN_PAYLOAD** ppPayload
83 + );
84 +HRESULT PayloadFindEmbeddedBySourcePath(
85 + __in BURN_PAYLOADS* pPayloads,
86 + __in_z LPCWSTR wzStreamName,
87 + __out BURN_PAYLOAD** ppPayload
88 + );
89 +
90 +
91 +#if defined(__cplusplus)
92 +}
93 +#endif
src/engine/pipe.cpp new
+873
@@ -0,0 +1,873 @@
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 +static const DWORD PIPE_64KB = 64 * 1024;
6 +static const DWORD PIPE_WAIT_FOR_CONNECTION = 100; // wait a 10th of a second,
7 +static const DWORD PIPE_RETRY_FOR_CONNECTION = 1800; // for up to 3 minutes.
8 +
9 +static const LPCWSTR PIPE_NAME_FORMAT_STRING = L"\\\\.\\pipe\\%ls";
10 +static const LPCWSTR CACHE_PIPE_NAME_FORMAT_STRING = L"\\\\.\\pipe\\%ls.Cache";
11 +
12 +static HRESULT AllocatePipeMessage(
13 + __in DWORD dwMessage,
14 + __in_bcount_opt(cbData) LPVOID pvData,
15 + __in DWORD cbData,
16 + __out_bcount(cb) LPVOID* ppvMessage,
17 + __out DWORD* cbMessage
18 + );
19 +static void FreePipeMessage(
20 + __in BURN_PIPE_MESSAGE *pMsg
21 + );
22 +static HRESULT WritePipeMessage(
23 + __in HANDLE hPipe,
24 + __in DWORD dwMessage,
25 + __in_bcount_opt(cbData) LPVOID pvData,
26 + __in DWORD cbData
27 + );
28 +static HRESULT GetPipeMessage(
29 + __in HANDLE hPipe,
30 + __in BURN_PIPE_MESSAGE* pMsg
31 + );
32 +static HRESULT ChildPipeConnected(
33 + __in HANDLE hPipe,
34 + __in_z LPCWSTR wzSecret,
35 + __inout DWORD* pdwProcessId
36 + );
37 +
38 +
39 +
40 +/*******************************************************************
41 + PipeConnectionInitialize - initialize pipe connection data.
42 +
43 +*******************************************************************/
44 +void PipeConnectionInitialize(
45 + __in BURN_PIPE_CONNECTION* pConnection
46 + )
47 +{
48 + memset(pConnection, 0, sizeof(BURN_PIPE_CONNECTION));
49 + pConnection->hPipe = INVALID_HANDLE_VALUE;
50 + pConnection->hCachePipe = INVALID_HANDLE_VALUE;
51 +}
52 +
53 +/*******************************************************************
54 + PipeConnectionUninitialize - free data in a pipe connection.
55 +
56 +*******************************************************************/
57 +void PipeConnectionUninitialize(
58 + __in BURN_PIPE_CONNECTION* pConnection
59 + )
60 +{
61 + ReleaseFileHandle(pConnection->hCachePipe);
62 + ReleaseFileHandle(pConnection->hPipe);
63 + ReleaseHandle(pConnection->hProcess);
64 + ReleaseStr(pConnection->sczSecret);
65 + ReleaseStr(pConnection->sczName);
66 +
67 + memset(pConnection, 0, sizeof(BURN_PIPE_CONNECTION));
68 + pConnection->hPipe = INVALID_HANDLE_VALUE;
69 + pConnection->hCachePipe = INVALID_HANDLE_VALUE;
70 +}
71 +
72 +/*******************************************************************
73 + PipeSendMessage -
74 +
75 +*******************************************************************/
76 +extern "C" HRESULT PipeSendMessage(
77 + __in HANDLE hPipe,
78 + __in DWORD dwMessage,
79 + __in_bcount_opt(cbData) LPVOID pvData,
80 + __in DWORD cbData,
81 + __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback,
82 + __in_opt LPVOID pvContext,
83 + __out DWORD* pdwResult
84 + )
85 +{
86 + HRESULT hr = S_OK;
87 + BURN_PIPE_RESULT result = { };
88 +
89 + hr = WritePipeMessage(hPipe, dwMessage, pvData, cbData);
90 + ExitOnFailure(hr, "Failed to write send message to pipe.");
91 +
92 + hr = PipePumpMessages(hPipe, pfnCallback, pvContext, &result);
93 + ExitOnFailure(hr, "Failed to pump messages during send message to pipe.");
94 +
95 + *pdwResult = result.dwResult;
96 +
97 +LExit:
98 + return hr;
99 +}
100 +
101 +/*******************************************************************
102 + PipePumpMessages -
103 +
104 +*******************************************************************/
105 +extern "C" HRESULT PipePumpMessages(
106 + __in HANDLE hPipe,
107 + __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback,
108 + __in_opt LPVOID pvContext,
109 + __in BURN_PIPE_RESULT* pResult
110 + )
111 +{
112 + HRESULT hr = S_OK;
113 + BURN_PIPE_MESSAGE msg = { };
114 + SIZE_T iData = 0;
115 + LPSTR sczMessage = NULL;
116 + DWORD dwResult = 0;
117 +
118 + // Pump messages from child process.
119 + while (S_OK == (hr = GetPipeMessage(hPipe, &msg)))
120 + {
121 + switch (msg.dwMessage)
122 + {
123 + case BURN_PIPE_MESSAGE_TYPE_LOG:
124 + iData = 0;
125 +
126 + hr = BuffReadStringAnsi((BYTE*)msg.pvData, msg.cbData, &iData, &sczMessage);
127 + ExitOnFailure(hr, "Failed to read log message.");
128 +
129 + hr = LogStringWorkRaw(sczMessage);
130 + ExitOnFailure(hr, "Failed to write log message:'%hs'.", sczMessage);
131 +
132 + dwResult = static_cast<DWORD>(hr);
133 + break;
134 +
135 + case BURN_PIPE_MESSAGE_TYPE_COMPLETE:
136 + if (!msg.pvData || sizeof(DWORD) != msg.cbData)
137 + {
138 + hr = E_INVALIDARG;
139 + ExitOnRootFailure(hr, "No status returned to PipePumpMessages()");
140 + }
141 +
142 + pResult->dwResult = *static_cast<DWORD*>(msg.pvData);
143 + ExitFunction1(hr = S_OK); // exit loop.
144 +
145 + case BURN_PIPE_MESSAGE_TYPE_TERMINATE:
146 + iData = 0;
147 +
148 + hr = BuffReadNumber(static_cast<BYTE*>(msg.pvData), msg.cbData, &iData, &pResult->dwResult);
149 + ExitOnFailure(hr, "Failed to read returned result to PipePumpMessages()");
150 +
151 + if (sizeof(DWORD) * 2 == msg.cbData)
152 + {
153 + hr = BuffReadNumber(static_cast<BYTE*>(msg.pvData), msg.cbData, &iData, (DWORD*)&pResult->fRestart);
154 + ExitOnFailure(hr, "Failed to read returned restart to PipePumpMessages()");
155 + }
156 +
157 + ExitFunction1(hr = S_OK); // exit loop.
158 +
159 + default:
160 + if (pfnCallback)
161 + {
162 + hr = pfnCallback(&msg, pvContext, &dwResult);
163 + }
164 + else
165 + {
166 + hr = E_INVALIDARG;
167 + }
168 + ExitOnFailure(hr, "Failed to process message: %u", msg.dwMessage);
169 + break;
170 + }
171 +
172 + // post result
173 + hr = WritePipeMessage(hPipe, static_cast<DWORD>(BURN_PIPE_MESSAGE_TYPE_COMPLETE), &dwResult, sizeof(dwResult));
174 + ExitOnFailure(hr, "Failed to post result to child process.");
175 +
176 + FreePipeMessage(&msg);
177 + }
178 + ExitOnFailure(hr, "Failed to get message over pipe");
179 +
180 + if (S_FALSE == hr)
181 + {
182 + hr = S_OK;
183 + }
184 +
185 +LExit:
186 + ReleaseStr(sczMessage);
187 + FreePipeMessage(&msg);
188 +
189 + return hr;
190 +}
191 +
192 +/*******************************************************************
193 + PipeCreateNameAndSecret -
194 +
195 +*******************************************************************/
196 +extern "C" HRESULT PipeCreateNameAndSecret(
197 + __out_z LPWSTR *psczConnectionName,
198 + __out_z LPWSTR *psczSecret
199 + )
200 +{
201 + HRESULT hr = S_OK;
202 + WCHAR wzGuid[GUID_STRING_LENGTH];
203 + LPWSTR sczConnectionName = NULL;
204 + LPWSTR sczSecret = NULL;
205 +
206 + // Create the unique pipe name.
207 + hr = GuidFixedCreate(wzGuid);
208 + ExitOnRootFailure(hr, "Failed to create pipe guid.");
209 +
210 + hr = StrAllocFormatted(&sczConnectionName, L"BurnPipe.%s", wzGuid);
211 + ExitOnFailure(hr, "Failed to allocate pipe name.");
212 +
213 + // Create the unique client secret.
214 + hr = GuidFixedCreate(wzGuid);
215 + ExitOnRootFailure(hr, "Failed to create pipe secret.");
216 +
217 + hr = StrAllocString(&sczSecret, wzGuid, 0);
218 + ExitOnFailure(hr, "Failed to allocate pipe secret.");
219 +
220 + *psczConnectionName = sczConnectionName;
221 + sczConnectionName = NULL;
222 + *psczSecret = sczSecret;
223 + sczSecret = NULL;
224 +
225 +LExit:
226 + ReleaseStr(sczSecret);
227 + ReleaseStr(sczConnectionName);
228 +
229 + return hr;
230 +}
231 +
232 +/*******************************************************************
233 + PipeCreatePipes - create the pipes and event to signal child process.
234 +
235 +*******************************************************************/
236 +extern "C" HRESULT PipeCreatePipes(
237 + __in BURN_PIPE_CONNECTION* pConnection,
238 + __in BOOL fCreateCachePipe,
239 + __out HANDLE* phEvent
240 + )
241 +{
242 + Assert(pConnection->sczName);
243 + Assert(INVALID_HANDLE_VALUE == pConnection->hPipe);
244 + Assert(INVALID_HANDLE_VALUE == pConnection->hCachePipe);
245 +
246 + HRESULT hr = S_OK;
247 + PSECURITY_DESCRIPTOR psd = NULL;
248 + SECURITY_ATTRIBUTES sa = { };
249 + LPWSTR sczFullPipeName = NULL;
250 + HANDLE hPipe = INVALID_HANDLE_VALUE;
251 + HANDLE hCachePipe = INVALID_HANDLE_VALUE;
252 +
253 + // Only the grant special rights when the pipe is being used for "embedded"
254 + // scenarios (aka: there is no cache pipe).
255 + if (!fCreateCachePipe)
256 + {
257 + // Create the security descriptor that grants read/write/sync access to Everyone.
258 + // TODO: consider locking down "WD" to LogonIds (logon session)
259 + LPCWSTR wzSddl = L"D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW0x00100000;;;WD)";
260 + if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(wzSddl, SDDL_REVISION_1, &psd, NULL))
261 + {
262 + ExitWithLastError(hr, "Failed to create the security descriptor for the connection event and pipe.");
263 + }
264 +
265 + sa.nLength = sizeof(sa);
266 + sa.lpSecurityDescriptor = psd;
267 + sa.bInheritHandle = FALSE;
268 + }
269 +
270 + // Create the pipe.
271 + hr = StrAllocFormatted(&sczFullPipeName, PIPE_NAME_FORMAT_STRING, pConnection->sczName);
272 + ExitOnFailure(hr, "Failed to allocate full name of pipe: %ls", pConnection->sczName);
273 +
274 + // TODO: consider using overlapped IO to do waits on the pipe and still be able to cancel and such.
275 + hPipe = ::CreateNamedPipeW(sczFullPipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, PIPE_64KB, PIPE_64KB, 1, psd ? &sa : NULL);
276 + if (INVALID_HANDLE_VALUE == hPipe)
277 + {
278 + ExitWithLastError(hr, "Failed to create pipe: %ls", sczFullPipeName);
279 + }
280 +
281 + if (fCreateCachePipe)
282 + {
283 + // Create the cache pipe.
284 + hr = StrAllocFormatted(&sczFullPipeName, CACHE_PIPE_NAME_FORMAT_STRING, pConnection->sczName);
285 + ExitOnFailure(hr, "Failed to allocate full name of cache pipe: %ls", pConnection->sczName);
286 +
287 + hCachePipe = ::CreateNamedPipeW(sczFullPipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, PIPE_64KB, PIPE_64KB, 1, NULL);
288 + if (INVALID_HANDLE_VALUE == hCachePipe)
289 + {
290 + ExitWithLastError(hr, "Failed to create pipe: %ls", sczFullPipeName);
291 + }
292 + }
293 +
294 + pConnection->hCachePipe = hCachePipe;
295 + hCachePipe = INVALID_HANDLE_VALUE;
296 +
297 + pConnection->hPipe = hPipe;
298 + hPipe = INVALID_HANDLE_VALUE;
299 +
300 + // TODO: remove the following
301 + *phEvent = NULL;
302 +
303 +LExit:
304 + ReleaseFileHandle(hCachePipe);
305 + ReleaseFileHandle(hPipe);
306 + ReleaseStr(sczFullPipeName);
307 +
308 + if (psd)
309 + {
310 + ::LocalFree(psd);
311 + }
312 +
313 + return hr;
314 +}
315 +
316 +/*******************************************************************
317 + PipeLaunchParentProcess - Called from the per-machine process to create
318 + a per-user process and set up the
319 + communication pipe.
320 +
321 +*******************************************************************/
322 +const LPCWSTR BURN_COMMANDLINE_SWITCH_UNELEVATED = L"burn.unelevated";
323 +HRESULT PipeLaunchParentProcess(
324 + __in_z LPCWSTR wzCommandLine,
325 + __in int nCmdShow,
326 + __in_z LPWSTR sczConnectionName,
327 + __in_z LPWSTR sczSecret,
328 + __in BOOL /*fDisableUnelevate*/
329 + )
330 +{
331 + HRESULT hr = S_OK;
332 + DWORD dwProcessId = 0;
333 + LPWSTR sczBurnPath = NULL;
334 + LPWSTR sczParameters = NULL;
335 + HANDLE hProcess = NULL;
336 +
337 + dwProcessId = ::GetCurrentProcessId();
338 +
339 + hr = PathForCurrentProcess(&sczBurnPath, NULL);
340 + ExitOnFailure(hr, "Failed to get current process path.");
341 +
342 + hr = StrAllocFormatted(&sczParameters, L"-%ls %ls %ls %u %ls", BURN_COMMANDLINE_SWITCH_UNELEVATED, sczConnectionName, sczSecret, dwProcessId, wzCommandLine);
343 + ExitOnFailure(hr, "Failed to allocate parameters for unelevated process.");
344 +
345 +#ifdef ENABLE_UNELEVATE
346 + if (fDisableUnelevate)
347 + {
348 + hr = ProcExec(sczBurnPath, sczParameters, nCmdShow, &hProcess);
349 + ExitOnFailure(hr, "Failed to launch parent process with unelevate disabled: %ls", sczBurnPath);
350 + }
351 + else
352 + {
353 + // Try to launch unelevated and if that fails for any reason, try launch our process normally (even though that may make it elevated).
354 + hr = ProcExecuteAsInteractiveUser(sczBurnPath, sczParameters, &hProcess);
355 + if (FAILED(hr))
356 + {
357 + hr = ShelExecUnelevated(sczBurnPath, sczParameters, L"open", NULL, nCmdShow);
358 + if (FAILED(hr))
359 + {
360 + hr = ShelExec(sczBurnPath, sczParameters, L"open", NULL, nCmdShow, NULL, NULL);
361 + ExitOnFailure(hr, "Failed to launch parent process: %ls", sczBurnPath);
362 + }
363 + }
364 + }
365 +#else
366 + hr = ProcExec(sczBurnPath, sczParameters, nCmdShow, &hProcess);
367 + ExitOnFailure(hr, "Failed to launch parent process with unelevate disabled: %ls", sczBurnPath);
368 +#endif
369 +
370 +LExit:
371 + ReleaseHandle(hProcess);
372 + ReleaseStr(sczParameters);
373 + ReleaseStr(sczBurnPath);
374 +
375 + return hr;
376 +}
377 +
378 +/*******************************************************************
379 + PipeLaunchChildProcess - Called from the per-user process to create
380 + the per-machine process and set up the
381 + communication pipe.
382 +
383 +*******************************************************************/
384 +extern "C" HRESULT PipeLaunchChildProcess(
385 + __in_z LPCWSTR wzExecutablePath,
386 + __in BURN_PIPE_CONNECTION* pConnection,
387 + __in BOOL fElevate,
388 + __in_opt HWND hwndParent
389 + )
390 +{
391 + HRESULT hr = S_OK;
392 + DWORD dwCurrentProcessId = ::GetCurrentProcessId();
393 + LPWSTR sczParameters = NULL;
394 + OS_VERSION osVersion = OS_VERSION_UNKNOWN;
395 + DWORD dwServicePack = 0;
396 + LPCWSTR wzVerb = NULL;
397 + HANDLE hProcess = NULL;
398 +
399 + hr = StrAllocFormatted(&sczParameters, L"-q -%ls %ls %ls %u", BURN_COMMANDLINE_SWITCH_ELEVATED, pConnection->sczName, pConnection->sczSecret, dwCurrentProcessId);
400 + ExitOnFailure(hr, "Failed to allocate parameters for elevated process.");
401 +
402 + OsGetVersion(&osVersion, &dwServicePack);
403 + wzVerb = (OS_VERSION_VISTA > osVersion) || !fElevate ? L"open" : L"runas";
404 +
405 + // Since ShellExecuteEx doesn't support passing inherited handles, don't bother with CoreAppendFileHandleSelfToCommandLine.
406 + // We could fallback to using ::DuplicateHandle to inject the file handle later if necessary.
407 + hr = ShelExec(wzExecutablePath, sczParameters, wzVerb, NULL, SW_HIDE, hwndParent, &hProcess);
408 + ExitOnFailure(hr, "Failed to launch elevated child process: %ls", wzExecutablePath);
409 +
410 + pConnection->dwProcessId = ::GetProcessId(hProcess);
411 + pConnection->hProcess = hProcess;
412 + hProcess = NULL;
413 +
414 +LExit:
415 + ReleaseHandle(hProcess);
416 + ReleaseStr(sczParameters);
417 +
418 + return hr;
419 +}
420 +
421 +/*******************************************************************
422 + PipeWaitForChildConnect -
423 +
424 +*******************************************************************/
425 +extern "C" HRESULT PipeWaitForChildConnect(
426 + __in BURN_PIPE_CONNECTION* pConnection
427 + )
428 +{
429 + HRESULT hr = S_OK;
430 + HANDLE hPipes[2] = { pConnection->hPipe, pConnection->hCachePipe};
431 + LPCWSTR wzSecret = pConnection->sczSecret;
432 + DWORD cbSecret = lstrlenW(wzSecret) * sizeof(WCHAR);
433 + DWORD dwCurrentProcessId = ::GetCurrentProcessId();
434 + DWORD dwAck = 0;
435 + DWORD cb = 0;
436 +
437 + for (DWORD i = 0; i < countof(hPipes) && INVALID_HANDLE_VALUE != hPipes[i]; ++i)
438 + {
439 + HANDLE hPipe = hPipes[i];
440 + DWORD dwPipeState = PIPE_READMODE_BYTE | PIPE_NOWAIT;
441 +
442 + // Temporarily make the pipe non-blocking so we will not get stuck in ::ConnectNamedPipe() forever
443 + // if the child decides not to show up.
444 + if (!::SetNamedPipeHandleState(hPipe, &dwPipeState, NULL, NULL))
445 + {
446 + ExitWithLastError(hr, "Failed to set pipe to non-blocking.");
447 + }
448 +
449 + // Loop for a while waiting for a connection from child process.
450 + DWORD cRetry = 0;
451 + do
452 + {
453 + if (!::ConnectNamedPipe(hPipe, NULL))
454 + {
455 + DWORD er = ::GetLastError();
456 + if (ERROR_PIPE_CONNECTED == er)
457 + {
458 + hr = S_OK;
459 + break;
460 + }
461 + else if (ERROR_PIPE_LISTENING == er)
462 + {
463 + if (cRetry < PIPE_RETRY_FOR_CONNECTION)
464 + {
465 + hr = HRESULT_FROM_WIN32(er);
466 + }
467 + else
468 + {
469 + hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT);
470 + break;
471 + }
472 +
473 + ++cRetry;
474 + ::Sleep(PIPE_WAIT_FOR_CONNECTION);
475 + }
476 + else
477 + {
478 + hr = HRESULT_FROM_WIN32(er);
479 + break;
480 + }
481 + }
482 + } while (HRESULT_FROM_WIN32(ERROR_PIPE_LISTENING) == hr);
483 + ExitOnRootFailure(hr, "Failed to wait for child to connect to pipe.");
484 +
485 + // Put the pipe back in blocking mode.
486 + dwPipeState = PIPE_READMODE_BYTE | PIPE_WAIT;
487 + if (!::SetNamedPipeHandleState(hPipe, &dwPipeState, NULL, NULL))
488 + {
489 + ExitWithLastError(hr, "Failed to reset pipe to blocking.");
490 + }
491 +
492 + // Prove we are the one that created the elevated process by passing the secret.
493 + if (!::WriteFile(hPipe, &cbSecret, sizeof(cbSecret), &cb, NULL))
494 + {
495 + ExitWithLastError(hr, "Failed to write secret length to pipe.");
496 + }
497 +
498 + if (!::WriteFile(hPipe, wzSecret, cbSecret, &cb, NULL))
499 + {
500 + ExitWithLastError(hr, "Failed to write secret to pipe.");
501 + }
502 +
503 + if (!::WriteFile(hPipe, &dwCurrentProcessId, sizeof(dwCurrentProcessId), &cb, NULL))
504 + {
505 + ExitWithLastError(hr, "Failed to write our process id to pipe.");
506 + }
507 +
508 + // Wait until the elevated process responds that it is ready to go.
509 + if (!::ReadFile(hPipe, &dwAck, sizeof(dwAck), &cb, NULL))
510 + {
511 + ExitWithLastError(hr, "Failed to read ACK from pipe.");
512 + }
513 +
514 + // The ACK should match out expected child process id.
515 + //if (pConnection->dwProcessId != dwAck)
516 + //{
517 + // hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
518 + // ExitOnRootFailure(hr, "Incorrect ACK from elevated pipe: %u", dwAck);
519 + //}
520 + }
521 +
522 +LExit:
523 + return hr;
524 +}
525 +
526 +/*******************************************************************
527 + PipeTerminateChildProcess -
528 +
529 +*******************************************************************/
530 +extern "C" HRESULT PipeTerminateChildProcess(
531 + __in BURN_PIPE_CONNECTION* pConnection,
532 + __in DWORD dwParentExitCode,
533 + __in BOOL fRestart
534 + )
535 +{
536 + HRESULT hr = S_OK;
537 + BYTE* pbData = NULL;
538 + SIZE_T cbData = 0;
539 +
540 + // Prepare the exit message.
541 + hr = BuffWriteNumber(&pbData, &cbData, dwParentExitCode);
542 + ExitOnFailure(hr, "Failed to write exit code to message buffer.");
543 +
544 + hr = BuffWriteNumber(&pbData, &cbData, fRestart);
545 + ExitOnFailure(hr, "Failed to write restart to message buffer.");
546 +
547 + // Send the messages.
548 + if (INVALID_HANDLE_VALUE != pConnection->hCachePipe)
549 + {
550 + hr = WritePipeMessage(pConnection->hCachePipe, static_cast<DWORD>(BURN_PIPE_MESSAGE_TYPE_TERMINATE), pbData, cbData);
551 + ExitOnFailure(hr, "Failed to post terminate message to child process cache thread.");
552 + }
553 +
554 + hr = WritePipeMessage(pConnection->hPipe, static_cast<DWORD>(BURN_PIPE_MESSAGE_TYPE_TERMINATE), pbData, cbData);
555 + ExitOnFailure(hr, "Failed to post terminate message to child process.");
556 +
557 + // If we were able to get a handle to the other process, wait for it to exit.
558 + if (pConnection->hProcess)
559 + {
560 + if (WAIT_FAILED == ::WaitForSingleObject(pConnection->hProcess, PIPE_WAIT_FOR_CONNECTION * PIPE_RETRY_FOR_CONNECTION))
561 + {
562 + ExitWithLastError(hr, "Failed to wait for child process exit.");
563 + }
564 +
565 +#ifdef DEBUG
566 + DWORD dwChildExitCode = 0;
567 + DWORD dwErrorCode = ERROR_SUCCESS;
568 + BOOL fReturnedExitCode = ::GetExitCodeProcess(pConnection->hProcess, &dwChildExitCode);
569 + if (!fReturnedExitCode)
570 + {
571 + dwErrorCode = ::GetLastError(); // if the other process is elevated and we are not, then we'll get ERROR_ACCESS_DENIED.
572 +
573 + // The unit test use a thread instead of a process so try to get the exit code from
574 + // the thread because we failed to get it from the process.
575 + if (ERROR_INVALID_HANDLE == dwErrorCode)
576 + {
577 + fReturnedExitCode = ::GetExitCodeThread(pConnection->hProcess, &dwChildExitCode);
578 + }
579 + }
580 + AssertSz((fReturnedExitCode && dwChildExitCode == dwParentExitCode) ||
581 + (!fReturnedExitCode && ERROR_ACCESS_DENIED == dwErrorCode),
582 + "Child elevated process did not return matching exit code to parent process.");
583 +#endif
584 + }
585 +
586 +LExit:
587 + return hr;
588 +}
589 +
590 +/*******************************************************************
591 + PipeChildConnect - Called from the child process to connect back
592 + to the pipe provided by the parent process.
593 +
594 +*******************************************************************/
595 +extern "C" HRESULT PipeChildConnect(
596 + __in BURN_PIPE_CONNECTION* pConnection,
597 + __in BOOL fConnectCachePipe
598 + )
599 +{
600 + Assert(pConnection->sczName);
601 + Assert(pConnection->sczSecret);
602 + Assert(!pConnection->hProcess);
603 + Assert(INVALID_HANDLE_VALUE == pConnection->hPipe);
604 + Assert(INVALID_HANDLE_VALUE == pConnection->hCachePipe);
605 +
606 + HRESULT hr = S_OK;
607 + LPWSTR sczPipeName = NULL;
608 +
609 + // Try to connect to the parent.
610 + hr = StrAllocFormatted(&sczPipeName, PIPE_NAME_FORMAT_STRING, pConnection->sczName);
611 + ExitOnFailure(hr, "Failed to allocate name of parent pipe.");
612 +
613 + hr = E_UNEXPECTED;
614 + for (DWORD cRetry = 0; FAILED(hr) && cRetry < PIPE_RETRY_FOR_CONNECTION; ++cRetry)
615 + {
616 + pConnection->hPipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
617 + if (INVALID_HANDLE_VALUE == pConnection->hPipe)
618 + {
619 + hr = HRESULT_FROM_WIN32(::GetLastError());
620 + if (E_FILENOTFOUND == hr) // if the pipe isn't created, call it a timeout waiting on the parent.
621 + {
622 + hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT);
623 + }
624 +
625 + ::Sleep(PIPE_WAIT_FOR_CONNECTION);
626 + }
627 + else // we have a connection, go with it.
628 + {
629 + hr = S_OK;
630 + }
631 + }
632 + ExitOnRootFailure(hr, "Failed to open parent pipe: %ls", sczPipeName)
633 +
634 + // Verify the parent and notify it that the child connected.
635 + hr = ChildPipeConnected(pConnection->hPipe, pConnection->sczSecret, &pConnection->dwProcessId);
636 + ExitOnFailure(hr, "Failed to verify parent pipe: %ls", sczPipeName);
637 +
638 + if (fConnectCachePipe)
639 + {
640 + // Connect to the parent for the cache pipe.
641 + hr = StrAllocFormatted(&sczPipeName, CACHE_PIPE_NAME_FORMAT_STRING, pConnection->sczName);
642 + ExitOnFailure(hr, "Failed to allocate name of parent cache pipe.");
643 +
644 + pConnection->hCachePipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
645 + if (INVALID_HANDLE_VALUE == pConnection->hCachePipe)
646 + {
647 + ExitWithLastError(hr, "Failed to open parent pipe: %ls", sczPipeName)
648 + }
649 +
650 + // Verify the parent and notify it that the child connected.
651 + hr = ChildPipeConnected(pConnection->hCachePipe, pConnection->sczSecret, &pConnection->dwProcessId);
652 + ExitOnFailure(hr, "Failed to verify parent pipe: %ls", sczPipeName);
653 + }
654 +
655 + pConnection->hProcess = ::OpenProcess(SYNCHRONIZE, FALSE, pConnection->dwProcessId);
656 + ExitOnNullWithLastError(pConnection->hProcess, hr, "Failed to open companion process with PID: %u", pConnection->dwProcessId);
657 +
658 +LExit:
659 + ReleaseStr(sczPipeName);
660 +
661 + return hr;
662 +}
663 +
664 +
665 +static HRESULT AllocatePipeMessage(
666 + __in DWORD dwMessage,
667 + __in_bcount_opt(cbData) LPVOID pvData,
668 + __in DWORD cbData,
669 + __out_bcount(cb) LPVOID* ppvMessage,
670 + __out DWORD* cbMessage
671 + )
672 +{
673 + HRESULT hr = S_OK;
674 + LPVOID pv = NULL;
675 + DWORD cb = 0;
676 +
677 + // If no data was provided, ensure the count of bytes is zero.
678 + if (!pvData)
679 + {
680 + cbData = 0;
681 + }
682 +
683 + // Allocate the message.
684 + cb = sizeof(dwMessage) + sizeof(cbData) + cbData;
685 + pv = MemAlloc(cb, FALSE);
686 + ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for message.");
687 +
688 + memcpy_s(pv, cb, &dwMessage, sizeof(dwMessage));
689 + memcpy_s(static_cast<BYTE*>(pv) + sizeof(dwMessage), cb - sizeof(dwMessage), &cbData, sizeof(cbData));
690 + if (cbData)
691 + {
692 + memcpy_s(static_cast<BYTE*>(pv) + sizeof(dwMessage) + sizeof(cbData), cb - sizeof(dwMessage) - sizeof(cbData), pvData, cbData);
693 + }
694 +
695 + *cbMessage = cb;
696 + *ppvMessage = pv;
697 + pv = NULL;
698 +
699 +LExit:
700 + ReleaseMem(pv);
701 + return hr;
702 +}
703 +
704 +static void FreePipeMessage(
705 + __in BURN_PIPE_MESSAGE *pMsg
706 + )
707 +{
708 + if (pMsg->fAllocatedData)
709 + {
710 + ReleaseNullMem(pMsg->pvData);
711 + pMsg->fAllocatedData = FALSE;
712 + }
713 +}
714 +
715 +static HRESULT WritePipeMessage(
716 + __in HANDLE hPipe,
717 + __in DWORD dwMessage,
718 + __in_bcount_opt(cbData) LPVOID pvData,
719 + __in DWORD cbData
720 + )
721 +{
722 + HRESULT hr = S_OK;
723 + LPVOID pv = NULL;
724 + DWORD cb = 0;
725 +
726 + hr = AllocatePipeMessage(dwMessage, pvData, cbData, &pv, &cb);
727 + ExitOnFailure(hr, "Failed to allocate message to write.");
728 +
729 + // Write the message.
730 + DWORD cbWrote = 0;
731 + DWORD cbTotalWritten = 0;
732 + while (cbTotalWritten < cb)
733 + {
734 + if (!::WriteFile(hPipe, pv, cb - cbTotalWritten, &cbWrote, NULL))
735 + {
736 + ExitWithLastError(hr, "Failed to write message type to pipe.");
737 + }
738 +
739 + cbTotalWritten += cbWrote;
740 + }
741 +
742 +LExit:
743 + ReleaseMem(pv);
744 + return hr;
745 +}
746 +
747 +static HRESULT GetPipeMessage(
748 + __in HANDLE hPipe,
749 + __in BURN_PIPE_MESSAGE* pMsg
750 + )
751 +{
752 + HRESULT hr = S_OK;
753 + DWORD rgdwMessageAndByteCount[2] = { };
754 + DWORD cb = 0;
755 + DWORD cbRead = 0;
756 +
757 + while (cbRead < sizeof(rgdwMessageAndByteCount))
758 + {
759 + if (!::ReadFile(hPipe, reinterpret_cast<BYTE*>(rgdwMessageAndByteCount) + cbRead, sizeof(rgdwMessageAndByteCount) - cbRead, &cb, NULL))
760 + {
761 + DWORD er = ::GetLastError();
762 + if (ERROR_MORE_DATA == er)
763 + {
764 + hr = S_OK;
765 + }
766 + else if (ERROR_BROKEN_PIPE == er) // parent process shut down, time to exit.
767 + {
768 + memset(rgdwMessageAndByteCount, 0, sizeof(rgdwMessageAndByteCount));
769 + hr = S_FALSE;
770 + break;
771 + }
772 + else
773 + {
774 + hr = HRESULT_FROM_WIN32(er);
775 + }
776 + ExitOnRootFailure(hr, "Failed to read message from pipe.");
777 + }
778 +
779 + cbRead += cb;
780 + }
781 +
782 + pMsg->dwMessage = rgdwMessageAndByteCount[0];
783 + pMsg->cbData = rgdwMessageAndByteCount[1];
784 + if (pMsg->cbData)
785 + {
786 + pMsg->pvData = MemAlloc(pMsg->cbData, FALSE);
787 + ExitOnNull(pMsg->pvData, hr, E_OUTOFMEMORY, "Failed to allocate data for message.");
788 +
789 + if (!::ReadFile(hPipe, pMsg->pvData, pMsg->cbData, &cb, NULL))
790 + {
791 + ExitWithLastError(hr, "Failed to read data for message.");
792 + }
793 +
794 + pMsg->fAllocatedData = TRUE;
795 + }
796 +
797 +LExit:
798 + if (!pMsg->fAllocatedData && pMsg->pvData)
799 + {
800 + MemFree(pMsg->pvData);
801 + }
802 +
803 + return hr;
804 +}
805 +
806 +static HRESULT ChildPipeConnected(
807 + __in HANDLE hPipe,
808 + __in_z LPCWSTR wzSecret,
809 + __inout DWORD* pdwProcessId
810 + )
811 +{
812 + HRESULT hr = S_OK;
813 + LPWSTR sczVerificationSecret = NULL;
814 + DWORD cbVerificationSecret = 0;
815 + DWORD dwVerificationProcessId = 0;
816 + DWORD dwRead = 0;
817 + DWORD dwAck = ::GetCurrentProcessId(); // send our process id as the ACK.
818 + DWORD cb = 0;
819 +
820 + // Read the verification secret.
821 + if (!::ReadFile(hPipe, &cbVerificationSecret, sizeof(cbVerificationSecret), &dwRead, NULL))
822 + {
823 + ExitWithLastError(hr, "Failed to read size of verification secret from parent pipe.");
824 + }
825 +
826 + if (255 < cbVerificationSecret / sizeof(WCHAR))
827 + {
828 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
829 + ExitOnRootFailure(hr, "Verification secret from parent is too big.");
830 + }
831 +
832 + hr = StrAlloc(&sczVerificationSecret, cbVerificationSecret / sizeof(WCHAR) + 1);
833 + ExitOnFailure(hr, "Failed to allocate buffer for verification secret.");
834 +
835 + if (!::ReadFile(hPipe, sczVerificationSecret, cbVerificationSecret, &dwRead, NULL))
836 + {
837 + ExitWithLastError(hr, "Failed to read verification secret from parent pipe.");
838 + }
839 +
840 + // Verify the secrets match.
841 + if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, 0, sczVerificationSecret, -1, wzSecret, -1))
842 + {
843 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
844 + ExitOnRootFailure(hr, "Verification secret from parent does not match.");
845 + }
846 +
847 + // Read the verification process id.
848 + if (!::ReadFile(hPipe, &dwVerificationProcessId, sizeof(dwVerificationProcessId), &dwRead, NULL))
849 + {
850 + ExitWithLastError(hr, "Failed to read verification process id from parent pipe.");
851 + }
852 +
853 + // If a process id was not provided, we'll trust the process id from the parent.
854 + if (*pdwProcessId == 0)
855 + {
856 + *pdwProcessId = dwVerificationProcessId;
857 + }
858 + else if (*pdwProcessId != dwVerificationProcessId) // verify the ids match.
859 + {
860 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
861 + ExitOnRootFailure(hr, "Verification process id from parent does not match.");
862 + }
863 +
864 + // All is well, tell the parent process.
865 + if (!::WriteFile(hPipe, &dwAck, sizeof(dwAck), &cb, NULL))
866 + {
867 + ExitWithLastError(hr, "Failed to inform parent process that child is running.");
868 + }
869 +
870 +LExit:
871 + ReleaseStr(sczVerificationSecret);
872 + return hr;
873 +}
src/engine/pipe.h new
+113
@@ -0,0 +1,113 @@
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 +typedef struct _BURN_PIPE_CONNECTION
10 +{
11 + LPWSTR sczName;
12 + LPWSTR sczSecret;
13 + DWORD dwProcessId;
14 +
15 + HANDLE hProcess;
16 + HANDLE hPipe;
17 + HANDLE hCachePipe;
18 +} BURN_PIPE_CONNECTION;
19 +
20 +typedef enum _BURN_PIPE_MESSAGE_TYPE
21 +{
22 + BURN_PIPE_MESSAGE_TYPE_LOG = 0xF0000001,
23 + BURN_PIPE_MESSAGE_TYPE_COMPLETE = 0xF0000002,
24 + BURN_PIPE_MESSAGE_TYPE_TERMINATE = 0xF0000003,
25 +} BURN_PIPE_MESSAGE_TYPE;
26 +
27 +typedef struct _BURN_PIPE_MESSAGE
28 +{
29 + DWORD dwMessage;
30 + DWORD cbData;
31 +
32 + BOOL fAllocatedData;
33 + LPVOID pvData;
34 +} BURN_PIPE_MESSAGE;
35 +
36 +typedef struct _BURN_PIPE_RESULT
37 +{
38 + DWORD dwResult;
39 + BOOL fRestart;
40 +} BURN_PIPE_RESULT;
41 +
42 +
43 +typedef HRESULT (*PFN_PIPE_MESSAGE_CALLBACK)(
44 + __in BURN_PIPE_MESSAGE* pMsg,
45 + __in_opt LPVOID pvContext,
46 + __out DWORD* pdwResult
47 + );
48 +
49 +
50 +// Common functions.
51 +void PipeConnectionInitialize(
52 + __in BURN_PIPE_CONNECTION* pConnection
53 + );
54 +void PipeConnectionUninitialize(
55 + __in BURN_PIPE_CONNECTION* pConnection
56 + );
57 +HRESULT PipeSendMessage(
58 + __in HANDLE hPipe,
59 + __in DWORD dwMessage,
60 + __in_bcount_opt(cbData) LPVOID pvData,
61 + __in DWORD cbData,
62 + __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback,
63 + __in_opt LPVOID pvContext,
64 + __out DWORD* pdwResult
65 + );
66 +HRESULT PipePumpMessages(
67 + __in HANDLE hPipe,
68 + __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback,
69 + __in_opt LPVOID pvContext,
70 + __in BURN_PIPE_RESULT* pResult
71 + );
72 +
73 +// Parent functions.
74 +HRESULT PipeCreateNameAndSecret(
75 + __out_z LPWSTR *psczConnectionName,
76 + __out_z LPWSTR *psczSecret
77 + );
78 +HRESULT PipeCreatePipes(
79 + __in BURN_PIPE_CONNECTION* pConnection,
80 + __in BOOL fCreateCachePipe,
81 + __out HANDLE* phEvent
82 + );
83 +HRESULT PipeLaunchParentProcess(
84 + __in LPCWSTR wzCommandLine,
85 + __in int nCmdShow,
86 + __in_z LPWSTR sczConnectionName,
87 + __in_z LPWSTR sczSecret,
88 + __in BOOL fDisableUnelevate
89 + );
90 +HRESULT PipeLaunchChildProcess(
91 + __in_z LPCWSTR wzExecutablePath,
92 + __in BURN_PIPE_CONNECTION* pConnection,
93 + __in BOOL fElevate,
94 + __in_opt HWND hwndParent
95 + );
96 +HRESULT PipeWaitForChildConnect(
97 + __in BURN_PIPE_CONNECTION* pConnection
98 + );
99 +HRESULT PipeTerminateChildProcess(
100 + __in BURN_PIPE_CONNECTION* pConnection,
101 + __in DWORD dwParentExitCode,
102 + __in BOOL fRestart
103 + );
104 +
105 +// Child functions.
106 +HRESULT PipeChildConnect(
107 + __in BURN_PIPE_CONNECTION* pConnection,
108 + __in BOOL fConnectCachePipe
109 + );
110 +
111 +#ifdef __cplusplus
112 +}
113 +#endif
src/engine/plan.cpp new
+3169
@@ -0,0 +1,3169 @@
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 +// internal struct definitions
6 +
7 +struct PLAN_NONPERMANENT_PACKAGE_INDICES
8 +{
9 + DWORD iAfterExecuteFirstNonPermanentPackage;
10 + DWORD iBeforeRollbackFirstNonPermanentPackage;
11 + DWORD iAfterExecuteLastNonPermanentPackage;
12 + DWORD iAfterRollbackLastNonPermanentPackage;
13 +};
14 +
15 +// internal function definitions
16 +
17 +static void UninitializeRegistrationAction(
18 + __in BURN_DEPENDENT_REGISTRATION_ACTION* pAction
19 + );
20 +static void UninitializeCacheAction(
21 + __in BURN_CACHE_ACTION* pCacheAction
22 + );
23 +static void ResetPlannedPackageState(
24 + __in BURN_PACKAGE* pPackage
25 + );
26 +static HRESULT ProcessPackage(
27 + __in BOOL fBundlePerMachine,
28 + __in BURN_PACKAGE* pCompatiblePackageParent,
29 + __in BURN_USER_EXPERIENCE* pUX,
30 + __in BURN_PLAN* pPlan,
31 + __in BURN_PACKAGE* pPackage,
32 + __in BURN_LOGGING* pLog,
33 + __in BURN_VARIABLES* pVariables,
34 + __in BOOTSTRAPPER_DISPLAY display,
35 + __in BOOTSTRAPPER_RELATION_TYPE relationType,
36 + __in_z_opt LPCWSTR wzLayoutDirectory,
37 + __inout HANDLE* phSyncpointEvent,
38 + __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary,
39 + __in_opt PLAN_NONPERMANENT_PACKAGE_INDICES* pNonpermanentPackageIndices
40 + );
41 +static HRESULT ProcessPackageRollbackBoundary(
42 + __in BURN_PLAN* pPlan,
43 + __in_opt BURN_ROLLBACK_BOUNDARY* pEffectiveRollbackBoundary,
44 + __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary
45 + );
46 +static HRESULT GetActionDefaultRequestState(
47 + __in BOOTSTRAPPER_ACTION action,
48 + __in BOOL fPermanent,
49 + __in BOOTSTRAPPER_PACKAGE_STATE currentState,
50 + __out BOOTSTRAPPER_REQUEST_STATE* pRequestState
51 + );
52 +static HRESULT AddRegistrationAction(
53 + __in BURN_PLAN* pPlan,
54 + __in BURN_DEPENDENT_REGISTRATION_ACTION_TYPE type,
55 + __in_z LPCWSTR wzDependentProviderKey,
56 + __in_z LPCWSTR wzOwnerBundleId
57 + );
58 +static HRESULT AddCachePackage(
59 + __in BURN_PLAN* pPlan,
60 + __in BURN_PACKAGE* pPackage,
61 + __out HANDLE* phSyncpointEvent
62 + );
63 +static HRESULT AddCachePackageHelper(
64 + __in BURN_PLAN* pPlan,
65 + __in BURN_PACKAGE* pPackage,
66 + __out HANDLE* phSyncpointEvent
67 + );
68 +static HRESULT AddCacheSlipstreamMsps(
69 + __in BURN_PLAN* pPlan,
70 + __in BURN_PACKAGE* pPackage
71 + );
72 +static BOOL AlreadyPlannedCachePackage(
73 + __in BURN_PLAN* pPlan,
74 + __in_z LPCWSTR wzPackageId,
75 + __out HANDLE* phSyncpointEvent
76 + );
77 +static DWORD GetNextCheckpointId();
78 +static HRESULT AppendCacheAction(
79 + __in BURN_PLAN* pPlan,
80 + __out BURN_CACHE_ACTION** ppCacheAction
81 + );
82 +static HRESULT AppendRollbackCacheAction(
83 + __in BURN_PLAN* pPlan,
84 + __out BURN_CACHE_ACTION** ppCacheAction
85 + );
86 +static HRESULT AppendLayoutContainerAction(
87 + __in BURN_PLAN* pPlan,
88 + __in_opt BURN_PACKAGE* pPackage,
89 + __in DWORD iPackageStartAction,
90 + __in BURN_CONTAINER* pContainer,
91 + __in BOOL fContainerCached,
92 + __in_z LPCWSTR wzLayoutDirectory
93 + );
94 +static HRESULT AppendCacheOrLayoutPayloadAction(
95 + __in BURN_PLAN* pPlan,
96 + __in_opt BURN_PACKAGE* pPackage,
97 + __in DWORD iPackageStartAction,
98 + __in BURN_PAYLOAD* pPayload,
99 + __in BOOL fPayloadCached,
100 + __in_z_opt LPCWSTR wzLayoutDirectory
101 + );
102 +static BOOL FindContainerCacheAction(
103 + __in BURN_CACHE_ACTION_TYPE type,
104 + __in BURN_PLAN* pPlan,
105 + __in BURN_CONTAINER* pContainer,
106 + __in DWORD iSearchStart,
107 + __in DWORD iSearchEnd,
108 + __out_opt BURN_CACHE_ACTION** ppCacheAction,
109 + __out_opt DWORD* piCacheAction
110 + );
111 +static HRESULT CreateContainerAcquireAndExtractAction(
112 + __in BURN_PLAN* pPlan,
113 + __in BURN_CONTAINER* pContainer,
114 + __in DWORD iPackageStartAction,
115 + __in BOOL fPayloadCached,
116 + __out BURN_CACHE_ACTION** ppContainerExtractAction,
117 + __out DWORD* piContainerTryAgainAction
118 + );
119 +static HRESULT AddAcquireContainer(
120 + __in BURN_PLAN* pPlan,
121 + __in BURN_CONTAINER* pContainer,
122 + __out_opt BURN_CACHE_ACTION** ppCacheAction,
123 + __out_opt DWORD* piCacheAction
124 + );
125 +static HRESULT AddExtractPayload(
126 + __in BURN_CACHE_ACTION* pCacheAction,
127 + __in_opt BURN_PACKAGE* pPackage,
128 + __in BURN_PAYLOAD* pPayload,
129 + __in_z LPCWSTR wzPayloadWorkingPath
130 + );
131 +static BURN_CACHE_ACTION* ProcessSharedPayload(
132 + __in BURN_PLAN* pPlan,
133 + __in BURN_PAYLOAD* pPayload
134 + );
135 +static HRESULT RemoveUnnecessaryActions(
136 + __in BOOL fExecute,
137 + __in BURN_EXECUTE_ACTION* rgActions,
138 + __in DWORD cActions
139 + );
140 +static HRESULT FinalizeSlipstreamPatchActions(
141 + __in BOOL fExecute,
142 + __in BURN_EXECUTE_ACTION* rgActions,
143 + __in DWORD cActions
144 + );
145 +static HRESULT PlanDependencyActions(
146 + __in BOOL fBundlePerMachine,
147 + __in BURN_PLAN* pPlan,
148 + __in BURN_PACKAGE* pPackage
149 + );
150 +static HRESULT CalculateExecuteActions(
151 + __in BURN_USER_EXPERIENCE* pUserExperience,
152 + __in BURN_PACKAGE* pPackage,
153 + __in BURN_VARIABLES* pVariables,
154 + __out_opt BOOL* pfBARequestedCache
155 + );
156 +static BOOL NeedsCache(
157 + __in BURN_PLAN* pPlan,
158 + __in BURN_PACKAGE* pPackage
159 + );
160 +static HRESULT CreateContainerProgress(
161 + __in BURN_PLAN* pPlan,
162 + __in BURN_CONTAINER* pContainer,
163 + __out BURN_CACHE_CONTAINER_PROGRESS** ppContainerProgress
164 + );
165 +static HRESULT CreatePayloadProgress(
166 + __in BURN_PLAN* pPlan,
167 + __in BURN_PAYLOAD* pPayload,
168 + __out BURN_CACHE_PAYLOAD_PROGRESS** ppPayloadProgress
169 + );
170 +
171 +// function definitions
172 +
173 +extern "C" void PlanReset(
174 + __in BURN_PLAN* pPlan,
175 + __in BURN_PACKAGES* pPackages
176 + )
177 +{
178 + if (pPlan->rgRegistrationActions)
179 + {
180 + for (DWORD i = 0; i < pPlan->cRegistrationActions; ++i)
181 + {
182 + UninitializeRegistrationAction(&pPlan->rgRegistrationActions[i]);
183 + }
184 + MemFree(pPlan->rgRegistrationActions);
185 + }
186 +
187 + if (pPlan->rgRollbackRegistrationActions)
188 + {
189 + for (DWORD i = 0; i < pPlan->cRollbackRegistrationActions; ++i)
190 + {
191 + UninitializeRegistrationAction(&pPlan->rgRollbackRegistrationActions[i]);
192 + }
193 + MemFree(pPlan->rgRollbackRegistrationActions);
194 + }
195 +
196 + if (pPlan->rgCacheActions)
197 + {
198 + for (DWORD i = 0; i < pPlan->cCacheActions; ++i)
199 + {
200 + UninitializeCacheAction(&pPlan->rgCacheActions[i]);
201 + }
202 + MemFree(pPlan->rgCacheActions);
203 + }
204 +
205 + if (pPlan->rgExecuteActions)
206 + {
207 + for (DWORD i = 0; i < pPlan->cExecuteActions; ++i)
208 + {
209 + PlanUninitializeExecuteAction(&pPlan->rgExecuteActions[i]);
210 + }
211 + MemFree(pPlan->rgExecuteActions);
212 + }
213 +
214 + if (pPlan->rgRollbackActions)
215 + {
216 + for (DWORD i = 0; i < pPlan->cRollbackActions; ++i)
217 + {
218 + PlanUninitializeExecuteAction(&pPlan->rgRollbackActions[i]);
219 + }
220 + MemFree(pPlan->rgRollbackActions);
221 + }
222 +
223 + if (pPlan->rgCleanActions)
224 + {
225 + // Nothing needs to be freed inside clean actions today.
226 + MemFree(pPlan->rgCleanActions);
227 + }
228 +
229 + if (pPlan->rgPlannedProviders)
230 + {
231 + ReleaseDependencyArray(pPlan->rgPlannedProviders, pPlan->cPlannedProviders);
232 + }
233 +
234 + if (pPlan->rgContainerProgress)
235 + {
236 + MemFree(pPlan->rgContainerProgress);
237 + }
238 +
239 + if (pPlan->shContainerProgress)
240 + {
241 + ReleaseDict(pPlan->shContainerProgress);
242 + }
243 +
244 + if (pPlan->rgPayloadProgress)
245 + {
246 + MemFree(pPlan->rgPayloadProgress);
247 + }
248 +
249 + if (pPlan->shPayloadProgress)
250 + {
251 + ReleaseDict(pPlan->shPayloadProgress);
252 + }
253 +
254 + memset(pPlan, 0, sizeof(BURN_PLAN));
255 +
256 + // Reset the planned actions for each package.
257 + if (pPackages->rgPackages)
258 + {
259 + for (DWORD i = 0; i < pPackages->cPackages; ++i)
260 + {
261 + ResetPlannedPackageState(&pPackages->rgPackages[i]);
262 + }
263 + }
264 +}
265 +
266 +extern "C" void PlanUninitializeExecuteAction(
267 + __in BURN_EXECUTE_ACTION* pExecuteAction
268 + )
269 +{
270 + switch (pExecuteAction->type)
271 + {
272 + case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE:
273 + ReleaseStr(pExecuteAction->exePackage.sczIgnoreDependencies);
274 + ReleaseStr(pExecuteAction->exePackage.sczAncestors);
275 + break;
276 +
277 + case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE:
278 + ReleaseStr(pExecuteAction->msiPackage.sczLogPath);
279 + ReleaseMem(pExecuteAction->msiPackage.rgFeatures);
280 + ReleaseMem(pExecuteAction->msiPackage.rgSlipstreamPatches);
281 + ReleaseMem(pExecuteAction->msiPackage.rgOrderedPatches);
282 + break;
283 +
284 + case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET:
285 + ReleaseStr(pExecuteAction->mspTarget.sczTargetProductCode);
286 + ReleaseStr(pExecuteAction->mspTarget.sczLogPath);
287 + ReleaseMem(pExecuteAction->mspTarget.rgOrderedPatches);
288 + break;
289 +
290 + case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE:
291 + ReleaseStr(pExecuteAction->msuPackage.sczLogPath);
292 + break;
293 +
294 + case BURN_EXECUTE_ACTION_TYPE_SERVICE_STOP: __fallthrough;
295 + case BURN_EXECUTE_ACTION_TYPE_SERVICE_START:
296 + ReleaseStr(pExecuteAction->service.sczServiceName);
297 + break;
298 +
299 + case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY:
300 + ReleaseStr(pExecuteAction->packageDependency.sczBundleProviderKey);
301 + break;
302 +
303 + case BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE:
304 + ReleaseStr(pExecuteAction->compatiblePackage.sczInstalledProductCode);
305 + break;
306 + }
307 +}
308 +
309 +extern "C" HRESULT PlanSetVariables(
310 + __in BOOTSTRAPPER_ACTION action,
311 + __in BURN_VARIABLES* pVariables
312 + )
313 +{
314 + HRESULT hr = S_OK;
315 +
316 + hr = VariableSetNumeric(pVariables, BURN_BUNDLE_ACTION, action, TRUE);
317 + ExitOnFailure(hr, "Failed to set the bundle action built-in variable.");
318 +
319 +LExit:
320 + return hr;
321 +}
322 +
323 +extern "C" HRESULT PlanDefaultPackageRequestState(
324 + __in BURN_PACKAGE_TYPE packageType,
325 + __in BOOTSTRAPPER_PACKAGE_STATE currentState,
326 + __in BOOL fPermanent,
327 + __in BOOTSTRAPPER_ACTION action,
328 + __in BURN_VARIABLES* pVariables,
329 + __in_z_opt LPCWSTR wzInstallCondition,
330 + __in BOOTSTRAPPER_RELATION_TYPE relationType,
331 + __out BOOTSTRAPPER_REQUEST_STATE* pRequestState
332 + )
333 +{
334 + HRESULT hr = S_OK;
335 + BOOTSTRAPPER_REQUEST_STATE defaultRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
336 + BOOL fCondition = FALSE;
337 +
338 + // If doing layout, then always default to requesting the file be cached.
339 + if (BOOTSTRAPPER_ACTION_LAYOUT == action)
340 + {
341 + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_CACHE;
342 + }
343 + else if (BOOTSTRAPPER_RELATION_PATCH == relationType && BURN_PACKAGE_TYPE_MSP == packageType)
344 + {
345 + // For patch related bundles, only install a patch if currently absent during install, modify, or repair.
346 + if (BOOTSTRAPPER_PACKAGE_STATE_ABSENT == currentState && BOOTSTRAPPER_ACTION_INSTALL <= action)
347 + {
348 + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT;
349 + }
350 + else
351 + {
352 + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
353 + }
354 + }
355 + else if (BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED == currentState && BOOTSTRAPPER_ACTION_UNINSTALL != action)
356 + {
357 + // Superseded means the package is on the machine but not active, so only uninstall operations are allowed.
358 + // All other operations do nothing.
359 + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
360 + }
361 + else if (BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE == currentState && !(BOOTSTRAPPER_ACTION_UNINSTALL == action && BURN_PACKAGE_TYPE_MSP == packageType))
362 + {
363 + // Obsolete means the package is not on the machine and should not be installed, *except* patches can be obsolete
364 + // and present so allow them to be removed during uninstall. Everyone else, gets nothing.
365 + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
366 + }
367 + else // pick the best option for the action state and install condition.
368 + {
369 + hr = GetActionDefaultRequestState(action, fPermanent, currentState, &defaultRequestState);
370 + ExitOnFailure(hr, "Failed to get default request state for action.");
371 +
372 + // If there is an install condition (and we're doing an install) evaluate the condition
373 + // to determine whether to use the default request state or make the package absent.
374 + if (BOOTSTRAPPER_ACTION_UNINSTALL != action && wzInstallCondition && *wzInstallCondition)
375 + {
376 + hr = ConditionEvaluate(pVariables, wzInstallCondition, &fCondition);
377 + ExitOnFailure(hr, "Failed to evaluate install condition.");
378 +
379 + *pRequestState = fCondition ? defaultRequestState : BOOTSTRAPPER_REQUEST_STATE_ABSENT;
380 + }
381 + else // just set the package to the default request state.
382 + {
383 + *pRequestState = defaultRequestState;
384 + }
385 + }
386 +
387 +LExit:
388 + return hr;
389 +}
390 +
391 +extern "C" HRESULT PlanLayoutBundle(
392 + __in BURN_PLAN* pPlan,
393 + __in_z LPCWSTR wzExecutableName,
394 + __in DWORD64 qwBundleSize,
395 + __in BURN_VARIABLES* pVariables,
396 + __in BURN_PAYLOADS* pPayloads,
397 + __out_z LPWSTR* psczLayoutDirectory
398 + )
399 +{
400 + HRESULT hr = S_OK;
401 + BURN_CACHE_ACTION* pCacheAction = NULL;
402 + LPWSTR sczExecutablePath = NULL;
403 + LPWSTR sczLayoutDirectory = NULL;
404 +
405 + // Get the layout directory.
406 + hr = VariableGetString(pVariables, BURN_BUNDLE_LAYOUT_DIRECTORY, &sczLayoutDirectory);
407 + if (E_NOTFOUND == hr) // if not set, use the current directory as the layout directory.
408 + {
409 + hr = VariableGetString(pVariables, BURN_BUNDLE_SOURCE_PROCESS_FOLDER, &sczLayoutDirectory);
410 + if (E_NOTFOUND == hr) // if not set, use the current directory as the layout directory.
411 + {
412 + hr = PathForCurrentProcess(&sczExecutablePath, NULL);
413 + ExitOnFailure(hr, "Failed to get path for current executing process as layout directory.");
414 +
415 + hr = PathGetDirectory(sczExecutablePath, &sczLayoutDirectory);
416 + ExitOnFailure(hr, "Failed to get executing process as layout directory.");
417 + }
418 + }
419 + ExitOnFailure(hr, "Failed to get bundle layout directory property.");
420 +
421 + hr = PathBackslashTerminate(&sczLayoutDirectory);
422 + ExitOnFailure(hr, "Failed to ensure layout directory is backslash terminated.");
423 +
424 + // Plan the layout of the bundle engine itself.
425 + hr = AppendCacheAction(pPlan, &pCacheAction);
426 + ExitOnFailure(hr, "Failed to append bundle start action.");
427 +
428 + pCacheAction->type = BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE;
429 +
430 + hr = StrAllocString(&pCacheAction->bundleLayout.sczExecutableName, wzExecutableName, 0);
431 + ExitOnFailure(hr, "Failed to to copy executable name for bundle.");
432 +
433 + hr = StrAllocString(&pCacheAction->bundleLayout.sczLayoutDirectory, sczLayoutDirectory, 0);
434 + ExitOnFailure(hr, "Failed to to copy layout directory for bundle.");
435 +
436 + hr = CacheCalculateBundleLayoutWorkingPath(pPlan->wzBundleId, &pCacheAction->bundleLayout.sczUnverifiedPath);
437 + ExitOnFailure(hr, "Failed to calculate bundle layout working path.");
438 +
439 + pCacheAction->bundleLayout.qwBundleSize = qwBundleSize;
440 +
441 + pPlan->qwCacheSizeTotal += qwBundleSize;
442 +
443 + ++pPlan->cOverallProgressTicksTotal;
444 +
445 + // Plan the layout of layout-only payloads.
446 + for (DWORD i = 0; i < pPayloads->cPayloads; ++i)
447 + {
448 + BURN_PAYLOAD* pPayload = pPayloads->rgPayloads + i;
449 + if (pPayload->fLayoutOnly)
450 + {
451 + // TODO: determine if a payload already exists in the layout and pass appropriate value fPayloadCached
452 + // (instead of always FALSE).
453 + hr = AppendCacheOrLayoutPayloadAction(pPlan, NULL, BURN_PLAN_INVALID_ACTION_INDEX, pPayload, FALSE, sczLayoutDirectory);
454 + ExitOnFailure(hr, "Failed to plan layout payload.");
455 + }
456 + }
457 +
458 + *psczLayoutDirectory = sczLayoutDirectory;
459 + sczLayoutDirectory = NULL;
460 +
461 +LExit:
462 + ReleaseStr(sczLayoutDirectory);
463 + ReleaseStr(sczExecutablePath);
464 +
465 + return hr;
466 +}
467 +
468 +extern "C" HRESULT PlanPackages(
469 + __in BURN_REGISTRATION* pRegistration,
470 + __in BURN_USER_EXPERIENCE* pUX,
471 + __in BURN_PACKAGES* pPackages,
472 + __in BURN_PLAN* pPlan,
473 + __in BURN_LOGGING* pLog,
474 + __in BURN_VARIABLES* pVariables,
475 + __in BOOL fBundleInstalled,
476 + __in BOOTSTRAPPER_DISPLAY display,
477 + __in BOOTSTRAPPER_RELATION_TYPE relationType,
478 + __in_z_opt LPCWSTR wzLayoutDirectory,
479 + __inout HANDLE* phSyncpointEvent
480 + )
481 +{
482 + HRESULT hr = S_OK;
483 + BOOL fBundlePerMachine = pPlan->fPerMachine; // bundle is per-machine if plan starts per-machine.
484 + BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL;
485 +
486 + PLAN_NONPERMANENT_PACKAGE_INDICES nonpermanentPackageIndices;
487 + nonpermanentPackageIndices.iAfterExecuteFirstNonPermanentPackage = BURN_PLAN_INVALID_ACTION_INDEX;
488 + nonpermanentPackageIndices.iBeforeRollbackFirstNonPermanentPackage = BURN_PLAN_INVALID_ACTION_INDEX;
489 + nonpermanentPackageIndices.iAfterExecuteLastNonPermanentPackage = BURN_PLAN_INVALID_ACTION_INDEX;
490 + nonpermanentPackageIndices.iAfterRollbackLastNonPermanentPackage = BURN_PLAN_INVALID_ACTION_INDEX;
491 +
492 + // Plan the packages.
493 + for (DWORD i = 0; i < pPackages->cPackages; ++i)
494 + {
495 + DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? pPackages->cPackages - 1 - i : i;
496 + BURN_PACKAGE* pPackage = pPackages->rgPackages + iPackage;
497 +
498 + // Support passing Ancestors to embedded burn bundles
499 + if (BURN_PACKAGE_TYPE_EXE == pPackage->type && BURN_EXE_PROTOCOL_TYPE_BURN == pPackage->Exe.protocol)
500 + {
501 + // Pass along any ancestors and ourself to prevent infinite loops.
502 + if (pRegistration->sczAncestors && *pRegistration->sczAncestors)
503 + {
504 + hr = StrAllocFormatted(&pPackage->Exe.sczAncestors, L"%ls;%ls", pRegistration->sczAncestors, pRegistration->sczId);
505 + ExitOnFailure(hr, "Failed to copy ancestors and self to related bundle ancestors.");
506 + }
507 + else
508 + {
509 + hr = StrAllocString(&pPackage->Exe.sczAncestors, pRegistration->sczId, 0);
510 + ExitOnFailure(hr, "Failed to copy self to related bundle ancestors.");
511 + }
512 + }
513 +
514 + hr = ProcessPackage(fBundlePerMachine, NULL, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, wzLayoutDirectory, phSyncpointEvent, &pRollbackBoundary, &nonpermanentPackageIndices);
515 + ExitOnFailure(hr, "Failed to process package.");
516 +
517 + // Attempt to remove orphaned packages during uninstall. Currently only MSI packages are supported and should not require source.
518 + if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action && BURN_PACKAGE_TYPE_MSI == pPackage->type && pPackage->Msi.fCompatibleInstalled)
519 + {
520 + BURN_PACKAGE* pCompatiblePackage = NULL;
521 + BURN_EXECUTE_ACTION* pAction = NULL;
522 +
523 + // Add the compatible package to the list.
524 + hr = MsiEngineAddCompatiblePackage(pPackages, pPackage, &pCompatiblePackage);
525 + ExitOnFailure(hr, "Failed to add compatible package for package: %ls", pPackage->sczId);
526 +
527 + // Plan to load the compatible package into the elevated engine before its needed.
528 + hr = PlanAppendExecuteAction(pPlan, &pAction);
529 + ExitOnFailure(hr, "Failed to append execute action.");
530 +
531 + pAction->type = BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE;
532 + pAction->compatiblePackage.pReferencePackage = pPackage;
533 + pAction->compatiblePackage.qwInstalledVersion = pCompatiblePackage->Msi.qwVersion;
534 +
535 + hr = StrAllocString(&pAction->compatiblePackage.sczInstalledProductCode, pCompatiblePackage->Msi.sczProductCode, 0);
536 + ExitOnFailure(hr, "Failed to copy installed ProductCode");
537 +
538 + // Process the compatible MSI package like any other.
539 + hr = ProcessPackage(fBundlePerMachine, pPackage, pUX, pPlan, pCompatiblePackage, pLog, pVariables, display, relationType, wzLayoutDirectory, phSyncpointEvent, &pRollbackBoundary, &nonpermanentPackageIndices);
540 + ExitOnFailure(hr, "Failed to process compatible package.");
541 +
542 + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pCompatiblePackage->execute)
543 + {
544 + LogId(REPORT_STANDARD, MSG_PLANNED_ORPHAN_PACKAGE_FROM_PROVIDER, pPackage->sczId, pCompatiblePackage->Msi.sczProductCode, pPackage->Msi.sczProductCode);
545 + }
546 + }
547 + }
548 +
549 + // Insert the "keep registration" and "remove registration" actions in the plan when installing the first time and anytime we are uninstalling respectively.
550 + if (!fBundleInstalled && (BOOTSTRAPPER_ACTION_INSTALL == pPlan->action || BOOTSTRAPPER_ACTION_MODIFY == pPlan->action || BOOTSTRAPPER_ACTION_REPAIR == pPlan->action))
551 + {
552 + if (BURN_PLAN_INVALID_ACTION_INDEX == nonpermanentPackageIndices.iAfterExecuteFirstNonPermanentPackage)
553 + {
554 + nonpermanentPackageIndices.iAfterExecuteFirstNonPermanentPackage = pPlan->cExecuteActions;
555 + nonpermanentPackageIndices.iBeforeRollbackFirstNonPermanentPackage = pPlan->cRollbackActions;
556 + }
557 +
558 + hr = PlanKeepRegistration(pPlan, nonpermanentPackageIndices.iAfterExecuteFirstNonPermanentPackage, nonpermanentPackageIndices.iBeforeRollbackFirstNonPermanentPackage);
559 + ExitOnFailure(hr, "Failed to plan install keep registration.");
560 + }
561 + else if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action)
562 + {
563 + if (BURN_PLAN_INVALID_ACTION_INDEX == nonpermanentPackageIndices.iAfterExecuteLastNonPermanentPackage)
564 + {
565 + nonpermanentPackageIndices.iAfterExecuteLastNonPermanentPackage = pPlan->cExecuteActions;
566 + nonpermanentPackageIndices.iAfterRollbackLastNonPermanentPackage = pPlan->cRollbackActions;
567 + }
568 +
569 + hr = PlanRemoveRegistration(pPlan, nonpermanentPackageIndices.iAfterExecuteLastNonPermanentPackage, nonpermanentPackageIndices.iAfterRollbackLastNonPermanentPackage);
570 + ExitOnFailure(hr, "Failed to plan uninstall remove registration.");
571 + }
572 +
573 + // If we still have an open rollback boundary, complete it.
574 + if (pRollbackBoundary)
575 + {
576 + hr = PlanRollbackBoundaryComplete(pPlan);
577 + ExitOnFailure(hr, "Failed to plan rollback boundary begin.");
578 +
579 + pRollbackBoundary = NULL;
580 + }
581 +
582 + // Plan clean up of packages.
583 + for (DWORD i = 0; i < pPackages->cPackages; ++i)
584 + {
585 + DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? pPackages->cPackages - 1 - i : i;
586 + BURN_PACKAGE* pPackage = pPackages->rgPackages + iPackage;
587 +
588 + hr = PlanCleanPackage(pPlan, pPackage);
589 + ExitOnFailure(hr, "Failed to plan clean package.");
590 + }
591 +
592 + // Plan best-effort clean up of compatible packages.
593 + for (DWORD i = 0; i < pPackages->cCompatiblePackages; ++i)
594 + {
595 + DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? pPackages->cCompatiblePackages - 1 - i : i;
596 + BURN_PACKAGE* pCompatiblePackage = pPackages->rgCompatiblePackages + iPackage;
597 +
598 + PlanCleanPackage(pPlan, pCompatiblePackage);
599 + }
600 +
601 +LExit:
602 + return hr;
603 +}
604 +
605 +extern "C" HRESULT PlanRegistration(
606 + __in BURN_PLAN* pPlan,
607 + __in BURN_REGISTRATION* pRegistration,
608 + __in BOOTSTRAPPER_RESUME_TYPE resumeType,
609 + __in BOOTSTRAPPER_RELATION_TYPE relationType,
610 + __in_z_opt LPCWSTR wzIgnoreDependencies,
611 + __out BOOL* pfContinuePlanning
612 + )
613 +{
614 + HRESULT hr = S_OK;
615 + LPCWSTR wzSelfDependent = NULL;
616 + STRINGDICT_HANDLE sdIgnoreDependents = NULL;
617 + DEPENDENCY* rgDependencies = NULL;
618 + UINT cDependencies = 0;
619 +
620 + pPlan->fRegister = TRUE; // register the bundle since we're modifying machine state.
621 +
622 + // Keep the registration if the bundle was already installed or we are planning after a restart.
623 + pPlan->fKeepRegistrationDefault = (pRegistration->fInstalled || BOOTSTRAPPER_RESUME_TYPE_REBOOT == resumeType);
624 +
625 + pPlan->fDisallowRemoval = FALSE; // by default the bundle can be planned to be removed
626 +
627 + // If no parent was specified at all, use the bundle id as the self dependent.
628 + if (!pRegistration->sczActiveParent)
629 + {
630 + wzSelfDependent = pRegistration->sczId;
631 + }
632 + else if (*pRegistration->sczActiveParent) // if parent was specified use that as the self dependent.
633 + {
634 + wzSelfDependent = pRegistration->sczActiveParent;
635 + }
636 + // else parent:none was used which means we should not register a dependency on ourself.
637 +
638 + if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action)
639 + {
640 + // If our provider key was detected and it points to our current bundle then we can
641 + // unregister the bundle dependency.
642 + if (pRegistration->sczDetectedProviderKeyBundleId &&
643 + CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczId, -1, pRegistration->sczDetectedProviderKeyBundleId, -1))
644 + {
645 + pPlan->dependencyRegistrationAction = BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER;
646 + }
647 + else // log that another bundle already owned our registration, hopefully this only happens when a newer version
648 + { // of a bundle installed and is in the process of upgrading us.
649 + LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_PROVIDER_KEY_REMOVAL, pRegistration->sczProviderKey, pRegistration->sczDetectedProviderKeyBundleId);
650 + }
651 +
652 + // Create the dictionary of dependents that should be ignored.
653 + hr = DictCreateStringList(&sdIgnoreDependents, 5, DICT_FLAG_CASEINSENSITIVE);
654 + ExitOnFailure(hr, "Failed to create the string dictionary.");
655 +
656 + // If the self-dependent dependent exists, plan its removal. If we did not do this, we
657 + // would prevent self-removal.
658 + if (wzSelfDependent && DependencyDependentExists(pRegistration, wzSelfDependent))
659 + {
660 + hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER, wzSelfDependent, pRegistration->sczId);
661 + ExitOnFailure(hr, "Failed to allocate registration action.");
662 +
663 + hr = DependencyAddIgnoreDependencies(sdIgnoreDependents, wzSelfDependent);
664 + ExitOnFailure(hr, "Failed to add self-dependent to ignore dependents.");
665 + }
666 +
667 + // If we are not doing an upgrade, we check to see if there are still dependents on us and if so we skip planning.
668 + // However, when being upgraded, we always execute our uninstall because a newer version of us is probably
669 + // already on the machine and we need to clean up the stuff specific to this bundle.
670 + if (BOOTSTRAPPER_RELATION_UPGRADE != relationType)
671 + {
672 + // If there were other dependencies to ignore, add them.
673 + if (wzIgnoreDependencies && *wzIgnoreDependencies)
674 + {
675 + hr = DependencyAddIgnoreDependencies(sdIgnoreDependents, wzIgnoreDependencies);
676 + ExitOnFailure(hr, "Failed to add dependents ignored from command-line.");
677 + }
678 +
679 + // For addon or patch bundles, dependent related bundles should be ignored. This allows
680 + // that addon or patch to be removed even though bundles it targets still are registered.
681 + for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i)
682 + {
683 + const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i;
684 +
685 + if (BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType)
686 + {
687 + for (DWORD j = 0; j < pRelatedBundle->package.cDependencyProviders; ++j)
688 + {
689 + const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders + j;
690 +
691 + hr = DependencyAddIgnoreDependencies(sdIgnoreDependents, pProvider->sczKey);
692 + ExitOnFailure(hr, "Failed to add dependent bundle provider key to ignore dependents.");
693 + }
694 + }
695 + }
696 +
697 + // If there are any (non-ignored and not-planned-to-be-removed) dependents left, uninstall.
698 + hr = DepCheckDependents(pRegistration->hkRoot, pRegistration->sczProviderKey, 0, sdIgnoreDependents, &rgDependencies, &cDependencies);
699 + if (E_FILENOTFOUND == hr)
700 + {
701 + hr = S_OK;
702 + }
703 + else if (SUCCEEDED(hr) && cDependencies)
704 + {
705 + // TODO: callback to the BA and let it have the option to ignore any of these dependents?
706 +
707 + pPlan->fDisallowRemoval = TRUE; // ensure the registration stays
708 + *pfContinuePlanning = FALSE; // skip the rest of planning.
709 +
710 + LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_DUE_TO_DEPENDENTS, cDependencies);
711 + }
712 + ExitOnFailure(hr, "Failed to check for remaining dependents during planning.");
713 + }
714 + }
715 + else
716 + {
717 + BOOL fAddonOrPatchBundle = (pRegistration->cAddonCodes || pRegistration->cPatchCodes);
718 +
719 + // If the bundle is not cached or will not be cached after restart, ensure the bundle is cached.
720 + if (!FileExistsAfterRestart(pRegistration->sczCacheExecutablePath, NULL))
721 + {
722 + pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE;
723 + pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION;
724 + }
725 + else if (BOOTSTRAPPER_ACTION_REPAIR == pPlan->action && !CacheBundleRunningFromCache()) // repairing but not not running from the cache.
726 + {
727 + pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE;
728 + pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION;
729 + }
730 + else if (BOOTSTRAPPER_ACTION_REPAIR == pPlan->action) // just repair, make sure the registration is "fixed up".
731 + {
732 + pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION;
733 + }
734 +
735 + // Always update our estimated size registration when installing/modify/repair since things
736 + // may have been added or removed or it just needs to be "fixed up".
737 + pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_UPDATE_SIZE;
738 +
739 + // Always plan to write our provider key registration when installing/modify/repair to "fix it"
740 + // if broken.
741 + pPlan->dependencyRegistrationAction = BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER;
742 +
743 + // Register each dependent related bundle. The ensures that addons and patches are reference
744 + // counted and stick around until the last targeted bundle is removed.
745 + for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i)
746 + {
747 + const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i;
748 +
749 + if (BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType)
750 + {
751 + for (DWORD j = 0; j < pRelatedBundle->package.cDependencyProviders; ++j)
752 + {
753 + const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders + j;
754 +
755 + if (!DependencyDependentExists(pRegistration, pProvider->sczKey))
756 + {
757 + hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER, pProvider->sczKey, pRelatedBundle->package.sczId);
758 + ExitOnFailure(hr, "Failed to add registration action for dependent related bundle.");
759 + }
760 + }
761 + }
762 + }
763 +
764 + // Only do the following if we decided there was a dependent self to register. If so and and an explicit parent was
765 + // provided, register dependent self. Otherwise, if this bundle is not an addon or patch bundle then self-regisiter
766 + // as our own dependent.
767 + if (wzSelfDependent && (pRegistration->sczActiveParent || !fAddonOrPatchBundle))
768 + {
769 + if (!DependencyDependentExists(pRegistration, wzSelfDependent))
770 + {
771 + hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER, wzSelfDependent, pRegistration->sczId);
772 + ExitOnFailure(hr, "Failed to add registration action for self dependent.");
773 + }
774 + }
775 + }
776 +
777 +LExit:
778 + ReleaseDict(sdIgnoreDependents);
779 + ReleaseDependencyArray(rgDependencies, cDependencies);
780 +
781 + return hr;
782 +}
783 +
784 +extern "C" HRESULT PlanPassThroughBundle(
785 + __in BURN_USER_EXPERIENCE* pUX,
786 + __in BURN_PACKAGE* pPackage,
787 + __in BURN_PLAN* pPlan,
788 + __in BURN_LOGGING* pLog,
789 + __in BURN_VARIABLES* pVariables,
790 + __in BOOTSTRAPPER_DISPLAY display,
791 + __in BOOTSTRAPPER_RELATION_TYPE relationType,
792 + __inout HANDLE* phSyncpointEvent
793 + )
794 +{
795 + HRESULT hr = S_OK;
796 + BOOL fBundlePerMachine = pPlan->fPerMachine; // bundle is per-machine if plan starts per-machine.
797 + BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL;
798 +
799 + // Plan passthrough package.
800 + hr = ProcessPackage(fBundlePerMachine, NULL, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, NULL, phSyncpointEvent, &pRollbackBoundary, NULL);
801 + ExitOnFailure(hr, "Failed to process passthrough package.");
802 +
803 + // If we still have an open rollback boundary, complete it.
804 + if (pRollbackBoundary)
805 + {
806 + hr = PlanRollbackBoundaryComplete(pPlan);
807 + ExitOnFailure(hr, "Failed to plan rollback boundary for passthrough package.");
808 + }
809 +
810 + // Notice that the PlanCleanPackage() function is purposefully missing here. Passthrough packages
811 + // are never cleaned up by the calling bundle (they delete themselves when appropriate) so we don't
812 + // need to plan clean up.
813 +
814 +LExit:
815 + return hr;
816 +}
817 +
818 +extern "C" HRESULT PlanUpdateBundle(
819 + __in BURN_USER_EXPERIENCE* pUX,
820 + __in BURN_PACKAGE* pPackage,
821 + __in BURN_PLAN* pPlan,
822 + __in BURN_LOGGING* pLog,
823 + __in BURN_VARIABLES* pVariables,
824 + __in BOOTSTRAPPER_DISPLAY display,
825 + __in BOOTSTRAPPER_RELATION_TYPE relationType,
826 + __inout HANDLE* phSyncpointEvent
827 + )
828 +{
829 + HRESULT hr = S_OK;
830 + BOOL fBundlePerMachine = pPlan->fPerMachine; // bundle is per-machine if plan starts per-machine.
831 + BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL;
832 +
833 + // Plan update package.
834 + hr = ProcessPackage(fBundlePerMachine, NULL, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, NULL, phSyncpointEvent, &pRollbackBoundary, NULL);
835 + ExitOnFailure(hr, "Failed to process update package.");
836 +
837 + // If we still have an open rollback boundary, complete it.
838 + if (pRollbackBoundary)
839 + {
840 + hr = PlanRollbackBoundaryComplete(pPlan);
841 + ExitOnFailure(hr, "Failed to plan rollback boundary for update package.");
842 + }
843 +
844 + // Plan clean up of update package.
845 + hr = PlanCleanPackage(pPlan, pPackage);
846 + ExitOnFailure(hr, "Failed to plan clean of update package.");
847 +
848 +LExit:
849 + return hr;
850 +}
851 +
852 +static HRESULT ProcessPackage(
853 + __in BOOL fBundlePerMachine,
854 + __in BURN_PACKAGE* pCompatiblePackageParent,
855 + __in BURN_USER_EXPERIENCE* pUX,
856 + __in BURN_PLAN* pPlan,
857 + __in BURN_PACKAGE* pPackage,
858 + __in BURN_LOGGING* pLog,
859 + __in BURN_VARIABLES* pVariables,
860 + __in BOOTSTRAPPER_DISPLAY display,
861 + __in BOOTSTRAPPER_RELATION_TYPE relationType,
862 + __in_z_opt LPCWSTR wzLayoutDirectory,
863 + __inout HANDLE* phSyncpointEvent,
864 + __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary,
865 + __in_opt PLAN_NONPERMANENT_PACKAGE_INDICES* pNonpermanentPackageIndices
866 + )
867 +{
868 + HRESULT hr = S_OK;
869 + BURN_ROLLBACK_BOUNDARY* pEffectiveRollbackBoundary = NULL;
870 + BOOL fPlanPackageBegan = FALSE;
871 +
872 + // Remember the default requested state so the engine doesn't get blamed for planning the wrong thing if the BA changes it.
873 + hr = PlanDefaultPackageRequestState(pPackage->type, pPackage->currentState, !pPackage->fUninstallable, pPlan->action, pVariables, pPackage->sczInstallCondition, relationType, &pPackage->defaultRequested);
874 + ExitOnFailure(hr, "Failed to set default package state.");
875 +
876 + pPackage->requested = pPackage->defaultRequested;
877 + fPlanPackageBegan = TRUE;
878 +
879 + if (pCompatiblePackageParent)
880 + {
881 + AssertSz(BURN_PACKAGE_TYPE_MSI == pPackage->type, "Currently only MSI packages have compatible packages.");
882 +
883 + hr = UserExperienceOnPlanCompatibleMsiPackageBegin(pUX, pCompatiblePackageParent->sczId, pPackage->sczId, pPackage->Msi.qwVersion, &pPackage->requested);
884 + ExitOnRootFailure(hr, "BA aborted plan compatible MSI package begin.");
885 + }
886 + else
887 + {
888 + hr = UserExperienceOnPlanPackageBegin(pUX, pPackage->sczId, &pPackage->requested);
889 + ExitOnRootFailure(hr, "BA aborted plan package begin.");
890 + }
891 +
892 + pEffectiveRollbackBoundary = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? pPackage->pRollbackBoundaryBackward : pPackage->pRollbackBoundaryForward;
893 + hr = ProcessPackageRollbackBoundary(pPlan, pEffectiveRollbackBoundary, ppRollbackBoundary);
894 + ExitOnFailure(hr, "Failed to process package rollback boundary.");
895 +
896 + // If the package is in a requested state, plan it.
897 + if (BOOTSTRAPPER_REQUEST_STATE_NONE != pPackage->requested)
898 + {
899 + if (BOOTSTRAPPER_ACTION_LAYOUT == pPlan->action)
900 + {
901 + hr = PlanLayoutPackage(pPlan, pPackage, wzLayoutDirectory);
902 + ExitOnFailure(hr, "Failed to plan layout package.");
903 + }
904 + else
905 + {
906 + if (pPackage->fUninstallable && pNonpermanentPackageIndices)
907 + {
908 + if (BURN_PLAN_INVALID_ACTION_INDEX == pNonpermanentPackageIndices->iBeforeRollbackFirstNonPermanentPackage)
909 + {
910 + pNonpermanentPackageIndices->iBeforeRollbackFirstNonPermanentPackage = pPlan->cRollbackActions;
911 + }
912 + }
913 +
914 + hr = PlanExecutePackage(fBundlePerMachine, display, pUX, pPlan, pPackage, pLog, pVariables, phSyncpointEvent);
915 + ExitOnFailure(hr, "Failed to plan execute package.");
916 +
917 + if (pPackage->fUninstallable && pNonpermanentPackageIndices)
918 + {
919 + if (BURN_PLAN_INVALID_ACTION_INDEX == pNonpermanentPackageIndices->iAfterExecuteFirstNonPermanentPackage)
920 + {
921 + pNonpermanentPackageIndices->iAfterExecuteFirstNonPermanentPackage = pPlan->cExecuteActions - 1;
922 + }
923 +
924 + pNonpermanentPackageIndices->iAfterExecuteLastNonPermanentPackage = pPlan->cExecuteActions;
925 + pNonpermanentPackageIndices->iAfterRollbackLastNonPermanentPackage = pPlan->cRollbackActions;
926 + }
927 + }
928 + }
929 + else if (BOOTSTRAPPER_ACTION_LAYOUT != pPlan->action)
930 + {
931 + // All packages that have cacheType set to always should be cached if the bundle is going to be present.
932 + if (BURN_CACHE_TYPE_ALWAYS == pPackage->cacheType && BOOTSTRAPPER_ACTION_INSTALL <= pPlan->action)
933 + {
934 + hr = PlanCachePackage(fBundlePerMachine, pUX, pPlan, pPackage, pVariables, phSyncpointEvent);
935 + ExitOnFailure(hr, "Failed to plan cache package.");
936 + }
937 + else
938 + {
939 + // Make sure the package is properly ref-counted even if no plan is requested.
940 + hr = PlanDependencyActions(fBundlePerMachine, pPlan, pPackage);
941 + ExitOnFailure(hr, "Failed to plan dependency actions for package: %ls", pPackage->sczId);
942 + }
943 + }
944 +
945 + // Add the checkpoint after each package and dependency registration action.
946 + if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute || BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback || BURN_DEPENDENCY_ACTION_NONE != pPackage->dependencyExecute)
947 + {
948 + hr = PlanExecuteCheckpoint(pPlan);
949 + ExitOnFailure(hr, "Failed to append execute checkpoint.");
950 + }
951 +
952 +LExit:
953 + if (fPlanPackageBegan)
954 + {
955 + if (pCompatiblePackageParent)
956 + {
957 + UserExperienceOnPlanCompatibleMsiPackageComplete(pUX, pCompatiblePackageParent->sczId, pPackage->sczId, hr, pPackage->currentState, pPackage->requested, pPackage->execute, pPackage->rollback);
958 + }
959 + else
960 + {
961 + UserExperienceOnPlanPackageComplete(pUX, pPackage->sczId, hr, pPackage->currentState, pPackage->requested, pPackage->execute, pPackage->rollback);
962 + }
963 + }
964 +
965 + return hr;
966 +}
967 +
968 +static HRESULT ProcessPackageRollbackBoundary(
969 + __in BURN_PLAN* pPlan,
970 + __in_opt BURN_ROLLBACK_BOUNDARY* pEffectiveRollbackBoundary,
971 + __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary
972 + )
973 +{
974 + HRESULT hr = S_OK;
975 +
976 + // If the package marks the start of a rollback boundary, start a new one.
977 + if (pEffectiveRollbackBoundary)
978 + {
979 + // Complete previous rollback boundary.
980 + if (*ppRollbackBoundary)
981 + {
982 + hr = PlanRollbackBoundaryComplete(pPlan);
983 + ExitOnFailure(hr, "Failed to plan rollback boundary complete.");
984 + }
985 +
986 + // Start new rollback boundary.
987 + hr = PlanRollbackBoundaryBegin(pPlan, pEffectiveRollbackBoundary);
988 + ExitOnFailure(hr, "Failed to plan rollback boundary begin.");
989 +
990 + *ppRollbackBoundary = pEffectiveRollbackBoundary;
991 + }
992 +
993 +LExit:
994 + return hr;
995 +}
996 +
997 +extern "C" HRESULT PlanLayoutPackage(
998 + __in BURN_PLAN* pPlan,
999 + __in BURN_PACKAGE* pPackage,
1000 + __in_z_opt LPCWSTR wzLayoutDirectory
1001 + )
1002 +{
1003 + HRESULT hr = S_OK;
1004 + BURN_CACHE_ACTION* pCacheAction = NULL;
1005 + DWORD iPackageStartAction = 0;
1006 +
1007 + hr = AppendCacheAction(pPlan, &pCacheAction);
1008 + ExitOnFailure(hr, "Failed to append package start action.");
1009 +
1010 + pCacheAction->type = BURN_CACHE_ACTION_TYPE_PACKAGE_START;
1011 + pCacheAction->packageStart.pPackage = pPackage;
1012 +
1013 + // Remember the index for the package start action (which is now the last in the cache
1014 + // actions array) because the array may be resized later and move around in memory.
1015 + iPackageStartAction = pPlan->cCacheActions - 1;
1016 +
1017 + // If any of the package payloads are not cached, add them to the plan.
1018 + for (DWORD i = 0; i < pPackage->cPayloads; ++i)
1019 + {
1020 + BURN_PACKAGE_PAYLOAD* pPackagePayload = &pPackage->rgPayloads[i];
1021 +
1022 + // If doing layout and the package is in a container.
1023 + if (wzLayoutDirectory && pPackagePayload->pPayload->pContainer)
1024 + {
1025 + // TODO: determine if a container already exists in the layout and pass appropriate value fPayloadCached (instead of always FALSE).
1026 + hr = AppendLayoutContainerAction(pPlan, pPackage, iPackageStartAction, pPackagePayload->pPayload->pContainer, FALSE, wzLayoutDirectory);
1027 + ExitOnFailure(hr, "Failed to append layout container action.");
1028 + }
1029 + else
1030 + {
1031 + // TODO: determine if a payload already exists in the layout and pass appropriate value fPayloadCached (instead of always FALSE).
1032 + hr = AppendCacheOrLayoutPayloadAction(pPlan, pPackage, iPackageStartAction, pPackagePayload->pPayload, FALSE, wzLayoutDirectory);
1033 + ExitOnFailure(hr, "Failed to append cache/layout payload action.");
1034 + }
1035 +
1036 + Assert(BURN_CACHE_ACTION_TYPE_PACKAGE_START == pPlan->rgCacheActions[iPackageStartAction].type);
1037 + ++pPlan->rgCacheActions[iPackageStartAction].packageStart.cCachePayloads;
1038 + pPlan->rgCacheActions[iPackageStartAction].packageStart.qwCachePayloadSizeTotal += pPackagePayload->pPayload->qwFileSize;
1039 + }
1040 +
1041 + // Create package stop action.
1042 + hr = AppendCacheAction(pPlan, &pCacheAction);
1043 + ExitOnFailure(hr, "Failed to append cache action.");
1044 +
1045 + pCacheAction->type = BURN_CACHE_ACTION_TYPE_PACKAGE_STOP;
1046 + pCacheAction->packageStop.pPackage = pPackage;
1047 +
1048 + // Update the start action with the location of the complete action.
1049 + pPlan->rgCacheActions[iPackageStartAction].packageStart.iPackageCompleteAction = pPlan->cCacheActions - 1;
1050 +
1051 + ++pPlan->cOverallProgressTicksTotal;
1052 +
1053 +LExit:
1054 + return hr;
1055 +}
1056 +
1057 +extern "C" HRESULT PlanCachePackage(
1058 + __in BOOL fPerMachine,
1059 + __in BURN_USER_EXPERIENCE* pUserExperience,
1060 + __in BURN_PLAN* pPlan,
1061 + __in BURN_PACKAGE* pPackage,
1062 + __in BURN_VARIABLES* pVariables,
1063 + __out HANDLE* phSyncpointEvent
1064 + )
1065 +{
1066 + HRESULT hr = S_OK;
1067 + BOOL fBARequestedCache = FALSE;
1068 +
1069 + // Calculate the execute actions because we need them to decide whether the package should be cached.
1070 + hr = CalculateExecuteActions(pUserExperience, pPackage, pVariables, &fBARequestedCache);
1071 + ExitOnFailure(hr, "Failed to calculate execute actions for package: %ls", pPackage->sczId);
1072 +
1073 + if (fBARequestedCache || NeedsCache(pPlan, pPackage))
1074 + {
1075 + hr = AddCachePackage(pPlan, pPackage, phSyncpointEvent);
1076 + ExitOnFailure(hr, "Failed to plan cache package.");
1077 +
1078 + if (pPackage->fPerMachine)
1079 + {
1080 + pPlan->fPerMachine = TRUE;
1081 + }
1082 + }
1083 +
1084 + // Make sure the package is properly ref-counted.
1085 + hr = PlanDependencyActions(fPerMachine, pPlan, pPackage);
1086 + ExitOnFailure(hr, "Failed to plan dependency actions for package: %ls", pPackage->sczId);
1087 +
1088 +LExit:
1089 + return hr;
1090 +}
1091 +
1092 +extern "C" HRESULT PlanExecutePackage(
1093 + __in BOOL fPerMachine,
1094 + __in BOOTSTRAPPER_DISPLAY display,
1095 + __in BURN_USER_EXPERIENCE* pUserExperience,
1096 + __in BURN_PLAN* pPlan,
1097 + __in BURN_PACKAGE* pPackage,
1098 + __in BURN_LOGGING* pLog,
1099 + __in BURN_VARIABLES* pVariables,
1100 + __inout HANDLE* phSyncpointEvent
1101 + )
1102 +{
1103 + HRESULT hr = S_OK;
1104 + BOOL fBARequestedCache = FALSE;
1105 +
1106 + hr = CalculateExecuteActions(pUserExperience, pPackage, pVariables, &fBARequestedCache);
1107 + ExitOnFailure(hr, "Failed to calculate plan actions for package: %ls", pPackage->sczId);
1108 +
1109 + // Calculate package states based on reference count and plan certain dependency actions prior to planning the package execute action.
1110 + hr = DependencyPlanPackageBegin(fPerMachine, pPackage, pPlan);
1111 + ExitOnFailure(hr, "Failed to begin plan dependency actions for package: %ls", pPackage->sczId);
1112 +
1113 + if (fBARequestedCache || NeedsCache(pPlan, pPackage))
1114 + {
1115 + hr = AddCachePackage(pPlan, pPackage, phSyncpointEvent);
1116 + ExitOnFailure(hr, "Failed to plan cache package.");
1117 + }
1118 + else if (BURN_CACHE_STATE_COMPLETE != pPackage->cache && // if the package is not in the cache, disable any rollback that would require the package from the cache.
1119 + (BOOTSTRAPPER_ACTION_STATE_UNINSTALL < pPackage->rollback || (BURN_PACKAGE_TYPE_EXE == pPackage->type && BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback))
1120 + )
1121 + {
1122 + LogId(REPORT_STANDARD, MSG_PLAN_DISABLING_ROLLBACK_NO_CACHE, pPackage->sczId, LoggingCacheStateToString(pPackage->cache), LoggingActionStateToString(pPackage->rollback));
1123 + pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
1124 + }
1125 +
1126 + // Add the cache and install size to estimated size if it will be on the machine at the end of the install
1127 + if (BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested ||
1128 + (BOOTSTRAPPER_PACKAGE_STATE_PRESENT == pPackage->currentState && BOOTSTRAPPER_REQUEST_STATE_ABSENT < pPackage->requested) ||
1129 + BURN_CACHE_TYPE_ALWAYS == pPackage->cacheType
1130 + )
1131 + {
1132 + // If the package will remain in the cache, add the package size to the estimated size
1133 + if (BURN_CACHE_TYPE_YES <= pPackage->cacheType)
1134 + {
1135 + pPlan->qwEstimatedSize += pPackage->qwSize;
1136 + }
1137 +
1138 + // If the package will end up installed on the machine, add the install size to the estimated size.
1139 + if (BOOTSTRAPPER_REQUEST_STATE_CACHE < pPackage->requested)
1140 + {
1141 + // MSP packages get cached automatically by windows installer with any embedded cabs, so include that in the size as well
1142 + if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
1143 + {
1144 + pPlan->qwEstimatedSize += pPackage->qwSize;
1145 + }
1146 +
1147 + pPlan->qwEstimatedSize += pPackage->qwInstallSize;
1148 + }
1149 + }
1150 +
1151 + // Add execute actions.
1152 + switch (pPackage->type)
1153 + {
1154 + case BURN_PACKAGE_TYPE_EXE:
1155 + hr = ExeEnginePlanAddPackage(NULL, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent, pPackage->fAcquire);
1156 + break;
1157 +
1158 + case BURN_PACKAGE_TYPE_MSI:
1159 + hr = MsiEnginePlanAddPackage(display, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent, pPackage->fAcquire);
1160 + break;
1161 +
1162 + case BURN_PACKAGE_TYPE_MSP:
1163 + hr = MspEnginePlanAddPackage(display, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent, pPackage->fAcquire);
1164 + break;
1165 +
1166 + case BURN_PACKAGE_TYPE_MSU:
1167 + hr = MsuEnginePlanAddPackage(pPackage, pPlan, pLog, pVariables, *phSyncpointEvent, pPackage->fAcquire);
1168 + break;
1169 +
1170 + default:
1171 + hr = E_UNEXPECTED;
1172 + ExitOnFailure(hr, "Invalid package type.");
1173 + }
1174 + ExitOnFailure(hr, "Failed to add plan actions for package: %ls", pPackage->sczId);
1175 +
1176 + // Plan certain dependency actions after planning the package execute action.
1177 + hr = DependencyPlanPackageComplete(pPackage, pPlan);
1178 + ExitOnFailure(hr, "Failed to complete plan dependency actions for package: %ls", pPackage->sczId);
1179 +
1180 + // If we are going to take any action on this package, add progress for it.
1181 + if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute || BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback)
1182 + {
1183 + LoggingIncrementPackageSequence();
1184 +
1185 + ++pPlan->cExecutePackagesTotal;
1186 + ++pPlan->cOverallProgressTicksTotal;
1187 +
1188 + // If package is per-machine and is being executed, flag the plan to be per-machine as well.
1189 + if (pPackage->fPerMachine)
1190 + {
1191 + pPlan->fPerMachine = TRUE;
1192 + }
1193 + }
1194 +
1195 +LExit:
1196 + return hr;
1197 +}
1198 +
1199 +extern "C" HRESULT PlanRelatedBundlesBegin(
1200 + __in BURN_USER_EXPERIENCE* pUserExperience,
1201 + __in BURN_REGISTRATION* pRegistration,
1202 + __in BOOTSTRAPPER_RELATION_TYPE relationType,
1203 + __in BURN_PLAN* pPlan
1204 + )
1205 +{
1206 + HRESULT hr = S_OK;
1207 + LPWSTR* rgsczAncestors = NULL;
1208 + UINT cAncestors = 0;
1209 + STRINGDICT_HANDLE sdAncestors = NULL;
1210 +
1211 + if (pRegistration->sczAncestors)
1212 + {
1213 + hr = StrSplitAllocArray(&rgsczAncestors, &cAncestors, pRegistration->sczAncestors, L";");
1214 + ExitOnFailure(hr, "Failed to create string array from ancestors.");
1215 +
1216 + hr = DictCreateStringListFromArray(&sdAncestors, rgsczAncestors, cAncestors, DICT_FLAG_CASEINSENSITIVE);
1217 + ExitOnFailure(hr, "Failed to create dictionary from ancestors array.");
1218 + }
1219 +
1220 + for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i)
1221 + {
1222 + BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i;
1223 + pRelatedBundle->package.defaultRequested = BOOTSTRAPPER_REQUEST_STATE_NONE;
1224 + pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_NONE;
1225 +
1226 + // Do not execute the same bundle twice.
1227 + if (sdAncestors)
1228 + {
1229 + hr = DictKeyExists(sdAncestors, pRelatedBundle->package.sczId);
1230 + if (SUCCEEDED(hr))
1231 + {
1232 + LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_RELATED_BUNDLE_SCHEDULED, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType));
1233 + continue;
1234 + }
1235 + else if (E_NOTFOUND != hr)
1236 + {
1237 + ExitOnFailure(hr, "Failed to lookup the bundle ID in the ancestors dictionary.");
1238 + }
1239 + }
1240 + else if (BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType && BOOTSTRAPPER_RELATION_NONE != relationType)
1241 + {
1242 + // Avoid repair loops for older bundles that do not handle ancestors.
1243 + LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_RELATED_BUNDLE_DEPENDENT, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingRelationTypeToString(relationType));
1244 + continue;
1245 + }
1246 +
1247 + // Pass along any ancestors and ourself to prevent infinite loops.
1248 + if (pRegistration->sczAncestors && *pRegistration->sczAncestors)
1249 + {
1250 + hr = StrAllocFormatted(&pRelatedBundle->package.Exe.sczAncestors, L"%ls;%ls", pRegistration->sczAncestors, pRegistration->sczId);
1251 + ExitOnFailure(hr, "Failed to copy ancestors and self to related bundle ancestors.");
1252 + }
1253 + else
1254 + {
1255 + hr = StrAllocString(&pRelatedBundle->package.Exe.sczAncestors, pRegistration->sczId, 0);
1256 + ExitOnFailure(hr, "Failed to copy self to related bundle ancestors.");
1257 + }
1258 +
1259 + switch (pRelatedBundle->relationType)
1260 + {
1261 + case BOOTSTRAPPER_RELATION_UPGRADE:
1262 + if (BOOTSTRAPPER_RELATION_UPGRADE != relationType && BOOTSTRAPPER_ACTION_UNINSTALL < pPlan->action)
1263 + {
1264 + pRelatedBundle->package.requested = (pRegistration->qwVersion > pRelatedBundle->qwVersion) ? BOOTSTRAPPER_REQUEST_STATE_ABSENT : BOOTSTRAPPER_REQUEST_STATE_NONE;
1265 + }
1266 + break;
1267 + case BOOTSTRAPPER_RELATION_PATCH: __fallthrough;
1268 + case BOOTSTRAPPER_RELATION_ADDON:
1269 + if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action)
1270 + {
1271 + pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_ABSENT;
1272 + }
1273 + else if (BOOTSTRAPPER_ACTION_INSTALL == pPlan->action || BOOTSTRAPPER_ACTION_MODIFY == pPlan->action)
1274 + {
1275 + pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_PRESENT;
1276 + }
1277 + else if (BOOTSTRAPPER_ACTION_REPAIR == pPlan->action)
1278 + {
1279 + pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_REPAIR;
1280 + }
1281 + break;
1282 + case BOOTSTRAPPER_RELATION_DEPENDENT:
1283 + // Automatically repair dependent bundles to restore missing
1284 + // packages after uninstall unless we're being upgraded with the
1285 + // assumption that upgrades are cumulative (as intended).
1286 + if (BOOTSTRAPPER_RELATION_UPGRADE != relationType && BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action)
1287 + {
1288 + pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_REPAIR;
1289 + }
1290 + break;
1291 + case BOOTSTRAPPER_RELATION_DETECT:
1292 + break;
1293 + default:
1294 + hr = E_UNEXPECTED;
1295 + ExitOnFailure(hr, "Unexpected relation type encountered during plan: %d", pRelatedBundle->relationType);
1296 + break;
1297 + }
1298 +
1299 + pRelatedBundle->package.defaultRequested = pRelatedBundle->package.requested;
1300 +
1301 + hr = UserExperienceOnPlanRelatedBundle(pUserExperience, pRelatedBundle->package.sczId, &pRelatedBundle->package.requested);
1302 + ExitOnRootFailure(hr, "BA aborted plan related bundle.");
1303 +
1304 + // Log when the BA changed the bundle state so the engine doesn't get blamed for planning the wrong thing.
1305 + if (pRelatedBundle->package.requested != pRelatedBundle->package.defaultRequested)
1306 + {
1307 + LogId(REPORT_STANDARD, MSG_PLANNED_BUNDLE_UX_CHANGED_REQUEST, pRelatedBundle->package.sczId, LoggingRequestStateToString(pRelatedBundle->package.requested), LoggingRequestStateToString(pRelatedBundle->package.defaultRequested));
1308 + }
1309 +
1310 + // If uninstalling and the dependent related bundle may be executed, ignore its provider key to allow for downgrades with ref-counting.
1311 + if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action && BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType && BOOTSTRAPPER_REQUEST_STATE_NONE != pRelatedBundle->package.requested)
1312 + {
1313 + if (0 < pRelatedBundle->package.cDependencyProviders)
1314 + {
1315 + // Bundles only support a single provider key.
1316 + const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders;
1317 +
1318 + hr = DepDependencyArrayAlloc(&pPlan->rgPlannedProviders, &pPlan->cPlannedProviders, pProvider->sczKey, pProvider->sczDisplayName);
1319 + ExitOnFailure(hr, "Failed to add the package provider key \"%ls\" to the planned list.", pProvider->sczKey);
1320 + }
1321 + }
1322 + }
1323 +
1324 +LExit:
1325 + ReleaseDict(sdAncestors);
1326 + ReleaseStrArray(rgsczAncestors, cAncestors);
1327 +
1328 + return hr;
1329 +}
1330 +
1331 +extern "C" HRESULT PlanRelatedBundlesComplete(
1332 + __in BURN_REGISTRATION* pRegistration,
1333 + __in BURN_PLAN* pPlan,
1334 + __in BURN_LOGGING* pLog,
1335 + __in BURN_VARIABLES* pVariables,
1336 + __inout HANDLE* phSyncpointEvent,
1337 + __in DWORD dwExecuteActionEarlyIndex
1338 + )
1339 +{
1340 + HRESULT hr = S_OK;
1341 + LPWSTR sczIgnoreDependencies = NULL;
1342 + STRINGDICT_HANDLE sdProviderKeys = NULL;
1343 +
1344 + // Get the list of dependencies to ignore to pass to related bundles.
1345 + hr = DependencyAllocIgnoreDependencies(pPlan, &sczIgnoreDependencies);
1346 + ExitOnFailure(hr, "Failed to get the list of dependencies to ignore.");
1347 +
1348 + hr = DictCreateStringList(&sdProviderKeys, pPlan->cExecuteActions, DICT_FLAG_CASEINSENSITIVE);
1349 + ExitOnFailure(hr, "Failed to create dictionary for planned packages.");
1350 +
1351 + BOOL fExecutingAnyPackage = FALSE;
1352 +
1353 + for (DWORD i = 0; i < pPlan->cExecuteActions; ++i)
1354 + {
1355 + if (BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE == pPlan->rgExecuteActions[i].type && BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].exePackage.action)
1356 + {
1357 + fExecutingAnyPackage = TRUE;
1358 +
1359 + BURN_PACKAGE* pPackage = pPlan->rgExecuteActions[i].packageProvider.pPackage;
1360 + if (BURN_PACKAGE_TYPE_EXE == pPackage->type && BURN_EXE_PROTOCOL_TYPE_BURN == pPackage->Exe.protocol)
1361 + {
1362 + if (0 < pPackage->cDependencyProviders)
1363 + {
1364 + // Bundles only support a single provider key.
1365 + const BURN_DEPENDENCY_PROVIDER* pProvider = pPackage->rgDependencyProviders;
1366 + DictAddKey(sdProviderKeys, pProvider->sczKey);
1367 + }
1368 + }
1369 + }
1370 + else
1371 + {
1372 + switch (pPlan->rgExecuteActions[i].type)
1373 + {
1374 + case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE:
1375 + fExecutingAnyPackage |= (BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].msiPackage.action);
1376 + break;
1377 +
1378 + case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET:
1379 + fExecutingAnyPackage |= (BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].mspTarget.action);
1380 + break;
1381 +
1382 + case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE:
1383 + fExecutingAnyPackage |= (BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].msuPackage.action);
1384 + break;
1385 + }
1386 + }
1387 + }
1388 +
1389 + for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i)
1390 + {
1391 + DWORD *pdwInsertIndex = NULL;
1392 + BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i;
1393 +
1394 + // Do not execute if a major upgrade to the related bundle is an embedded bundle (Provider keys are the same)
1395 + if (0 < pRelatedBundle->package.cDependencyProviders)
1396 + {
1397 + // Bundles only support a single provider key.
1398 + const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders;
1399 + hr = DictKeyExists(sdProviderKeys, pProvider->sczKey);
1400 + if (E_NOTFOUND != hr)
1401 + {
1402 + ExitOnFailure(hr, "Failed to check the dictionary for a related bundle provider key: \"%ls\".", pProvider->sczKey);
1403 + // Key found, so there is an embedded bundle with the same provider key that will be executed. So this related bundle should not be added to the plan
1404 + LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_RELATED_BUNDLE_EMBEDDED_BUNDLE_NEWER, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), pProvider->sczKey);
1405 + continue;
1406 + }
1407 + else
1408 + {
1409 + hr = S_OK;
1410 + }
1411 + }
1412 +
1413 + // For an uninstall, there is no need to repair dependent bundles if no packages are executing.
1414 + if (!fExecutingAnyPackage && BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType && BOOTSTRAPPER_REQUEST_STATE_REPAIR == pRelatedBundle->package.requested && BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action)
1415 + {
1416 + pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_NONE;
1417 + LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_DEPENDENT_BUNDLE_REPAIR, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType));
1418 + }
1419 +
1420 + if (BOOTSTRAPPER_RELATION_ADDON == pRelatedBundle->relationType || BOOTSTRAPPER_RELATION_PATCH == pRelatedBundle->relationType)
1421 + {
1422 + // Addon and patch bundles will be passed a list of dependencies to ignore for planning.
1423 + hr = StrAllocString(&pRelatedBundle->package.Exe.sczIgnoreDependencies, sczIgnoreDependencies, 0);
1424 + ExitOnFailure(hr, "Failed to copy the list of dependencies to ignore.");
1425 +
1426 + // Uninstall addons and patches early in the chain, before other packages are uninstalled.
1427 + if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action)
1428 + {
1429 + pdwInsertIndex = &dwExecuteActionEarlyIndex;
1430 + }
1431 + }
1432 +
1433 + if (BOOTSTRAPPER_REQUEST_STATE_NONE != pRelatedBundle->package.requested)
1434 + {
1435 + hr = ExeEnginePlanCalculatePackage(&pRelatedBundle->package, NULL);
1436 + ExitOnFailure(hr, "Failed to calcuate plan for related bundle: %ls", pRelatedBundle->package.sczId);
1437 +
1438 + // Calculate package states based on reference count for addon and patch related bundles.
1439 + if (BOOTSTRAPPER_RELATION_ADDON == pRelatedBundle->relationType || BOOTSTRAPPER_RELATION_PATCH == pRelatedBundle->relationType)
1440 + {
1441 + hr = DependencyPlanPackageBegin(pRegistration->fPerMachine, &pRelatedBundle->package, pPlan);
1442 + ExitOnFailure(hr, "Failed to begin plan dependency actions to package: %ls", pRelatedBundle->package.sczId);
1443 +
1444 + // If uninstalling a related bundle, make sure the bundle is uninstalled after removing registration.
1445 + if (pdwInsertIndex && BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action)
1446 + {
1447 + ++(*pdwInsertIndex);
1448 + }
1449 + }
1450 +
1451 + hr = ExeEnginePlanAddPackage(pdwInsertIndex, &pRelatedBundle->package, pPlan, pLog, pVariables, *phSyncpointEvent, FALSE);
1452 + ExitOnFailure(hr, "Failed to add to plan related bundle: %ls", pRelatedBundle->package.sczId);
1453 +
1454 + // Calculate package states based on reference count for addon and patch related bundles.
1455 + if (BOOTSTRAPPER_RELATION_ADDON == pRelatedBundle->relationType || BOOTSTRAPPER_RELATION_PATCH == pRelatedBundle->relationType)
1456 + {
1457 + hr = DependencyPlanPackageComplete(&pRelatedBundle->package, pPlan);
1458 + ExitOnFailure(hr, "Failed to complete plan dependency actions for related bundle package: %ls", pRelatedBundle->package.sczId);
1459 + }
1460 +
1461 + // If we are going to take any action on this package, add progress for it.
1462 + if (BOOTSTRAPPER_ACTION_STATE_NONE != pRelatedBundle->package.execute || BOOTSTRAPPER_ACTION_STATE_NONE != pRelatedBundle->package.rollback)
1463 + {
1464 + LoggingIncrementPackageSequence();
1465 +
1466 + ++pPlan->cExecutePackagesTotal;
1467 + ++pPlan->cOverallProgressTicksTotal;
1468 + }
1469 +
1470 + // If package is per-machine and is being executed, flag the plan to be per-machine as well.
1471 + if (pRelatedBundle->package.fPerMachine)
1472 + {
1473 + pPlan->fPerMachine = TRUE;
1474 + }
1475 + }
1476 + else if (BOOTSTRAPPER_RELATION_ADDON == pRelatedBundle->relationType || BOOTSTRAPPER_RELATION_PATCH == pRelatedBundle->relationType)
1477 + {
1478 + // Make sure the package is properly ref-counted even if no plan is requested.
1479 + hr = DependencyPlanPackageBegin(pRegistration->fPerMachine, &pRelatedBundle->package, pPlan);
1480 + ExitOnFailure(hr, "Failed to begin plan dependency actions for related bundle package: %ls", pRelatedBundle->package.sczId);
1481 +
1482 + hr = DependencyPlanPackage(pdwInsertIndex, &pRelatedBundle->package, pPlan);
1483 + ExitOnFailure(hr, "Failed to plan related bundle package provider actions.");
1484 +
1485 + hr = DependencyPlanPackageComplete(&pRelatedBundle->package, pPlan);
1486 + ExitOnFailure(hr, "Failed to complete plan dependency actions for related bundle package: %ls", pRelatedBundle->package.sczId);
1487 + }
1488 + }
1489 +
1490 +LExit:
1491 + ReleaseDict(sdProviderKeys);
1492 + ReleaseStr(sczIgnoreDependencies);
1493 +
1494 + return hr;
1495 +}
1496 +
1497 +extern "C" HRESULT PlanFinalizeActions(
1498 + __in BURN_PLAN* pPlan
1499 + )
1500 +{
1501 + HRESULT hr = S_OK;
1502 +
1503 + hr = RemoveUnnecessaryActions(TRUE, pPlan->rgExecuteActions, pPlan->cExecuteActions);
1504 + ExitOnFailure(hr, "Failed to remove unnecessary execute actions.");
1505 +
1506 + hr = RemoveUnnecessaryActions(FALSE, pPlan->rgRollbackActions, pPlan->cRollbackActions);
1507 + ExitOnFailure(hr, "Failed to remove unnecessary execute actions.");
1508 +
1509 + hr = FinalizeSlipstreamPatchActions(TRUE, pPlan->rgExecuteActions, pPlan->cExecuteActions);
1510 + ExitOnFailure(hr, "Failed to finalize slipstream execute actions.");
1511 +
1512 + hr = FinalizeSlipstreamPatchActions(FALSE, pPlan->rgRollbackActions, pPlan->cRollbackActions);
1513 + ExitOnFailure(hr, "Failed to finalize slipstream rollback actions.");
1514 +
1515 +LExit:
1516 + return hr;
1517 +}
1518 +
1519 +extern "C" HRESULT PlanCleanPackage(
1520 + __in BURN_PLAN* pPlan,
1521 + __in BURN_PACKAGE* pPackage
1522 + )
1523 +{
1524 + HRESULT hr = S_OK;
1525 + BOOL fPlanCleanPackage = FALSE;
1526 + BURN_CLEAN_ACTION* pCleanAction = NULL;
1527 +
1528 + // The following is a complex set of logic that determines when a package should be cleaned
1529 + // from the cache. Start by noting that we only clean if the package is being acquired or
1530 + // already cached and the package is not supposed to always be cached.
1531 + if ((pPackage->fAcquire || BURN_CACHE_STATE_PARTIAL == pPackage->cache || BURN_CACHE_STATE_COMPLETE == pPackage->cache) &&
1532 + (BURN_CACHE_TYPE_ALWAYS > pPackage->cacheType || BOOTSTRAPPER_ACTION_INSTALL > pPlan->action))
1533 + {
1534 + // The following are all different reasons why the package should be cleaned from the cache.
1535 + // The else-ifs are used to make the conditions easier to see (rather than have them combined
1536 + // in one huge condition).
1537 + if (BURN_CACHE_TYPE_YES > pPackage->cacheType) // easy, package is not supposed to stay cached.
1538 + {
1539 + fPlanCleanPackage = TRUE;
1540 + }
1541 + else if ((BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT == pPackage->requested ||
1542 + BOOTSTRAPPER_REQUEST_STATE_ABSENT == pPackage->requested) && // requested to be removed and
1543 + BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) // actually being removed.
1544 + {
1545 + fPlanCleanPackage = TRUE;
1546 + }
1547 + else if ((BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT == pPackage->requested ||
1548 + BOOTSTRAPPER_REQUEST_STATE_ABSENT == pPackage->requested) && // requested to be removed but
1549 + BOOTSTRAPPER_ACTION_STATE_NONE == pPackage->execute && // execute is do nothing and
1550 + !pPackage->fDependencyManagerWasHere && // dependency manager didn't change execute and
1551 + BOOTSTRAPPER_PACKAGE_STATE_PRESENT > pPackage->currentState) // currently not installed.
1552 + {
1553 + fPlanCleanPackage = TRUE;
1554 + }
1555 + else if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action && // uninstalling and
1556 + BOOTSTRAPPER_REQUEST_STATE_NONE == pPackage->requested && // requested do nothing (aka: default) and
1557 + BOOTSTRAPPER_ACTION_STATE_NONE == pPackage->execute && // execute is still do nothing and
1558 + !pPackage->fDependencyManagerWasHere && // dependency manager didn't change execute and
1559 + BOOTSTRAPPER_PACKAGE_STATE_PRESENT > pPackage->currentState) // currently not installed.
1560 + {
1561 + fPlanCleanPackage = TRUE;
1562 + }
1563 + }
1564 +
1565 + if (fPlanCleanPackage)
1566 + {
1567 + hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPlan->rgCleanActions), pPlan->cCleanActions + 1, sizeof(BURN_CLEAN_ACTION), 5);
1568 + ExitOnFailure(hr, "Failed to grow plan's array of clean actions.");
1569 +
1570 + pCleanAction = pPlan->rgCleanActions + pPlan->cCleanActions;
1571 + ++pPlan->cCleanActions;
1572 +
1573 + pCleanAction->pPackage = pPackage;
1574 +
1575 + pPackage->fUncache = TRUE;
1576 + }
1577 +
1578 +LExit:
1579 + return hr;
1580 +}
1581 +
1582 +extern "C" HRESULT PlanExecuteCacheSyncAndRollback(
1583 + __in BURN_PLAN* pPlan,
1584 + __in BURN_PACKAGE* pPackage,
1585 + __in HANDLE hCacheEvent,
1586 + __in BOOL fPlanPackageCacheRollback
1587 + )
1588 +{
1589 + HRESULT hr = S_OK;
1590 + BURN_EXECUTE_ACTION* pAction = NULL;
1591 +
1592 + hr = PlanAppendExecuteAction(pPlan, &pAction);
1593 + ExitOnFailure(hr, "Failed to append wait action for caching.");
1594 +
1595 + pAction->type = BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT;
1596 + pAction->syncpoint.hEvent = hCacheEvent;
1597 +
1598 + if (fPlanPackageCacheRollback)
1599 + {
1600 + hr = PlanAppendRollbackAction(pPlan, &pAction);
1601 + ExitOnFailure(hr, "Failed to append rollback action.");
1602 +
1603 + pAction->type = BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE;
1604 + pAction->uncachePackage.pPackage = pPackage;
1605 +
1606 + hr = PlanExecuteCheckpoint(pPlan);
1607 + ExitOnFailure(hr, "Failed to append execute checkpoint for cache rollback.");
1608 + }
1609 +
1610 +LExit:
1611 + return hr;
1612 +}
1613 +
1614 +extern "C" HRESULT PlanExecuteCheckpoint(
1615 + __in BURN_PLAN* pPlan
1616 + )
1617 +{
1618 + HRESULT hr = S_OK;
1619 + BURN_EXECUTE_ACTION* pAction = NULL;
1620 + DWORD dwCheckpointId = GetNextCheckpointId();
1621 +
1622 + // execute checkpoint
1623 + hr = PlanAppendExecuteAction(pPlan, &pAction);
1624 + ExitOnFailure(hr, "Failed to append execute action.");
1625 +
1626 + pAction->type = BURN_EXECUTE_ACTION_TYPE_CHECKPOINT;
1627 + pAction->checkpoint.dwId = dwCheckpointId;
1628 +
1629 + // rollback checkpoint
1630 + hr = PlanAppendRollbackAction(pPlan, &pAction);
1631 + ExitOnFailure(hr, "Failed to append rollback action.");
1632 +
1633 + pAction->type = BURN_EXECUTE_ACTION_TYPE_CHECKPOINT;
1634 + pAction->checkpoint.dwId = dwCheckpointId;
1635 +
1636 +LExit:
1637 + return hr;
1638 +}
1639 +
1640 +extern "C" HRESULT PlanInsertExecuteAction(
1641 + __in DWORD dwIndex,
1642 + __in BURN_PLAN* pPlan,
1643 + __out BURN_EXECUTE_ACTION** ppExecuteAction
1644 + )
1645 +{
1646 + HRESULT hr = S_OK;
1647 +
1648 + hr = MemInsertIntoArray((void**)&pPlan->rgExecuteActions, dwIndex, 1, pPlan->cExecuteActions + 1, sizeof(BURN_EXECUTE_ACTION), 5);
1649 + ExitOnFailure(hr, "Failed to grow plan's array of execute actions.");
1650 +
1651 + *ppExecuteAction = pPlan->rgExecuteActions + dwIndex;
1652 + ++pPlan->cExecuteActions;
1653 +
1654 +LExit:
1655 + return hr;
1656 +}
1657 +
1658 +extern "C" HRESULT PlanInsertRollbackAction(
1659 + __in DWORD dwIndex,
1660 + __in BURN_PLAN* pPlan,
1661 + __out BURN_EXECUTE_ACTION** ppRollbackAction
1662 + )
1663 +{
1664 + HRESULT hr = S_OK;
1665 +
1666 + hr = MemInsertIntoArray((void**)&pPlan->rgRollbackActions, dwIndex, 1, pPlan->cRollbackActions + 1, sizeof(BURN_EXECUTE_ACTION), 5);
1667 + ExitOnFailure(hr, "Failed to grow plan's array of rollback actions.");
1668 +
1669 + *ppRollbackAction = pPlan->rgRollbackActions + dwIndex;
1670 + ++pPlan->cRollbackActions;
1671 +
1672 +LExit:
1673 + return hr;
1674 +}
1675 +
1676 +extern "C" HRESULT PlanAppendExecuteAction(
1677 + __in BURN_PLAN* pPlan,
1678 + __out BURN_EXECUTE_ACTION** ppExecuteAction
1679 + )
1680 +{
1681 + HRESULT hr = S_OK;
1682 +
1683 + hr = MemEnsureArraySize((void**)&pPlan->rgExecuteActions, pPlan->cExecuteActions + 1, sizeof(BURN_EXECUTE_ACTION), 5);
1684 + ExitOnFailure(hr, "Failed to grow plan's array of execute actions.");
1685 +
1686 + *ppExecuteAction = pPlan->rgExecuteActions + pPlan->cExecuteActions;
1687 + ++pPlan->cExecuteActions;
1688 +
1689 +LExit:
1690 + return hr;
1691 +}
1692 +
1693 +extern "C" HRESULT PlanAppendRollbackAction(
1694 + __in BURN_PLAN* pPlan,
1695 + __out BURN_EXECUTE_ACTION** ppRollbackAction
1696 + )
1697 +{
1698 + HRESULT hr = S_OK;
1699 +
1700 + hr = MemEnsureArraySize((void**)&pPlan->rgRollbackActions, pPlan->cRollbackActions + 1, sizeof(BURN_EXECUTE_ACTION), 5);
1701 + ExitOnFailure(hr, "Failed to grow plan's array of rollback actions.");
1702 +
1703 + *ppRollbackAction = pPlan->rgRollbackActions + pPlan->cRollbackActions;
1704 + ++pPlan->cRollbackActions;
1705 +
1706 +LExit:
1707 + return hr;
1708 +}
1709 +
1710 +extern "C" HRESULT PlanKeepRegistration(
1711 + __in BURN_PLAN* pPlan,
1712 + __in DWORD iAfterExecutePackageAction,
1713 + __in DWORD iBeforeRollbackPackageAction
1714 + )
1715 +{
1716 + HRESULT hr = S_OK;
1717 + BURN_EXECUTE_ACTION* pAction = NULL;
1718 +
1719 + if (BURN_PLAN_INVALID_ACTION_INDEX != iAfterExecutePackageAction)
1720 + {
1721 + hr = PlanInsertExecuteAction(iAfterExecutePackageAction, pPlan, &pAction);
1722 + ExitOnFailure(hr, "Failed to insert keep registration execute action.");
1723 +
1724 + pAction->type = BURN_EXECUTE_ACTION_TYPE_REGISTRATION;
1725 + pAction->registration.fKeep = TRUE;
1726 + }
1727 +
1728 + if (BURN_PLAN_INVALID_ACTION_INDEX != iBeforeRollbackPackageAction)
1729 + {
1730 + hr = PlanInsertRollbackAction(iBeforeRollbackPackageAction, pPlan, &pAction);
1731 + ExitOnFailure(hr, "Failed to insert keep registration rollback action.");
1732 +
1733 + pAction->type = BURN_EXECUTE_ACTION_TYPE_REGISTRATION;
1734 + pAction->registration.fKeep = FALSE;
1735 + }
1736 +
1737 +LExit:
1738 + return hr;
1739 +}
1740 +
1741 +extern "C" HRESULT PlanRemoveRegistration(
1742 + __in BURN_PLAN* pPlan,
1743 + __in DWORD iAfterExecutePackageAction,
1744 + __in DWORD iAfterRollbackPackageAction
1745 + )
1746 +{
1747 + HRESULT hr = S_OK;
1748 + BURN_EXECUTE_ACTION* pAction = NULL;
1749 +
1750 + if (BURN_PLAN_INVALID_ACTION_INDEX != iAfterExecutePackageAction)
1751 + {
1752 + hr = PlanInsertExecuteAction(iAfterExecutePackageAction, pPlan, &pAction);
1753 + ExitOnFailure(hr, "Failed to insert remove registration execute action.");
1754 +
1755 + pAction->type = BURN_EXECUTE_ACTION_TYPE_REGISTRATION;
1756 + pAction->registration.fKeep = FALSE;
1757 + }
1758 +
1759 + if (BURN_PLAN_INVALID_ACTION_INDEX != iAfterRollbackPackageAction)
1760 + {
1761 + hr = PlanInsertRollbackAction(iAfterRollbackPackageAction, pPlan, &pAction);
1762 + ExitOnFailure(hr, "Failed to insert remove registration rollback action.");
1763 +
1764 + pAction->type = BURN_EXECUTE_ACTION_TYPE_REGISTRATION;
1765 + pAction->registration.fKeep = TRUE;
1766 + }
1767 +
1768 +LExit:
1769 + return hr;
1770 +}
1771 +
1772 +extern "C" HRESULT PlanRollbackBoundaryBegin(
1773 + __in BURN_PLAN* pPlan,
1774 + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary
1775 + )
1776 +{
1777 + HRESULT hr = S_OK;
1778 + BURN_EXECUTE_ACTION* pExecuteAction = NULL;
1779 +
1780 + // Add begin rollback boundary to execute plan.
1781 + hr = PlanAppendExecuteAction(pPlan, &pExecuteAction);
1782 + ExitOnFailure(hr, "Failed to append rollback boundary begin action.");
1783 +
1784 + pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY;
1785 + pExecuteAction->rollbackBoundary.pRollbackBoundary = pRollbackBoundary;
1786 +
1787 + // Add begin rollback boundary to rollback plan.
1788 + hr = PlanAppendRollbackAction(pPlan, &pExecuteAction);
1789 + ExitOnFailure(hr, "Failed to append rollback boundary begin action.");
1790 +
1791 + pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY;
1792 + pExecuteAction->rollbackBoundary.pRollbackBoundary = pRollbackBoundary;
1793 +
1794 +LExit:
1795 + return hr;
1796 +}
1797 +
1798 +extern "C" HRESULT PlanRollbackBoundaryComplete(
1799 + __in BURN_PLAN* pPlan
1800 + )
1801 +{
1802 + HRESULT hr = S_OK;
1803 + BURN_EXECUTE_ACTION* pExecuteAction = NULL;
1804 + DWORD dwCheckpointId = 0;
1805 +
1806 + // Add checkpoints.
1807 + dwCheckpointId = GetNextCheckpointId();
1808 +
1809 + hr = PlanAppendExecuteAction(pPlan, &pExecuteAction);
1810 + ExitOnFailure(hr, "Failed to append execute action.");
1811 +
1812 + pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_CHECKPOINT;
1813 + pExecuteAction->checkpoint.dwId = dwCheckpointId;
1814 +
1815 + hr = PlanAppendRollbackAction(pPlan, &pExecuteAction);
1816 + ExitOnFailure(hr, "Failed to append rollback action.");
1817 +
1818 + pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_CHECKPOINT;
1819 + pExecuteAction->checkpoint.dwId = dwCheckpointId;
1820 +
1821 +LExit:
1822 + return hr;
1823 +}
1824 +
1825 +/*******************************************************************
1826 + PlanSetResumeCommand - Initializes resume command string
1827 +
1828 +*******************************************************************/
1829 +extern "C" HRESULT PlanSetResumeCommand(
1830 + __in BURN_REGISTRATION* pRegistration,
1831 + __in BOOTSTRAPPER_ACTION action,
1832 + __in BOOTSTRAPPER_COMMAND* pCommand,
1833 + __in BURN_LOGGING* pLog
1834 + )
1835 +{
1836 + HRESULT hr = S_OK;
1837 +
1838 + // build the resume command-line.
1839 + hr = CoreRecreateCommandLine(&pRegistration->sczResumeCommandLine, action, pCommand->display, pCommand->restart, pCommand->relationType, pCommand->fPassthrough, pRegistration->sczActiveParent, pRegistration->sczAncestors, pLog->sczPath, pCommand->wzCommandLine);
1840 + ExitOnFailure(hr, "Failed to recreate resume command-line.");
1841 +
1842 +LExit:
1843 + return hr;
1844 +}
1845 +
1846 +
1847 +// internal function definitions
1848 +
1849 +static void UninitializeRegistrationAction(
1850 + __in BURN_DEPENDENT_REGISTRATION_ACTION* pAction
1851 + )
1852 +{
1853 + ReleaseStr(pAction->sczDependentProviderKey);
1854 + ReleaseStr(pAction->sczBundleId);
1855 + memset(pAction, 0, sizeof(BURN_DEPENDENT_REGISTRATION_ACTION));
1856 +}
1857 +
1858 +static void UninitializeCacheAction(
1859 + __in BURN_CACHE_ACTION* pCacheAction
1860 + )
1861 +{
1862 + switch (pCacheAction->type)
1863 + {
1864 + case BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT:
1865 + ReleaseHandle(pCacheAction->syncpoint.hEvent);
1866 + break;
1867 +
1868 + case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE:
1869 + ReleaseStr(pCacheAction->bundleLayout.sczExecutableName);
1870 + ReleaseStr(pCacheAction->bundleLayout.sczLayoutDirectory);
1871 + ReleaseStr(pCacheAction->bundleLayout.sczUnverifiedPath);
1872 + break;
1873 +
1874 + case BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER:
1875 + ReleaseStr(pCacheAction->resolveContainer.sczUnverifiedPath);
1876 + break;
1877 +
1878 + case BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER:
1879 + ReleaseStr(pCacheAction->extractContainer.sczContainerUnverifiedPath);
1880 + ReleaseMem(pCacheAction->extractContainer.rgPayloads);
1881 + break;
1882 +
1883 + case BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD:
1884 + ReleaseStr(pCacheAction->resolvePayload.sczUnverifiedPath);
1885 + break;
1886 +
1887 + case BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD:
1888 + ReleaseStr(pCacheAction->cachePayload.sczUnverifiedPath);
1889 + break;
1890 + }
1891 +}
1892 +
1893 +static void ResetPlannedPackageState(
1894 + __in BURN_PACKAGE* pPackage
1895 + )
1896 +{
1897 + // Reset package state that is a result of planning.
1898 + pPackage->expected = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN;
1899 + pPackage->defaultRequested = BOOTSTRAPPER_REQUEST_STATE_NONE;
1900 + pPackage->requested = BOOTSTRAPPER_REQUEST_STATE_NONE;
1901 + pPackage->fAcquire = FALSE;
1902 + pPackage->fUncache = FALSE;
1903 + pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE;
1904 + pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
1905 + pPackage->providerExecute = BURN_DEPENDENCY_ACTION_NONE;
1906 + pPackage->providerRollback = BURN_DEPENDENCY_ACTION_NONE;
1907 + pPackage->dependencyExecute = BURN_DEPENDENCY_ACTION_NONE;
1908 + pPackage->dependencyRollback = BURN_DEPENDENCY_ACTION_NONE;
1909 + pPackage->fDependencyManagerWasHere = FALSE;
1910 +
1911 + if (BURN_PACKAGE_TYPE_MSI == pPackage->type && pPackage->Msi.rgFeatures)
1912 + {
1913 + for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i)
1914 + {
1915 + BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i];
1916 +
1917 + pFeature->execute = BOOTSTRAPPER_FEATURE_ACTION_NONE;
1918 + pFeature->rollback = BOOTSTRAPPER_FEATURE_ACTION_NONE;
1919 + }
1920 + }
1921 + else if (BURN_PACKAGE_TYPE_MSP == pPackage->type && pPackage->Msp.rgTargetProducts)
1922 + {
1923 + for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i)
1924 + {
1925 + BURN_MSPTARGETPRODUCT* pTargetProduct = &pPackage->Msp.rgTargetProducts[i];
1926 +
1927 + pTargetProduct->execute = BOOTSTRAPPER_ACTION_STATE_NONE;
1928 + pTargetProduct->rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
1929 + }
1930 + }
1931 +}
1932 +
1933 +static HRESULT GetActionDefaultRequestState(
1934 + __in BOOTSTRAPPER_ACTION action,
1935 + __in BOOL fPermanent,
1936 + __in BOOTSTRAPPER_PACKAGE_STATE currentState,
1937 + __out BOOTSTRAPPER_REQUEST_STATE* pRequestState
1938 + )
1939 +{
1940 + HRESULT hr = S_OK;
1941 +
1942 + switch (action)
1943 + {
1944 + case BOOTSTRAPPER_ACTION_CACHE:
1945 + switch (currentState)
1946 + {
1947 + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
1948 + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT;
1949 + break;
1950 +
1951 + case BOOTSTRAPPER_PACKAGE_STATE_CACHED:
1952 + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
1953 + break;
1954 +
1955 + default:
1956 + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_CACHE;
1957 + break;
1958 + }
1959 + break;
1960 +
1961 + case BOOTSTRAPPER_ACTION_INSTALL: __fallthrough;
1962 + case BOOTSTRAPPER_ACTION_UPDATE_REPLACE: __fallthrough;
1963 + case BOOTSTRAPPER_ACTION_UPDATE_REPLACE_EMBEDDED:
1964 + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT;
1965 + break;
1966 +
1967 + case BOOTSTRAPPER_ACTION_REPAIR:
1968 + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_REPAIR;
1969 + break;
1970 +
1971 + case BOOTSTRAPPER_ACTION_UNINSTALL:
1972 + *pRequestState = fPermanent ? BOOTSTRAPPER_REQUEST_STATE_NONE : BOOTSTRAPPER_REQUEST_STATE_ABSENT;
1973 + break;
1974 +
1975 + case BOOTSTRAPPER_ACTION_MODIFY:
1976 + switch (currentState)
1977 + {
1978 + case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
1979 + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_ABSENT;
1980 + break;
1981 +
1982 + case BOOTSTRAPPER_PACKAGE_STATE_CACHED:
1983 + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_CACHE;
1984 + break;
1985 +
1986 + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
1987 + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT;
1988 + break;
1989 +
1990 + default:
1991 + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
1992 + break;
1993 + }
1994 + break;
1995 +
1996 + default:
1997 + hr = E_INVALIDARG;
1998 + ExitOnRootFailure(hr, "Invalid action state.");
1999 + }
2000 +
2001 +LExit:
2002 + return hr;
2003 +}
2004 +
2005 +static HRESULT AddRegistrationAction(
2006 + __in BURN_PLAN* pPlan,
2007 + __in BURN_DEPENDENT_REGISTRATION_ACTION_TYPE type,
2008 + __in_z LPCWSTR wzDependentProviderKey,
2009 + __in_z LPCWSTR wzOwnerBundleId
2010 + )
2011 +{
2012 + HRESULT hr = S_OK;
2013 + BURN_DEPENDENT_REGISTRATION_ACTION_TYPE rollbackType = (BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER == type) ? BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER : BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER;
2014 + BURN_DEPENDENT_REGISTRATION_ACTION* pAction = NULL;
2015 +
2016 + // Create forward registration action.
2017 + hr = MemEnsureArraySize((void**)&pPlan->rgRegistrationActions, pPlan->cRegistrationActions + 1, sizeof(BURN_DEPENDENT_REGISTRATION_ACTION), 5);
2018 + ExitOnFailure(hr, "Failed to grow plan's array of registration actions.");
2019 +
2020 + pAction = pPlan->rgRegistrationActions + pPlan->cRegistrationActions;
2021 + ++pPlan->cRegistrationActions;
2022 +
2023 + pAction->type = type;
2024 +
2025 + hr = StrAllocString(&pAction->sczBundleId, wzOwnerBundleId, 0);
2026 + ExitOnFailure(hr, "Failed to copy owner bundle to registration action.");
2027 +
2028 + hr = StrAllocString(&pAction->sczDependentProviderKey, wzDependentProviderKey, 0);
2029 + ExitOnFailure(hr, "Failed to copy dependent provider key to registration action.");
2030 +
2031 + // Create rollback registration action.
2032 + hr = MemEnsureArraySize((void**)&pPlan->rgRollbackRegistrationActions, pPlan->cRollbackRegistrationActions + 1, sizeof(BURN_DEPENDENT_REGISTRATION_ACTION), 5);
2033 + ExitOnFailure(hr, "Failed to grow plan's array of rollback registration actions.");
2034 +
2035 + pAction = pPlan->rgRollbackRegistrationActions + pPlan->cRollbackRegistrationActions;
2036 + ++pPlan->cRollbackRegistrationActions;
2037 +
2038 + pAction->type = rollbackType;
2039 +
2040 + hr = StrAllocString(&pAction->sczBundleId, wzOwnerBundleId, 0);
2041 + ExitOnFailure(hr, "Failed to copy owner bundle to registration action.");
2042 +
2043 + hr = StrAllocString(&pAction->sczDependentProviderKey, wzDependentProviderKey, 0);
2044 + ExitOnFailure(hr, "Failed to copy dependent provider key to rollback registration action.");
2045 +
2046 +LExit:
2047 + return hr;
2048 +}
2049 +
2050 +static HRESULT AddCachePackage(
2051 + __in BURN_PLAN* pPlan,
2052 + __in BURN_PACKAGE* pPackage,
2053 + __out HANDLE* phSyncpointEvent
2054 + )
2055 +{
2056 + HRESULT hr = S_OK;
2057 +
2058 + // If this is an MSI package with slipstream MSPs, ensure the MSPs are cached first.
2059 + if (BURN_PACKAGE_TYPE_MSI == pPackage->type && 0 < pPackage->Msi.cSlipstreamMspPackages)
2060 + {
2061 + hr = AddCacheSlipstreamMsps(pPlan, pPackage);
2062 + ExitOnFailure(hr, "Failed to plan slipstream patches for package.");
2063 + }
2064 +
2065 + hr = AddCachePackageHelper(pPlan, pPackage, phSyncpointEvent);
2066 + ExitOnFailure(hr, "Failed to plan cache package.");
2067 +
2068 +LExit:
2069 + return hr;
2070 +}
2071 +
2072 +static HRESULT AddCachePackageHelper(
2073 + __in BURN_PLAN* pPlan,
2074 + __in BURN_PACKAGE* pPackage,
2075 + __out HANDLE* phSyncpointEvent
2076 + )
2077 +{
2078 + AssertSz(pPackage->sczCacheId && *pPackage->sczCacheId, "AddCachePackageHelper() expects the package to have a cache id.");
2079 +
2080 + HRESULT hr = S_OK;
2081 + BURN_CACHE_ACTION* pCacheAction = NULL;
2082 + DWORD dwCheckpoint = 0;
2083 + DWORD iPackageStartAction = 0;
2084 +
2085 + BOOL fPlanned = AlreadyPlannedCachePackage(pPlan, pPackage->sczId, phSyncpointEvent);
2086 + if (fPlanned)
2087 + {
2088 + ExitFunction();
2089 + }
2090 +
2091 + // Cache checkpoints happen before the package is cached because downloading packages'
2092 + // payloads will not roll themselves back the way installation packages rollback on
2093 + // failure automatically.
2094 + dwCheckpoint = GetNextCheckpointId();
2095 +
2096 + hr = AppendCacheAction(pPlan, &pCacheAction);
2097 + ExitOnFailure(hr, "Failed to append package start action.");
2098 +
2099 + pCacheAction->type = BURN_CACHE_ACTION_TYPE_CHECKPOINT;
2100 + pCacheAction->checkpoint.dwId = dwCheckpoint;
2101 +
2102 + // Only plan the cache rollback if the package is also going to be uninstalled;
2103 + // otherwise, future operations like repair will not be able to locate the cached package.
2104 + BOOL fPlanCacheRollback = (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->rollback);
2105 +
2106 + if (fPlanCacheRollback)
2107 + {
2108 + hr = AppendRollbackCacheAction(pPlan, &pCacheAction);
2109 + ExitOnFailure(hr, "Failed to append rollback cache action.");
2110 +
2111 + pCacheAction->type = BURN_CACHE_ACTION_TYPE_CHECKPOINT;
2112 + pCacheAction->checkpoint.dwId = dwCheckpoint;
2113 + }
2114 +
2115 + // Plan the package start.
2116 + hr = AppendCacheAction(pPlan, &pCacheAction);
2117 + ExitOnFailure(hr, "Failed to append package start action.");
2118 +
2119 + pCacheAction->type = BURN_CACHE_ACTION_TYPE_PACKAGE_START;
2120 + pCacheAction->packageStart.pPackage = pPackage;
2121 +
2122 + // Remember the index for the package start action (which is now the last in the cache
2123 + // actions array) because we have to update this action after processing all the payloads
2124 + // and the array may be resized later which would move a pointer around in memory.
2125 + iPackageStartAction = pPlan->cCacheActions - 1;
2126 +
2127 + if (fPlanCacheRollback)
2128 + {
2129 + // Create a package cache rollback action.
2130 + hr = AppendRollbackCacheAction(pPlan, &pCacheAction);
2131 + ExitOnFailure(hr, "Failed to append rollback cache action.");
2132 +
2133 + pCacheAction->type = BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE;
2134 + pCacheAction->rollbackPackage.pPackage = pPackage;
2135 + }
2136 +
2137 + // Add all the payload cache operations to the plan for this package.
2138 + for (DWORD i = 0; i < pPackage->cPayloads; ++i)
2139 + {
2140 + BURN_PACKAGE_PAYLOAD* pPackagePayload = &pPackage->rgPayloads[i];
2141 +
2142 + hr = AppendCacheOrLayoutPayloadAction(pPlan, pPackage, iPackageStartAction, pPackagePayload->pPayload, pPackagePayload->fCached, NULL);
2143 + ExitOnFailure(hr, "Failed to append payload cache action.");
2144 +
2145 + Assert(BURN_CACHE_ACTION_TYPE_PACKAGE_START == pPlan->rgCacheActions[iPackageStartAction].type);
2146 + ++pPlan->rgCacheActions[iPackageStartAction].packageStart.cCachePayloads;
2147 + pPlan->rgCacheActions[iPackageStartAction].packageStart.qwCachePayloadSizeTotal += pPackagePayload->pPayload->qwFileSize;
2148 + }
2149 +
2150 + // Create package stop action.
2151 + hr = AppendCacheAction(pPlan, &pCacheAction);
2152 + ExitOnFailure(hr, "Failed to append cache action.");
2153 +
2154 + pCacheAction->type = BURN_CACHE_ACTION_TYPE_PACKAGE_STOP;
2155 + pCacheAction->packageStop.pPackage = pPackage;
2156 +
2157 + // Update the start action with the location of the complete action.
2158 + pPlan->rgCacheActions[iPackageStartAction].packageStart.iPackageCompleteAction = pPlan->cCacheActions - 1;
2159 +
2160 + // Create syncpoint action.
2161 + hr = AppendCacheAction(pPlan, &pCacheAction);
2162 + ExitOnFailure(hr, "Failed to append cache action.");
2163 +
2164 + pCacheAction->type = BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT;
2165 + pCacheAction->syncpoint.hEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL);
2166 + ExitOnNullWithLastError(pCacheAction->syncpoint.hEvent, hr, "Failed to create syncpoint event.");
2167 +
2168 + *phSyncpointEvent = pCacheAction->syncpoint.hEvent;
2169 +
2170 + ++pPlan->cOverallProgressTicksTotal;
2171 +
2172 + // If the package was not already fully cached then note that we planned the cache here. Otherwise, we only
2173 + // did cache operations to verify the cache is valid so we did not plan the acquisition of the package.
2174 + pPackage->fAcquire = (BURN_CACHE_STATE_COMPLETE != pPackage->cache);
2175 +
2176 +LExit:
2177 + return hr;
2178 +}
2179 +
2180 +static HRESULT AddCacheSlipstreamMsps(
2181 + __in BURN_PLAN* pPlan,
2182 + __in BURN_PACKAGE* pPackage
2183 + )
2184 +{
2185 + HRESULT hr = S_OK;
2186 + HANDLE hIgnored = NULL;
2187 +
2188 + AssertSz(BURN_PACKAGE_TYPE_MSI == pPackage->type, "Only MSI packages can have slipstream patches.");
2189 +
2190 + for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i)
2191 + {
2192 + BURN_PACKAGE* pMspPackage = pPackage->Msi.rgpSlipstreamMspPackages[i];
2193 + AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Only MSP packages can be slipstream patches.");
2194 +
2195 + hr = AddCachePackageHelper(pPlan, pMspPackage, &hIgnored);
2196 + ExitOnFailure(hr, "Failed to plan slipstream MSP: %ls", pMspPackage->sczId);
2197 + }
2198 +
2199 +LExit:
2200 + return hr;
2201 +}
2202 +
2203 +static BOOL AlreadyPlannedCachePackage(
2204 + __in BURN_PLAN* pPlan,
2205 + __in_z LPCWSTR wzPackageId,
2206 + __out HANDLE* phSyncpointEvent
2207 + )
2208 +{
2209 + BOOL fPlanned = FALSE;
2210 +
2211 + for (DWORD iCacheAction = 0; iCacheAction < pPlan->cCacheActions; ++iCacheAction)
2212 + {
2213 + BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + iCacheAction;
2214 +
2215 + if (BURN_CACHE_ACTION_TYPE_PACKAGE_STOP == pCacheAction->type)
2216 + {
2217 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pCacheAction->packageStop.pPackage->sczId, -1, wzPackageId, -1))
2218 + {
2219 + if (iCacheAction + 1 < pPlan->cCacheActions && BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT == pPlan->rgCacheActions[iCacheAction + 1].type)
2220 + {
2221 + *phSyncpointEvent = pPlan->rgCacheActions[iCacheAction + 1].syncpoint.hEvent;
2222 + }
2223 +
2224 + fPlanned = TRUE;
2225 + break;
2226 + }
2227 + }
2228 + }
2229 +
2230 + return fPlanned;
2231 +}
2232 +
2233 +static DWORD GetNextCheckpointId()
2234 +{
2235 + static DWORD dwCounter = 0;
2236 + return ++dwCounter;
2237 +}
2238 +
2239 +static HRESULT AppendCacheAction(
2240 + __in BURN_PLAN* pPlan,
2241 + __out BURN_CACHE_ACTION** ppCacheAction
2242 + )
2243 +{
2244 + HRESULT hr = S_OK;
2245 +
2246 + hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPlan->rgCacheActions), pPlan->cCacheActions + 1, sizeof(BURN_CACHE_ACTION), 5);
2247 + ExitOnFailure(hr, "Failed to grow plan's array of cache actions.");
2248 +
2249 + *ppCacheAction = pPlan->rgCacheActions + pPlan->cCacheActions;
2250 + ++pPlan->cCacheActions;
2251 +
2252 +LExit:
2253 + return hr;
2254 +}
2255 +
2256 +static HRESULT AppendRollbackCacheAction(
2257 + __in BURN_PLAN* pPlan,
2258 + __out BURN_CACHE_ACTION** ppCacheAction
2259 + )
2260 +{
2261 + HRESULT hr = S_OK;
2262 +
2263 + hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPlan->rgRollbackCacheActions), pPlan->cRollbackCacheActions + 1, sizeof(BURN_CACHE_ACTION), 5);
2264 + ExitOnFailure(hr, "Failed to grow plan's array of rollback cache actions.");
2265 +
2266 + *ppCacheAction = pPlan->rgRollbackCacheActions + pPlan->cRollbackCacheActions;
2267 + ++pPlan->cRollbackCacheActions;
2268 +
2269 +LExit:
2270 + return hr;
2271 +}
2272 +
2273 +static HRESULT AppendLayoutContainerAction(
2274 + __in BURN_PLAN* pPlan,
2275 + __in_opt BURN_PACKAGE* pPackage,
2276 + __in DWORD iPackageStartAction,
2277 + __in BURN_CONTAINER* pContainer,
2278 + __in BOOL fContainerCached,
2279 + __in_z LPCWSTR wzLayoutDirectory
2280 + )
2281 +{
2282 + HRESULT hr = S_OK;
2283 + BURN_CACHE_ACTION* pAcquireAction = NULL;
2284 + DWORD iAcquireAction = BURN_PLAN_INVALID_ACTION_INDEX;
2285 + LPWSTR sczContainerWorkingPath = NULL;
2286 + BURN_CACHE_ACTION* pCacheAction = NULL;
2287 + BURN_CACHE_CONTAINER_PROGRESS* pContainerProgress = NULL;
2288 +
2289 + // No need to do anything if the container is already cached or is attached to the bundle (since the
2290 + // bundle itself will already have a layout action).
2291 + if (fContainerCached || pContainer->fAttached)
2292 + {
2293 + ExitFunction();
2294 + }
2295 +
2296 + // Ensure the container is being acquired. If it is, then some earlier package already planned the layout of this container so
2297 + // don't do it again. Otherwise, plan away!
2298 + if (!FindContainerCacheAction(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER, pPlan, pContainer, 0, iPackageStartAction, NULL, NULL))
2299 + {
2300 + hr = AddAcquireContainer(pPlan, pContainer, &pAcquireAction, &iAcquireAction);
2301 + ExitOnFailure(hr, "Failed to append acquire container action for layout to plan.");
2302 +
2303 + Assert(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER == pAcquireAction->type);
2304 +
2305 + // Create the layout container action.
2306 + hr = StrAllocString(&sczContainerWorkingPath, pAcquireAction->resolveContainer.sczUnverifiedPath, 0);
2307 + ExitOnFailure(hr, "Failed to copy container working path for layout.");
2308 +
2309 + hr = AppendCacheAction(pPlan, &pCacheAction);
2310 + ExitOnFailure(hr, "Failed to append cache action to cache payload.");
2311 +
2312 + hr = CreateContainerProgress(pPlan, pContainer, &pContainerProgress);
2313 + ExitOnFailure(hr, "Failed to create container progress.");
2314 +
2315 + hr = StrAllocString(&pCacheAction->layoutContainer.sczLayoutDirectory, wzLayoutDirectory, 0);
2316 + ExitOnFailure(hr, "Failed to copy layout directory into plan.");
2317 +
2318 + pCacheAction->type = BURN_CACHE_ACTION_TYPE_LAYOUT_CONTAINER;
2319 + pCacheAction->layoutContainer.pPackage = pPackage;
2320 + pCacheAction->layoutContainer.pContainer = pContainer;
2321 + pCacheAction->layoutContainer.iProgress = pContainerProgress->iIndex;
2322 + pCacheAction->layoutContainer.fMove = TRUE;
2323 + pCacheAction->layoutContainer.iTryAgainAction = iAcquireAction;
2324 + pCacheAction->layoutContainer.sczUnverifiedPath = sczContainerWorkingPath;
2325 + sczContainerWorkingPath = NULL;
2326 + }
2327 +
2328 +LExit:
2329 + ReleaseNullStr(sczContainerWorkingPath);
2330 +
2331 + return hr;
2332 +}
2333 +
2334 +static HRESULT AppendCacheOrLayoutPayloadAction(
2335 + __in BURN_PLAN* pPlan,
2336 + __in_opt BURN_PACKAGE* pPackage,
2337 + __in DWORD iPackageStartAction,
2338 + __in BURN_PAYLOAD* pPayload,
2339 + __in BOOL fPayloadCached,
2340 + __in_z_opt LPCWSTR wzLayoutDirectory
2341 + )
2342 +{
2343 + HRESULT hr = S_OK;
2344 + LPWSTR sczPayloadWorkingPath = NULL;
2345 + BURN_CACHE_ACTION* pCacheAction = NULL;
2346 + DWORD iTryAgainAction = BURN_PLAN_INVALID_ACTION_INDEX;
2347 + BURN_CACHE_PAYLOAD_PROGRESS* pPayloadProgress = NULL;
2348 +
2349 + hr = CacheCalculatePayloadWorkingPath(pPlan->wzBundleId, pPayload, &sczPayloadWorkingPath);
2350 + ExitOnFailure(hr, "Failed to calculate unverified path for payload.");
2351 +
2352 + // If the payload is in a container, ensure the container is being acquired
2353 + // then add this payload to the list of payloads to extract already in the plan.
2354 + if (pPayload->pContainer)
2355 + {
2356 + BURN_CACHE_ACTION* pPreviousPackageExtractAction = NULL;
2357 + BURN_CACHE_ACTION* pThisPackageExtractAction = NULL;
2358 +
2359 + // If the payload is not already cached, then add it to the first extract container action in the plan. Extracting
2360 + // all the needed payloads from the container in a single pass is the most efficient way to extract files from
2361 + // containers. If there is not an extract container action before our package, that is okay because we'll create
2362 + // an extract container action for our package in a second anyway.
2363 + if (!fPayloadCached)
2364 + {
2365 + if (FindContainerCacheAction(BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER, pPlan, pPayload->pContainer, 0, iPackageStartAction, &pPreviousPackageExtractAction, NULL))
2366 + {
2367 + hr = AddExtractPayload(pPreviousPackageExtractAction, pPackage, pPayload, sczPayloadWorkingPath);
2368 + ExitOnFailure(hr, "Failed to add extract payload action to previous package.");
2369 + }
2370 + }
2371 +
2372 + // If there is already an extract container action after our package start action then try to find an acquire action
2373 + // that is matched with it. If there is an acquire action then that is our "try again" action, otherwise we'll use the existing
2374 + // extract action as the "try again" action.
2375 + if (FindContainerCacheAction(BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER, pPlan, pPayload->pContainer, iPackageStartAction, BURN_PLAN_INVALID_ACTION_INDEX, &pThisPackageExtractAction, &iTryAgainAction))
2376 + {
2377 + DWORD iAcquireAction = BURN_PLAN_INVALID_ACTION_INDEX;
2378 + if (FindContainerCacheAction(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER, pPlan, pPayload->pContainer, iPackageStartAction, iTryAgainAction, NULL, &iAcquireAction))
2379 + {
2380 + iTryAgainAction = iAcquireAction;
2381 + }
2382 + }
2383 + else // did not find an extract container action for our package.
2384 + {
2385 + // Ensure there is an extract action (and maybe an acquire action) for every package that has payloads. The
2386 + // acquire and extract action will be skipped if the payload is already cached or was added to a previous
2387 + // package's extract action above.
2388 + //
2389 + // These actions always exist (even when they are likely to be skipped) so that "try again" will not
2390 + // jump so far back in the plan that you end up extracting payloads for other packages. With these actions
2391 + // "try again" will only retry the extraction for payloads in this package.
2392 + hr = CreateContainerAcquireAndExtractAction(pPlan, pPayload->pContainer, iPackageStartAction, pPreviousPackageExtractAction ? TRUE : fPayloadCached, &pThisPackageExtractAction, &iTryAgainAction);
2393 + ExitOnFailure(hr, "Failed to create container extract action.");
2394 + }
2395 + ExitOnFailure(hr, "Failed while searching for package's container extract action.");
2396 +
2397 + // We *always* add the payload to this package's extract action even though the extract action
2398 + // is probably being skipped until retry if there was a previous package extract action.
2399 + hr = AddExtractPayload(pThisPackageExtractAction, pPackage, pPayload, sczPayloadWorkingPath);
2400 + ExitOnFailure(hr, "Failed to add extract payload to current package.");
2401 + }
2402 + else // add a payload acquire action to the plan.
2403 + {
2404 + // Try to find an existing acquire action for this payload. If one is not found,
2405 + // we'll create it. At the same time we will change any cache/layout payload actions
2406 + // that would "MOVE" the file to "COPY" so that our new cache/layout action below
2407 + // can do the move.
2408 + pCacheAction = ProcessSharedPayload(pPlan, pPayload);
2409 + if (!pCacheAction)
2410 + {
2411 + hr = AppendCacheAction(pPlan, &pCacheAction);
2412 + ExitOnFailure(hr, "Failed to append cache action to acquire payload.");
2413 +
2414 + pCacheAction->type = BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD;
2415 + pCacheAction->fSkipUntilRetried = fPayloadCached;
2416 + pCacheAction->resolvePayload.pPackage = pPackage;
2417 + pCacheAction->resolvePayload.pPayload = pPayload;
2418 + hr = StrAllocString(&pCacheAction->resolvePayload.sczUnverifiedPath, sczPayloadWorkingPath, 0);
2419 + ExitOnFailure(hr, "Failed to copy unverified path for payload to acquire.");
2420 + }
2421 +
2422 + iTryAgainAction = static_cast<DWORD>(pCacheAction - pPlan->rgCacheActions);
2423 + pCacheAction = NULL;
2424 + }
2425 +
2426 + Assert(BURN_PLAN_INVALID_ACTION_INDEX != iTryAgainAction);
2427 + Assert(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER == pPlan->rgCacheActions[iTryAgainAction].type ||
2428 + BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER == pPlan->rgCacheActions[iTryAgainAction].type ||
2429 + BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD == pPlan->rgCacheActions[iTryAgainAction].type);
2430 +
2431 + hr = AppendCacheAction(pPlan, &pCacheAction);
2432 + ExitOnFailure(hr, "Failed to append cache action to cache payload.");
2433 +
2434 + hr = CreatePayloadProgress(pPlan, pPayload, &pPayloadProgress);
2435 + ExitOnFailure(hr, "Failed to create payload progress.");
2436 +
2437 + if (!wzLayoutDirectory)
2438 + {
2439 + pCacheAction->type = BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD;
2440 + pCacheAction->cachePayload.pPackage = pPackage;
2441 + pCacheAction->cachePayload.pPayload = pPayload;
2442 + pCacheAction->cachePayload.iProgress = pPayloadProgress->iIndex;
2443 + pCacheAction->cachePayload.fMove = TRUE;
2444 + pCacheAction->cachePayload.iTryAgainAction = iTryAgainAction;
2445 + pCacheAction->cachePayload.sczUnverifiedPath = sczPayloadWorkingPath;
2446 + sczPayloadWorkingPath = NULL;
2447 + }
2448 + else
2449 + {
2450 + hr = StrAllocString(&pCacheAction->layoutPayload.sczLayoutDirectory, wzLayoutDirectory, 0);
2451 + ExitOnFailure(hr, "Failed to copy layout directory into plan.");
2452 +
2453 + pCacheAction->type = BURN_CACHE_ACTION_TYPE_LAYOUT_PAYLOAD;
2454 + pCacheAction->layoutPayload.pPackage = pPackage;
2455 + pCacheAction->layoutPayload.pPayload = pPayload;
2456 + pCacheAction->layoutPayload.iProgress = pPayloadProgress->iIndex;
2457 + pCacheAction->layoutPayload.fMove = TRUE;
2458 + pCacheAction->layoutPayload.iTryAgainAction = iTryAgainAction;
2459 + pCacheAction->layoutPayload.sczUnverifiedPath = sczPayloadWorkingPath;
2460 + sczPayloadWorkingPath = NULL;
2461 + }
2462 +
2463 + pCacheAction = NULL;
2464 +
2465 +LExit:
2466 + ReleaseStr(sczPayloadWorkingPath);
2467 +
2468 + return hr;
2469 +}
2470 +
2471 +static BOOL FindContainerCacheAction(
2472 + __in BURN_CACHE_ACTION_TYPE type,
2473 + __in BURN_PLAN* pPlan,
2474 + __in BURN_CONTAINER* pContainer,
2475 + __in DWORD iSearchStart,
2476 + __in DWORD iSearchEnd,
2477 + __out_opt BURN_CACHE_ACTION** ppCacheAction,
2478 + __out_opt DWORD* piCacheAction
2479 + )
2480 +{
2481 + BOOL fFound = FALSE; // assume we won't find what we are looking for.
2482 +
2483 + Assert(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER == type || BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER == type);
2484 +
2485 + iSearchStart = (BURN_PLAN_INVALID_ACTION_INDEX == iSearchStart) ? 0 : iSearchStart;
2486 + iSearchEnd = (BURN_PLAN_INVALID_ACTION_INDEX == iSearchEnd) ? pPlan->cCacheActions : iSearchEnd;
2487 +
2488 + for (DWORD iSearch = iSearchStart; iSearch < iSearchEnd; ++iSearch)
2489 + {
2490 + BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + iSearch;
2491 + if (pCacheAction->type == type &&
2492 + ((BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER == pCacheAction->type && pCacheAction->resolveContainer.pContainer == pContainer) ||
2493 + (BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER == pCacheAction->type && pCacheAction->extractContainer.pContainer == pContainer)))
2494 + {
2495 + if (ppCacheAction)
2496 + {
2497 + *ppCacheAction = pCacheAction;
2498 + }
2499 +
2500 + if (piCacheAction)
2501 + {
2502 + *piCacheAction = iSearch;
2503 + }
2504 +
2505 + fFound = TRUE;
2506 + break;
2507 + }
2508 + }
2509 +
2510 + return fFound;
2511 +}
2512 +
2513 +static HRESULT CreateContainerAcquireAndExtractAction(
2514 + __in BURN_PLAN* pPlan,
2515 + __in BURN_CONTAINER* pContainer,
2516 + __in DWORD iPackageStartAction,
2517 + __in BOOL fPayloadCached,
2518 + __out BURN_CACHE_ACTION** ppContainerExtractAction,
2519 + __out DWORD* piContainerTryAgainAction
2520 + )
2521 +{
2522 + HRESULT hr = S_OK;
2523 + DWORD iAcquireAction = BURN_PLAN_INVALID_ACTION_INDEX;
2524 + BURN_CACHE_ACTION* pContainerExtractAction = NULL;
2525 + DWORD iExtractAction = BURN_PLAN_INVALID_ACTION_INDEX;
2526 + DWORD iTryAgainAction = BURN_PLAN_INVALID_ACTION_INDEX;
2527 + LPWSTR sczContainerWorkingPath = NULL;
2528 +
2529 + // If the container is actually attached to the executable then we will not need an acquire
2530 + // container action.
2531 + if (!pContainer->fActuallyAttached)
2532 + {
2533 + BURN_CACHE_ACTION* pAcquireContainerAction = NULL;
2534 +
2535 + // If there is no plan to acquire the container then add acquire action since we
2536 + // can't extract stuff out of a container until we acquire the container.
2537 + if (!FindContainerCacheAction(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER, pPlan, pContainer, iPackageStartAction, BURN_PLAN_INVALID_ACTION_INDEX, &pAcquireContainerAction, &iAcquireAction))
2538 + {
2539 + hr = AddAcquireContainer(pPlan, pContainer, &pAcquireContainerAction, &iAcquireAction);
2540 + ExitOnFailure(hr, "Failed to append acquire container action to plan.");
2541 +
2542 + pAcquireContainerAction->fSkipUntilRetried = TRUE; // we'll start by assuming the acquire is not necessary and the fPayloadCached below will set us straight if wrong.
2543 + }
2544 +
2545 + Assert(BURN_PLAN_INVALID_ACTION_INDEX != iAcquireAction);
2546 + Assert(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER == pAcquireContainerAction->type);
2547 + Assert(pContainer == pAcquireContainerAction->resolveContainer.pContainer);
2548 + }
2549 +
2550 + Assert((pContainer->fActuallyAttached && BURN_PLAN_INVALID_ACTION_INDEX == iAcquireAction) ||
2551 + (!pContainer->fActuallyAttached && BURN_PLAN_INVALID_ACTION_INDEX != iAcquireAction));
2552 +
2553 + // If we do not find an action for extracting payloads from this container, create it now.
2554 + if (!FindContainerCacheAction(BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER, pPlan, pContainer, (BURN_PLAN_INVALID_ACTION_INDEX == iAcquireAction) ? iPackageStartAction : iAcquireAction, BURN_PLAN_INVALID_ACTION_INDEX, &pContainerExtractAction, &iExtractAction))
2555 + {
2556 + // Attached containers that are actually attached use the executable path for their working path.
2557 + if (pContainer->fActuallyAttached)
2558 + {
2559 + Assert(BURN_PLAN_INVALID_ACTION_INDEX == iAcquireAction);
2560 +
2561 + hr = PathForCurrentProcess(&sczContainerWorkingPath, NULL);
2562 + ExitOnFailure(hr, "Failed to get path for executing module as attached container working path.");
2563 + }
2564 + else // use the acquired working path as the location of the container.
2565 + {
2566 + Assert(BURN_PLAN_INVALID_ACTION_INDEX != iAcquireAction);
2567 +
2568 + hr = StrAllocString(&sczContainerWorkingPath, pPlan->rgCacheActions[iAcquireAction].resolveContainer.sczUnverifiedPath, 0);
2569 + ExitOnFailure(hr, "Failed to copy container unverified path for cache action to extract container.");
2570 + }
2571 +
2572 + hr = AppendCacheAction(pPlan, &pContainerExtractAction);
2573 + ExitOnFailure(hr, "Failed to append cache action to extract payloads from container.");
2574 +
2575 + iExtractAction = pPlan->cCacheActions - 1;
2576 +
2577 + pContainerExtractAction->type = BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER;
2578 + pContainerExtractAction->fSkipUntilRetried = pContainer->fActuallyAttached; // assume we can skip the extract engine when the container is already attached and the fPayloadCached below will set us straight if wrong.
2579 + pContainerExtractAction->extractContainer.pContainer = pContainer;
2580 + pContainerExtractAction->extractContainer.iSkipUntilAcquiredByAction = iAcquireAction;
2581 + pContainerExtractAction->extractContainer.sczContainerUnverifiedPath = sczContainerWorkingPath;
2582 + sczContainerWorkingPath = NULL;
2583 + }
2584 +
2585 + Assert(BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER == pContainerExtractAction->type);
2586 + Assert(BURN_PLAN_INVALID_ACTION_INDEX != iExtractAction);
2587 +
2588 + // If there is an acquire action, that is our try again action. Otherwise, we'll use the extract action.
2589 + iTryAgainAction = (BURN_PLAN_INVALID_ACTION_INDEX != iAcquireAction) ? iAcquireAction : iExtractAction;
2590 +
2591 + // If the try again action thinks it can be skipped but the payload is not cached,
2592 + // ensure the action will not be skipped.
2593 + BURN_CACHE_ACTION* pTryAgainAction = pPlan->rgCacheActions + iTryAgainAction;
2594 + Assert((BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER == pTryAgainAction->type && pContainer == pTryAgainAction->resolveContainer.pContainer) ||
2595 + (BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER == pTryAgainAction->type && pContainer == pTryAgainAction->extractContainer.pContainer));
2596 + if (pTryAgainAction->fSkipUntilRetried && !fPayloadCached)
2597 + {
2598 + pTryAgainAction->fSkipUntilRetried = FALSE;
2599 + }
2600 +
2601 + *ppContainerExtractAction = pContainerExtractAction;
2602 + *piContainerTryAgainAction = iTryAgainAction;
2603 +
2604 +LExit:
2605 + ReleaseStr(sczContainerWorkingPath);
2606 +
2607 + return hr;
2608 +}
2609 +
2610 +static HRESULT AddAcquireContainer(
2611 + __in BURN_PLAN* pPlan,
2612 + __in BURN_CONTAINER* pContainer,
2613 + __out_opt BURN_CACHE_ACTION** ppCacheAction,
2614 + __out_opt DWORD* piCacheAction
2615 + )
2616 +{
2617 + HRESULT hr = S_OK;
2618 + LPWSTR sczContainerWorkingPath = NULL;
2619 + BURN_CACHE_ACTION* pAcquireContainerAction = NULL;
2620 + BURN_CACHE_CONTAINER_PROGRESS* pContainerProgress = NULL;
2621 +
2622 + hr = CacheCalculateContainerWorkingPath(pPlan->wzBundleId, pContainer, &sczContainerWorkingPath);
2623 + ExitOnFailure(hr, "Failed to calculate unverified path for container.");
2624 +
2625 + hr = AppendCacheAction(pPlan, &pAcquireContainerAction);
2626 + ExitOnFailure(hr, "Failed to append acquire container action to plan.");
2627 +
2628 + hr = CreateContainerProgress(pPlan, pContainer, &pContainerProgress);
2629 + ExitOnFailure(hr, "Failed to create container progress.");
2630 +
2631 + pAcquireContainerAction->type = BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER;
2632 + pAcquireContainerAction->resolveContainer.pContainer = pContainer;
2633 + pAcquireContainerAction->resolveContainer.iProgress = pContainerProgress->iIndex;
2634 + pAcquireContainerAction->resolveContainer.sczUnverifiedPath = sczContainerWorkingPath;
2635 + sczContainerWorkingPath = NULL;
2636 +
2637 + if (ppCacheAction)
2638 + {
2639 + *ppCacheAction = pAcquireContainerAction;
2640 + }
2641 +
2642 + if (piCacheAction)
2643 + {
2644 + *piCacheAction = pPlan->cCacheActions - 1;
2645 + }
2646 +
2647 +LExit:
2648 + ReleaseStr(sczContainerWorkingPath);
2649 +
2650 + return hr;
2651 +}
2652 +
2653 +static HRESULT AddExtractPayload(
2654 + __in BURN_CACHE_ACTION* pCacheAction,
2655 + __in_opt BURN_PACKAGE* pPackage,
2656 + __in BURN_PAYLOAD* pPayload,
2657 + __in_z LPCWSTR wzPayloadWorkingPath
2658 + )
2659 +{
2660 + HRESULT hr = S_OK;
2661 +
2662 + Assert(BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER == pCacheAction->type);
2663 +
2664 + hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pCacheAction->extractContainer.rgPayloads), pCacheAction->extractContainer.cPayloads + 1, sizeof(BURN_EXTRACT_PAYLOAD), 5);
2665 + ExitOnFailure(hr, "Failed to grow list of payloads to extract from container.");
2666 +
2667 + BURN_EXTRACT_PAYLOAD* pExtractPayload = pCacheAction->extractContainer.rgPayloads + pCacheAction->extractContainer.cPayloads;
2668 + pExtractPayload->pPackage = pPackage;
2669 + pExtractPayload->pPayload = pPayload;
2670 + hr = StrAllocString(&pExtractPayload->sczUnverifiedPath, wzPayloadWorkingPath, 0);
2671 + ExitOnFailure(hr, "Failed to copy unverified path for payload to extract.");
2672 + ++pCacheAction->extractContainer.cPayloads;
2673 +
2674 +LExit:
2675 + return hr;
2676 +}
2677 +
2678 +static BURN_CACHE_ACTION* ProcessSharedPayload(
2679 + __in BURN_PLAN* pPlan,
2680 + __in BURN_PAYLOAD* pPayload
2681 + )
2682 +{
2683 + BURN_CACHE_ACTION* pAcquireAction = NULL;
2684 +#ifdef DEBUG
2685 + DWORD cMove = 0;
2686 +#endif
2687 +
2688 + for (DWORD i = 0; i < pPlan->cCacheActions; ++i)
2689 + {
2690 + BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + i;
2691 +
2692 + if (BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD == pCacheAction->type &&
2693 + pCacheAction->resolvePayload.pPayload == pPayload)
2694 + {
2695 + AssertSz(!pAcquireAction, "There should be at most one acquire cache action per payload.");
2696 + pAcquireAction = pCacheAction;
2697 + }
2698 + else if (BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD == pCacheAction->type &&
2699 + pCacheAction->cachePayload.pPayload == pPayload &&
2700 + pCacheAction->cachePayload.fMove)
2701 + {
2702 + // Since we found a shared payload, change its operation from MOVE to COPY.
2703 + pCacheAction->cachePayload.fMove = FALSE;
2704 +
2705 + AssertSz(1 == ++cMove, "Shared payload should be moved once and only once.");
2706 +#ifndef DEBUG
2707 + break;
2708 +#endif
2709 + }
2710 + else if (BURN_CACHE_ACTION_TYPE_LAYOUT_PAYLOAD == pCacheAction->type &&
2711 + pCacheAction->layoutPayload.pPayload == pPayload &&
2712 + pCacheAction->layoutPayload.fMove)
2713 + {
2714 + // Since we found a shared payload, change its operation from MOVE to COPY if necessary
2715 + pCacheAction->layoutPayload.fMove = FALSE;
2716 +
2717 + AssertSz(1 == ++cMove, "Shared payload should be moved once and only once.");
2718 +#ifndef DEBUG
2719 + break;
2720 +#endif
2721 + }
2722 + }
2723 +
2724 + return pAcquireAction;
2725 +}
2726 +
2727 +static HRESULT RemoveUnnecessaryActions(
2728 + __in BOOL fExecute,
2729 + __in BURN_EXECUTE_ACTION* rgActions,
2730 + __in DWORD cActions
2731 + )
2732 +{
2733 + HRESULT hr = S_OK;
2734 + LPCSTR szExecuteOrRollback = fExecute ? "execute" : "rollback";
2735 +
2736 + for (DWORD i = 0; i < cActions; ++i)
2737 + {
2738 + BURN_EXECUTE_ACTION* pAction = rgActions + i;
2739 +
2740 + // If this MSP targets a package in the chain, check the target's execute state
2741 + // to see if this patch should be skipped.
2742 + if (BURN_EXECUTE_ACTION_TYPE_MSP_TARGET == pAction->type && pAction->mspTarget.pChainedTargetPackage)
2743 + {
2744 + BOOTSTRAPPER_ACTION_STATE chainedTargetPackageAction = fExecute ? pAction->mspTarget.pChainedTargetPackage->execute : pAction->mspTarget.pChainedTargetPackage->rollback;
2745 + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == chainedTargetPackageAction)
2746 + {
2747 + LogId(REPORT_STANDARD, MSG_PLAN_SKIP_PATCH_ACTION, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.pChainedTargetPackage->sczId, LoggingActionStateToString(chainedTargetPackageAction), szExecuteOrRollback);
2748 + pAction->fDeleted = TRUE;
2749 + }
2750 + else if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL < chainedTargetPackageAction && pAction->mspTarget.fSlipstream && BOOTSTRAPPER_ACTION_STATE_UNINSTALL < pAction->mspTarget.action)
2751 + {
2752 + // If the slipstream target is being installed or upgraded (not uninstalled or repaired) then we will slipstream so skip
2753 + // this action to install the patch standalone. Also, if the slipstream target is being repaired and the patch is being
2754 + // repaired, skip this operation since it will be redundant.
2755 + //
2756 + // The primary goal here is to ensure that a slipstream patch that is yet not installed is installed even if the MSI
2757 + // is already on the machine. The slipstream must be installed standalone if the MSI is being repaired.
2758 + if (BOOTSTRAPPER_ACTION_STATE_REPAIR != chainedTargetPackageAction || BOOTSTRAPPER_ACTION_STATE_REPAIR == pAction->mspTarget.action)
2759 + {
2760 + LogId(REPORT_STANDARD, MSG_PLAN_SKIP_SLIPSTREAM_ACTION, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.pChainedTargetPackage->sczId, LoggingActionStateToString(chainedTargetPackageAction), szExecuteOrRollback);
2761 + pAction->fDeleted = TRUE;
2762 + }
2763 + }
2764 + }
2765 + }
2766 +
2767 + return hr;
2768 +}
2769 +
2770 +static HRESULT FinalizeSlipstreamPatchActions(
2771 + __in BOOL fExecute,
2772 + __in BURN_EXECUTE_ACTION* rgActions,
2773 + __in DWORD cActions
2774 + )
2775 +{
2776 + HRESULT hr = S_OK;
2777 +
2778 + for (DWORD i = 0; i < cActions; ++i)
2779 + {
2780 + BURN_EXECUTE_ACTION* pAction = rgActions + i;
2781 +
2782 + // If this MSI package contains slipstream patches store the slipstream actions.
2783 + if (BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE == pAction->type && pAction->msiPackage.pPackage->Msi.cSlipstreamMspPackages)
2784 + {
2785 + BURN_PACKAGE* pPackage = pAction->msiPackage.pPackage;
2786 +
2787 + // By default all slipstream actions will be initialized to "no action" (aka: 0).
2788 + pAction->msiPackage.rgSlipstreamPatches = (BOOTSTRAPPER_ACTION_STATE*)MemAlloc(sizeof(BOOTSTRAPPER_ACTION_STATE) * pPackage->Msi.cSlipstreamMspPackages, TRUE);
2789 + ExitOnNull(pAction->msiPackage.rgSlipstreamPatches, hr, E_OUTOFMEMORY, "Failed to allocate memory for patch actions.");
2790 +
2791 + // If we are uninstalling or repairing the MSI, we must ignore all the slipstream patches because they cannot
2792 + // be applied right now.
2793 + if (BOOTSTRAPPER_ACTION_STATE_REPAIR != pAction->msiPackage.action && BOOTSTRAPPER_ACTION_STATE_UNINSTALL != pAction->msiPackage.action)
2794 + {
2795 + for (DWORD j = 0; j < pPackage->Msi.cSlipstreamMspPackages; ++j)
2796 + {
2797 + BURN_PACKAGE* pMspPackage = pPackage->Msi.rgpSlipstreamMspPackages[j];
2798 + AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Only MSP packages can be slipstream patches.");
2799 +
2800 + pAction->msiPackage.rgSlipstreamPatches[j] = fExecute ? pMspPackage->execute : pMspPackage->rollback;
2801 + for (DWORD k = 0; k < pMspPackage->Msp.cTargetProductCodes; ++k)
2802 + {
2803 + BURN_MSPTARGETPRODUCT* pTargetProduct = pMspPackage->Msp.rgTargetProducts + k;
2804 + if (pPackage == pTargetProduct->pChainedTargetPackage)
2805 + {
2806 + pAction->msiPackage.rgSlipstreamPatches[j] = fExecute ? pTargetProduct->execute : pTargetProduct->rollback;
2807 + break;
2808 + }
2809 + }
2810 + }
2811 + }
2812 + }
2813 + }
2814 +
2815 +LExit:
2816 + return hr;
2817 +}
2818 +
2819 +static HRESULT PlanDependencyActions(
2820 + __in BOOL fBundlePerMachine,
2821 + __in BURN_PLAN* pPlan,
2822 + __in BURN_PACKAGE* pPackage
2823 + )
2824 +{
2825 + HRESULT hr = S_OK;
2826 +
2827 + hr = DependencyPlanPackageBegin(fBundlePerMachine, pPackage, pPlan);
2828 + ExitOnFailure(hr, "Failed to begin plan dependency actions for package: %ls", pPackage->sczId);
2829 +
2830 + hr = DependencyPlanPackage(NULL, pPackage, pPlan);
2831 + ExitOnFailure(hr, "Failed to plan package dependency actions.");
2832 +
2833 + hr = DependencyPlanPackageComplete(pPackage, pPlan);
2834 + ExitOnFailure(hr, "Failed to complete plan dependency actions for package: %ls", pPackage->sczId);
2835 +
2836 +LExit:
2837 + return hr;
2838 +}
2839 +
2840 +static HRESULT CalculateExecuteActions(
2841 + __in BURN_USER_EXPERIENCE* pUserExperience,
2842 + __in BURN_PACKAGE* pPackage,
2843 + __in BURN_VARIABLES* pVariables,
2844 + __out_opt BOOL* pfBARequestedCache
2845 + )
2846 +{
2847 + HRESULT hr = S_OK;
2848 +
2849 + // Calculate execute actions.
2850 + switch (pPackage->type)
2851 + {
2852 + case BURN_PACKAGE_TYPE_EXE:
2853 + hr = ExeEnginePlanCalculatePackage(pPackage, pfBARequestedCache);
2854 + break;
2855 +
2856 + case BURN_PACKAGE_TYPE_MSI:
2857 + hr = MsiEnginePlanCalculatePackage(pPackage, pVariables, pUserExperience, pfBARequestedCache);
2858 + break;
2859 +
2860 + case BURN_PACKAGE_TYPE_MSP:
2861 + hr = MspEnginePlanCalculatePackage(pPackage, pUserExperience, pfBARequestedCache);
2862 + break;
2863 +
2864 + case BURN_PACKAGE_TYPE_MSU:
2865 + hr = MsuEnginePlanCalculatePackage(pPackage, pfBARequestedCache);
2866 + break;
2867 +
2868 + default:
2869 + hr = E_UNEXPECTED;
2870 + ExitOnFailure(hr, "Invalid package type.");
2871 + }
2872 +
2873 +LExit:
2874 + return hr;
2875 +}
2876 +
2877 +static BOOL NeedsCache(
2878 + __in BURN_PLAN* pPlan,
2879 + __in BURN_PACKAGE* pPackage
2880 + )
2881 +{
2882 + // All packages that have cacheType set to always should be cached if the bundle is going to be present.
2883 + if (BURN_CACHE_TYPE_ALWAYS == pPackage->cacheType && BOOTSTRAPPER_ACTION_INSTALL <= pPlan->action)
2884 + {
2885 + return TRUE;
2886 + }
2887 + else if (BURN_PACKAGE_TYPE_EXE == pPackage->type) // Exe packages require the package for all operations (even uninstall).
2888 + {
2889 + return BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute;
2890 + }
2891 + else // The other package types can uninstall without the original package.
2892 + {
2893 + return BOOTSTRAPPER_ACTION_STATE_UNINSTALL < pPackage->execute;
2894 + }
2895 +}
2896 +
2897 +static HRESULT CreateContainerProgress(
2898 + __in BURN_PLAN* pPlan,
2899 + __in BURN_CONTAINER* pContainer,
2900 + __out BURN_CACHE_CONTAINER_PROGRESS** ppContainerProgress
2901 + )
2902 +{
2903 + HRESULT hr = S_OK;
2904 + BURN_CACHE_CONTAINER_PROGRESS* pContainerProgress = NULL;
2905 +
2906 + hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPlan->rgContainerProgress), pPlan->cContainerProgress + 1, sizeof(BURN_CACHE_CONTAINER_PROGRESS), 5);
2907 + ExitOnFailure(hr, "Failed to grow container progress list.");
2908 +
2909 + if (!pPlan->shContainerProgress)
2910 + {
2911 + hr = DictCreateWithEmbeddedKey(&pPlan->shContainerProgress, 5, reinterpret_cast<void **>(&pPlan->rgContainerProgress), offsetof(BURN_CACHE_CONTAINER_PROGRESS, wzId), DICT_FLAG_NONE);
2912 + ExitOnFailure(hr, "Failed to create container progress dictionary.");
2913 + }
2914 +
2915 + hr = DictGetValue(pPlan->shContainerProgress, pContainer->sczId, reinterpret_cast<void **>(&pContainerProgress));
2916 + if (E_NOTFOUND == hr)
2917 + {
2918 + pContainerProgress = &pPlan->rgContainerProgress[pPlan->cContainerProgress];
2919 + pContainerProgress->iIndex = pPlan->cContainerProgress;
2920 + pContainerProgress->pContainer = pContainer;
2921 + pContainerProgress->wzId = pContainer->sczId;
2922 +
2923 + hr = DictAddValue(pPlan->shContainerProgress, pContainerProgress);
2924 + ExitOnFailure(hr, "Failed to add \"%ls\" to the container progress dictionary.", pContainerProgress->wzId);
2925 +
2926 + ++pPlan->cContainerProgress;
2927 + pPlan->qwCacheSizeTotal += pContainer->qwFileSize;
2928 + }
2929 + ExitOnFailure(hr, "Failed to retrieve \"%ls\" from the container progress dictionary.", pContainer->sczId);
2930 +
2931 + *ppContainerProgress = pContainerProgress;
2932 +
2933 +LExit:
2934 + return hr;
2935 +}
2936 +
2937 +static HRESULT CreatePayloadProgress(
2938 + __in BURN_PLAN* pPlan,
2939 + __in BURN_PAYLOAD* pPayload,
2940 + __out BURN_CACHE_PAYLOAD_PROGRESS** ppPayloadProgress
2941 + )
2942 +{
2943 + HRESULT hr = S_OK;
2944 + BURN_CACHE_PAYLOAD_PROGRESS* pPayloadProgress = NULL;
2945 +
2946 + hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPlan->rgPayloadProgress), pPlan->cPayloadProgress + 1, sizeof(BURN_CACHE_PAYLOAD_PROGRESS), 5);
2947 + ExitOnFailure(hr, "Failed to grow payload progress list.");
2948 +
2949 + if (!pPlan->shPayloadProgress)
2950 + {
2951 + hr = DictCreateWithEmbeddedKey(&pPlan->shPayloadProgress, 5, reinterpret_cast<void **>(&pPlan->rgPayloadProgress), offsetof(BURN_CACHE_PAYLOAD_PROGRESS, wzId), DICT_FLAG_NONE);
2952 + ExitOnFailure(hr, "Failed to create payload progress dictionary.");
2953 + }
2954 +
2955 + hr = DictGetValue(pPlan->shPayloadProgress, pPayload->sczKey, reinterpret_cast<void **>(&pPayloadProgress));
2956 + if (E_NOTFOUND == hr)
2957 + {
2958 + pPayloadProgress = &pPlan->rgPayloadProgress[pPlan->cPayloadProgress];
2959 + pPayloadProgress->iIndex = pPlan->cPayloadProgress;
2960 + pPayloadProgress->pPayload = pPayload;
2961 + pPayloadProgress->wzId = pPayload->sczKey;
2962 +
2963 + hr = DictAddValue(pPlan->shPayloadProgress, pPayloadProgress);
2964 + ExitOnFailure(hr, "Failed to add \"%ls\" to the payload progress dictionary.", pPayloadProgress->wzId);
2965 +
2966 + ++pPlan->cPayloadProgress;
2967 + pPlan->qwCacheSizeTotal += pPayload->qwFileSize;
2968 + }
2969 + ExitOnFailure(hr, "Failed to retrieve \"%ls\" from the payload progress dictionary.", pPayload->sczKey);
2970 +
2971 + *ppPayloadProgress = pPayloadProgress;
2972 +
2973 +LExit:
2974 + return hr;
2975 +}
2976 +
2977 +
2978 +#ifdef DEBUG
2979 +
2980 +static void CacheActionLog(
2981 + __in DWORD iAction,
2982 + __in BURN_CACHE_ACTION* pAction,
2983 + __in BOOL fRollback
2984 + )
2985 +{
2986 + LPCWSTR wzBase = fRollback ? L" Rollback cache" : L" Cache";
2987 + switch (pAction->type)
2988 + {
2989 + case BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER:
2990 + LogStringLine(REPORT_STANDARD, "%ls action[%u]: ACQUIRE_CONTAINER id: %ls, source path: %ls, working path: %ls, skip until retried: %hs", wzBase, iAction, pAction->resolveContainer.pContainer->sczId, pAction->resolveContainer.pContainer->sczSourcePath, pAction->resolveContainer.sczUnverifiedPath, LoggingBoolToString(pAction->fSkipUntilRetried));
2991 + break;
2992 +
2993 + case BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD:
2994 + LogStringLine(REPORT_STANDARD, "%ls action[%u]: ACQUIRE_PAYLOAD package id: %ls, payload id: %ls, source path: %ls, working path: %ls, skip until retried: %hs", wzBase, iAction, pAction->resolvePayload.pPackage ? pAction->resolvePayload.pPackage->sczId : L"", pAction->resolvePayload.pPayload->sczKey, pAction->resolvePayload.pPayload->sczSourcePath, pAction->resolvePayload.sczUnverifiedPath, LoggingBoolToString(pAction->fSkipUntilRetried));
2995 + break;
2996 +
2997 + case BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD:
2998 + LogStringLine(REPORT_STANDARD, "%ls action[%u]: CACHE_PAYLOAD package id: %ls, payload id: %ls, working path: %ls, operation: %ls, skip until retried: %hs, retry action: %u", wzBase, iAction, pAction->cachePayload.pPackage->sczId, pAction->cachePayload.pPayload->sczKey, pAction->cachePayload.sczUnverifiedPath, pAction->cachePayload.fMove ? L"move" : L"copy", LoggingBoolToString(pAction->fSkipUntilRetried), pAction->cachePayload.iTryAgainAction);
2999 + break;
3000 +
3001 + case BURN_CACHE_ACTION_TYPE_CHECKPOINT:
3002 + LogStringLine(REPORT_STANDARD, "%ls action[%u]: CHECKPOINT id: %u", wzBase, iAction, pAction->checkpoint.dwId);
3003 + break;
3004 +
3005 + case BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER:
3006 + LogStringLine(REPORT_STANDARD, "%ls action[%u]: EXTRACT_CONTAINER id: %ls, working path: %ls, skip until retried: %hs, skip until acquired by action: %u", wzBase, iAction, pAction->extractContainer.pContainer->sczId, pAction->extractContainer.sczContainerUnverifiedPath, LoggingBoolToString(pAction->fSkipUntilRetried), pAction->extractContainer.iSkipUntilAcquiredByAction);
3007 + for (DWORD j = 0; j < pAction->extractContainer.cPayloads; j++)
3008 + {
3009 + LogStringLine(REPORT_STANDARD, " extract package id: %ls, payload id: %ls, working path: %ls", pAction->extractContainer.rgPayloads[j].pPackage->sczId, pAction->extractContainer.rgPayloads[j].pPayload->sczKey, pAction->extractContainer.rgPayloads[j].sczUnverifiedPath);
3010 + }
3011 + break;
3012 +
3013 + case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE:
3014 + LogStringLine(REPORT_STANDARD, "%ls action[%u]: LAYOUT_BUNDLE working path: %ls, layout directory: %ls, exe name: %ls, skip until retried: %hs", wzBase, iAction, pAction->bundleLayout.sczUnverifiedPath, pAction->bundleLayout.sczLayoutDirectory, pAction->bundleLayout.sczExecutableName, LoggingBoolToString(pAction->fSkipUntilRetried));
3015 + break;
3016 +
3017 + case BURN_CACHE_ACTION_TYPE_LAYOUT_CONTAINER:
3018 + LogStringLine(REPORT_STANDARD, "%ls action[%u]: LAYOUT_CONTAINER package id: %ls, container id: %ls, working path: %ls, layout directory: %ls, operation: %ls, skip until retried: %hs, retry action: %u", wzBase, iAction, pAction->layoutContainer.pPackage ? pAction->layoutContainer.pPackage->sczId : L"", pAction->layoutContainer.pContainer->sczId, pAction->layoutContainer.sczUnverifiedPath, pAction->layoutContainer.sczLayoutDirectory, pAction->layoutContainer.fMove ? L"move" : L"copy", LoggingBoolToString(pAction->fSkipUntilRetried), pAction->layoutContainer.iTryAgainAction);
3019 + break;
3020 +
3021 + case BURN_CACHE_ACTION_TYPE_LAYOUT_PAYLOAD:
3022 + LogStringLine(REPORT_STANDARD, "%ls action[%u]: LAYOUT_PAYLOAD package id: %ls, payload id: %ls, working path: %ls, layout directory: %ls, operation: %ls, skip until retried: %hs, retry action: %u", wzBase, iAction, pAction->layoutPayload.pPackage ? pAction->layoutPayload.pPackage->sczId : L"", pAction->layoutPayload.pPayload->sczKey, pAction->layoutPayload.sczUnverifiedPath, pAction->layoutPayload.sczLayoutDirectory, pAction->layoutPayload.fMove ? L"move" : L"copy", LoggingBoolToString(pAction->fSkipUntilRetried), pAction->layoutPayload.iTryAgainAction);
3023 + break;
3024 +
3025 + case BURN_CACHE_ACTION_TYPE_PACKAGE_START:
3026 + LogStringLine(REPORT_STANDARD, "%ls action[%u]: PACKAGE_START id: %ls, plan index for skip: %u, payloads to cache: %u, bytes to cache: %llu, skip until retried: %hs", wzBase, iAction, pAction->packageStart.pPackage->sczId, pAction->packageStart.iPackageCompleteAction, pAction->packageStart.cCachePayloads, pAction->packageStart.qwCachePayloadSizeTotal, LoggingBoolToString(pAction->fSkipUntilRetried));
3027 + break;
3028 +
3029 + case BURN_CACHE_ACTION_TYPE_PACKAGE_STOP:
3030 + LogStringLine(REPORT_STANDARD, "%ls action[%u]: PACKAGE_STOP id: %ls, skip until retried: %hs", wzBase, iAction, pAction->packageStop.pPackage->sczId, LoggingBoolToString(pAction->fSkipUntilRetried));
3031 + break;
3032 +
3033 + case BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE:
3034 + LogStringLine(REPORT_STANDARD, "%ls action[%u]: ROLLBACK_PACKAGE id: %ls, skip until retried: %hs", wzBase, iAction, pAction->rollbackPackage.pPackage->sczId, LoggingBoolToString(pAction->fSkipUntilRetried));
3035 + break;
3036 +
3037 + case BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT:
3038 + LogStringLine(REPORT_STANDARD, "%ls action[%u]: SIGNAL_SYNCPOINT event handle: 0x%x, skip until retried: %hs", wzBase, iAction, pAction->syncpoint.hEvent, LoggingBoolToString(pAction->fSkipUntilRetried));
3039 + break;
3040 +
3041 + case BURN_CACHE_ACTION_TYPE_TRANSACTION_BOUNDARY:
3042 + LogStringLine(REPORT_STANDARD, "%ls action[%u]: TRANSACTION_BOUNDARY id: %ls, event handle: 0x%x, vital: %ls, transaction: %ls", wzBase, iAction, pAction->rollbackBoundary.pRollbackBoundary->sczId, pAction->rollbackBoundary.hEvent, pAction->rollbackBoundary.pRollbackBoundary->fVital ? L"yes" : L"no", pAction->rollbackBoundary.pRollbackBoundary->fTransaction ? L"yes" : L"no");
3043 + break;
3044 +
3045 + default:
3046 + AssertSz(FALSE, "Unknown cache action type.");
3047 + break;
3048 + }
3049 +}
3050 +
3051 +static void ExecuteActionLog(
3052 + __in DWORD iAction,
3053 + __in BURN_EXECUTE_ACTION* pAction,
3054 + __in BOOL fRollback
3055 + )
3056 +{
3057 + LPCWSTR wzBase = fRollback ? L" Rollback" : L" Execute";
3058 + switch (pAction->type)
3059 + {
3060 + case BURN_EXECUTE_ACTION_TYPE_CHECKPOINT:
3061 + LogStringLine(REPORT_STANDARD, "%ls action[%u]: CHECKPOINT id: %u", wzBase, iAction, pAction->checkpoint.dwId);
3062 + break;
3063 +
3064 + case BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER:
3065 + LogStringLine(REPORT_STANDARD, "%ls action[%u]: PACKAGE_PROVIDER package id: %ls, action: %u", wzBase, iAction, pAction->packageProvider.pPackage->sczId, pAction->packageProvider.action);
3066 + break;
3067 +
3068 + case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY:
3069 + LogStringLine(REPORT_STANDARD, "%ls action[%u]: PACKAGE_DEPENDENCY package id: %ls, bundle provider key: %ls, action: %u", wzBase, iAction, pAction->packageDependency.pPackage->sczId, pAction->packageDependency.sczBundleProviderKey, pAction->packageDependency.action);
3070 + break;
3071 +
3072 + case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE:
3073 + LogStringLine(REPORT_STANDARD, "%ls action[%u]: EXE_PACKAGE package id: %ls, action: %hs, ignore dependencies: %ls", wzBase, iAction, pAction->exePackage.pPackage->sczId, LoggingActionStateToString(pAction->exePackage.action), pAction->exePackage.sczIgnoreDependencies);
3074 + break;
3075 +
3076 + case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE:
3077 + LogStringLine(REPORT_STANDARD, "%ls action[%u]: MSI_PACKAGE package id: %ls, action: %hs, ui level: %u, log path: %ls, logging attrib: %u", wzBase, iAction, pAction->msiPackage.pPackage->sczId, LoggingActionStateToString(pAction->msiPackage.action), pAction->msiPackage.uiLevel, pAction->msiPackage.sczLogPath, pAction->msiPackage.dwLoggingAttributes);
3078 + for (DWORD j = 0; j < pAction->msiPackage.cPatches; ++j)
3079 + {
3080 + LogStringLine(REPORT_STANDARD, " Patch[%u]: order: %u, msp package id: %ls", j, pAction->msiPackage.rgOrderedPatches->dwOrder, pAction->msiPackage.rgOrderedPatches[j].dwOrder, pAction->msiPackage.rgOrderedPatches[j].pPackage->sczId);
3081 + }
3082 + break;
3083 +
3084 + case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET:
3085 + LogStringLine(REPORT_STANDARD, "%ls action[%u]: MSP_TARGET package id: %ls, action: %hs, target product code: %ls, target per-machine: %ls, ui level: %u, log path: %ls", wzBase, iAction, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.sczTargetProductCode, pAction->mspTarget.fPerMachineTarget ? L"yes" : L"no", pAction->mspTarget.uiLevel, pAction->mspTarget.sczLogPath);
3086 + for (DWORD j = 0; j < pAction->mspTarget.cOrderedPatches; ++j)
3087 + {
3088 + LogStringLine(REPORT_STANDARD, " Patch[%u]: order: %u, msp package id: %ls", j, pAction->mspTarget.rgOrderedPatches[j].dwOrder, pAction->mspTarget.rgOrderedPatches[j].pPackage->sczId);
3089 + }
3090 + break;
3091 +
3092 + case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE:
3093 + LogStringLine(REPORT_STANDARD, "%ls action[%u]: MSU_PACKAGE package id: %ls, action: %hs, log path: %ls", wzBase, iAction, pAction->msuPackage.pPackage->sczId, LoggingActionStateToString(pAction->msuPackage.action), pAction->msuPackage.sczLogPath);
3094 + break;
3095 +
3096 + case BURN_EXECUTE_ACTION_TYPE_REGISTRATION:
3097 + LogStringLine(REPORT_STANDARD, "%ls action[%u]: REGISTRATION keep: %ls", wzBase, iAction, pAction->registration.fKeep ? L"yes" : L"no");
3098 + break;
3099 +
3100 + case BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY:
3101 + LogStringLine(REPORT_STANDARD, "%ls action[%u]: ROLLBACK_BOUNDARY id: %ls, vital: %ls", wzBase, iAction, pAction->rollbackBoundary.pRollbackBoundary->sczId, pAction->rollbackBoundary.pRollbackBoundary->fVital ? L"yes" : L"no");
3102 + break;
3103 +
3104 + case BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT:
3105 + LogStringLine(REPORT_STANDARD, "%ls action[%u]: WAIT_SYNCPOINT event handle: 0x%x", wzBase, iAction, pAction->syncpoint.hEvent);
3106 + break;
3107 +
3108 + case BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE:
3109 + LogStringLine(REPORT_STANDARD, "%ls action[%u]: UNCACHE_PACKAGE id: %ls", wzBase, iAction, pAction->uncachePackage.pPackage->sczId);
3110 + break;
3111 +
3112 + case BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE:
3113 + LogStringLine(REPORT_STANDARD, "%ls action[%u]: COMPATIBLE_PACKAGE reference id: %ls, installed ProductCode: %ls", wzBase, iAction, pAction->compatiblePackage.pReferencePackage->sczId, pAction->compatiblePackage.sczInstalledProductCode);
3114 + break;
3115 +
3116 + default:
3117 + AssertSz(FALSE, "Unknown execute action type.");
3118 + break;
3119 + }
3120 +}
3121 +
3122 +extern "C" void PlanDump(
3123 + __in BURN_PLAN* pPlan
3124 + )
3125 +{
3126 + LogStringLine(REPORT_STANDARD, "--- Begin plan dump ---");
3127 +
3128 + LogStringLine(REPORT_STANDARD, "Plan action: %hs", LoggingBurnActionToString(pPlan->action));
3129 + LogStringLine(REPORT_STANDARD, " per-machine: %hs", LoggingTrueFalseToString(pPlan->fPerMachine));
3130 + LogStringLine(REPORT_STANDARD, " keep registration by default: %hs", LoggingTrueFalseToString(pPlan->fKeepRegistrationDefault));
3131 + LogStringLine(REPORT_STANDARD, " estimated size: %llu", pPlan->qwEstimatedSize);
3132 +
3133 + LogStringLine(REPORT_STANDARD, "Plan cache size: %llu", pPlan->qwCacheSizeTotal);
3134 + for (DWORD i = 0; i < pPlan->cCacheActions; ++i)
3135 + {
3136 + CacheActionLog(i, pPlan->rgCacheActions + i, FALSE);
3137 + }
3138 +
3139 + for (DWORD i = 0; i < pPlan->cRollbackCacheActions; ++i)
3140 + {
3141 + CacheActionLog(i, pPlan->rgRollbackCacheActions + i, TRUE);
3142 + }
3143 +
3144 + LogStringLine(REPORT_STANDARD, "Plan execute package count: %u", pPlan->cExecutePackagesTotal);
3145 + LogStringLine(REPORT_STANDARD, " overall progress ticks: %u", pPlan->cOverallProgressTicksTotal);
3146 + for (DWORD i = 0; i < pPlan->cExecuteActions; ++i)
3147 + {
3148 + ExecuteActionLog(i, pPlan->rgExecuteActions + i, FALSE);
3149 + }
3150 +
3151 + for (DWORD i = 0; i < pPlan->cRollbackActions; ++i)
3152 + {
3153 + ExecuteActionLog(i, pPlan->rgRollbackActions + i, TRUE);
3154 + }
3155 +
3156 + for (DWORD i = 0; i < pPlan->cCleanActions; ++i)
3157 + {
3158 + LogStringLine(REPORT_STANDARD, " Clean action[%u]: CLEAN_PACKAGE package id: %ls", i, pPlan->rgCleanActions[i].pPackage->sczId);
3159 + }
3160 +
3161 + for (DWORD i = 0; i < pPlan->cPlannedProviders; ++i)
3162 + {
3163 + LogStringLine(REPORT_STANDARD, " Dependency action[%u]: PLANNED_PROVIDER key: %ls, name: %ls", i, pPlan->rgPlannedProviders[i].sczKey, pPlan->rgPlannedProviders[i].sczName);
3164 + }
3165 +
3166 + LogStringLine(REPORT_STANDARD, "--- End plan dump ---");
3167 +}
3168 +
3169 +#endif
src/engine/plan.h new
+543
@@ -0,0 +1,543 @@
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 +#if defined(__cplusplus)
6 +extern "C" {
7 +#endif
8 +
9 +
10 +// constants
11 +
12 +const DWORD BURN_PLAN_INVALID_ACTION_INDEX = 0x80000000;
13 +
14 +enum BURN_REGISTRATION_ACTION_OPERATIONS
15 +{
16 + BURN_REGISTRATION_ACTION_OPERATIONS_NONE = 0x0,
17 + BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE = 0x1,
18 + BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION = 0x2,
19 + BURN_REGISTRATION_ACTION_OPERATIONS_UPDATE_SIZE = 0x4,
20 +};
21 +
22 +enum BURN_DEPENDENCY_REGISTRATION_ACTION
23 +{
24 + BURN_DEPENDENCY_REGISTRATION_ACTION_NONE,
25 + BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER,
26 + BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER,
27 +};
28 +
29 +enum BURN_DEPENDENT_REGISTRATION_ACTION_TYPE
30 +{
31 + BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_NONE,
32 + BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER,
33 + BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER,
34 +};
35 +
36 +enum BURN_CACHE_ACTION_TYPE
37 +{
38 + BURN_CACHE_ACTION_TYPE_NONE,
39 + BURN_CACHE_ACTION_TYPE_CHECKPOINT,
40 + BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE,
41 + BURN_CACHE_ACTION_TYPE_PACKAGE_START,
42 + BURN_CACHE_ACTION_TYPE_PACKAGE_STOP,
43 + BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE,
44 + BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT,
45 + BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER,
46 + BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER,
47 + BURN_CACHE_ACTION_TYPE_LAYOUT_CONTAINER,
48 + BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD,
49 + BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD,
50 + BURN_CACHE_ACTION_TYPE_LAYOUT_PAYLOAD,
51 + BURN_CACHE_ACTION_TYPE_TRANSACTION_BOUNDARY,
52 +};
53 +
54 +enum BURN_EXECUTE_ACTION_TYPE
55 +{
56 + BURN_EXECUTE_ACTION_TYPE_NONE,
57 + BURN_EXECUTE_ACTION_TYPE_CHECKPOINT,
58 + BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT,
59 + BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE,
60 + BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE,
61 + BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE,
62 + BURN_EXECUTE_ACTION_TYPE_MSP_TARGET,
63 + BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE,
64 + BURN_EXECUTE_ACTION_TYPE_SERVICE_STOP,
65 + BURN_EXECUTE_ACTION_TYPE_SERVICE_START,
66 + BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER,
67 + BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY,
68 + BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY,
69 + BURN_EXECUTE_ACTION_TYPE_REGISTRATION,
70 + BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE,
71 +};
72 +
73 +enum BURN_CLEAN_ACTION_TYPE
74 +{
75 + BURN_CLEAN_ACTION_TYPE_NONE,
76 + BURN_CLEAN_ACTION_TYPE_BUNDLE,
77 + BURN_CLEAN_ACTION_TYPE_PACKAGE,
78 +};
79 +
80 +
81 +// structs
82 +
83 +typedef struct _BURN_EXTRACT_PAYLOAD
84 +{
85 + BURN_PACKAGE* pPackage;
86 + BURN_PAYLOAD* pPayload;
87 + LPWSTR sczUnverifiedPath;
88 +} BURN_EXTRACT_PAYLOAD;
89 +
90 +typedef struct _BURN_DEPENDENT_REGISTRATION_ACTION
91 +{
92 + BURN_DEPENDENT_REGISTRATION_ACTION_TYPE type;
93 + LPWSTR sczBundleId;
94 + LPWSTR sczDependentProviderKey;
95 +} BURN_DEPENDENT_REGISTRATION_ACTION;
96 +
97 +typedef struct _BURN_CACHE_CONTAINER_PROGRESS
98 +{
99 + LPWSTR wzId;
100 + DWORD iIndex;
101 + BOOL fCachedDuringApply;
102 + BURN_CONTAINER* pContainer;
103 +} BURN_CACHE_CONTAINER_PROGRESS;
104 +
105 +typedef struct _BURN_CACHE_PAYLOAD_PROGRESS
106 +{
107 + LPWSTR wzId;
108 + DWORD iIndex;
109 + BOOL fCachedDuringApply;
110 + BURN_PAYLOAD* pPayload;
111 +} BURN_CACHE_PAYLOAD_PROGRESS;
112 +
113 +typedef struct _BURN_CACHE_ACTION
114 +{
115 + BURN_CACHE_ACTION_TYPE type;
116 + BOOL fSkipUntilRetried;
117 + union
118 + {
119 + struct
120 + {
121 + DWORD dwId;
122 + } checkpoint;
123 + struct
124 + {
125 + LPWSTR sczExecutableName;
126 + LPWSTR sczLayoutDirectory;
127 + LPWSTR sczUnverifiedPath;
128 + DWORD64 qwBundleSize;
129 + } bundleLayout;
130 + struct
131 + {
132 + BURN_PACKAGE* pPackage;
133 + DWORD cCachePayloads;
134 + DWORD64 qwCachePayloadSizeTotal;
135 + DWORD iPackageCompleteAction;
136 + } packageStart;
137 + struct
138 + {
139 + BURN_PACKAGE* pPackage;
140 + } packageStop;
141 + struct
142 + {
143 + BURN_PACKAGE* pPackage;
144 + } rollbackPackage;
145 + struct
146 + {
147 + HANDLE hEvent;
148 + } syncpoint;
149 + struct
150 + {
151 + BURN_CONTAINER* pContainer;
152 + DWORD iProgress;
153 + LPWSTR sczUnverifiedPath;
154 + } resolveContainer;
155 + struct
156 + {
157 + BURN_CONTAINER* pContainer;
158 + DWORD iSkipUntilAcquiredByAction;
159 + LPWSTR sczContainerUnverifiedPath;
160 +
161 + BURN_EXTRACT_PAYLOAD* rgPayloads;
162 + DWORD cPayloads;
163 + } extractContainer;
164 + struct
165 + {
166 + BURN_PACKAGE* pPackage;
167 + BURN_CONTAINER* pContainer;
168 + DWORD iProgress;
169 + DWORD iTryAgainAction;
170 + DWORD cTryAgainAttempts;
171 + LPWSTR sczLayoutDirectory;
172 + LPWSTR sczUnverifiedPath;
173 + BOOL fMove;
174 + } layoutContainer;
175 + struct
176 + {
177 + BURN_PACKAGE* pPackage;
178 + BURN_PAYLOAD* pPayload;
179 + DWORD iProgress;
180 + LPWSTR sczUnverifiedPath;
181 + } resolvePayload;
182 + struct
183 + {
184 + BURN_PACKAGE* pPackage;
185 + BURN_PAYLOAD* pPayload;
186 + DWORD iProgress;
187 + DWORD iTryAgainAction;
188 + DWORD cTryAgainAttempts;
189 + LPWSTR sczUnverifiedPath;
190 + BOOL fMove;
191 + } cachePayload;
192 + struct
193 + {
194 + BURN_PACKAGE* pPackage;
195 + BURN_PAYLOAD* pPayload;
196 + DWORD iProgress;
197 + DWORD iTryAgainAction;
198 + DWORD cTryAgainAttempts;
199 + LPWSTR sczLayoutDirectory;
200 + LPWSTR sczUnverifiedPath;
201 + BOOL fMove;
202 + } layoutPayload;
203 + struct
204 + {
205 + BURN_ROLLBACK_BOUNDARY* pRollbackBoundary;
206 + HANDLE hEvent;
207 + } rollbackBoundary;
208 + };
209 +} BURN_CACHE_ACTION;
210 +
211 +typedef struct _BURN_ORDERED_PATCHES
212 +{
213 + DWORD dwOrder;
214 + BURN_PACKAGE* pPackage;
215 +} BURN_ORDERED_PATCHES;
216 +
217 +typedef struct _BURN_EXECUTE_ACTION
218 +{
219 + BURN_EXECUTE_ACTION_TYPE type;
220 + BOOL fDeleted; // used to skip an action after it was planned since deleting actions out of the plan is too hard.
221 + union
222 + {
223 + struct
224 + {
225 + DWORD dwId;
226 + } checkpoint;
227 + struct
228 + {
229 + HANDLE hEvent;
230 + } syncpoint;
231 + struct
232 + {
233 + BURN_PACKAGE* pPackage;
234 + } uncachePackage;
235 + struct
236 + {
237 + BURN_PACKAGE* pPackage;
238 + BOOL fFireAndForget;
239 + BOOTSTRAPPER_ACTION_STATE action;
240 + LPWSTR sczIgnoreDependencies;
241 + LPWSTR sczAncestors;
242 + } exePackage;
243 + struct
244 + {
245 + BURN_PACKAGE* pPackage;
246 + LPWSTR sczLogPath;
247 + DWORD dwLoggingAttributes;
248 + INSTALLUILEVEL uiLevel;
249 + BOOTSTRAPPER_ACTION_STATE action;
250 +
251 + BOOTSTRAPPER_FEATURE_ACTION* rgFeatures;
252 + BOOTSTRAPPER_ACTION_STATE* rgSlipstreamPatches;
253 +
254 + BURN_ORDERED_PATCHES* rgOrderedPatches;
255 + DWORD cPatches;
256 + } msiPackage;
257 + struct
258 + {
259 + BURN_PACKAGE* pPackage;
260 + LPWSTR sczTargetProductCode;
261 + BURN_PACKAGE* pChainedTargetPackage;
262 + BOOL fSlipstream;
263 + BOOL fPerMachineTarget;
264 + LPWSTR sczLogPath;
265 + INSTALLUILEVEL uiLevel;
266 + BOOTSTRAPPER_ACTION_STATE action;
267 +
268 + BURN_ORDERED_PATCHES* rgOrderedPatches;
269 + DWORD cOrderedPatches;
270 + } mspTarget;
271 + struct
272 + {
273 + BURN_PACKAGE* pPackage;
274 + LPWSTR sczLogPath;
275 + BOOTSTRAPPER_ACTION_STATE action;
276 + } msuPackage;
277 + struct
278 + {
279 + LPWSTR sczServiceName;
280 + } service;
281 + struct
282 + {
283 + BOOL fKeep;
284 + } registration;
285 + struct
286 + {
287 + BURN_ROLLBACK_BOUNDARY* pRollbackBoundary;
288 + } rollbackBoundary;
289 + struct
290 + {
291 + BURN_PACKAGE* pPackage;
292 + BURN_DEPENDENCY_ACTION action;
293 + } packageProvider;
294 + struct
295 + {
296 + BURN_PACKAGE* pPackage;
297 + LPWSTR sczBundleProviderKey;
298 + BURN_DEPENDENCY_ACTION action;
299 + } packageDependency;
300 + struct
301 + {
302 + BURN_PACKAGE* pReferencePackage;
303 + LPWSTR sczInstalledProductCode;
304 + DWORD64 qwInstalledVersion;
305 + } compatiblePackage;
306 + };
307 +} BURN_EXECUTE_ACTION;
308 +
309 +typedef struct _BURN_CLEAN_ACTION
310 +{
311 + BURN_PACKAGE* pPackage;
312 +} BURN_CLEAN_ACTION;
313 +
314 +typedef struct _BURN_PLAN
315 +{
316 + BOOTSTRAPPER_ACTION action;
317 + LPWSTR wzBundleId; // points directly into parent the ENGINE_STATE.
318 + LPWSTR wzBundleProviderKey; // points directly into parent the ENGINE_STATE.
319 + BOOL fPerMachine;
320 + BOOL fRegister;
321 + DWORD dwRegistrationOperations;
322 + BOOL fKeepRegistrationDefault;
323 + BOOL fDisallowRemoval;
324 +
325 + DWORD64 qwCacheSizeTotal;
326 +
327 + DWORD64 qwEstimatedSize;
328 +
329 + DWORD cExecutePackagesTotal;
330 + DWORD cOverallProgressTicksTotal;
331 +
332 + BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction;
333 +
334 + BURN_DEPENDENT_REGISTRATION_ACTION* rgRegistrationActions;
335 + DWORD cRegistrationActions;
336 +
337 + BURN_DEPENDENT_REGISTRATION_ACTION* rgRollbackRegistrationActions;
338 + DWORD cRollbackRegistrationActions;
339 +
340 + BURN_CACHE_ACTION* rgCacheActions;
341 + DWORD cCacheActions;
342 +
343 + BURN_CACHE_ACTION* rgRollbackCacheActions;
344 + DWORD cRollbackCacheActions;
345 +
346 + BURN_EXECUTE_ACTION* rgExecuteActions;
347 + DWORD cExecuteActions;
348 +
349 + BURN_EXECUTE_ACTION* rgRollbackActions;
350 + DWORD cRollbackActions;
351 +
352 + BURN_CLEAN_ACTION* rgCleanActions;
353 + DWORD cCleanActions;
354 +
355 + DEPENDENCY* rgPlannedProviders;
356 + UINT cPlannedProviders;
357 +
358 + BURN_CACHE_CONTAINER_PROGRESS* rgContainerProgress;
359 + DWORD cContainerProgress;
360 + STRINGDICT_HANDLE shContainerProgress;
361 +
362 + BURN_CACHE_PAYLOAD_PROGRESS* rgPayloadProgress;
363 + DWORD cPayloadProgress;
364 + STRINGDICT_HANDLE shPayloadProgress;
365 +} BURN_PLAN;
366 +
367 +
368 +// functions
369 +
370 +void PlanReset(
371 + __in BURN_PLAN* pPlan,
372 + __in BURN_PACKAGES* pPackages
373 + );
374 +void PlanUninitializeExecuteAction(
375 + __in BURN_EXECUTE_ACTION* pExecuteAction
376 + );
377 +HRESULT PlanSetVariables(
378 + __in BOOTSTRAPPER_ACTION action,
379 + __in BURN_VARIABLES* pVariables
380 + );
381 +HRESULT PlanDefaultPackageRequestState(
382 + __in BURN_PACKAGE_TYPE packageType,
383 + __in BOOTSTRAPPER_PACKAGE_STATE currentState,
384 + __in BOOL fPermanent,
385 + __in BOOTSTRAPPER_ACTION action,
386 + __in BURN_VARIABLES* pVariables,
387 + __in_z_opt LPCWSTR wzInstallCondition,
388 + __in BOOTSTRAPPER_RELATION_TYPE relationType,
389 + __out BOOTSTRAPPER_REQUEST_STATE* pRequestState
390 + );
391 +HRESULT PlanLayoutBundle(
392 + __in BURN_PLAN* pPlan,
393 + __in_z LPCWSTR wzExecutableName,
394 + __in DWORD64 qwBundleSize,
395 + __in BURN_VARIABLES* pVariables,
396 + __in BURN_PAYLOADS* pPayloads,
397 + __out_z LPWSTR* psczLayoutDirectory
398 + );
399 +HRESULT PlanPackages(
400 + __in BURN_REGISTRATION* pRegistration,
401 + __in BURN_USER_EXPERIENCE* pUX,
402 + __in BURN_PACKAGES* pPackages,
403 + __in BURN_PLAN* pPlan,
404 + __in BURN_LOGGING* pLog,
405 + __in BURN_VARIABLES* pVariables,
406 + __in BOOL fBundleInstalled,
407 + __in BOOTSTRAPPER_DISPLAY display,
408 + __in BOOTSTRAPPER_RELATION_TYPE relationType,
409 + __in_z_opt LPCWSTR wzLayoutDirectory,
410 + __inout HANDLE* phSyncpointEvent
411 + );
412 +HRESULT PlanRegistration(
413 + __in BURN_PLAN* pPlan,
414 + __in BURN_REGISTRATION* pRegistration,
415 + __in BOOTSTRAPPER_RESUME_TYPE resumeType,
416 + __in BOOTSTRAPPER_RELATION_TYPE relationType,
417 + __in_z_opt LPCWSTR wzIgnoreDependencies,
418 + __out BOOL* pfContinuePlanning
419 + );
420 +HRESULT PlanPassThroughBundle(
421 + __in BURN_USER_EXPERIENCE* pUX,
422 + __in BURN_PACKAGE* pPackage,
423 + __in BURN_PLAN* pPlan,
424 + __in BURN_LOGGING* pLog,
425 + __in BURN_VARIABLES* pVariables,
426 + __in BOOTSTRAPPER_DISPLAY display,
427 + __in BOOTSTRAPPER_RELATION_TYPE relationType,
428 + __inout HANDLE* phSyncpointEvent
429 + );
430 +HRESULT PlanUpdateBundle(
431 + __in BURN_USER_EXPERIENCE* pUX,
432 + __in BURN_PACKAGE* pPackage,
433 + __in BURN_PLAN* pPlan,
434 + __in BURN_LOGGING* pLog,
435 + __in BURN_VARIABLES* pVariables,
436 + __in BOOTSTRAPPER_DISPLAY display,
437 + __in BOOTSTRAPPER_RELATION_TYPE relationType,
438 + __inout HANDLE* phSyncpointEvent
439 + );
440 +HRESULT PlanLayoutPackage(
441 + __in BURN_PLAN* pPlan,
442 + __in BURN_PACKAGE* pPackage,
443 + __in_z_opt LPCWSTR wzLayoutDirectory
444 + );
445 +HRESULT PlanCachePackage(
446 + __in BOOL fPerMachine,
447 + __in BURN_USER_EXPERIENCE* pUserExperience,
448 + __in BURN_PLAN* pPlan,
449 + __in BURN_PACKAGE* pPackage,
450 + __in BURN_VARIABLES* pVariables,
451 + __out HANDLE* phSyncpointEvent
452 + );
453 +HRESULT PlanExecutePackage(
454 + __in BOOL fPerMachine,
455 + __in BOOTSTRAPPER_DISPLAY display,
456 + __in BURN_USER_EXPERIENCE* pUserExperience,
457 + __in BURN_PLAN* pPlan,
458 + __in BURN_PACKAGE* pPackage,
459 + __in BURN_LOGGING* pLog,
460 + __in BURN_VARIABLES* pVariables,
461 + __inout HANDLE* phSyncpointEvent
462 + );
463 +HRESULT PlanRelatedBundlesBegin(
464 + __in BURN_USER_EXPERIENCE* pUserExperience,
465 + __in BURN_REGISTRATION* pRegistration,
466 + __in BOOTSTRAPPER_RELATION_TYPE relationType,
467 + __in BURN_PLAN* pPlan
468 + );
469 +HRESULT PlanRelatedBundlesComplete(
470 + __in BURN_REGISTRATION* pRegistration,
471 + __in BURN_PLAN* pPlan,
472 + __in BURN_LOGGING* pLog,
473 + __in BURN_VARIABLES* pVariables,
474 + __inout HANDLE* phSyncpointEvent,
475 + __in DWORD dwExecuteActionEarlyIndex
476 + );
477 +HRESULT PlanFinalizeActions(
478 + __in BURN_PLAN* pPlan
479 + );
480 +HRESULT PlanCleanPackage(
481 + __in BURN_PLAN* pPlan,
482 + __in BURN_PACKAGE* pPackage
483 + );
484 +HRESULT PlanExecuteCacheSyncAndRollback(
485 + __in BURN_PLAN* pPlan,
486 + __in BURN_PACKAGE* pPackage,
487 + __in HANDLE hCacheEvent,
488 + __in BOOL fPlanPackageCacheRollback
489 + );
490 +HRESULT PlanExecuteCheckpoint(
491 + __in BURN_PLAN* pPlan
492 + );
493 +HRESULT PlanInsertExecuteAction(
494 + __in DWORD dwIndex,
495 + __in BURN_PLAN* pPlan,
496 + __out BURN_EXECUTE_ACTION** ppExecuteAction
497 + );
498 +HRESULT PlanInsertRollbackAction(
499 + __in DWORD dwIndex,
500 + __in BURN_PLAN* pPlan,
501 + __out BURN_EXECUTE_ACTION** ppRollbackAction
502 + );
503 +HRESULT PlanAppendExecuteAction(
504 + __in BURN_PLAN* pPlan,
505 + __out BURN_EXECUTE_ACTION** ppExecuteAction
506 + );
507 +HRESULT PlanAppendRollbackAction(
508 + __in BURN_PLAN* pPlan,
509 + __out BURN_EXECUTE_ACTION** ppExecuteAction
510 + );
511 +HRESULT PlanKeepRegistration(
512 + __in BURN_PLAN* pPlan,
513 + __in DWORD iAfterExecutePackageAction,
514 + __in DWORD iBeforeRollbackPackageAction
515 + );
516 +HRESULT PlanRemoveRegistration(
517 + __in BURN_PLAN* pPlan,
518 + __in DWORD iAfterExecutePackageAction,
519 + __in DWORD iAfterRollbackPackageAction
520 + );
521 +HRESULT PlanRollbackBoundaryBegin(
522 + __in BURN_PLAN* pPlan,
523 + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary
524 + );
525 +HRESULT PlanRollbackBoundaryComplete(
526 + __in BURN_PLAN* pPlan
527 + );
528 +HRESULT PlanSetResumeCommand(
529 + __in BURN_REGISTRATION* pRegistration,
530 + __in BOOTSTRAPPER_ACTION action,
531 + __in BOOTSTRAPPER_COMMAND* pCommand,
532 + __in BURN_LOGGING* pLog
533 + );
534 +
535 +#ifdef DEBUG
536 +void PlanDump(
537 + __in BURN_PLAN* pPlan
538 + );
539 +#endif
540 +
541 +#if defined(__cplusplus)
542 +}
543 +#endif
src/engine/platform.cpp new
+16
@@ -0,0 +1,16 @@
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 +// variables
7 +
8 +PFN_INITIATESYSTEMSHUTDOWNEXW vpfnInitiateSystemShutdownExW;
9 +
10 +
11 +// function definitions
12 +
13 +extern "C" void PlatformInitialize()
14 +{
15 + vpfnInitiateSystemShutdownExW = ::InitiateSystemShutdownExW;
16 +}
src/engine/platform.h new
+34
@@ -0,0 +1,34 @@
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 +#if defined(__cplusplus)
6 +extern "C" {
7 +#endif
8 +
9 +
10 +// typedefs
11 +
12 +typedef BOOL (WINAPI *PFN_INITIATESYSTEMSHUTDOWNEXW)(
13 + __in_opt LPWSTR lpMachineName,
14 + __in_opt LPWSTR lpMessage,
15 + __in DWORD dwTimeout,
16 + __in BOOL bForceAppsClosed,
17 + __in BOOL bRebootAfterShutdown,
18 + __in DWORD dwReason
19 + );
20 +
21 +
22 +// variable declarations
23 +
24 +extern PFN_INITIATESYSTEMSHUTDOWNEXW vpfnInitiateSystemShutdownExW;
25 +
26 +
27 +// function declarations
28 +
29 +void PlatformInitialize();
30 +
31 +
32 +#if defined(__cplusplus)
33 +}
34 +#endif
src/engine/precomp.h new
+100
@@ -0,0 +1,100 @@
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 +#define ExitTrace LogErrorString
6 +
7 +#include <wixver.h>
8 +
9 +#include <windows.h>
10 +#include <aclapi.h>
11 +#include <Bits.h>
12 +#include <gdiplus.h>
13 +#include <math.h>
14 +#include <msiquery.h>
15 +#include <sddl.h>
16 +#include <shlobj.h>
17 +#include <shlwapi.h>
18 +#include <softpub.h>
19 +#include <strsafe.h>
20 +#include <intsafe.h>
21 +#include <mscat.h>
22 +#include <lmcons.h>
23 +#include <wininet.h>
24 +#include <stddef.h>
25 +
26 +#include <dutil.h>
27 +#include <aclutil.h>
28 +#include <apputil.h>
29 +#include <buffutil.h>
30 +#include <cabutil.h>
31 +#include <certutil.h>
32 +#include <cryputil.h>
33 +#include <dirutil.h>
34 +#include <fileutil.h>
35 +#include <gdiputil.h>
36 +#include <guidutil.h>
37 +#include <logutil.h>
38 +#include <memutil.h>
39 +#include <osutil.h>
40 +#include <pathutil.h>
41 +#include <polcutil.h>
42 +#include <procutil.h>
43 +#include <regutil.h>
44 +#include <resrutil.h>
45 +#include <shelutil.h>
46 +#include <srputil.h>
47 +#include <strutil.h>
48 +#include <svcutil.h>
49 +#include <userutil.h>
50 +#include <wiutil.h>
51 +#include <wuautil.h>
52 +#include <xmlutil.h>
53 +#include <dictutil.h>
54 +#include <deputil.h>
55 +#include <dlutil.h>
56 +#include <atomutil.h>
57 +#include <apuputil.h>
58 +
59 +#include "BootstrapperEngine.h"
60 +#include "BootstrapperApplication.h"
61 +
62 +#include "platform.h"
63 +#include "variant.h"
64 +#include "variable.h"
65 +#include "condition.h"
66 +#include "search.h"
67 +#include "section.h"
68 +#include "approvedexe.h"
69 +#include "container.h"
70 +#include "catalog.h"
71 +#include "payload.h"
72 +#include "cabextract.h"
73 +#include "userexperience.h"
74 +#include "package.h"
75 +#include "update.h"
76 +#include "pseudobundle.h"
77 +#include "registration.h"
78 +#include "relatedbundle.h"
79 +#include "detect.h"
80 +#include "plan.h"
81 +#include "logging.h"
82 +#include "pipe.h"
83 +#include "core.h"
84 +#include "cache.h"
85 +#include "apply.h"
86 +#include "exeengine.h"
87 +#include "msiengine.h"
88 +#include "mspengine.h"
89 +#include "msuengine.h"
90 +#include "dependency.h"
91 +#include "elevation.h"
92 +#include "embedded.h"
93 +#include "manifest.h"
94 +#include "splashscreen.h"
95 +#include "uithread.h"
96 +#include "bitsengine.h"
97 +#include "netfxchainer.h"
98 +
99 +#include "EngineForApplication.h"
100 +#include "engine.messages.h"
src/engine/pseudobundle.cpp new
+271
@@ -0,0 +1,271 @@
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 +extern "C" HRESULT PseudoBundleInitialize(
7 + __in DWORD64 qwEngineVersion,
8 + __in BURN_PACKAGE* pPackage,
9 + __in BOOL fPerMachine,
10 + __in_z LPCWSTR wzId,
11 + __in BOOTSTRAPPER_RELATION_TYPE relationType,
12 + __in BOOTSTRAPPER_PACKAGE_STATE state,
13 + __in_z LPCWSTR wzFilePath,
14 + __in_z LPCWSTR wzLocalSource,
15 + __in_z_opt LPCWSTR wzDownloadSource,
16 + __in DWORD64 qwSize,
17 + __in BOOL fVital,
18 + __in_z_opt LPCWSTR wzInstallArguments,
19 + __in_z_opt LPCWSTR wzRepairArguments,
20 + __in_z_opt LPCWSTR wzUninstallArguments,
21 + __in_opt BURN_DEPENDENCY_PROVIDER* pDependencyProvider,
22 + __in_opt BYTE* pbHash,
23 + __in DWORD cbHash
24 + )
25 +{
26 + HRESULT hr = S_OK;
27 + LPWSTR sczRelationTypeCommandLineSwitch = NULL;
28 +
29 + LPCWSTR wzRelationTypeCommandLine = CoreRelationTypeToCommandLineString(relationType);
30 + if (wzRelationTypeCommandLine)
31 + {
32 + hr = StrAllocFormatted(&sczRelationTypeCommandLineSwitch, L" -%ls", wzRelationTypeCommandLine);
33 + }
34 +
35 + // Initialize the single payload, and fill out all the necessary fields
36 + pPackage->rgPayloads = (BURN_PACKAGE_PAYLOAD *)MemAlloc(sizeof(BURN_PACKAGE_PAYLOAD), TRUE);
37 + ExitOnNull(pPackage->rgPayloads, hr, E_OUTOFMEMORY, "Failed to allocate space for burn package payload inside of related bundle struct");
38 + pPackage->cPayloads = 1;
39 +
40 + pPackage->rgPayloads->pPayload = (BURN_PAYLOAD *)MemAlloc(sizeof(BURN_PAYLOAD), TRUE);
41 + ExitOnNull(pPackage->rgPayloads, hr, E_OUTOFMEMORY, "Failed to allocate space for burn payload inside of related bundle struct");
42 + pPackage->rgPayloads->pPayload->packaging = BURN_PAYLOAD_PACKAGING_EXTERNAL;
43 + pPackage->rgPayloads->pPayload->qwFileSize = qwSize;
44 +
45 + hr = StrAllocString(&pPackage->rgPayloads->pPayload->sczKey, wzId, 0);
46 + ExitOnFailure(hr, "Failed to copy key for pseudo bundle payload.");
47 +
48 + hr = StrAllocString(&pPackage->rgPayloads->pPayload->sczFilePath, wzFilePath, 0);
49 + ExitOnFailure(hr, "Failed to copy filename for pseudo bundle.");
50 +
51 + hr = StrAllocString(&pPackage->rgPayloads->pPayload->sczSourcePath, wzLocalSource, 0);
52 + ExitOnFailure(hr, "Failed to copy local source path for pseudo bundle.");
53 +
54 + if (wzDownloadSource && *wzDownloadSource)
55 + {
56 + hr = StrAllocString(&pPackage->rgPayloads->pPayload->downloadSource.sczUrl, wzDownloadSource, 0);
57 + ExitOnFailure(hr, "Failed to copy download source for pseudo bundle.");
58 + }
59 +
60 + if (pbHash)
61 + {
62 + pPackage->rgPayloads->pPayload->pbHash = static_cast<BYTE*>(MemAlloc(cbHash, FALSE));
63 + ExitOnNull(pPackage->rgPayloads->pPayload->pbHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for pseudo bundle payload hash.");
64 +
65 + pPackage->rgPayloads->pPayload->cbHash = cbHash;
66 + memcpy_s(pPackage->rgPayloads->pPayload->pbHash, pPackage->rgPayloads->pPayload->cbHash, pbHash, cbHash);
67 + }
68 +
69 + pPackage->rgPayloads->fCached = (BOOTSTRAPPER_PACKAGE_STATE_PRESENT == state || BOOTSTRAPPER_PACKAGE_STATE_CACHED == state);
70 +
71 + pPackage->Exe.fPseudoBundle = TRUE;
72 +
73 + pPackage->type = BURN_PACKAGE_TYPE_EXE;
74 + pPackage->fPerMachine = fPerMachine;
75 + pPackage->currentState = state;
76 + pPackage->cache = (BOOTSTRAPPER_PACKAGE_STATE_PRESENT == state || BOOTSTRAPPER_PACKAGE_STATE_CACHED == state) ? BURN_CACHE_STATE_COMPLETE : BURN_CACHE_STATE_NONE;
77 + pPackage->qwInstallSize = qwSize;
78 + pPackage->qwSize = qwSize;
79 + pPackage->fVital = fVital;
80 +
81 + hr = StrAllocString(&pPackage->sczId, wzId, 0);
82 + ExitOnFailure(hr, "Failed to copy key for pseudo bundle.");
83 +
84 + hr = StrAllocString(&pPackage->sczCacheId, wzId, 0);
85 + ExitOnFailure(hr, "Failed to copy cache id for pseudo bundle.");
86 +
87 + // If we are a self updating bundle, we don't have to have Install arguments.
88 + if (wzInstallArguments)
89 + {
90 + hr = StrAllocString(&pPackage->Exe.sczInstallArguments, wzInstallArguments, 0);
91 + ExitOnFailure(hr, "Failed to copy install arguments for related bundle package");
92 + }
93 +
94 + if (sczRelationTypeCommandLineSwitch)
95 + {
96 + hr = StrAllocConcat(&pPackage->Exe.sczInstallArguments, sczRelationTypeCommandLineSwitch, 0);
97 + ExitOnFailure(hr, "Failed to append relation type to install arguments for related bundle package");
98 + }
99 +
100 + if (wzRepairArguments)
101 + {
102 + hr = StrAllocString(&pPackage->Exe.sczRepairArguments, wzRepairArguments, 0);
103 + ExitOnFailure(hr, "Failed to copy repair arguments for related bundle package");
104 +
105 + if (sczRelationTypeCommandLineSwitch)
106 + {
107 + hr = StrAllocConcat(&pPackage->Exe.sczRepairArguments, sczRelationTypeCommandLineSwitch, 0);
108 + ExitOnFailure(hr, "Failed to append relation type to repair arguments for related bundle package");
109 + }
110 +
111 + pPackage->Exe.fRepairable = TRUE;
112 + }
113 +
114 + if (wzUninstallArguments)
115 + {
116 + hr = StrAllocString(&pPackage->Exe.sczUninstallArguments, wzUninstallArguments, 0);
117 + ExitOnFailure(hr, "Failed to copy uninstall arguments for related bundle package");
118 +
119 + if (sczRelationTypeCommandLineSwitch)
120 + {
121 + hr = StrAllocConcat(&pPackage->Exe.sczUninstallArguments, sczRelationTypeCommandLineSwitch, 0);
122 + ExitOnFailure(hr, "Failed to append relation type to uninstall arguments for related bundle package");
123 + }
124 +
125 + pPackage->fUninstallable = TRUE;
126 + }
127 +
128 + // Only support progress from engines that are compatible (aka: version greater than or equal to last protocol breaking change *and* versions that are older or the same as this engine).
129 + pPackage->Exe.protocol = (FILEMAKEVERSION(3, 6, 2221, 0) <= qwEngineVersion && qwEngineVersion <= FILEMAKEVERSION(rmj, rmm, rup, 0)) ? BURN_EXE_PROTOCOL_TYPE_BURN : BURN_EXE_PROTOCOL_TYPE_NONE;
130 +
131 + // All versions of Burn past v3.9 RTM support suppressing ancestors.
132 + pPackage->Exe.fSupportsAncestors = FILEMAKEVERSION(3, 9, 1006, 0) <= qwEngineVersion;
133 +
134 + if (pDependencyProvider)
135 + {
136 + pPackage->rgDependencyProviders = (BURN_DEPENDENCY_PROVIDER*)MemAlloc(sizeof(BURN_DEPENDENCY_PROVIDER), TRUE);
137 + ExitOnNull(pPackage->rgDependencyProviders, hr, E_OUTOFMEMORY, "Failed to allocate memory for dependency providers.");
138 + pPackage->cDependencyProviders = 1;
139 +
140 + pPackage->rgDependencyProviders[0].fImported = pDependencyProvider->fImported;
141 +
142 + hr = StrAllocString(&pPackage->rgDependencyProviders[0].sczKey, pDependencyProvider->sczKey, 0);
143 + ExitOnFailure(hr, "Failed to copy key for pseudo bundle.");
144 +
145 + hr = StrAllocString(&pPackage->rgDependencyProviders[0].sczVersion, pDependencyProvider->sczVersion, 0);
146 + ExitOnFailure(hr, "Failed to copy version for pseudo bundle.");
147 +
148 + hr = StrAllocString(&pPackage->rgDependencyProviders[0].sczDisplayName, pDependencyProvider->sczDisplayName, 0);
149 + ExitOnFailure(hr, "Failed to copy display name for pseudo bundle.");
150 + }
151 +
152 +LExit:
153 + ReleaseStr(sczRelationTypeCommandLineSwitch);
154 +
155 + return hr;
156 +}
157 +
158 +extern "C" HRESULT PseudoBundleInitializePassthrough(
159 + __in BURN_PACKAGE* pPassthroughPackage,
160 + __in BOOTSTRAPPER_COMMAND* pCommand,
161 + __in_z_opt LPCWSTR wzAppendLogPath,
162 + __in_z_opt LPWSTR wzActiveParent,
163 + __in_z_opt LPWSTR wzAncestors,
164 + __in BURN_PACKAGE* pPackage
165 + )
166 +{
167 + Assert(BURN_PACKAGE_TYPE_EXE == pPackage->type);
168 +
169 + HRESULT hr = S_OK;
170 + LPWSTR sczArguments = NULL;
171 +
172 + // Initialize the payloads, and copy the necessary fields.
173 + pPassthroughPackage->rgPayloads = (BURN_PACKAGE_PAYLOAD *)MemAlloc(sizeof(BURN_PACKAGE_PAYLOAD) * pPackage->cPayloads, TRUE);
174 + ExitOnNull(pPassthroughPackage->rgPayloads, hr, E_OUTOFMEMORY, "Failed to allocate space for burn package payload inside of passthrough bundle.");
175 + pPassthroughPackage->cPayloads = pPackage->cPayloads;
176 +
177 + for (DWORD iPayload = 0; iPayload < pPackage->cPayloads; ++iPayload)
178 + {
179 + BURN_PACKAGE_PAYLOAD* pPayload = pPackage->rgPayloads + iPayload;
180 +
181 + pPassthroughPackage->rgPayloads[iPayload].pPayload = (BURN_PAYLOAD *)MemAlloc(sizeof(BURN_PAYLOAD), TRUE);
182 + ExitOnNull(pPassthroughPackage->rgPayloads[iPayload].pPayload, hr, E_OUTOFMEMORY, "Failed to allocate space for burn payload inside of related bundle struct");
183 + pPassthroughPackage->rgPayloads[iPayload].pPayload->packaging = pPayload->pPayload->packaging;
184 + pPassthroughPackage->rgPayloads[iPayload].pPayload->qwFileSize = pPayload->pPayload->qwFileSize;
185 +
186 + hr = StrAllocString(&pPassthroughPackage->rgPayloads[iPayload].pPayload->sczKey, pPayload->pPayload->sczKey, 0);
187 + ExitOnFailure(hr, "Failed to copy key for passthrough pseudo bundle payload.");
188 +
189 + hr = StrAllocString(&pPassthroughPackage->rgPayloads[iPayload].pPayload->sczFilePath, pPayload->pPayload->sczFilePath, 0);
190 + ExitOnFailure(hr, "Failed to copy filename for passthrough pseudo bundle.");
191 +
192 + hr = StrAllocString(&pPassthroughPackage->rgPayloads[iPayload].pPayload->sczSourcePath, pPayload->pPayload->sczSourcePath, 0);
193 + ExitOnFailure(hr, "Failed to copy local source path for passthrough pseudo bundle.");
194 +
195 + if (pPayload->pPayload->downloadSource.sczUrl)
196 + {
197 + hr = StrAllocString(&pPassthroughPackage->rgPayloads[iPayload].pPayload->downloadSource.sczUrl, pPayload->pPayload->downloadSource.sczUrl, 0);
198 + ExitOnFailure(hr, "Failed to copy download source for passthrough pseudo bundle.");
199 + }
200 +
201 + if (pPayload->pPayload->pbHash)
202 + {
203 + pPassthroughPackage->rgPayloads[iPayload].pPayload->pbHash = static_cast<BYTE*>(MemAlloc(pPayload->pPayload->cbHash, FALSE));
204 + ExitOnNull(pPassthroughPackage->rgPayloads[iPayload].pPayload->pbHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for pseudo bundle payload hash.");
205 +
206 + pPassthroughPackage->rgPayloads[iPayload].pPayload->cbHash = pPayload->pPayload->cbHash;
207 + memcpy_s(pPassthroughPackage->rgPayloads[iPayload].pPayload->pbHash, pPassthroughPackage->rgPayloads[iPayload].pPayload->cbHash, pPayload->pPayload->pbHash, pPayload->pPayload->cbHash);
208 + }
209 +
210 + pPassthroughPackage->rgPayloads[iPayload].fCached = pPayload->fCached;
211 + }
212 +
213 + pPassthroughPackage->Exe.fPseudoBundle = TRUE;
214 +
215 + pPassthroughPackage->fPerMachine = FALSE; // passthrough bundles are always launched per-user.
216 + pPassthroughPackage->type = pPackage->type;
217 + pPassthroughPackage->currentState = pPackage->currentState;
218 + pPassthroughPackage->cache = pPackage->cache;
219 + pPassthroughPackage->qwInstallSize = pPackage->qwInstallSize;
220 + pPassthroughPackage->qwSize = pPackage->qwSize;
221 + pPassthroughPackage->fVital = pPackage->fVital;
222 +
223 + hr = StrAllocString(&pPassthroughPackage->sczId, pPackage->sczId, 0);
224 + ExitOnFailure(hr, "Failed to copy key for passthrough pseudo bundle.");
225 +
226 + hr = StrAllocString(&pPassthroughPackage->sczCacheId, pPackage->sczCacheId, 0);
227 + ExitOnFailure(hr, "Failed to copy cache id for passthrough pseudo bundle.");
228 +
229 + pPassthroughPackage->Exe.protocol = pPackage->Exe.protocol;
230 +
231 + // No matter the operation, we're passing the same command-line. That's what makes
232 + // this a passthrough bundle.
233 + hr = CoreRecreateCommandLine(&sczArguments, pCommand->action, pCommand->display, pCommand->restart, pCommand->relationType, TRUE, wzActiveParent, wzAncestors, wzAppendLogPath, pCommand->wzCommandLine);
234 + ExitOnFailure(hr, "Failed to recreate command-line arguments.");
235 +
236 + hr = StrAllocString(&pPassthroughPackage->Exe.sczInstallArguments, sczArguments, 0);
237 + ExitOnFailure(hr, "Failed to copy install arguments for passthrough bundle package");
238 +
239 + hr = StrAllocString(&pPassthroughPackage->Exe.sczRepairArguments, sczArguments, 0);
240 + ExitOnFailure(hr, "Failed to copy related arguments for passthrough bundle package");
241 +
242 + pPassthroughPackage->Exe.fRepairable = TRUE;
243 +
244 + hr = StrAllocString(&pPassthroughPackage->Exe.sczUninstallArguments, sczArguments, 0);
245 + ExitOnFailure(hr, "Failed to copy uninstall arguments for passthrough bundle package");
246 +
247 + pPassthroughPackage->fUninstallable = TRUE;
248 +
249 + // TODO: consider bringing this back in the near future.
250 + //if (pDependencyProvider)
251 + //{
252 + // pPassthroughPackage->rgDependencyProviders = (BURN_DEPENDENCY_PROVIDER*)MemAlloc(sizeof(BURN_DEPENDENCY_PROVIDER), TRUE);
253 + // ExitOnNull(pPassthroughPackage->rgDependencyProviders, hr, E_OUTOFMEMORY, "Failed to allocate memory for dependency providers.");
254 + // pPassthroughPackage->cDependencyProviders = 1;
255 +
256 + // pPassthroughPackage->rgDependencyProviders[0].fImported = pDependencyProvider->fImported;
257 +
258 + // hr = StrAllocString(&pPassthroughPackage->rgDependencyProviders[0].sczKey, pDependencyProvider->sczKey, 0);
259 + // ExitOnFailure(hr, "Failed to copy key for pseudo bundle.");
260 +
261 + // hr = StrAllocString(&pPassthroughPackage->rgDependencyProviders[0].sczVersion, pDependencyProvider->sczVersion, 0);
262 + // ExitOnFailure(hr, "Failed to copy version for pseudo bundle.");
263 +
264 + // hr = StrAllocString(&pPassthroughPackage->rgDependencyProviders[0].sczDisplayName, pDependencyProvider->sczDisplayName, 0);
265 + // ExitOnFailure(hr, "Failed to copy display name for pseudo bundle.");
266 + //}
267 +
268 +LExit:
269 + ReleaseStr(sczArguments);
270 + return hr;
271 +}
src/engine/pseudobundle.h new
+39
@@ -0,0 +1,39 @@
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 +#if defined(__cplusplus)
6 +extern "C" {
7 +#endif
8 +
9 +HRESULT PseudoBundleInitialize(
10 + __in DWORD64 qwEngineVersion,
11 + __in BURN_PACKAGE* pPackage,
12 + __in BOOL fPerMachine,
13 + __in_z LPCWSTR wzId,
14 + __in BOOTSTRAPPER_RELATION_TYPE relationType,
15 + __in BOOTSTRAPPER_PACKAGE_STATE state,
16 + __in_z LPCWSTR wzFilePath,
17 + __in_z LPCWSTR wzLocalSource,
18 + __in_z_opt LPCWSTR wzDownloadSource,
19 + __in DWORD64 qwSize,
20 + __in BOOL fVital,
21 + __in_z_opt LPCWSTR wzInstallArguments,
22 + __in_z_opt LPCWSTR wzRepairArguments,
23 + __in_z_opt LPCWSTR wzUninstallArguments,
24 + __in_opt BURN_DEPENDENCY_PROVIDER* pDependencyProvider,
25 + __in_opt BYTE* pbHash,
26 + __in DWORD cbHash
27 + );
28 +HRESULT PseudoBundleInitializePassthrough(
29 + __in BURN_PACKAGE* pPassthroughPackage,
30 + __in BOOTSTRAPPER_COMMAND* pCommand,
31 + __in_z_opt LPCWSTR wzAppendLogPath,
32 + __in_z_opt LPWSTR wzActiveParent,
33 + __in_z_opt LPWSTR wzAncestors,
34 + __in BURN_PACKAGE* pPackage
35 + );
36 +
37 +#if defined(__cplusplus)
38 +}
39 +#endif
src/engine/registration.cpp new
+1599
@@ -0,0 +1,1599 @@
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 +// constants
7 +
8 +const LPCWSTR REGISTRY_RUN_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run";
9 +const LPCWSTR REGISTRY_RUN_ONCE_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce";
10 +const LPCWSTR REGISTRY_REBOOT_PENDING_FORMAT = L"%ls.RebootRequired";
11 +const LPCWSTR REGISTRY_BUNDLE_INSTALLED = L"Installed";
12 +const LPCWSTR REGISTRY_BUNDLE_DISPLAY_ICON = L"DisplayIcon";
13 +const LPCWSTR REGISTRY_BUNDLE_DISPLAY_VERSION = L"DisplayVersion";
14 +const LPCWSTR REGISTRY_BUNDLE_ESTIMATED_SIZE = L"EstimatedSize";
15 +const LPCWSTR REGISTRY_BUNDLE_PUBLISHER = L"Publisher";
16 +const LPCWSTR REGISTRY_BUNDLE_HELP_LINK = L"HelpLink";
17 +const LPCWSTR REGISTRY_BUNDLE_HELP_TELEPHONE = L"HelpTelephone";
18 +const LPCWSTR REGISTRY_BUNDLE_URL_INFO_ABOUT = L"URLInfoAbout";
19 +const LPCWSTR REGISTRY_BUNDLE_URL_UPDATE_INFO = L"URLUpdateInfo";
20 +const LPCWSTR REGISTRY_BUNDLE_PARENT_DISPLAY_NAME = L"ParentDisplayName";
21 +const LPCWSTR REGISTRY_BUNDLE_PARENT_KEY_NAME = L"ParentKeyName";
22 +const LPCWSTR REGISTRY_BUNDLE_COMMENTS = L"Comments";
23 +const LPCWSTR REGISTRY_BUNDLE_CONTACT = L"Contact";
24 +const LPCWSTR REGISTRY_BUNDLE_NO_MODIFY = L"NoModify";
25 +const LPCWSTR REGISTRY_BUNDLE_MODIFY_PATH = L"ModifyPath";
26 +const LPCWSTR REGISTRY_BUNDLE_NO_ELEVATE_ON_MODIFY = L"NoElevateOnModify";
27 +const LPCWSTR REGISTRY_BUNDLE_NO_REMOVE = L"NoRemove";
28 +const LPCWSTR REGISTRY_BUNDLE_SYSTEM_COMPONENT = L"SystemComponent";
29 +const LPCWSTR REGISTRY_BUNDLE_QUIET_UNINSTALL_STRING = L"QuietUninstallString";
30 +const LPCWSTR REGISTRY_BUNDLE_UNINSTALL_STRING = L"UninstallString";
31 +const LPCWSTR REGISTRY_BUNDLE_RESUME_COMMAND_LINE = L"BundleResumeCommandLine";
32 +const LPCWSTR REGISTRY_BUNDLE_VERSION_MAJOR = L"VersionMajor";
33 +const LPCWSTR REGISTRY_BUNDLE_VERSION_MINOR = L"VersionMinor";
34 +
35 +// internal function declarations
36 +
37 +static HRESULT ParseSoftwareTagsFromXml(
38 + __in IXMLDOMNode* pixnRegistrationNode,
39 + __out BURN_SOFTWARE_TAG** prgSoftwareTags,
40 + __out DWORD* pcSoftwareTags
41 + );
42 +static HRESULT SetPaths(
43 + __in BURN_REGISTRATION* pRegistration
44 + );
45 +static HRESULT GetBundleManufacturer(
46 + __in BURN_REGISTRATION* pRegistration,
47 + __in BURN_VARIABLES* pVariables,
48 + __out LPWSTR* psczBundleManufacturer
49 + );
50 +static HRESULT GetBundleName(
51 + __in BURN_REGISTRATION* pRegistration,
52 + __in BURN_VARIABLES* pVariables,
53 + __out LPWSTR* psczBundleName
54 + );
55 +static HRESULT UpdateResumeMode(
56 + __in BURN_REGISTRATION* pRegistration,
57 + __in HKEY hkRegistration,
58 + __in BURN_RESUME_MODE resumeMode,
59 + __in BOOL fRestartInitiated
60 + );
61 +static HRESULT ParseRelatedCodes(
62 + __in BURN_REGISTRATION* pRegistration,
63 + __in IXMLDOMNode* pixnBundle
64 + );
65 +static HRESULT FormatUpdateRegistrationKey(
66 + __in BURN_REGISTRATION* pRegistration,
67 + __out_z LPWSTR* psczKey
68 + );
69 +static HRESULT WriteSoftwareTags(
70 + __in BOOL fPerMachine,
71 + __in BURN_SOFTWARE_TAGS* pSoftwareTags
72 + );
73 +static HRESULT RemoveSoftwareTags(
74 + __in BOOL fPerMachine,
75 + __in BURN_SOFTWARE_TAGS* pSoftwareTags
76 + );
77 +static HRESULT WriteUpdateRegistration(
78 + __in BURN_REGISTRATION* pRegistration,
79 + __in BURN_VARIABLES* pVariables
80 + );
81 +static HRESULT RemoveUpdateRegistration(
82 + __in BURN_REGISTRATION* pRegistration
83 + );
84 +static HRESULT RegWriteStringVariable(
85 + __in HKEY hkKey,
86 + __in BURN_VARIABLES* pVariables,
87 + __in LPCWSTR wzVariable,
88 + __in LPCWSTR wzName
89 + );
90 +static HRESULT UpdateBundleNameRegistration(
91 + __in BURN_REGISTRATION* pRegistration,
92 + __in BURN_VARIABLES* pVariables,
93 + __in HKEY hkRegistration
94 + );
95 +
96 +// function definitions
97 +
98 +/*******************************************************************
99 + RegistrationParseFromXml - Parses registration information from manifest.
100 +
101 +*******************************************************************/
102 +extern "C" HRESULT RegistrationParseFromXml(
103 + __in BURN_REGISTRATION* pRegistration,
104 + __in IXMLDOMNode* pixnBundle
105 + )
106 +{
107 + HRESULT hr = S_OK;
108 + IXMLDOMNode* pixnRegistrationNode = NULL;
109 + IXMLDOMNode* pixnArpNode = NULL;
110 + IXMLDOMNode* pixnUpdateNode = NULL;
111 + LPWSTR scz = NULL;
112 +
113 + // select registration node
114 + hr = XmlSelectSingleNode(pixnBundle, L"Registration", &pixnRegistrationNode);
115 + if (S_FALSE == hr)
116 + {
117 + hr = E_NOTFOUND;
118 + }
119 + ExitOnFailure(hr, "Failed to select registration node.");
120 +
121 + // @Id
122 + hr = XmlGetAttributeEx(pixnRegistrationNode, L"Id", &pRegistration->sczId);
123 + ExitOnFailure(hr, "Failed to get @Id.");
124 +
125 + // @Tag
126 + hr = XmlGetAttributeEx(pixnRegistrationNode, L"Tag", &pRegistration->sczTag);
127 + ExitOnFailure(hr, "Failed to get @Tag.");
128 +
129 + hr = ParseRelatedCodes(pRegistration, pixnBundle);
130 + ExitOnFailure(hr, "Failed to parse related bundles");
131 +
132 + // @Version
133 + hr = XmlGetAttributeEx(pixnRegistrationNode, L"Version", &scz);
134 + ExitOnFailure(hr, "Failed to get @Version.");
135 +
136 + hr = FileVersionFromStringEx(scz, 0, &pRegistration->qwVersion);
137 + ExitOnFailure(hr, "Failed to parse @Version: %ls", scz);
138 +
139 + // @ProviderKey
140 + hr = XmlGetAttributeEx(pixnRegistrationNode, L"ProviderKey", &pRegistration->sczProviderKey);
141 + ExitOnFailure(hr, "Failed to get @ProviderKey.");
142 +
143 + // @ExecutableName
144 + hr = XmlGetAttributeEx(pixnRegistrationNode, L"ExecutableName", &pRegistration->sczExecutableName);
145 + ExitOnFailure(hr, "Failed to get @ExecutableName.");
146 +
147 + // @PerMachine
148 + hr = XmlGetYesNoAttribute(pixnRegistrationNode, L"PerMachine", &pRegistration->fPerMachine);
149 + ExitOnFailure(hr, "Failed to get @PerMachine.");
150 +
151 + // select ARP node
152 + hr = XmlSelectSingleNode(pixnRegistrationNode, L"Arp", &pixnArpNode);
153 + if (S_FALSE != hr)
154 + {
155 + ExitOnFailure(hr, "Failed to select ARP node.");
156 +
157 + // @Register
158 + hr = XmlGetYesNoAttribute(pixnArpNode, L"Register", &pRegistration->fRegisterArp);
159 + ExitOnFailure(hr, "Failed to get @Register.");
160 +
161 + // @DisplayName
162 + hr = XmlGetAttributeEx(pixnArpNode, L"DisplayName", &pRegistration->sczDisplayName);
163 + if (E_NOTFOUND != hr)
164 + {
165 + ExitOnFailure(hr, "Failed to get @DisplayName.");
166 + }
167 +
168 + // @DisplayVersion
169 + hr = XmlGetAttributeEx(pixnArpNode, L"DisplayVersion", &pRegistration->sczDisplayVersion);
170 + if (E_NOTFOUND != hr)
171 + {
172 + ExitOnFailure(hr, "Failed to get @DisplayVersion.");
173 + }
174 +
175 + // @Publisher
176 + hr = XmlGetAttributeEx(pixnArpNode, L"Publisher", &pRegistration->sczPublisher);
177 + if (E_NOTFOUND != hr)
178 + {
179 + ExitOnFailure(hr, "Failed to get @Publisher.");
180 + }
181 +
182 + // @HelpLink
183 + hr = XmlGetAttributeEx(pixnArpNode, L"HelpLink", &pRegistration->sczHelpLink);
184 + if (E_NOTFOUND != hr)
185 + {
186 + ExitOnFailure(hr, "Failed to get @HelpLink.");
187 + }
188 +
189 + // @HelpTelephone
190 + hr = XmlGetAttributeEx(pixnArpNode, L"HelpTelephone", &pRegistration->sczHelpTelephone);
191 + if (E_NOTFOUND != hr)
192 + {
193 + ExitOnFailure(hr, "Failed to get @HelpTelephone.");
194 + }
195 +
196 + // @AboutUrl
197 + hr = XmlGetAttributeEx(pixnArpNode, L"AboutUrl", &pRegistration->sczAboutUrl);
198 + if (E_NOTFOUND != hr)
199 + {
200 + ExitOnFailure(hr, "Failed to get @AboutUrl.");
201 + }
202 +
203 + // @UpdateUrl
204 + hr = XmlGetAttributeEx(pixnArpNode, L"UpdateUrl", &pRegistration->sczUpdateUrl);
205 + if (E_NOTFOUND != hr)
206 + {
207 + ExitOnFailure(hr, "Failed to get @UpdateUrl.");
208 + }
209 +
210 + // @ParentDisplayName
211 + hr = XmlGetAttributeEx(pixnArpNode, L"ParentDisplayName", &pRegistration->sczParentDisplayName);
212 + if (E_NOTFOUND != hr)
213 + {
214 + ExitOnFailure(hr, "Failed to get @ParentDisplayName.");
215 + }
216 +
217 + // @Comments
218 + hr = XmlGetAttributeEx(pixnArpNode, L"Comments", &pRegistration->sczComments);
219 + if (E_NOTFOUND != hr)
220 + {
221 + ExitOnFailure(hr, "Failed to get @Comments.");
222 + }
223 +
224 + // @Contact
225 + hr = XmlGetAttributeEx(pixnArpNode, L"Contact", &pRegistration->sczContact);
226 + if (E_NOTFOUND != hr)
227 + {
228 + ExitOnFailure(hr, "Failed to get @Contact.");
229 + }
230 +
231 + // @DisableModify
232 + hr = XmlGetAttributeEx(pixnArpNode, L"DisableModify", &scz);
233 + if (SUCCEEDED(hr))
234 + {
235 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"button", -1))
236 + {
237 + pRegistration->modify = BURN_REGISTRATION_MODIFY_DISABLE_BUTTON;
238 + }
239 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"yes", -1))
240 + {
241 + pRegistration->modify = BURN_REGISTRATION_MODIFY_DISABLE;
242 + }
243 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"no", -1))
244 + {
245 + pRegistration->modify = BURN_REGISTRATION_MODIFY_ENABLED;
246 + }
247 + else
248 + {
249 + hr = E_UNEXPECTED;
250 + ExitOnRootFailure(hr, "Invalid modify disabled type: %ls", scz);
251 + }
252 + }
253 + else if (E_NOTFOUND == hr)
254 + {
255 + pRegistration->modify = BURN_REGISTRATION_MODIFY_ENABLED;
256 + hr = S_OK;
257 + }
258 + ExitOnFailure(hr, "Failed to get @DisableModify.");
259 +
260 + // @DisableRemove
261 + hr = XmlGetYesNoAttribute(pixnArpNode, L"DisableRemove", &pRegistration->fNoRemove);
262 + if (E_NOTFOUND != hr)
263 + {
264 + ExitOnFailure(hr, "Failed to get @DisableRemove.");
265 + pRegistration->fNoRemoveDefined = TRUE;
266 + }
267 + }
268 +
269 + hr = ParseSoftwareTagsFromXml(pixnRegistrationNode, &pRegistration->softwareTags.rgSoftwareTags, &pRegistration->softwareTags.cSoftwareTags);
270 + ExitOnFailure(hr, "Failed to parse software tag.");
271 +
272 + // select Update node
273 + hr = XmlSelectSingleNode(pixnRegistrationNode, L"Update", &pixnUpdateNode);
274 + if (S_FALSE != hr)
275 + {
276 + ExitOnFailure(hr, "Failed to select Update node.");
277 +
278 + pRegistration->update.fRegisterUpdate = TRUE;
279 +
280 + // @Manufacturer
281 + hr = XmlGetAttributeEx(pixnUpdateNode, L"Manufacturer", &pRegistration->update.sczManufacturer);
282 + ExitOnFailure(hr, "Failed to get @Manufacturer.");
283 +
284 + // @Department
285 + hr = XmlGetAttributeEx(pixnUpdateNode, L"Department", &pRegistration->update.sczDepartment);
286 + if (E_NOTFOUND != hr)
287 + {
288 + ExitOnFailure(hr, "Failed to get @Department.");
289 + }
290 +
291 + // @ProductFamily
292 + hr = XmlGetAttributeEx(pixnUpdateNode, L"ProductFamily", &pRegistration->update.sczProductFamily);
293 + if (E_NOTFOUND != hr)
294 + {
295 + ExitOnFailure(hr, "Failed to get @ProductFamily.");
296 + }
297 +
298 + // @Name
299 + hr = XmlGetAttributeEx(pixnUpdateNode, L"Name", &pRegistration->update.sczName);
300 + ExitOnFailure(hr, "Failed to get @Name.");
301 +
302 + // @Classification
303 + hr = XmlGetAttributeEx(pixnUpdateNode, L"Classification", &pRegistration->update.sczClassification);
304 + ExitOnFailure(hr, "Failed to get @Classification.");
305 + }
306 +
307 + hr = SetPaths(pRegistration);
308 + ExitOnFailure(hr, "Failed to set registration paths.");
309 +
310 +LExit:
311 + ReleaseObject(pixnRegistrationNode);
312 + ReleaseObject(pixnArpNode);
313 + ReleaseObject(pixnUpdateNode);
314 + ReleaseStr(scz);
315 +
316 + return hr;
317 +}
318 +
319 +/*******************************************************************
320 + RegistrationUninitialize -
321 +
322 +*******************************************************************/
323 +extern "C" void RegistrationUninitialize(
324 + __in BURN_REGISTRATION* pRegistration
325 + )
326 +{
327 + ReleaseStr(pRegistration->sczId);
328 + ReleaseStr(pRegistration->sczTag);
329 +
330 + for (DWORD i = 0; i < pRegistration->cDetectCodes; ++i)
331 + {
332 + ReleaseStr(pRegistration->rgsczDetectCodes[i]);
333 + }
334 + ReleaseMem(pRegistration->rgsczDetectCodes);
335 +
336 + for (DWORD i = 0; i < pRegistration->cUpgradeCodes; ++i)
337 + {
338 + ReleaseStr(pRegistration->rgsczUpgradeCodes[i]);
339 + }
340 + ReleaseMem(pRegistration->rgsczUpgradeCodes);
341 +
342 + for (DWORD i = 0; i < pRegistration->cAddonCodes; ++i)
343 + {
344 + ReleaseStr(pRegistration->rgsczAddonCodes[i]);
345 + }
346 + ReleaseMem(pRegistration->rgsczAddonCodes);
347 +
348 + for (DWORD i = 0; i < pRegistration->cPatchCodes; ++i)
349 + {
350 + ReleaseStr(pRegistration->rgsczPatchCodes[i]);
351 + }
352 + ReleaseMem(pRegistration->rgsczPatchCodes);
353 +
354 + ReleaseStr(pRegistration->sczProviderKey);
355 + ReleaseStr(pRegistration->sczActiveParent);
356 + ReleaseStr(pRegistration->sczExecutableName);
357 +
358 + ReleaseStr(pRegistration->sczRegistrationKey);
359 + ReleaseStr(pRegistration->sczCacheExecutablePath);
360 + ReleaseStr(pRegistration->sczResumeCommandLine);
361 + ReleaseStr(pRegistration->sczStateFile);
362 +
363 + ReleaseStr(pRegistration->sczDisplayName);
364 + ReleaseStr(pRegistration->sczDisplayVersion);
365 + ReleaseStr(pRegistration->sczPublisher);
366 + ReleaseStr(pRegistration->sczHelpLink);
367 + ReleaseStr(pRegistration->sczHelpTelephone);
368 + ReleaseStr(pRegistration->sczAboutUrl);
369 + ReleaseStr(pRegistration->sczUpdateUrl);
370 + ReleaseStr(pRegistration->sczParentDisplayName);
371 + ReleaseStr(pRegistration->sczComments);
372 + ReleaseStr(pRegistration->sczContact);
373 +
374 + ReleaseStr(pRegistration->update.sczManufacturer);
375 + ReleaseStr(pRegistration->update.sczDepartment);
376 + ReleaseStr(pRegistration->update.sczProductFamily);
377 + ReleaseStr(pRegistration->update.sczName);
378 + ReleaseStr(pRegistration->update.sczClassification);
379 +
380 + if (pRegistration->softwareTags.rgSoftwareTags)
381 + {
382 + for (DWORD i = 0; i < pRegistration->softwareTags.cSoftwareTags; ++i)
383 + {
384 + ReleaseStr(pRegistration->softwareTags.rgSoftwareTags[i].sczFilename);
385 + ReleaseStr(pRegistration->softwareTags.rgSoftwareTags[i].sczRegid);
386 + ReleaseStr(pRegistration->softwareTags.rgSoftwareTags[i].sczTag);
387 + }
388 +
389 + MemFree(pRegistration->softwareTags.rgSoftwareTags);
390 + }
391 +
392 + ReleaseStr(pRegistration->sczDetectedProviderKeyBundleId);
393 + ReleaseStr(pRegistration->sczAncestors);
394 + RelatedBundlesUninitialize(&pRegistration->relatedBundles);
395 +
396 + // clear struct
397 + memset(pRegistration, 0, sizeof(BURN_REGISTRATION));
398 +}
399 +
400 +/*******************************************************************
401 + RegistrationSetVariables - Initializes bundle variables that map to
402 + registration entities.
403 +
404 +*******************************************************************/
405 +extern "C" HRESULT RegistrationSetVariables(
406 + __in BURN_REGISTRATION* pRegistration,
407 + __in BURN_VARIABLES* pVariables
408 + )
409 +{
410 + HRESULT hr = S_OK;
411 + LPWSTR sczBundleManufacturer = NULL;
412 + LPWSTR sczBundleName = NULL;
413 +
414 + if (pRegistration->fInstalled)
415 + {
416 + hr = VariableSetNumeric(pVariables, BURN_BUNDLE_INSTALLED, 1, TRUE);
417 + ExitOnFailure(hr, "Failed to set the bundle installed built-in variable.");
418 + }
419 +
420 + // Ensure the registration bundle name is updated.
421 + hr = GetBundleName(pRegistration, pVariables, &sczBundleName);
422 + ExitOnFailure(hr, "Failed to initialize bundle name.");
423 +
424 + hr = GetBundleManufacturer(pRegistration, pVariables, &sczBundleName);
425 + ExitOnFailure(hr, "Failed to initialize bundle manufacturer.");
426 +
427 + if (pRegistration->sczActiveParent && *pRegistration->sczActiveParent)
428 + {
429 + hr = VariableSetString(pVariables, BURN_BUNDLE_ACTIVE_PARENT, pRegistration->sczActiveParent, TRUE);
430 + ExitOnFailure(hr, "Failed to overwrite the bundle active parent built-in variable.");
431 + }
432 +
433 + hr = VariableSetString(pVariables, BURN_BUNDLE_PROVIDER_KEY, pRegistration->sczProviderKey, TRUE);
434 + ExitOnFailure(hr, "Failed to overwrite the bundle provider key built-in variable.");
435 +
436 + hr = VariableSetString(pVariables, BURN_BUNDLE_TAG, pRegistration->sczTag, TRUE);
437 + ExitOnFailure(hr, "Failed to overwrite the bundle tag built-in variable.");
438 +
439 + hr = VariableSetVersion(pVariables, BURN_BUNDLE_VERSION, pRegistration->qwVersion, TRUE);
440 + ExitOnFailure(hr, "Failed to overwrite the bundle tag built-in variable.");
441 +
442 +LExit:
443 + ReleaseStr(sczBundleManufacturer);
444 + ReleaseStr(sczBundleName);
445 +
446 + return hr;
447 +}
448 +
449 +extern "C" HRESULT RegistrationDetectInstalled(
450 + __in BURN_REGISTRATION* pRegistration,
451 + __out BOOL* pfInstalled
452 + )
453 +{
454 + HRESULT hr = S_OK;
455 + HKEY hkRegistration = NULL;
456 + DWORD dwInstalled = 0;
457 +
458 + // open registration key
459 + hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_QUERY_VALUE, &hkRegistration);
460 + if (SUCCEEDED(hr))
461 + {
462 + hr = RegReadNumber(hkRegistration, REGISTRY_BUNDLE_INSTALLED, &dwInstalled);
463 + }
464 +
465 + // Not finding the key or value is okay.
466 + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
467 + {
468 + hr = S_OK;
469 + }
470 +
471 + *pfInstalled = (1 == dwInstalled);
472 +
473 + ReleaseRegKey(hkRegistration);
474 + return hr;
475 +}
476 +
477 +/*******************************************************************
478 + RegistrationDetectResumeMode - Detects registration information on the system
479 + to determine if a resume is taking place.
480 +
481 +*******************************************************************/
482 +extern "C" HRESULT RegistrationDetectResumeType(
483 + __in BURN_REGISTRATION* pRegistration,
484 + __out BOOTSTRAPPER_RESUME_TYPE* pResumeType
485 + )
486 +{
487 + HRESULT hr = S_OK;
488 + LPWSTR sczRebootRequiredKey = NULL;
489 + HKEY hkRebootRequired = NULL;
490 + HKEY hkRegistration = NULL;
491 + DWORD dwResume = 0;
492 +
493 + // Check to see if a restart is pending for this bundle.
494 + hr = StrAllocFormatted(&sczRebootRequiredKey, REGISTRY_REBOOT_PENDING_FORMAT, pRegistration->sczRegistrationKey);
495 + ExitOnFailure(hr, "Failed to format pending restart registry key to read.");
496 +
497 + hr = RegOpen(pRegistration->hkRoot, sczRebootRequiredKey, KEY_QUERY_VALUE, &hkRebootRequired);
498 + if (SUCCEEDED(hr))
499 + {
500 + *pResumeType = BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING;
501 + ExitFunction1(hr = S_OK);
502 + }
503 +
504 + // open registration key
505 + hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_QUERY_VALUE, &hkRegistration);
506 + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
507 + {
508 + *pResumeType = BOOTSTRAPPER_RESUME_TYPE_NONE;
509 + ExitFunction1(hr = S_OK);
510 + }
511 + ExitOnFailure(hr, "Failed to open registration key.");
512 +
513 + // read Resume value
514 + hr = RegReadNumber(hkRegistration, L"Resume", &dwResume);
515 + if (E_FILENOTFOUND == hr)
516 + {
517 + *pResumeType = BOOTSTRAPPER_RESUME_TYPE_INVALID;
518 + ExitFunction1(hr = S_OK);
519 + }
520 + ExitOnFailure(hr, "Failed to read Resume value.");
521 +
522 + switch (dwResume)
523 + {
524 + case BURN_RESUME_MODE_ACTIVE:
525 + // a previous run was interrupted
526 + *pResumeType = BOOTSTRAPPER_RESUME_TYPE_INTERRUPTED;
527 + break;
528 +
529 + case BURN_RESUME_MODE_SUSPEND:
530 + *pResumeType = BOOTSTRAPPER_RESUME_TYPE_SUSPEND;
531 + break;
532 +
533 + case BURN_RESUME_MODE_ARP:
534 + *pResumeType = BOOTSTRAPPER_RESUME_TYPE_ARP;
535 + break;
536 +
537 + case BURN_RESUME_MODE_REBOOT_PENDING:
538 + // The volatile pending registry doesn't exist (checked above) which means
539 + // the system was successfully restarted.
540 + *pResumeType = BOOTSTRAPPER_RESUME_TYPE_REBOOT;
541 + break;
542 +
543 + default:
544 + // the value stored in the registry is not valid
545 + *pResumeType = BOOTSTRAPPER_RESUME_TYPE_INVALID;
546 + break;
547 + }
548 +
549 +LExit:
550 + ReleaseRegKey(hkRegistration);
551 + ReleaseRegKey(hkRebootRequired);
552 + ReleaseStr(sczRebootRequiredKey);
553 +
554 + return hr;
555 +}
556 +
557 +/*******************************************************************
558 + RegistrationDetectRelatedBundles - finds the bundles with same
559 + upgrade/detect/addon/patch codes.
560 +
561 +*******************************************************************/
562 +extern "C" HRESULT RegistrationDetectRelatedBundles(
563 + __in BURN_REGISTRATION* pRegistration
564 + )
565 +{
566 + HRESULT hr = S_OK;
567 +
568 + hr = RelatedBundlesInitializeForScope(TRUE, pRegistration, &pRegistration->relatedBundles);
569 + ExitOnFailure(hr, "Failed to initialize per-machine related bundles.");
570 +
571 + hr = RelatedBundlesInitializeForScope(FALSE, pRegistration, &pRegistration->relatedBundles);
572 + ExitOnFailure(hr, "Failed to initialize per-user related bundles.");
573 +
574 +LExit:
575 + return hr;
576 +}
577 +
578 +/*******************************************************************
579 + RegistrationSessionBegin - Registers a run session on the system.
580 +
581 +*******************************************************************/
582 +extern "C" HRESULT RegistrationSessionBegin(
583 + __in_z LPCWSTR wzEngineWorkingPath,
584 + __in BURN_REGISTRATION* pRegistration,
585 + __in BURN_VARIABLES* pVariables,
586 + __in BURN_USER_EXPERIENCE* pUserExperience,
587 + __in DWORD dwRegistrationOptions,
588 + __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction,
589 + __in DWORD64 qwEstimatedSize
590 + )
591 +{
592 + HRESULT hr = S_OK;
593 + DWORD dwSize = 0;
594 + HKEY hkRegistration = NULL;
595 + LPWSTR sczPublisher = NULL;
596 +
597 + LogId(REPORT_VERBOSE, MSG_SESSION_BEGIN, pRegistration->sczRegistrationKey, dwRegistrationOptions, LoggingBoolToString(pRegistration->fDisableResume));
598 +
599 + // Cache bundle executable.
600 + if (dwRegistrationOptions & BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE)
601 + {
602 + hr = CacheCompleteBundle(pRegistration->fPerMachine, pRegistration->sczExecutableName, pRegistration->sczId, &pUserExperience->payloads, wzEngineWorkingPath
603 +#ifdef DEBUG
604 + , pRegistration->sczCacheExecutablePath
605 +#endif
606 + );
607 + ExitOnFailure(hr, "Failed to cache bundle from path: %ls", wzEngineWorkingPath);
608 + }
609 +
610 + // create registration key
611 + hr = RegCreate(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_WRITE, &hkRegistration);
612 + ExitOnFailure(hr, "Failed to create registration key.");
613 +
614 + // Write any ARP values and software tags.
615 + if (dwRegistrationOptions & BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION)
616 + {
617 + // Upgrade information
618 + hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH, pRegistration->sczCacheExecutablePath);
619 + ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH);
620 +
621 + hr = RegWriteStringArray(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, pRegistration->rgsczUpgradeCodes, pRegistration->cUpgradeCodes);
622 + ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE);
623 +
624 + hr = RegWriteStringArray(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE, pRegistration->rgsczAddonCodes, pRegistration->cAddonCodes);
625 + ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE);
626 +
627 + hr = RegWriteStringArray(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE, pRegistration->rgsczDetectCodes, pRegistration->cDetectCodes);
628 + ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE);
629 +
630 + hr = RegWriteStringArray(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE, pRegistration->rgsczPatchCodes, pRegistration->cPatchCodes);
631 + ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE);
632 +
633 + hr = RegWriteStringFormatted(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION, L"%hu.%hu.%hu.%hu",
634 + static_cast<WORD>(pRegistration->qwVersion >> 48), static_cast<WORD>(pRegistration->qwVersion >> 32),
635 + static_cast<WORD>(pRegistration->qwVersion >> 16), static_cast<WORD>(pRegistration->qwVersion));
636 + ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION);
637 +
638 + hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_VERSION_MAJOR, static_cast<WORD>(pRegistration->qwVersion >> 48));
639 + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_VERSION_MAJOR);
640 +
641 + hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_VERSION_MINOR, static_cast<WORD>(pRegistration->qwVersion >> 32));
642 + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_VERSION_MINOR);
643 +
644 + if (pRegistration->sczProviderKey)
645 + {
646 + hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY, pRegistration->sczProviderKey);
647 + ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY);
648 + }
649 +
650 + if (pRegistration->sczTag)
651 + {
652 + hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_TAG, pRegistration->sczTag);
653 + ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_TAG);
654 + }
655 +
656 + hr = RegWriteStringFormatted(hkRegistration, BURN_REGISTRATION_REGISTRY_ENGINE_VERSION, L"%hs", szVerMajorMinorBuild);
657 + ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_ENGINE_VERSION);
658 +
659 + // DisplayIcon: [path to exe] and ",0" to refer to the first icon in the executable.
660 + hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_DISPLAY_ICON, L"%s,0", pRegistration->sczCacheExecutablePath);
661 + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_DISPLAY_ICON);
662 +
663 + // update display name
664 + hr = UpdateBundleNameRegistration(pRegistration, pVariables, hkRegistration);
665 + ExitOnFailure(hr, "Failed to update name and publisher.");
666 +
667 + // DisplayVersion: provided by UI
668 + if (pRegistration->sczDisplayVersion)
669 + {
670 + hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_DISPLAY_VERSION, pRegistration->sczDisplayVersion);
671 + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_DISPLAY_VERSION);
672 + }
673 +
674 + // Publisher: provided by UI
675 + hr = GetBundleManufacturer(pRegistration, pVariables, &sczPublisher);
676 + hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_PUBLISHER, SUCCEEDED(hr) ? sczPublisher : pRegistration->sczPublisher);
677 + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_PUBLISHER);
678 +
679 + // HelpLink: provided by UI
680 + if (pRegistration->sczHelpLink)
681 + {
682 + hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_HELP_LINK, pRegistration->sczHelpLink);
683 + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_HELP_LINK);
684 + }
685 +
686 + // HelpTelephone: provided by UI
687 + if (pRegistration->sczHelpTelephone)
688 + {
689 + hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_HELP_TELEPHONE, pRegistration->sczHelpTelephone);
690 + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_HELP_TELEPHONE);
691 + }
692 +
693 + // URLInfoAbout, provided by UI
694 + if (pRegistration->sczAboutUrl)
695 + {
696 + hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_URL_INFO_ABOUT, pRegistration->sczAboutUrl);
697 + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_URL_INFO_ABOUT);
698 + }
699 +
700 + // URLUpdateInfo, provided by UI
701 + if (pRegistration->sczUpdateUrl)
702 + {
703 + hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_URL_UPDATE_INFO, pRegistration->sczUpdateUrl);
704 + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_URL_UPDATE_INFO);
705 + }
706 +
707 + // ParentDisplayName
708 + if (pRegistration->sczParentDisplayName)
709 + {
710 + hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_PARENT_DISPLAY_NAME, pRegistration->sczParentDisplayName);
711 + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_PARENT_DISPLAY_NAME);
712 +
713 + // Need to write the ParentKeyName but can be set to anything.
714 + hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_PARENT_KEY_NAME, pRegistration->sczParentDisplayName);
715 + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_PARENT_KEY_NAME);
716 + }
717 +
718 + // Comments, provided by UI
719 + if (pRegistration->sczComments)
720 + {
721 + hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_COMMENTS, pRegistration->sczComments);
722 + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_COMMENTS);
723 + }
724 +
725 + // Contact, provided by UI
726 + if (pRegistration->sczContact)
727 + {
728 + hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_CONTACT, pRegistration->sczContact);
729 + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_CONTACT);
730 + }
731 +
732 + // InstallLocation: provided by UI
733 + // TODO: need to figure out what "InstallLocation" means in a chainer. <smile/>
734 +
735 + // NoModify
736 + if (BURN_REGISTRATION_MODIFY_DISABLE == pRegistration->modify)
737 + {
738 + hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_NO_MODIFY, 1);
739 + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_NO_MODIFY);
740 + }
741 + else if (BURN_REGISTRATION_MODIFY_DISABLE_BUTTON != pRegistration->modify) // if support modify (aka: did not disable anything)
742 + {
743 + // ModifyPath: [path to exe] /modify
744 + hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_MODIFY_PATH, L"\"%ls\" /modify", pRegistration->sczCacheExecutablePath);
745 + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_MODIFY_PATH);
746 +
747 + // NoElevateOnModify: 1
748 + hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_NO_ELEVATE_ON_MODIFY, 1);
749 + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_NO_ELEVATE_ON_MODIFY);
750 + }
751 +
752 + // NoRemove: should this be allowed?
753 + if (pRegistration->fNoRemoveDefined)
754 + {
755 + hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_NO_REMOVE, (DWORD)pRegistration->fNoRemove);
756 + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_NO_REMOVE);
757 + }
758 +
759 + // Conditionally hide the ARP entry.
760 + if (!pRegistration->fRegisterArp)
761 + {
762 + hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_SYSTEM_COMPONENT, 1);
763 + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_SYSTEM_COMPONENT);
764 + }
765 +
766 + // QuietUninstallString: [path to exe] /uninstall /quiet
767 + hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_QUIET_UNINSTALL_STRING, L"\"%ls\" /uninstall /quiet", pRegistration->sczCacheExecutablePath);
768 + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_QUIET_UNINSTALL_STRING);
769 +
770 + // UninstallString, [path to exe]
771 + // If the modify button is to be disabled, we'll add "/modify" to the uninstall string because the button is "Uninstall/Change". Otherwise,
772 + // it's just the "Uninstall" button so we add "/uninstall" to make the program just go away.
773 + LPCWSTR wzUninstallParameters = (BURN_REGISTRATION_MODIFY_DISABLE_BUTTON == pRegistration->modify) ? L"/modify" : L" /uninstall";
774 + hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_UNINSTALL_STRING, L"\"%ls\" %ls", pRegistration->sczCacheExecutablePath, wzUninstallParameters);
775 + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_UNINSTALL_STRING);
776 +
777 + if (pRegistration->softwareTags.cSoftwareTags)
778 + {
779 + hr = WriteSoftwareTags(pRegistration->fPerMachine, &pRegistration->softwareTags);
780 + ExitOnFailure(hr, "Failed to write software tags.");
781 + }
782 +
783 + // Update registration.
784 + if (pRegistration->update.fRegisterUpdate)
785 + {
786 + hr = WriteUpdateRegistration(pRegistration, pVariables);
787 + ExitOnFailure(hr, "Failed to write update registration.");
788 + }
789 + }
790 +
791 + // Update estimated size.
792 + if (dwRegistrationOptions & BURN_REGISTRATION_ACTION_OPERATIONS_UPDATE_SIZE)
793 + {
794 + qwEstimatedSize /= 1024; // Convert bytes to KB
795 + if (0 < qwEstimatedSize)
796 + {
797 + if (DWORD_MAX < qwEstimatedSize)
798 + {
799 + // ARP doesn't support QWORDs here
800 + dwSize = DWORD_MAX;
801 + }
802 + else
803 + {
804 + dwSize = static_cast<DWORD>(qwEstimatedSize);
805 + }
806 +
807 + hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_ESTIMATED_SIZE, dwSize);
808 + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_ESTIMATED_SIZE);
809 + }
810 + }
811 +
812 + // Register the bundle dependency key.
813 + if (BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER == dependencyRegistrationAction)
814 + {
815 + hr = DependencyRegisterBundle(pRegistration);
816 + ExitOnFailure(hr, "Failed to register the bundle dependency key.");
817 + }
818 +
819 + // update resume mode
820 + hr = UpdateResumeMode(pRegistration, hkRegistration, BURN_RESUME_MODE_ACTIVE, FALSE);
821 + ExitOnFailure(hr, "Failed to update resume mode.");
822 +
823 +LExit:
824 + ReleaseStr(sczPublisher);
825 + ReleaseRegKey(hkRegistration);
826 +
827 + return hr;
828 +}
829 +
830 +
831 +/*******************************************************************
832 + RegistrationSessionResume - Resumes a previous run session.
833 +
834 +*******************************************************************/
835 +extern "C" HRESULT RegistrationSessionResume(
836 + __in BURN_REGISTRATION* pRegistration,
837 + __in BURN_VARIABLES* pVariables
838 + )
839 +{
840 + HRESULT hr = S_OK;
841 + HKEY hkRegistration = NULL;
842 +
843 + // open registration key
844 + hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_WRITE, &hkRegistration);
845 + ExitOnFailure(hr, "Failed to open registration key.");
846 +
847 + // update resume mode
848 + hr = UpdateResumeMode(pRegistration, hkRegistration, BURN_RESUME_MODE_ACTIVE, FALSE);
849 + ExitOnFailure(hr, "Failed to update resume mode.");
850 +
851 + // update display name
852 + hr = UpdateBundleNameRegistration(pRegistration, pVariables, hkRegistration);
853 + ExitOnFailure(hr, "Failed to update name and publisher.");
854 +
855 +LExit:
856 + ReleaseRegKey(hkRegistration);
857 +
858 + return hr;
859 +}
860 +
861 +
862 +/*******************************************************************
863 + RegistrationSessionEnd - Unregisters a run session from the system.
864 +
865 + *******************************************************************/
866 +extern "C" HRESULT RegistrationSessionEnd(
867 + __in BURN_REGISTRATION* pRegistration,
868 + __in BURN_RESUME_MODE resumeMode,
869 + __in BOOTSTRAPPER_APPLY_RESTART restart,
870 + __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction
871 + )
872 +{
873 + HRESULT hr = S_OK;
874 + LPWSTR sczRebootRequiredKey = NULL;
875 + HKEY hkRebootRequired = NULL;
876 + HKEY hkRegistration = NULL;
877 +
878 + LogId(REPORT_STANDARD, MSG_SESSION_END, pRegistration->sczRegistrationKey, LoggingResumeModeToString(resumeMode), LoggingRestartToString(restart), LoggingBoolToString(pRegistration->fDisableResume));
879 +
880 + // If a restart is required for any reason, write a volatile registry key to track of
881 + // of that fact until the reboot has taken place.
882 + if (BOOTSTRAPPER_APPLY_RESTART_NONE != restart)
883 + {
884 + // We'll write the volatile registry key right next to the bundle ARP registry key
885 + // because that's easy. This is all best effort since the worst case just means in
886 + // the rare case the user launches the same install again before taking the restart
887 + // the BA won't know a restart was still required.
888 + hr = StrAllocFormatted(&sczRebootRequiredKey, REGISTRY_REBOOT_PENDING_FORMAT, pRegistration->sczRegistrationKey);
889 + if (SUCCEEDED(hr))
890 + {
891 + hr = RegCreateEx(pRegistration->hkRoot, sczRebootRequiredKey, KEY_WRITE, TRUE, NULL, &hkRebootRequired, NULL);
892 + }
893 +
894 + if (FAILED(hr))
895 + {
896 + ExitTrace(hr, "Failed to write volatile reboot required registry key.");
897 + hr = S_OK;
898 + }
899 + }
900 +
901 + // If no resume mode, then remove the bundle registration.
902 + if (BURN_RESUME_MODE_NONE == resumeMode)
903 + {
904 + // If we just registered the bundle dependency but something went wrong and caused us to not
905 + // keep the bundle registration (like rollback) or we are supposed to unregister the bundle
906 + // dependency when unregistering the bundle, do so.
907 + if (BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER == dependencyRegistrationAction ||
908 + BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER == dependencyRegistrationAction)
909 + {
910 + // Remove the bundle dependency key.
911 + DependencyUnregisterBundle(pRegistration);
912 + }
913 +
914 + // Delete update registration key.
915 + if (pRegistration->update.fRegisterUpdate)
916 + {
917 + RemoveUpdateRegistration(pRegistration);
918 + }
919 +
920 + RemoveSoftwareTags(pRegistration->fPerMachine, &pRegistration->softwareTags);
921 +
922 + // Delete registration key.
923 + hr = RegDelete(pRegistration->hkRoot, pRegistration->sczRegistrationKey, REG_KEY_DEFAULT, FALSE);
924 + if (E_FILENOTFOUND != hr)
925 + {
926 + ExitOnFailure(hr, "Failed to delete registration key: %ls", pRegistration->sczRegistrationKey);
927 + }
928 +
929 + CacheRemoveBundle(pRegistration->fPerMachine, pRegistration->sczId);
930 + }
931 + else // the mode needs to be updated so open the registration key.
932 + {
933 + // Open registration key.
934 + hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_WRITE, &hkRegistration);
935 + ExitOnFailure(hr, "Failed to open registration key.");
936 + }
937 +
938 + // Update resume mode.
939 + hr = UpdateResumeMode(pRegistration, hkRegistration, resumeMode, BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart);
940 + ExitOnFailure(hr, "Failed to update resume mode.");
941 +
942 +LExit:
943 + ReleaseRegKey(hkRegistration);
944 + ReleaseRegKey(hkRebootRequired);
945 + ReleaseStr(sczRebootRequiredKey);
946 +
947 + return hr;
948 +}
949 +
950 +/*******************************************************************
951 + RegistrationSaveState - Saves an engine state BLOB for retreval after a resume.
952 +
953 +*******************************************************************/
954 +extern "C" HRESULT RegistrationSaveState(
955 + __in BURN_REGISTRATION* pRegistration,
956 + __in_bcount(cbBuffer) BYTE* pbBuffer,
957 + __in SIZE_T cbBuffer
958 + )
959 +{
960 + HRESULT hr = S_OK;
961 +
962 + // write data to file
963 + hr = FileWrite(pRegistration->sczStateFile, FILE_ATTRIBUTE_NORMAL, pbBuffer, cbBuffer, NULL);
964 + if (E_PATHNOTFOUND == hr)
965 + {
966 + // TODO: should we log that the bundle's cache folder was not present so the state file wasn't created either?
967 + hr = S_OK;
968 + }
969 + ExitOnFailure(hr, "Failed to write state to file: %ls", pRegistration->sczStateFile);
970 +
971 +LExit:
972 + return hr;
973 +}
974 +
975 +/*******************************************************************
976 + RegistrationLoadState - Loads a previously stored engine state BLOB.
977 +
978 +*******************************************************************/
979 +extern "C" HRESULT RegistrationLoadState(
980 + __in BURN_REGISTRATION* pRegistration,
981 + __out_bcount(*pcbBuffer) BYTE** ppbBuffer,
982 + __out DWORD* pcbBuffer
983 + )
984 +{
985 + // read data from file
986 + HRESULT hr = FileRead(ppbBuffer, pcbBuffer, pRegistration->sczStateFile);
987 + return hr;
988 +}
989 +
990 +/*******************************************************************
991 +RegistrationGetResumeCommandLine - Gets the resume command line from the registry
992 +
993 +*******************************************************************/
994 +extern "C" HRESULT RegistrationGetResumeCommandLine(
995 + __in const BURN_REGISTRATION* pRegistration,
996 + __deref_out_z LPWSTR* psczResumeCommandLine
997 + )
998 +{
999 + HRESULT hr = S_OK;
1000 + HKEY hkRegistration = NULL;
1001 +
1002 + // open registration key
1003 + hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_QUERY_VALUE, &hkRegistration);
1004 + if (SUCCEEDED(hr))
1005 + {
1006 + hr = RegReadString(hkRegistration, REGISTRY_BUNDLE_RESUME_COMMAND_LINE, psczResumeCommandLine);
1007 + }
1008 +
1009 + // Not finding the key or value is okay.
1010 + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
1011 + {
1012 + hr = S_OK;
1013 + }
1014 +
1015 + ReleaseRegKey(hkRegistration);
1016 +
1017 + return hr;
1018 +}
1019 +
1020 +
1021 +// internal helper functions
1022 +
1023 +static HRESULT ParseSoftwareTagsFromXml(
1024 + __in IXMLDOMNode* pixnRegistrationNode,
1025 + __out BURN_SOFTWARE_TAG** prgSoftwareTags,
1026 + __out DWORD* pcSoftwareTags
1027 + )
1028 +{
1029 + HRESULT hr = S_OK;
1030 + IXMLDOMNodeList* pixnNodes = NULL;
1031 + IXMLDOMNode* pixnNode = NULL;
1032 + DWORD cNodes = 0;
1033 +
1034 + BURN_SOFTWARE_TAG* pSoftwareTags = NULL;
1035 + BSTR bstrTagXml = NULL;
1036 +
1037 + // select tag nodes
1038 + hr = XmlSelectNodes(pixnRegistrationNode, L"SoftwareTag", &pixnNodes);
1039 + ExitOnFailure(hr, "Failed to select software tag nodes.");
1040 +
1041 + // get tag node count
1042 + hr = pixnNodes->get_length((long*)&cNodes);
1043 + ExitOnFailure(hr, "Failed to get software tag count.");
1044 +
1045 + if (cNodes)
1046 + {
1047 + pSoftwareTags = (BURN_SOFTWARE_TAG*)MemAlloc(sizeof(BURN_SOFTWARE_TAG) * cNodes, TRUE);
1048 + ExitOnNull(pSoftwareTags, hr, E_OUTOFMEMORY, "Failed to allocate memory for software tag structs.");
1049 +
1050 + for (DWORD i = 0; i < cNodes; ++i)
1051 + {
1052 + BURN_SOFTWARE_TAG* pSoftwareTag = &pSoftwareTags[i];
1053 +
1054 + hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
1055 + ExitOnFailure(hr, "Failed to get next node.");
1056 +
1057 + hr = XmlGetAttributeEx(pixnNode, L"Filename", &pSoftwareTag->sczFilename);
1058 + ExitOnFailure(hr, "Failed to get @Filename.");
1059 +
1060 + hr = XmlGetAttributeEx(pixnNode, L"Regid", &pSoftwareTag->sczRegid);
1061 + ExitOnFailure(hr, "Failed to get @Regid.");
1062 +
1063 + hr = XmlGetText(pixnNode, &bstrTagXml);
1064 + ExitOnFailure(hr, "Failed to get SoftwareTag text.");
1065 +
1066 + hr = StrAnsiAllocString(&pSoftwareTag->sczTag, bstrTagXml, 0, CP_UTF8);
1067 + ExitOnFailure(hr, "Failed to convert SoftwareTag text to UTF-8");
1068 +
1069 + // prepare next iteration
1070 + ReleaseNullBSTR(bstrTagXml);
1071 + ReleaseNullObject(pixnNode);
1072 + }
1073 + }
1074 +
1075 + *pcSoftwareTags = cNodes;
1076 + *prgSoftwareTags = pSoftwareTags;
1077 + pSoftwareTags = NULL;
1078 +
1079 + hr = S_OK;
1080 +
1081 +LExit:
1082 + ReleaseBSTR(bstrTagXml);
1083 + ReleaseObject(pixnNode);
1084 + ReleaseObject(pixnNodes);
1085 + ReleaseMem(pSoftwareTags);
1086 +
1087 + return hr;
1088 +}
1089 +
1090 +static HRESULT SetPaths(
1091 + __in BURN_REGISTRATION* pRegistration
1092 + )
1093 +{
1094 + HRESULT hr = S_OK;
1095 + LPWSTR sczCacheDirectory = NULL;
1096 +
1097 + // save registration key root
1098 + pRegistration->hkRoot = pRegistration->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
1099 +
1100 + // build uninstall registry key path
1101 + hr = StrAllocFormatted(&pRegistration->sczRegistrationKey, L"%s\\%s", BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY, pRegistration->sczId);
1102 + ExitOnFailure(hr, "Failed to build uninstall registry key path.");
1103 +
1104 + // build cache directory
1105 + hr = CacheGetCompletedPath(pRegistration->fPerMachine, pRegistration->sczId, &sczCacheDirectory);
1106 + ExitOnFailure(hr, "Failed to build cache directory.");
1107 +
1108 + // build cached executable path
1109 + hr = PathConcat(sczCacheDirectory, pRegistration->sczExecutableName, &pRegistration->sczCacheExecutablePath);
1110 + ExitOnFailure(hr, "Failed to build cached executable path.");
1111 +
1112 + // build state file path
1113 + hr = StrAllocFormatted(&pRegistration->sczStateFile, L"%s\\state.rsm", sczCacheDirectory);
1114 + ExitOnFailure(hr, "Failed to build state file path.");
1115 +
1116 +LExit:
1117 + ReleaseStr(sczCacheDirectory);
1118 + return hr;
1119 +}
1120 +
1121 +static HRESULT GetBundleManufacturer(
1122 + __in BURN_REGISTRATION* pRegistration,
1123 + __in BURN_VARIABLES* pVariables,
1124 + __out LPWSTR* psczBundleManufacturer
1125 + )
1126 +{
1127 + HRESULT hr = S_OK;
1128 +
1129 + hr = VariableGetString(pVariables, BURN_BUNDLE_MANUFACTURER, psczBundleManufacturer);
1130 + if (E_NOTFOUND == hr)
1131 + {
1132 + hr = VariableSetLiteralString(pVariables, BURN_BUNDLE_MANUFACTURER, pRegistration->sczPublisher, FALSE);
1133 + ExitOnFailure(hr, "Failed to set bundle manufacturer.");
1134 +
1135 + hr = StrAllocString(psczBundleManufacturer, pRegistration->sczPublisher, 0);
1136 + }
1137 + ExitOnFailure(hr, "Failed to get bundle manufacturer.");
1138 +
1139 +LExit:
1140 + return hr;
1141 +}
1142 +
1143 +static HRESULT GetBundleName(
1144 + __in BURN_REGISTRATION* pRegistration,
1145 + __in BURN_VARIABLES* pVariables,
1146 + __out LPWSTR* psczBundleName
1147 + )
1148 +{
1149 + HRESULT hr = S_OK;
1150 +
1151 + hr = VariableGetString(pVariables, BURN_BUNDLE_NAME, psczBundleName);
1152 + if (E_NOTFOUND == hr)
1153 + {
1154 + hr = VariableSetLiteralString(pVariables, BURN_BUNDLE_NAME, pRegistration->sczDisplayName, FALSE);
1155 + ExitOnFailure(hr, "Failed to set bundle name.");
1156 +
1157 + hr = StrAllocString(psczBundleName, pRegistration->sczDisplayName, 0);
1158 + }
1159 + ExitOnFailure(hr, "Failed to get bundle name.");
1160 +
1161 +LExit:
1162 + return hr;
1163 +}
1164 +
1165 +static HRESULT UpdateResumeMode(
1166 + __in BURN_REGISTRATION* pRegistration,
1167 + __in HKEY hkRegistration,
1168 + __in BURN_RESUME_MODE resumeMode,
1169 + __in BOOL fRestartInitiated
1170 + )
1171 +{
1172 + HRESULT hr = S_OK;
1173 + DWORD er = ERROR_SUCCESS;
1174 + HKEY hkRebootRequired = NULL;
1175 + HKEY hkRun = NULL;
1176 + LPWSTR sczResumeCommandLine = NULL;
1177 + LPCWSTR sczResumeKey = REGISTRY_RUN_ONCE_KEY;
1178 + OS_VERSION osv = OS_VERSION_UNKNOWN;
1179 + DWORD dwServicePack = 0;
1180 +
1181 + LogId(REPORT_STANDARD, MSG_SESSION_UPDATE, pRegistration->sczRegistrationKey, LoggingResumeModeToString(resumeMode), LoggingBoolToString(fRestartInitiated), LoggingBoolToString(pRegistration->fDisableResume));
1182 +
1183 + // On Windows XP and Server 2003, write the resume information to the Run key
1184 + // instead of RunOnce. That avoids the problem that driver installation might
1185 + // trigger RunOnce commands to be executed before the reboot.
1186 + OsGetVersion(&osv, &dwServicePack);
1187 + if (osv < OS_VERSION_VISTA)
1188 + {
1189 + sczResumeKey = REGISTRY_RUN_KEY;
1190 + }
1191 +
1192 + // write resume information
1193 + if (hkRegistration)
1194 + {
1195 + // write Resume value
1196 + hr = RegWriteNumber(hkRegistration, L"Resume", (DWORD)resumeMode);
1197 + ExitOnFailure(hr, "Failed to write Resume value.");
1198 +
1199 + // Write the Installed value *only* when the mode is ARP. This will tell us
1200 + // that the bundle considers itself "installed" on the machine. Note that we
1201 + // never change the value to "0" after that. The bundle will be considered
1202 + // "uninstalled" when all of the registration is removed.
1203 + if (BURN_RESUME_MODE_ARP == resumeMode)
1204 + {
1205 + // Write Installed value.
1206 + hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_INSTALLED, 1);
1207 + ExitOnFailure(hr, "Failed to write Installed value.");
1208 + }
1209 + }
1210 +
1211 + // If the engine is active write the run key so we resume if there is an unexpected
1212 + // power loss. Also, if a restart was initiated in the middle of the chain then
1213 + // ensure the run key exists (it should since going active would have written it).
1214 + // Do not write the run key when embedded since the containing bundle
1215 + // is expected to detect for and restart the embedded bundle.
1216 + if ((BURN_RESUME_MODE_ACTIVE == resumeMode || fRestartInitiated) && !pRegistration->fDisableResume)
1217 + {
1218 + // append RunOnce switch
1219 + hr = StrAllocFormatted(&sczResumeCommandLine, L"\"%ls\" /%ls", pRegistration->sczCacheExecutablePath, BURN_COMMANDLINE_SWITCH_RUNONCE);
1220 + ExitOnFailure(hr, "Failed to format resume command line for RunOnce.");
1221 +
1222 + // write run key
1223 + hr = RegCreate(pRegistration->hkRoot, sczResumeKey, KEY_WRITE, &hkRun);
1224 + ExitOnFailure(hr, "Failed to create run key.");
1225 +
1226 + hr = RegWriteString(hkRun, pRegistration->sczId, sczResumeCommandLine);
1227 + ExitOnFailure(hr, "Failed to write run key value.");
1228 +
1229 + hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_RESUME_COMMAND_LINE, pRegistration->sczResumeCommandLine);
1230 + ExitOnFailure(hr, "Failed to write resume command line value.");
1231 + }
1232 + else // delete run key value
1233 + {
1234 + hr = RegOpen(pRegistration->hkRoot, sczResumeKey, KEY_WRITE, &hkRun);
1235 + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
1236 + {
1237 + hr = S_OK;
1238 + }
1239 + else
1240 + {
1241 + ExitOnWin32Error(er, hr, "Failed to open run key.");
1242 +
1243 + er = ::RegDeleteValueW(hkRun, pRegistration->sczId);
1244 + if (ERROR_FILE_NOT_FOUND == er)
1245 + {
1246 + er = ERROR_SUCCESS;
1247 + }
1248 + ExitOnWin32Error(er, hr, "Failed to delete run key value.");
1249 + }
1250 +
1251 + if (hkRegistration)
1252 + {
1253 + er = ::RegDeleteValueW(hkRegistration, REGISTRY_BUNDLE_RESUME_COMMAND_LINE);
1254 + if (ERROR_FILE_NOT_FOUND == er)
1255 + {
1256 + er = ERROR_SUCCESS;
1257 + }
1258 + ExitOnWin32Error(er, hr, "Failed to delete resume command line value.");
1259 + }
1260 + }
1261 +
1262 +LExit:
1263 + ReleaseStr(sczResumeCommandLine);
1264 + ReleaseRegKey(hkRebootRequired);
1265 + ReleaseRegKey(hkRun);
1266 +
1267 + return hr;
1268 +}
1269 +
1270 +static HRESULT ParseRelatedCodes(
1271 + __in BURN_REGISTRATION* pRegistration,
1272 + __in IXMLDOMNode* pixnBundle
1273 + )
1274 +{
1275 + HRESULT hr = S_OK;
1276 + IXMLDOMNodeList* pixnNodes = NULL;
1277 + IXMLDOMNode* pixnElement = NULL;
1278 + LPWSTR sczAction = NULL;
1279 + LPWSTR sczId = NULL;
1280 + DWORD cElements = 0;
1281 +
1282 + hr = XmlSelectNodes(pixnBundle, L"RelatedBundle", &pixnNodes);
1283 + ExitOnFailure(hr, "Failed to get RelatedBundle nodes");
1284 +
1285 + hr = pixnNodes->get_length((long*)&cElements);
1286 + ExitOnFailure(hr, "Failed to get RelatedBundle element count.");
1287 +
1288 + for (DWORD i = 0; i < cElements; ++i)
1289 + {
1290 + hr = XmlNextElement(pixnNodes, &pixnElement, NULL);
1291 + ExitOnFailure(hr, "Failed to get next RelatedBundle element.");
1292 +
1293 + hr = XmlGetAttributeEx(pixnElement, L"Action", &sczAction);
1294 + ExitOnFailure(hr, "Failed to get @Action.");
1295 +
1296 + hr = XmlGetAttributeEx(pixnElement, L"Id", &sczId);
1297 + ExitOnFailure(hr, "Failed to get @Id.");
1298 +
1299 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Detect", -1))
1300 + {
1301 + hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes + 1, sizeof(LPWSTR), 5);
1302 + ExitOnFailure(hr, "Failed to resize Detect code array in registration");
1303 +
1304 + pRegistration->rgsczDetectCodes[pRegistration->cDetectCodes] = sczId;
1305 + sczId = NULL;
1306 + ++pRegistration->cDetectCodes;
1307 + }
1308 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Upgrade", -1))
1309 + {
1310 + hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes + 1, sizeof(LPWSTR), 5);
1311 + ExitOnFailure(hr, "Failed to resize Upgrade code array in registration");
1312 +
1313 + pRegistration->rgsczUpgradeCodes[pRegistration->cUpgradeCodes] = sczId;
1314 + sczId = NULL;
1315 + ++pRegistration->cUpgradeCodes;
1316 + }
1317 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Addon", -1))
1318 + {
1319 + hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pRegistration->rgsczAddonCodes), pRegistration->cAddonCodes + 1, sizeof(LPWSTR), 5);
1320 + ExitOnFailure(hr, "Failed to resize Addon code array in registration");
1321 +
1322 + pRegistration->rgsczAddonCodes[pRegistration->cAddonCodes] = sczId;
1323 + sczId = NULL;
1324 + ++pRegistration->cAddonCodes;
1325 + }
1326 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Patch", -1))
1327 + {
1328 + hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pRegistration->rgsczPatchCodes), pRegistration->cPatchCodes + 1, sizeof(LPWSTR), 5);
1329 + ExitOnFailure(hr, "Failed to resize Patch code array in registration");
1330 +
1331 + pRegistration->rgsczPatchCodes[pRegistration->cPatchCodes] = sczId;
1332 + sczId = NULL;
1333 + ++pRegistration->cPatchCodes;
1334 + }
1335 + else
1336 + {
1337 + hr = E_INVALIDARG;
1338 + ExitOnFailure(hr, "Invalid value for @Action: %ls", sczAction);
1339 + }
1340 + }
1341 +
1342 +LExit:
1343 + ReleaseObject(pixnNodes);
1344 + ReleaseObject(pixnElement);
1345 + ReleaseStr(sczAction);
1346 + ReleaseStr(sczId);
1347 +
1348 + return hr;
1349 +}
1350 +
1351 +static HRESULT FormatUpdateRegistrationKey(
1352 + __in BURN_REGISTRATION* pRegistration,
1353 + __out_z LPWSTR* psczKey
1354 + )
1355 +{
1356 + HRESULT hr = S_OK;
1357 + LPWSTR sczKey = NULL;
1358 +
1359 + hr = StrAllocFormatted(&sczKey, L"SOFTWARE\\%ls\\Updates\\", pRegistration->update.sczManufacturer);
1360 + ExitOnFailure(hr, "Failed to format the key path for update registration.");
1361 +
1362 + if (pRegistration->update.sczProductFamily)
1363 + {
1364 + hr = StrAllocFormatted(&sczKey, L"%ls%ls\\", sczKey, pRegistration->update.sczProductFamily);
1365 + ExitOnFailure(hr, "Failed to format the key path for update registration.");
1366 + }
1367 +
1368 + hr = StrAllocConcat(&sczKey, pRegistration->update.sczName, 0);
1369 + ExitOnFailure(hr, "Failed to format the key path for update registration.");
1370 +
1371 + *psczKey = sczKey;
1372 + sczKey = NULL;
1373 +
1374 +LExit:
1375 + ReleaseStr(sczKey);
1376 +
1377 + return hr;
1378 +}
1379 +
1380 +static HRESULT WriteSoftwareTags(
1381 + __in BOOL fPerMachine,
1382 + __in BURN_SOFTWARE_TAGS* pSoftwareTags
1383 + )
1384 +{
1385 + HRESULT hr = S_OK;
1386 + LPWSTR sczRootFolder = NULL;
1387 + LPWSTR sczRegidFolder = NULL;
1388 + LPWSTR sczPath = NULL;
1389 +
1390 + hr = PathGetKnownFolder(fPerMachine ? CSIDL_COMMON_APPDATA : CSIDL_LOCAL_APPDATA, &sczRootFolder);
1391 + ExitOnFailure(hr, "Failed to find local %hs appdata directory.", fPerMachine ? "per-machine" : "per-user");
1392 +
1393 + for (DWORD iTag = 0; iTag < pSoftwareTags->cSoftwareTags; ++iTag)
1394 + {
1395 + BURN_SOFTWARE_TAG* pSoftwareTag = pSoftwareTags->rgSoftwareTags + iTag;
1396 +
1397 + hr = PathConcat(sczRootFolder, pSoftwareTag->sczRegid, &sczRegidFolder);
1398 + ExitOnFailure(hr, "Failed to allocate regid folder path.");
1399 +
1400 + hr = PathConcat(sczRegidFolder, pSoftwareTag->sczFilename, &sczPath);
1401 + ExitOnFailure(hr, "Failed to allocate regid folder path.");
1402 +
1403 + hr = DirEnsureExists(sczRegidFolder, NULL);
1404 + ExitOnFailure(hr, "Failed to create regid folder: %ls", sczRegidFolder);
1405 +
1406 + hr = FileWrite(sczPath, FILE_ATTRIBUTE_NORMAL, reinterpret_cast<LPBYTE>(pSoftwareTag->sczTag), lstrlenA(pSoftwareTag->sczTag), NULL);
1407 + ExitOnFailure(hr, "Failed to write tag xml to file: %ls", sczPath);
1408 + }
1409 +
1410 +LExit:
1411 + ReleaseStr(sczPath);
1412 + ReleaseStr(sczRegidFolder);
1413 + ReleaseStr(sczRootFolder);
1414 +
1415 + return hr;
1416 +}
1417 +
1418 +static HRESULT RemoveSoftwareTags(
1419 + __in BOOL fPerMachine,
1420 + __in BURN_SOFTWARE_TAGS* pSoftwareTags
1421 + )
1422 +{
1423 + HRESULT hr = S_OK;
1424 + LPWSTR sczRootFolder = NULL;
1425 + LPWSTR sczRegidFolder = NULL;
1426 + LPWSTR sczPath = NULL;
1427 +
1428 + hr = PathGetKnownFolder(fPerMachine ? CSIDL_COMMON_APPDATA : CSIDL_LOCAL_APPDATA, &sczRootFolder);
1429 + ExitOnFailure(hr, "Failed to find local %hs appdata directory.", fPerMachine ? "per-machine" : "per-user");
1430 +
1431 + for (DWORD iTag = 0; iTag < pSoftwareTags->cSoftwareTags; ++iTag)
1432 + {
1433 + BURN_SOFTWARE_TAG* pSoftwareTag = pSoftwareTags->rgSoftwareTags + iTag;
1434 +
1435 + hr = PathConcat(sczRootFolder, pSoftwareTag->sczRegid, &sczRegidFolder);
1436 + ExitOnFailure(hr, "Failed to allocate regid folder path.");
1437 +
1438 + hr = PathConcat(sczRegidFolder, pSoftwareTag->sczFilename, &sczPath);
1439 + ExitOnFailure(hr, "Failed to allocate regid folder path.");
1440 +
1441 + // Best effort to delete the software tag file and the regid folder.
1442 + FileEnsureDelete(sczPath);
1443 +
1444 + ::RemoveDirectoryW(sczRegidFolder);
1445 + }
1446 +
1447 +LExit:
1448 + ReleaseStr(sczPath);
1449 + ReleaseStr(sczRegidFolder);
1450 + ReleaseStr(sczRootFolder);
1451 +
1452 + return hr;
1453 +}
1454 +
1455 +static HRESULT WriteUpdateRegistration(
1456 + __in BURN_REGISTRATION* pRegistration,
1457 + __in BURN_VARIABLES* pVariables
1458 + )
1459 +{
1460 + HRESULT hr = S_OK;
1461 + LPWSTR sczKey = NULL;
1462 + HKEY hkKey = NULL;
1463 +
1464 + hr = FormatUpdateRegistrationKey(pRegistration, &sczKey);
1465 + ExitOnFailure(hr, "Failed to get the formatted key path for update registration.");
1466 +
1467 + hr = RegCreate(pRegistration->hkRoot, sczKey, KEY_WRITE, &hkKey);
1468 + ExitOnFailure(hr, "Failed to create the key for update registration.");
1469 +
1470 + hr = RegWriteString(hkKey, L"ThisVersionInstalled", L"Y");
1471 + ExitOnFailure(hr, "Failed to write %ls value.", L"ThisVersionInstalled");
1472 +
1473 + hr = RegWriteString(hkKey, L"PackageName", pRegistration->sczDisplayName);
1474 + ExitOnFailure(hr, "Failed to write %ls value.", L"PackageName");
1475 +
1476 + hr = RegWriteString(hkKey, L"PackageVersion", pRegistration->sczDisplayVersion);
1477 + ExitOnFailure(hr, "Failed to write %ls value.", L"PackageVersion");
1478 +
1479 + hr = RegWriteString(hkKey, L"Publisher", pRegistration->sczPublisher);
1480 + ExitOnFailure(hr, "Failed to write %ls value.", L"Publisher");
1481 +
1482 + if (pRegistration->update.sczDepartment)
1483 + {
1484 + hr = RegWriteString(hkKey, L"PublishingGroup", pRegistration->update.sczDepartment);
1485 + ExitOnFailure(hr, "Failed to write %ls value.", L"PublishingGroup");
1486 + }
1487 +
1488 + hr = RegWriteString(hkKey, L"ReleaseType", pRegistration->update.sczClassification);
1489 + ExitOnFailure(hr, "Failed to write %ls value.", L"ReleaseType");
1490 +
1491 + hr = RegWriteStringVariable(hkKey, pVariables, VARIABLE_LOGONUSER, L"InstalledBy");
1492 + ExitOnFailure(hr, "Failed to write %ls value.", L"InstalledBy");
1493 +
1494 + hr = RegWriteStringVariable(hkKey, pVariables, VARIABLE_DATE, L"InstalledDate");
1495 + ExitOnFailure(hr, "Failed to write %ls value.", L"InstalledDate");
1496 +
1497 + hr = RegWriteStringVariable(hkKey, pVariables, VARIABLE_INSTALLERNAME, L"InstallerName");
1498 + ExitOnFailure(hr, "Failed to write %ls value.", L"InstallerName");
1499 +
1500 + hr = RegWriteStringVariable(hkKey, pVariables, VARIABLE_INSTALLERVERSION, L"InstallerVersion");
1501 + ExitOnFailure(hr, "Failed to write %ls value.", L"InstallerVersion");
1502 +
1503 +LExit:
1504 + ReleaseRegKey(hkKey);
1505 + ReleaseStr(sczKey);
1506 +
1507 + return hr;
1508 +}
1509 +
1510 +static HRESULT RemoveUpdateRegistration(
1511 + __in BURN_REGISTRATION* pRegistration
1512 + )
1513 +{
1514 + HRESULT hr = S_OK;
1515 + LPWSTR sczKey = NULL;
1516 + LPWSTR sczPackageVersion = NULL;
1517 + HKEY hkKey = NULL;
1518 + BOOL fDeleteRegKey = TRUE;
1519 +
1520 + hr = FormatUpdateRegistrationKey(pRegistration, &sczKey);
1521 + ExitOnFailure(hr, "Failed to format key for update registration.");
1522 +
1523 + // Only delete if the uninstalling bundle's PackageVersion is the same as the
1524 + // PackageVersion in the update registration key.
1525 + // This is to support build to build upgrades
1526 + hr = RegOpen(pRegistration->hkRoot, sczKey, KEY_QUERY_VALUE, &hkKey);
1527 + if (SUCCEEDED(hr))
1528 + {
1529 + hr = RegReadString(hkKey, L"PackageVersion", &sczPackageVersion);
1530 + if (SUCCEEDED(hr))
1531 + {
1532 + if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, sczPackageVersion, -1, pRegistration->sczDisplayVersion, -1))
1533 + {
1534 + fDeleteRegKey = FALSE;
1535 + }
1536 + }
1537 + ReleaseRegKey(hkKey);
1538 + }
1539 +
1540 + // Unable to open the key or read the value is okay.
1541 + hr = S_OK;
1542 +
1543 + if (fDeleteRegKey)
1544 + {
1545 + hr = RegDelete(pRegistration->hkRoot, sczKey, REG_KEY_DEFAULT, FALSE);
1546 + if (E_FILENOTFOUND != hr)
1547 + {
1548 + ExitOnFailure(hr, "Failed to remove update registration key: %ls", sczKey);
1549 + }
1550 + }
1551 +
1552 +LExit:
1553 + ReleaseStr(sczPackageVersion);
1554 + ReleaseStr(sczKey);
1555 +
1556 + return hr;
1557 +}
1558 +
1559 +static HRESULT RegWriteStringVariable(
1560 + __in HKEY hk,
1561 + __in BURN_VARIABLES* pVariables,
1562 + __in LPCWSTR wzVariable,
1563 + __in LPCWSTR wzName
1564 + )
1565 +{
1566 + HRESULT hr = S_OK;
1567 + LPWSTR sczValue = NULL;
1568 +
1569 + hr = VariableGetString(pVariables, wzVariable, &sczValue);
1570 + ExitOnFailure(hr, "Failed to get the %ls variable.", wzVariable);
1571 +
1572 + hr = RegWriteString(hk, wzName, sczValue);
1573 + ExitOnFailure(hr, "Failed to write %ls value.", wzName);
1574 +
1575 +LExit:
1576 + StrSecureZeroFreeString(sczValue);
1577 +
1578 + return hr;
1579 +}
1580 +
1581 +static HRESULT UpdateBundleNameRegistration(
1582 + __in BURN_REGISTRATION* pRegistration,
1583 + __in BURN_VARIABLES* pVariables,
1584 + __in HKEY hkRegistration
1585 + )
1586 +{
1587 + HRESULT hr = S_OK;
1588 + LPWSTR sczDisplayName = NULL;
1589 +
1590 + // DisplayName: provided by UI
1591 + hr = GetBundleName(pRegistration, pVariables, &sczDisplayName);
1592 + hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME, SUCCEEDED(hr) ? sczDisplayName : pRegistration->sczDisplayName);
1593 + ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME);
1594 +
1595 +LExit:
1596 + ReleaseStr(sczDisplayName);
1597 +
1598 + return hr;
1599 +}
src/engine/registration.h new
+214
@@ -0,0 +1,214 @@
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 +#if defined(__cplusplus)
6 +extern "C" {
7 +#endif
8 +
9 +
10 +enum BURN_MODE;
11 +enum BURN_DEPENDENCY_REGISTRATION_ACTION;
12 +struct _BURN_LOGGING;
13 +typedef _BURN_LOGGING BURN_LOGGING;
14 +
15 +// constants
16 +
17 +const LPCWSTR BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
18 +const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH = L"BundleCachePath";
19 +const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE = L"BundleAddonCode";
20 +const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE = L"BundleDetectCode";
21 +const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE = L"BundlePatchCode";
22 +const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE = L"BundleUpgradeCode";
23 +const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME = L"DisplayName";
24 +const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION = L"BundleVersion";
25 +const LPCWSTR BURN_REGISTRATION_REGISTRY_ENGINE_VERSION = L"EngineVersion";
26 +const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY = L"BundleProviderKey";
27 +const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_TAG = L"BundleTag";
28 +
29 +enum BURN_RESUME_MODE
30 +{
31 + BURN_RESUME_MODE_NONE,
32 + BURN_RESUME_MODE_ACTIVE,
33 + BURN_RESUME_MODE_SUSPEND,
34 + BURN_RESUME_MODE_ARP,
35 + BURN_RESUME_MODE_REBOOT_PENDING,
36 +};
37 +
38 +enum BURN_REGISTRATION_MODIFY_TYPE
39 +{
40 + BURN_REGISTRATION_MODIFY_ENABLED,
41 + BURN_REGISTRATION_MODIFY_DISABLE,
42 + BURN_REGISTRATION_MODIFY_DISABLE_BUTTON,
43 +};
44 +
45 +
46 +// structs
47 +
48 +typedef struct _BURN_UPDATE_REGISTRATION
49 +{
50 + BOOL fRegisterUpdate;
51 + LPWSTR sczManufacturer;
52 + LPWSTR sczDepartment;
53 + LPWSTR sczProductFamily;
54 + LPWSTR sczName;
55 + LPWSTR sczClassification;
56 +} BURN_UPDATE_REGISTRATION;
57 +
58 +typedef struct _BURN_RELATED_BUNDLE
59 +{
60 + BOOTSTRAPPER_RELATION_TYPE relationType;
61 +
62 + DWORD64 qwVersion;
63 + LPWSTR sczTag;
64 +
65 + BURN_PACKAGE package;
66 +} BURN_RELATED_BUNDLE;
67 +
68 +typedef struct _BURN_RELATED_BUNDLES
69 +{
70 + BURN_RELATED_BUNDLE* rgRelatedBundles;
71 + DWORD cRelatedBundles;
72 +} BURN_RELATED_BUNDLES;
73 +
74 +typedef struct _BURN_SOFTWARE_TAG
75 +{
76 + LPWSTR sczFilename;
77 + LPWSTR sczRegid;
78 + LPSTR sczTag;
79 +} BURN_SOFTWARE_TAG;
80 +
81 +typedef struct _BURN_SOFTWARE_TAGS
82 +{
83 + BURN_SOFTWARE_TAG* rgSoftwareTags;
84 + DWORD cSoftwareTags;
85 +} BURN_SOFTWARE_TAGS;
86 +
87 +typedef struct _BURN_REGISTRATION
88 +{
89 + BOOL fPerMachine;
90 + BOOL fRegisterArp;
91 + BOOL fDisableResume;
92 + BOOL fInstalled;
93 + LPWSTR sczId;
94 + LPWSTR sczTag;
95 +
96 + LPWSTR *rgsczDetectCodes;
97 + DWORD cDetectCodes;
98 +
99 + LPWSTR *rgsczUpgradeCodes;
100 + DWORD cUpgradeCodes;
101 +
102 + LPWSTR *rgsczAddonCodes;
103 + DWORD cAddonCodes;
104 +
105 + LPWSTR *rgsczPatchCodes;
106 + DWORD cPatchCodes;
107 +
108 + DWORD64 qwVersion;
109 + LPWSTR sczActiveParent;
110 + LPWSTR sczProviderKey;
111 + LPWSTR sczExecutableName;
112 +
113 + // paths
114 + HKEY hkRoot;
115 + LPWSTR sczRegistrationKey;
116 + LPWSTR sczCacheExecutablePath;
117 + LPWSTR sczResumeCommandLine;
118 + LPWSTR sczStateFile;
119 +
120 + // ARP registration
121 + LPWSTR sczDisplayName;
122 + LPWSTR sczDisplayVersion;
123 + LPWSTR sczPublisher;
124 + LPWSTR sczHelpLink;
125 + LPWSTR sczHelpTelephone;
126 + LPWSTR sczAboutUrl;
127 + LPWSTR sczUpdateUrl;
128 + LPWSTR sczParentDisplayName;
129 + LPWSTR sczComments;
130 + //LPWSTR sczReadme; // TODO: this would be a file path
131 + LPWSTR sczContact;
132 + //DWORD64 qwEstimatedSize; // TODO: size should come from disk cost calculation
133 + BURN_REGISTRATION_MODIFY_TYPE modify;
134 + BOOL fNoRemoveDefined;
135 + BOOL fNoRemove;
136 +
137 + BURN_SOFTWARE_TAGS softwareTags;
138 +
139 + // Update registration
140 + BURN_UPDATE_REGISTRATION update;
141 +
142 + // Only valid after detect.
143 + BURN_RELATED_BUNDLES relatedBundles;
144 +
145 + LPWSTR sczDetectedProviderKeyBundleId;
146 + LPWSTR sczAncestors;
147 +
148 + BOOL fEnabledForwardCompatibleBundle;
149 + BURN_PACKAGE forwardCompatibleBundle;
150 +} BURN_REGISTRATION;
151 +
152 +
153 +// functions
154 +
155 +HRESULT RegistrationParseFromXml(
156 + __in BURN_REGISTRATION* pRegistration,
157 + __in IXMLDOMNode* pixnBundle
158 + );
159 +void RegistrationUninitialize(
160 + __in BURN_REGISTRATION* pRegistration
161 + );
162 +HRESULT RegistrationSetVariables(
163 + __in BURN_REGISTRATION* pRegistration,
164 + __in BURN_VARIABLES* pVariables
165 + );
166 +HRESULT RegistrationDetectInstalled(
167 + __in BURN_REGISTRATION* pRegistration,
168 + __out BOOL* pfInstalled
169 + );
170 +HRESULT RegistrationDetectResumeType(
171 + __in BURN_REGISTRATION* pRegistration,
172 + __out BOOTSTRAPPER_RESUME_TYPE* pResumeType
173 + );
174 +HRESULT RegistrationDetectRelatedBundles(
175 + __in BURN_REGISTRATION* pRegistration
176 + );
177 +HRESULT RegistrationSessionBegin(
178 + __in_z LPCWSTR wzEngineWorkingPath,
179 + __in BURN_REGISTRATION* pRegistration,
180 + __in BURN_VARIABLES* pVariables,
181 + __in BURN_USER_EXPERIENCE* pUserExperience,
182 + __in DWORD dwRegistrationOptions,
183 + __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction,
184 + __in DWORD64 qwEstimatedSize
185 + );
186 +HRESULT RegistrationSessionResume(
187 + __in BURN_REGISTRATION* pRegistration,
188 + __in BURN_VARIABLES* pVariables
189 + );
190 +HRESULT RegistrationSessionEnd(
191 + __in BURN_REGISTRATION* pRegistration,
192 + __in BURN_RESUME_MODE resumeMode,
193 + __in BOOTSTRAPPER_APPLY_RESTART restart,
194 + __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction
195 + );
196 +HRESULT RegistrationSaveState(
197 + __in BURN_REGISTRATION* pRegistration,
198 + __in_bcount_opt(cbBuffer) BYTE* pbBuffer,
199 + __in_opt DWORD cbBuffer
200 + );
201 +HRESULT RegistrationLoadState(
202 + __in BURN_REGISTRATION* pRegistration,
203 + __out_bcount(*pcbBuffer) BYTE** ppbBuffer,
204 + __out DWORD* pcbBuffer
205 + );
206 +HRESULT RegistrationGetResumeCommandLine(
207 + __in const BURN_REGISTRATION* pRegistration,
208 + __deref_out_z LPWSTR* psczResumeCommandLine
209 + );
210 +
211 +
212 +#if defined(__cplusplus)
213 +}
214 +#endif
src/engine/relatedbundle.cpp new
+457
@@ -0,0 +1,457 @@
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 +// internal function declarations
6 +
7 +static HRESULT LoadIfRelatedBundle(
8 + __in BOOL fPerMachine,
9 + __in HKEY hkUninstallKey,
10 + __in_z LPCWSTR sczRelatedBundleId,
11 + __in BURN_REGISTRATION* pRegistration,
12 + __in BURN_RELATED_BUNDLES* pRelatedBundles
13 + );
14 +static HRESULT DetermineRelationType(
15 + __in HKEY hkBundleId,
16 + __in BURN_REGISTRATION* pRegistration,
17 + __out BOOTSTRAPPER_RELATION_TYPE* pRelationType
18 + );
19 +static HRESULT LoadRelatedBundleFromKey(
20 + __in_z LPCWSTR wzRelatedBundleId,
21 + __in HKEY hkBundleId,
22 + __in BOOL fPerMachine,
23 + __in BOOTSTRAPPER_RELATION_TYPE relationType,
24 + __inout BURN_RELATED_BUNDLE *pRelatedBundle
25 + );
26 +
27 +
28 +// function definitions
29 +
30 +extern "C" HRESULT RelatedBundlesInitializeForScope(
31 + __in BOOL fPerMachine,
32 + __in BURN_REGISTRATION* pRegistration,
33 + __in BURN_RELATED_BUNDLES* pRelatedBundles
34 + )
35 +{
36 + HRESULT hr = S_OK;
37 + HKEY hkRoot = fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
38 + HKEY hkUninstallKey = NULL;
39 + LPWSTR sczRelatedBundleId = NULL;
40 +
41 + hr = RegOpen(hkRoot, BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY, KEY_READ, &hkUninstallKey);
42 + if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr)
43 + {
44 + ExitFunction1(hr = S_OK);
45 + }
46 + ExitOnFailure(hr, "Failed to open uninstall registry key.");
47 +
48 + for (DWORD dwIndex = 0; /* exit via break below */; ++dwIndex)
49 + {
50 + hr = RegKeyEnum(hkUninstallKey, dwIndex, &sczRelatedBundleId);
51 + if (E_NOMOREITEMS == hr)
52 + {
53 + hr = S_OK;
54 + break;
55 + }
56 + ExitOnFailure(hr, "Failed to enumerate uninstall key for related bundles.");
57 +
58 + // If we did not find our bundle id, try to load the subkey as a related bundle.
59 + if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczRelatedBundleId, -1, pRegistration->sczId, -1))
60 + {
61 + // Ignore failures here since we'll often find products that aren't actually
62 + // related bundles (or even bundles at all).
63 + HRESULT hrRelatedBundle = LoadIfRelatedBundle(fPerMachine, hkUninstallKey, sczRelatedBundleId, pRegistration, pRelatedBundles);
64 + UNREFERENCED_PARAMETER(hrRelatedBundle);
65 + }
66 + }
67 +
68 +LExit:
69 + ReleaseStr(sczRelatedBundleId);
70 + ReleaseRegKey(hkUninstallKey);
71 +
72 + return hr;
73 +}
74 +
75 +extern "C" void RelatedBundlesUninitialize(
76 + __in BURN_RELATED_BUNDLES* pRelatedBundles
77 + )
78 +{
79 + if (pRelatedBundles->rgRelatedBundles)
80 + {
81 + for (DWORD i = 0; i < pRelatedBundles->cRelatedBundles; ++i)
82 + {
83 + PackageUninitialize(&pRelatedBundles->rgRelatedBundles[i].package);
84 + ReleaseStr(pRelatedBundles->rgRelatedBundles[i].sczTag);
85 + }
86 +
87 + MemFree(pRelatedBundles->rgRelatedBundles);
88 + }
89 +
90 + memset(pRelatedBundles, 0, sizeof(BURN_RELATED_BUNDLES));
91 +}
92 +
93 +
94 +// internal helper functions
95 +
96 +static HRESULT LoadIfRelatedBundle(
97 + __in BOOL fPerMachine,
98 + __in HKEY hkUninstallKey,
99 + __in_z LPCWSTR sczRelatedBundleId,
100 + __in BURN_REGISTRATION* pRegistration,
101 + __in BURN_RELATED_BUNDLES* pRelatedBundles
102 + )
103 +{
104 + HRESULT hr = S_OK;
105 + HKEY hkBundleId = NULL;
106 + BOOTSTRAPPER_RELATION_TYPE relationType = BOOTSTRAPPER_RELATION_NONE;
107 +
108 + hr = RegOpen(hkUninstallKey, sczRelatedBundleId, KEY_READ, &hkBundleId);
109 + ExitOnFailure(hr, "Failed to open uninstall key for potential related bundle: %ls", sczRelatedBundleId);
110 +
111 + hr = DetermineRelationType(hkBundleId, pRegistration, &relationType);
112 + if (FAILED(hr) || BOOTSTRAPPER_RELATION_NONE == relationType)
113 + {
114 + // Must not be a related bundle.
115 + hr = E_NOTFOUND;
116 + }
117 + else // load the related bundle.
118 + {
119 + hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pRelatedBundles->rgRelatedBundles), pRelatedBundles->cRelatedBundles + 1, sizeof(BURN_RELATED_BUNDLE), 5);
120 + ExitOnFailure(hr, "Failed to ensure there is space for related bundles.");
121 +
122 + BURN_RELATED_BUNDLE* pRelatedBundle = pRelatedBundles->rgRelatedBundles + pRelatedBundles->cRelatedBundles;
123 +
124 + hr = LoadRelatedBundleFromKey(sczRelatedBundleId, hkBundleId, fPerMachine, relationType, pRelatedBundle);
125 + ExitOnFailure(hr, "Failed to initialize package from related bundle id: %ls", sczRelatedBundleId);
126 +
127 + ++pRelatedBundles->cRelatedBundles;
128 + }
129 +
130 +LExit:
131 + ReleaseRegKey(hkBundleId);
132 +
133 + return hr;
134 +}
135 +
136 +static HRESULT DetermineRelationType(
137 + __in HKEY hkBundleId,
138 + __in BURN_REGISTRATION* pRegistration,
139 + __out BOOTSTRAPPER_RELATION_TYPE* pRelationType
140 + )
141 +{
142 + HRESULT hr = S_OK;
143 + LPWSTR* rgsczUpgradeCodes = NULL;
144 + DWORD cUpgradeCodes = 0;
145 + STRINGDICT_HANDLE sdUpgradeCodes = NULL;
146 + LPWSTR* rgsczAddonCodes = NULL;
147 + DWORD cAddonCodes = 0;
148 + STRINGDICT_HANDLE sdAddonCodes = NULL;
149 + LPWSTR* rgsczDetectCodes = NULL;
150 + DWORD cDetectCodes = 0;
151 + STRINGDICT_HANDLE sdDetectCodes = NULL;
152 + LPWSTR* rgsczPatchCodes = NULL;
153 + DWORD cPatchCodes = 0;
154 + STRINGDICT_HANDLE sdPatchCodes = NULL;
155 +
156 + *pRelationType = BOOTSTRAPPER_RELATION_NONE;
157 +
158 + // All remaining operations should treat all related bundles as non-vital.
159 + hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczUpgradeCodes, &cUpgradeCodes);
160 + if (HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE) == hr)
161 + {
162 + TraceError(hr, "Failed to read upgrade codes as REG_MULTI_SZ. Trying again as REG_SZ in case of older bundles.");
163 +
164 + rgsczUpgradeCodes = reinterpret_cast<LPWSTR*>(MemAlloc(sizeof(LPWSTR), TRUE));
165 + ExitOnNull(rgsczUpgradeCodes, hr, E_OUTOFMEMORY, "Failed to allocate list for a single upgrade code from older bundle.");
166 +
167 + hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczUpgradeCodes[0]);
168 + if (SUCCEEDED(hr))
169 + {
170 + cUpgradeCodes = 1;
171 + }
172 + }
173 +
174 + // Compare upgrade codes.
175 + if (SUCCEEDED(hr))
176 + {
177 + hr = DictCreateStringListFromArray(&sdUpgradeCodes, rgsczUpgradeCodes, cUpgradeCodes, DICT_FLAG_CASEINSENSITIVE);
178 + ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "upgrade codes");
179 +
180 + // Upgrade relationship: when their upgrade codes match our upgrade codes.
181 + hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes);
182 + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
183 + {
184 + hr = S_OK;
185 + }
186 + else
187 + {
188 + ExitOnFailure(hr, "Failed to do array search for upgrade code match.");
189 +
190 + *pRelationType = BOOTSTRAPPER_RELATION_UPGRADE;
191 + ExitFunction();
192 + }
193 +
194 + // Detect relationship: when their upgrade codes match our detect codes.
195 + hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes);
196 + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
197 + {
198 + hr = S_OK;
199 + }
200 + else
201 + {
202 + ExitOnFailure(hr, "Failed to do array search for detect code match.");
203 +
204 + *pRelationType = BOOTSTRAPPER_RELATION_DETECT;
205 + ExitFunction();
206 + }
207 +
208 + // Dependent relationship: when their upgrade codes match our addon codes.
209 + hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pRegistration->rgsczAddonCodes), pRegistration->cAddonCodes);
210 + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
211 + {
212 + hr = S_OK;
213 + }
214 + else
215 + {
216 + ExitOnFailure(hr, "Failed to do array search for addon code match.");
217 +
218 + *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT;
219 + ExitFunction();
220 + }
221 +
222 + // Dependent relationship: when their upgrade codes match our patch codes.
223 + hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pRegistration->rgsczPatchCodes), pRegistration->cPatchCodes);
224 + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
225 + {
226 + hr = S_OK;
227 + }
228 + else
229 + {
230 + ExitOnFailure(hr, "Failed to do array search for addon code match.");
231 +
232 + *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT;
233 + ExitFunction();
234 + }
235 +
236 + ReleaseNullDict(sdUpgradeCodes);
237 + ReleaseNullStrArray(rgsczUpgradeCodes, cUpgradeCodes);
238 + }
239 +
240 + // Compare addon codes.
241 + hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE, &rgsczAddonCodes, &cAddonCodes);
242 + if (SUCCEEDED(hr))
243 + {
244 + hr = DictCreateStringListFromArray(&sdAddonCodes, rgsczAddonCodes, cAddonCodes, DICT_FLAG_CASEINSENSITIVE);
245 + ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "addon codes");
246 +
247 + // Addon relationship: when their addon codes match our detect codes.
248 + hr = DictCompareStringListToArray(sdAddonCodes, const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes);
249 + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
250 + {
251 + hr = S_OK;
252 + }
253 + else
254 + {
255 + ExitOnFailure(hr, "Failed to do array search for addon code match.");
256 +
257 + *pRelationType = BOOTSTRAPPER_RELATION_ADDON;
258 + ExitFunction();
259 + }
260 +
261 + // Addon relationship: when their addon codes match our upgrade codes.
262 + hr = DictCompareStringListToArray(sdAddonCodes, const_cast<LPCWSTR*>(pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes);
263 + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
264 + {
265 + hr = S_OK;
266 + }
267 + else
268 + {
269 + ExitOnFailure(hr, "Failed to do array search for addon code match.");
270 +
271 + *pRelationType = BOOTSTRAPPER_RELATION_ADDON;
272 + ExitFunction();
273 + }
274 +
275 + ReleaseNullDict(sdAddonCodes);
276 + ReleaseNullStrArray(rgsczAddonCodes, cAddonCodes);
277 + }
278 +
279 + // Compare patch codes.
280 + hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE, &rgsczPatchCodes, &cPatchCodes);
281 + if (SUCCEEDED(hr))
282 + {
283 + hr = DictCreateStringListFromArray(&sdPatchCodes, rgsczPatchCodes, cPatchCodes, DICT_FLAG_CASEINSENSITIVE);
284 + ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "patch codes");
285 +
286 + // Patch relationship: when their patch codes match our detect codes.
287 + hr = DictCompareStringListToArray(sdPatchCodes, const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes);
288 + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
289 + {
290 + hr = S_OK;
291 + }
292 + else
293 + {
294 + ExitOnFailure(hr, "Failed to do array search for patch code match.");
295 +
296 + *pRelationType = BOOTSTRAPPER_RELATION_PATCH;
297 + ExitFunction();
298 + }
299 +
300 + // Patch relationship: when their patch codes match our upgrade codes.
301 + hr = DictCompareStringListToArray(sdPatchCodes, const_cast<LPCWSTR*>(pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes);
302 + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
303 + {
304 + hr = S_OK;
305 + }
306 + else
307 + {
308 + ExitOnFailure(hr, "Failed to do array search for patch code match.");
309 +
310 + *pRelationType = BOOTSTRAPPER_RELATION_PATCH;
311 + ExitFunction();
312 + }
313 +
314 + ReleaseNullDict(sdPatchCodes);
315 + ReleaseNullStrArray(rgsczPatchCodes, cPatchCodes);
316 + }
317 +
318 + // Compare detect codes.
319 + hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE, &rgsczDetectCodes, &cDetectCodes);
320 + if (SUCCEEDED(hr))
321 + {
322 + hr = DictCreateStringListFromArray(&sdDetectCodes, rgsczDetectCodes, cDetectCodes, DICT_FLAG_CASEINSENSITIVE);
323 + ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "detect codes");
324 +
325 + // Detect relationship: when their detect codes match our detect codes.
326 + hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes);
327 + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
328 + {
329 + hr = S_OK;
330 + }
331 + else
332 + {
333 + ExitOnFailure(hr, "Failed to do array search for detect code match.");
334 +
335 + *pRelationType = BOOTSTRAPPER_RELATION_DETECT;
336 + ExitFunction();
337 + }
338 +
339 + // Dependent relationship: when their detect codes match our addon codes.
340 + hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pRegistration->rgsczAddonCodes), pRegistration->cAddonCodes);
341 + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
342 + {
343 + hr = S_OK;
344 + }
345 + else
346 + {
347 + ExitOnFailure(hr, "Failed to do array search for addon code match.");
348 +
349 + *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT;
350 + ExitFunction();
351 + }
352 +
353 + // Dependent relationship: when their detect codes match our patch codes.
354 + hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pRegistration->rgsczPatchCodes), pRegistration->cPatchCodes);
355 + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
356 + {
357 + hr = S_OK;
358 + }
359 + else
360 + {
361 + ExitOnFailure(hr, "Failed to do array search for addon code match.");
362 +
363 + *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT;
364 + ExitFunction();
365 + }
366 +
367 + ReleaseNullDict(sdDetectCodes);
368 + ReleaseNullStrArray(rgsczDetectCodes, cDetectCodes);
369 + }
370 +
371 +LExit:
372 + if (SUCCEEDED(hr) && BOOTSTRAPPER_RELATION_NONE == *pRelationType)
373 + {
374 + hr = E_NOTFOUND;
375 + }
376 +
377 + ReleaseDict(sdUpgradeCodes);
378 + ReleaseStrArray(rgsczUpgradeCodes, cUpgradeCodes);
379 + ReleaseDict(sdAddonCodes);
380 + ReleaseStrArray(rgsczAddonCodes, cAddonCodes);
381 + ReleaseDict(sdDetectCodes);
382 + ReleaseStrArray(rgsczDetectCodes, cDetectCodes);
383 + ReleaseDict(sdPatchCodes);
384 + ReleaseStrArray(rgsczPatchCodes, cPatchCodes);
385 +
386 + return hr;
387 +}
388 +
389 +static HRESULT LoadRelatedBundleFromKey(
390 + __in_z LPCWSTR wzRelatedBundleId,
391 + __in HKEY hkBundleId,
392 + __in BOOL fPerMachine,
393 + __in BOOTSTRAPPER_RELATION_TYPE relationType,
394 + __inout BURN_RELATED_BUNDLE *pRelatedBundle
395 + )
396 +{
397 + HRESULT hr = S_OK;
398 + DWORD64 qwEngineVersion = 0;
399 + LPWSTR sczCachePath = NULL;
400 + DWORD64 qwFileSize = 0;
401 + BURN_DEPENDENCY_PROVIDER dependencyProvider = { };
402 +
403 + hr = RegReadVersion(hkBundleId, BURN_REGISTRATION_REGISTRY_ENGINE_VERSION, &qwEngineVersion);
404 + if (FAILED(hr))
405 + {
406 + qwEngineVersion = 0;
407 + hr = S_OK;
408 + }
409 +
410 + hr = RegReadVersion(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION, &pRelatedBundle->qwVersion);
411 + ExitOnFailure(hr, "Failed to read version from registry for bundle: %ls", wzRelatedBundleId);
412 +
413 + hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH, &sczCachePath);
414 + ExitOnFailure(hr, "Failed to read cache path from registry for bundle: %ls", wzRelatedBundleId);
415 +
416 + hr = FileSize(sczCachePath, reinterpret_cast<LONGLONG *>(&qwFileSize));
417 + ExitOnFailure(hr, "Failed to get size of pseudo bundle: %ls", sczCachePath);
418 +
419 + hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY, &dependencyProvider.sczKey);
420 + if (E_FILENOTFOUND != hr)
421 + {
422 + ExitOnFailure(hr, "Failed to read provider key from registry for bundle: %ls", wzRelatedBundleId);
423 +
424 + dependencyProvider.fImported = TRUE;
425 +
426 + hr = FileVersionToStringEx(pRelatedBundle->qwVersion, &dependencyProvider.sczVersion);
427 + ExitOnFailure(hr, "Failed to copy version for bundle: %ls", wzRelatedBundleId);
428 +
429 + hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME, &dependencyProvider.sczDisplayName);
430 + if (E_FILENOTFOUND != hr)
431 + {
432 + ExitOnFailure(hr, "Failed to copy display name for bundle: %ls", wzRelatedBundleId);
433 + }
434 + }
435 +
436 + hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_TAG, &pRelatedBundle->sczTag);
437 + if (E_FILENOTFOUND == hr)
438 + {
439 + hr = S_OK;
440 + }
441 + ExitOnFailure(hr, "Failed to read tag from registry for bundle: %ls", wzRelatedBundleId);
442 +
443 + pRelatedBundle->relationType = relationType;
444 +
445 + hr = PseudoBundleInitialize(qwEngineVersion, &pRelatedBundle->package, fPerMachine, wzRelatedBundleId, pRelatedBundle->relationType,
446 + BOOTSTRAPPER_PACKAGE_STATE_PRESENT, sczCachePath, sczCachePath, NULL, qwFileSize, FALSE,
447 + L"-quiet", L"-repair -quiet", L"-uninstall -quiet",
448 + (dependencyProvider.sczKey && *dependencyProvider.sczKey) ? &dependencyProvider : NULL,
449 + NULL, 0);
450 + ExitOnFailure(hr, "Failed to initialize related bundle to represent bundle: %ls", wzRelatedBundleId);
451 +
452 +LExit:
453 + DependencyUninitialize(&dependencyProvider);
454 + ReleaseStr(sczCachePath);
455 +
456 + return hr;
457 +}
src/engine/relatedbundle.h new
+20
@@ -0,0 +1,20 @@
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 +#if defined(__cplusplus)
6 +extern "C" {
7 +#endif
8 +
9 +HRESULT RelatedBundlesInitializeForScope(
10 + __in BOOL fPerMachine,
11 + __in BURN_REGISTRATION* pRegistration,
12 + __in BURN_RELATED_BUNDLES* pRelatedBundles
13 + );
14 +void RelatedBundlesUninitialize(
15 + __in BURN_RELATED_BUNDLES* pRelatedBundles
16 + );
17 +
18 +#if defined(__cplusplus)
19 +}
20 +#endif
src/engine/search.cpp new
+1195
@@ -0,0 +1,1195 @@
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 +// internal function declarations
7 +
8 +static HRESULT DirectorySearchExists(
9 + __in BURN_SEARCH* pSearch,
10 + __in BURN_VARIABLES* pVariables
11 + );
12 +static HRESULT DirectorySearchPath(
13 + __in BURN_SEARCH* pSearch,
14 + __in BURN_VARIABLES* pVariables
15 + );
16 +static HRESULT FileSearchExists(
17 + __in BURN_SEARCH* pSearch,
18 + __in BURN_VARIABLES* pVariables
19 + );
20 +static HRESULT FileSearchVersion(
21 + __in BURN_SEARCH* pSearch,
22 + __in BURN_VARIABLES* pVariables
23 + );
24 +static HRESULT FileSearchPath(
25 + __in BURN_SEARCH* pSearch,
26 + __in BURN_VARIABLES* pVariables
27 + );
28 +static HRESULT RegistrySearchExists(
29 + __in BURN_SEARCH* pSearch,
30 + __in BURN_VARIABLES* pVariables
31 + );
32 +static HRESULT RegistrySearchValue(
33 + __in BURN_SEARCH* pSearch,
34 + __in BURN_VARIABLES* pVariables
35 + );
36 +static HRESULT MsiComponentSearch(
37 + __in BURN_SEARCH* pSearch,
38 + __in BURN_VARIABLES* pVariables
39 + );
40 +static HRESULT MsiProductSearch(
41 + __in BURN_SEARCH* pSearch,
42 + __in BURN_VARIABLES* pVariables
43 + );
44 +static HRESULT MsiFeatureSearch(
45 + __in BURN_SEARCH* pSearch,
46 + __in BURN_VARIABLES* pVariables
47 + );
48 +
49 +
50 +// function definitions
51 +
52 +extern "C" HRESULT SearchesParseFromXml(
53 + __in BURN_SEARCHES* pSearches,
54 + __in IXMLDOMNode* pixnBundle
55 + )
56 +{
57 + HRESULT hr = S_OK;
58 + IXMLDOMNodeList* pixnNodes = NULL;
59 + IXMLDOMNode* pixnNode = NULL;
60 + DWORD cNodes = 0;
61 + BSTR bstrNodeName = NULL;
62 + LPWSTR scz = NULL;
63 +
64 + // select search nodes
65 + hr = XmlSelectNodes(pixnBundle, L"DirectorySearch|FileSearch|RegistrySearch|MsiComponentSearch|MsiProductSearch|MsiFeatureSearch", &pixnNodes);
66 + ExitOnFailure(hr, "Failed to select search nodes.");
67 +
68 + // get search node count
69 + hr = pixnNodes->get_length((long*)&cNodes);
70 + ExitOnFailure(hr, "Failed to get search node count.");
71 +
72 + if (!cNodes)
73 + {
74 + ExitFunction();
75 + }
76 +
77 + // allocate memory for searches
78 + pSearches->rgSearches = (BURN_SEARCH*)MemAlloc(sizeof(BURN_SEARCH) * cNodes, TRUE);
79 + ExitOnNull(pSearches->rgSearches, hr, E_OUTOFMEMORY, "Failed to allocate memory for search structs.");
80 +
81 + pSearches->cSearches = cNodes;
82 +
83 + // parse search elements
84 + for (DWORD i = 0; i < cNodes; ++i)
85 + {
86 + BURN_SEARCH* pSearch = &pSearches->rgSearches[i];
87 +
88 + hr = XmlNextElement(pixnNodes, &pixnNode, &bstrNodeName);
89 + ExitOnFailure(hr, "Failed to get next node.");
90 +
91 + // @Id
92 + hr = XmlGetAttributeEx(pixnNode, L"Id", &pSearch->sczKey);
93 + ExitOnFailure(hr, "Failed to get @Id.");
94 +
95 + // @Variable
96 + hr = XmlGetAttributeEx(pixnNode, L"Variable", &pSearch->sczVariable);
97 + ExitOnFailure(hr, "Failed to get @Variable.");
98 +
99 + // @Condition
100 + hr = XmlGetAttributeEx(pixnNode, L"Condition", &pSearch->sczCondition);
101 + if (E_NOTFOUND != hr)
102 + {
103 + ExitOnFailure(hr, "Failed to get @Condition.");
104 + }
105 +
106 + // read type specific attributes
107 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"DirectorySearch", -1))
108 + {
109 + pSearch->Type = BURN_SEARCH_TYPE_DIRECTORY;
110 +
111 + // @Path
112 + hr = XmlGetAttributeEx(pixnNode, L"Path", &pSearch->DirectorySearch.sczPath);
113 + ExitOnFailure(hr, "Failed to get @Path.");
114 +
115 + // @Type
116 + hr = XmlGetAttributeEx(pixnNode, L"Type", &scz);
117 + ExitOnFailure(hr, "Failed to get @Type.");
118 +
119 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"exists", -1))
120 + {
121 + pSearch->DirectorySearch.Type = BURN_DIRECTORY_SEARCH_TYPE_EXISTS;
122 + }
123 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"path", -1))
124 + {
125 + pSearch->DirectorySearch.Type = BURN_DIRECTORY_SEARCH_TYPE_PATH;
126 + }
127 + else
128 + {
129 + hr = E_INVALIDARG;
130 + ExitOnFailure(hr, "Invalid value for @Type: %ls", scz);
131 + }
132 + }
133 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"FileSearch", -1))
134 + {
135 + pSearch->Type = BURN_SEARCH_TYPE_FILE;
136 +
137 + // @Path
138 + hr = XmlGetAttributeEx(pixnNode, L"Path", &pSearch->FileSearch.sczPath);
139 + ExitOnFailure(hr, "Failed to get @Path.");
140 +
141 + // @Type
142 + hr = XmlGetAttributeEx(pixnNode, L"Type", &scz);
143 + ExitOnFailure(hr, "Failed to get @Type.");
144 +
145 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"exists", -1))
146 + {
147 + pSearch->FileSearch.Type = BURN_FILE_SEARCH_TYPE_EXISTS;
148 + }
149 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1))
150 + {
151 + pSearch->FileSearch.Type = BURN_FILE_SEARCH_TYPE_VERSION;
152 + }
153 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"path", -1))
154 + {
155 + pSearch->FileSearch.Type = BURN_FILE_SEARCH_TYPE_PATH;
156 + }
157 + else
158 + {
159 + hr = E_INVALIDARG;
160 + ExitOnFailure(hr, "Invalid value for @Type: %ls", scz);
161 + }
162 + }
163 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"RegistrySearch", -1))
164 + {
165 + pSearch->Type = BURN_SEARCH_TYPE_REGISTRY;
166 +
167 + // @Root
168 + hr = XmlGetAttributeEx(pixnNode, L"Root", &scz);
169 + ExitOnFailure(hr, "Failed to get @Root.");
170 +
171 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"HKCR", -1))
172 + {
173 + pSearch->RegistrySearch.hRoot = HKEY_CLASSES_ROOT;
174 + }
175 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"HKCU", -1))
176 + {
177 + pSearch->RegistrySearch.hRoot = HKEY_CURRENT_USER;
178 + }
179 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"HKLM", -1))
180 + {
181 + pSearch->RegistrySearch.hRoot = HKEY_LOCAL_MACHINE;
182 + }
183 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"HKU", -1))
184 + {
185 + pSearch->RegistrySearch.hRoot = HKEY_USERS;
186 + }
187 + else
188 + {
189 + hr = E_INVALIDARG;
190 + ExitOnFailure(hr, "Invalid value for @Root: %ls", scz);
191 + }
192 +
193 + // @Key
194 + hr = XmlGetAttributeEx(pixnNode, L"Key", &pSearch->RegistrySearch.sczKey);
195 + ExitOnFailure(hr, "Failed to get Key attribute.");
196 +
197 + // @Value
198 + hr = XmlGetAttributeEx(pixnNode, L"Value", &pSearch->RegistrySearch.sczValue);
199 + if (E_NOTFOUND != hr)
200 + {
201 + ExitOnFailure(hr, "Failed to get Value attribute.");
202 + }
203 +
204 + // @Type
205 + hr = XmlGetAttributeEx(pixnNode, L"Type", &scz);
206 + ExitOnFailure(hr, "Failed to get @Type.");
207 +
208 + hr = XmlGetYesNoAttribute(pixnNode, L"Win64", &pSearch->RegistrySearch.fWin64);
209 + if (E_NOTFOUND != hr)
210 + {
211 + ExitOnFailure(hr, "Failed to get Win64 attribute.");
212 + }
213 +
214 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"exists", -1))
215 + {
216 + pSearch->RegistrySearch.Type = BURN_REGISTRY_SEARCH_TYPE_EXISTS;
217 + }
218 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"value", -1))
219 + {
220 + pSearch->RegistrySearch.Type = BURN_REGISTRY_SEARCH_TYPE_VALUE;
221 +
222 + // @ExpandEnvironment
223 + hr = XmlGetYesNoAttribute(pixnNode, L"ExpandEnvironment", &pSearch->RegistrySearch.fExpandEnvironment);
224 + if (E_NOTFOUND != hr)
225 + {
226 + ExitOnFailure(hr, "Failed to get @ExpandEnvironment.");
227 + }
228 +
229 + // @VariableType
230 + hr = XmlGetAttributeEx(pixnNode, L"VariableType", &scz);
231 + ExitOnFailure(hr, "Failed to get @VariableType.");
232 +
233 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"numeric", -1))
234 + {
235 + pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_NUMERIC;
236 + }
237 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"string", -1))
238 + {
239 + pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_STRING;
240 + }
241 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1))
242 + {
243 + pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_VERSION;
244 + }
245 + else
246 + {
247 + hr = E_INVALIDARG;
248 + ExitOnFailure(hr, "Invalid value for @VariableType: %ls", scz);
249 + }
250 + }
251 + else
252 + {
253 + hr = E_INVALIDARG;
254 + ExitOnFailure(hr, "Invalid value for @Type: %ls", scz);
255 + }
256 + }
257 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsiComponentSearch", -1))
258 + {
259 + pSearch->Type = BURN_SEARCH_TYPE_MSI_COMPONENT;
260 +
261 + // @ProductCode
262 + hr = XmlGetAttributeEx(pixnNode, L"ProductCode", &pSearch->MsiComponentSearch.sczProductCode);
263 + if (E_NOTFOUND != hr)
264 + {
265 + ExitOnFailure(hr, "Failed to get @ProductCode.");
266 + }
267 +
268 + // @ComponentId
269 + hr = XmlGetAttributeEx(pixnNode, L"ComponentId", &pSearch->MsiComponentSearch.sczComponentId);
270 + ExitOnFailure(hr, "Failed to get @ComponentId.");
271 +
272 + // @Type
273 + hr = XmlGetAttributeEx(pixnNode, L"Type", &scz);
274 + ExitOnFailure(hr, "Failed to get @Type.");
275 +
276 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"keyPath", -1))
277 + {
278 + pSearch->MsiComponentSearch.Type = BURN_MSI_COMPONENT_SEARCH_TYPE_KEYPATH;
279 + }
280 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"state", -1))
281 + {
282 + pSearch->MsiComponentSearch.Type = BURN_MSI_COMPONENT_SEARCH_TYPE_STATE;
283 + }
284 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"directory", -1))
285 + {
286 + pSearch->MsiComponentSearch.Type = BURN_MSI_COMPONENT_SEARCH_TYPE_DIRECTORY;
287 + }
288 + else
289 + {
290 + hr = E_INVALIDARG;
291 + ExitOnFailure(hr, "Invalid value for @Type: %ls", scz);
292 + }
293 + }
294 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsiProductSearch", -1))
295 + {
296 + pSearch->Type = BURN_SEARCH_TYPE_MSI_PRODUCT;
297 + pSearch->MsiProductSearch.GuidType = BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_NONE;
298 +
299 + // @ProductCode (if we don't find a product code then look for an upgrade code)
300 + hr = XmlGetAttributeEx(pixnNode, L"ProductCode", &pSearch->MsiProductSearch.sczGuid);
301 + if (E_NOTFOUND != hr)
302 + {
303 + ExitOnFailure(hr, "Failed to get @ProductCode.");
304 + pSearch->MsiProductSearch.GuidType = BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_PRODUCTCODE;
305 + }
306 + else
307 + {
308 + // @UpgradeCode
309 + hr = XmlGetAttributeEx(pixnNode, L"UpgradeCode", &pSearch->MsiProductSearch.sczGuid);
310 + if (E_NOTFOUND != hr)
311 + {
312 + ExitOnFailure(hr, "Failed to get @UpgradeCode.");
313 + pSearch->MsiProductSearch.GuidType = BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_UPGRADECODE;
314 + }
315 + }
316 +
317 + // make sure we found either a product or upgrade code
318 + if (BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_NONE == pSearch->MsiProductSearch.GuidType)
319 + {
320 + hr = E_NOTFOUND;
321 + ExitOnFailure(hr, "Failed to get @ProductCode or @UpgradeCode.");
322 + }
323 +
324 + // @Type
325 + hr = XmlGetAttributeEx(pixnNode, L"Type", &scz);
326 + ExitOnFailure(hr, "Failed to get @Type.");
327 +
328 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1))
329 + {
330 + pSearch->MsiProductSearch.Type = BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION;
331 + }
332 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"language", -1))
333 + {
334 + pSearch->MsiProductSearch.Type = BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE;
335 + }
336 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"state", -1))
337 + {
338 + pSearch->MsiProductSearch.Type = BURN_MSI_PRODUCT_SEARCH_TYPE_STATE;
339 + }
340 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"assignment", -1))
341 + {
342 + pSearch->MsiProductSearch.Type = BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT;
343 + }
344 + else
345 + {
346 + hr = E_INVALIDARG;
347 + ExitOnFailure(hr, "Invalid value for @Type: %ls", scz);
348 + }
349 + }
350 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsiFeatureSearch", -1))
351 + {
352 + pSearch->Type = BURN_SEARCH_TYPE_MSI_FEATURE;
353 +
354 + // @ProductCode
355 + hr = XmlGetAttributeEx(pixnNode, L"ProductCode", &pSearch->MsiFeatureSearch.sczProductCode);
356 + ExitOnFailure(hr, "Failed to get @ProductCode.");
357 +
358 + // @FeatureId
359 + hr = XmlGetAttributeEx(pixnNode, L"FeatureId", &pSearch->MsiFeatureSearch.sczFeatureId);
360 + ExitOnFailure(hr, "Failed to get @FeatureId.");
361 +
362 + // @Type
363 + hr = XmlGetAttributeEx(pixnNode, L"Type", &scz);
364 + ExitOnFailure(hr, "Failed to get @Type.");
365 +
366 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"state", -1))
367 + {
368 + pSearch->MsiFeatureSearch.Type = BURN_MSI_FEATURE_SEARCH_TYPE_STATE;
369 + }
370 + else
371 + {
372 + hr = E_INVALIDARG;
373 + ExitOnFailure(hr, "Invalid value for @Type: %ls", scz);
374 + }
375 + }
376 + else
377 + {
378 + hr = E_UNEXPECTED;
379 + ExitOnFailure(hr, "Unexpected element name: %ls", bstrNodeName);
380 + }
381 +
382 + // prepare next iteration
383 + ReleaseNullObject(pixnNode);
384 + ReleaseNullBSTR(bstrNodeName);
385 + }
386 +
387 + hr = S_OK;
388 +
389 +LExit:
390 + ReleaseObject(pixnNodes);
391 + ReleaseObject(pixnNode);
392 + ReleaseBSTR(bstrNodeName);
393 + ReleaseStr(scz);
394 + return hr;
395 +}
396 +
397 +extern "C" HRESULT SearchesExecute(
398 + __in BURN_SEARCHES* pSearches,
399 + __in BURN_VARIABLES* pVariables
400 + )
401 +{
402 + HRESULT hr = S_OK;
403 + BOOL f = FALSE;
404 +
405 + for (DWORD i = 0; i < pSearches->cSearches; ++i)
406 + {
407 + BURN_SEARCH* pSearch = &pSearches->rgSearches[i];
408 +
409 + // evaluate condition
410 + if (pSearch->sczCondition && *pSearch->sczCondition)
411 + {
412 + hr = ConditionEvaluate(pVariables, pSearch->sczCondition, &f);
413 + if (E_INVALIDDATA == hr)
414 + {
415 + TraceError(hr, "Failed to parse search condition. Id = '%ls', Condition = '%ls'", pSearch->sczKey, pSearch->sczCondition);
416 + hr = S_OK;
417 + continue;
418 + }
419 + ExitOnFailure(hr, "Failed to evaluate search condition. Id = '%ls', Condition = '%ls'", pSearch->sczKey, pSearch->sczCondition);
420 +
421 + if (!f)
422 + {
423 + continue; // condition evaluated to false, skip
424 + }
425 + }
426 +
427 + switch (pSearch->Type)
428 + {
429 + case BURN_SEARCH_TYPE_DIRECTORY:
430 + switch (pSearch->DirectorySearch.Type)
431 + {
432 + case BURN_DIRECTORY_SEARCH_TYPE_EXISTS:
433 + hr = DirectorySearchExists(pSearch, pVariables);
434 + break;
435 + case BURN_DIRECTORY_SEARCH_TYPE_PATH:
436 + hr = DirectorySearchPath(pSearch, pVariables);
437 + break;
438 + default:
439 + hr = E_UNEXPECTED;
440 + }
441 + break;
442 + case BURN_SEARCH_TYPE_FILE:
443 + switch (pSearch->FileSearch.Type)
444 + {
445 + case BURN_FILE_SEARCH_TYPE_EXISTS:
446 + hr = FileSearchExists(pSearch, pVariables);
447 + break;
448 + case BURN_FILE_SEARCH_TYPE_VERSION:
449 + hr = FileSearchVersion(pSearch, pVariables);
450 + break;
451 + case BURN_FILE_SEARCH_TYPE_PATH:
452 + hr = FileSearchPath(pSearch, pVariables);
453 + break;
454 + default:
455 + hr = E_UNEXPECTED;
456 + }
457 + break;
458 + case BURN_SEARCH_TYPE_REGISTRY:
459 + switch (pSearch->RegistrySearch.Type)
460 + {
461 + case BURN_REGISTRY_SEARCH_TYPE_EXISTS:
462 + hr = RegistrySearchExists(pSearch, pVariables);
463 + break;
464 + case BURN_REGISTRY_SEARCH_TYPE_VALUE:
465 + hr = RegistrySearchValue(pSearch, pVariables);
466 + break;
467 + default:
468 + hr = E_UNEXPECTED;
469 + }
470 + break;
471 + case BURN_SEARCH_TYPE_MSI_COMPONENT:
472 + hr = MsiComponentSearch(pSearch, pVariables);
473 + break;
474 + case BURN_SEARCH_TYPE_MSI_PRODUCT:
475 + hr = MsiProductSearch(pSearch, pVariables);
476 + break;
477 + case BURN_SEARCH_TYPE_MSI_FEATURE:
478 + hr = MsiFeatureSearch(pSearch, pVariables);
479 + break;
480 + default:
481 + hr = E_UNEXPECTED;
482 + }
483 +
484 + if (FAILED(hr))
485 + {
486 + TraceError(hr, "Search failed. Id = '%ls'", pSearch->sczKey);
487 + continue;
488 + }
489 + }
490 +
491 + hr = S_OK;
492 +
493 +LExit:
494 + return hr;
495 +}
496 +
497 +extern "C" void SearchesUninitialize(
498 + __in BURN_SEARCHES* pSearches
499 + )
500 +{
501 + if (pSearches->rgSearches)
502 + {
503 + for (DWORD i = 0; i < pSearches->cSearches; ++i)
504 + {
505 + BURN_SEARCH* pSearch = &pSearches->rgSearches[i];
506 +
507 + ReleaseStr(pSearch->sczKey);
508 + ReleaseStr(pSearch->sczVariable);
509 + ReleaseStr(pSearch->sczCondition);
510 +
511 + switch (pSearch->Type)
512 + {
513 + case BURN_SEARCH_TYPE_DIRECTORY:
514 + ReleaseStr(pSearch->DirectorySearch.sczPath);
515 + break;
516 + case BURN_SEARCH_TYPE_FILE:
517 + ReleaseStr(pSearch->FileSearch.sczPath);
518 + break;
519 + case BURN_SEARCH_TYPE_REGISTRY:
520 + ReleaseStr(pSearch->RegistrySearch.sczKey);
521 + ReleaseStr(pSearch->RegistrySearch.sczValue);
522 + break;
523 + case BURN_SEARCH_TYPE_MSI_COMPONENT:
524 + ReleaseStr(pSearch->MsiComponentSearch.sczProductCode);
525 + ReleaseStr(pSearch->MsiComponentSearch.sczComponentId);
526 + break;
527 + case BURN_SEARCH_TYPE_MSI_PRODUCT:
528 + ReleaseStr(pSearch->MsiProductSearch.sczGuid);
529 + break;
530 + case BURN_SEARCH_TYPE_MSI_FEATURE:
531 + ReleaseStr(pSearch->MsiFeatureSearch.sczProductCode);
532 + ReleaseStr(pSearch->MsiFeatureSearch.sczFeatureId);
533 + break;
534 + }
535 + }
536 + MemFree(pSearches->rgSearches);
537 + }
538 +}
539 +
540 +
541 +// internal function definitions
542 +
543 +static HRESULT DirectorySearchExists(
544 + __in BURN_SEARCH* pSearch,
545 + __in BURN_VARIABLES* pVariables
546 + )
547 +{
548 + HRESULT hr = S_OK;
549 + LPWSTR sczPath = NULL;
550 + BOOL fExists = FALSE;
551 +
552 + // format path
553 + hr = VariableFormatString(pVariables, pSearch->DirectorySearch.sczPath, &sczPath, NULL);
554 + ExitOnFailure(hr, "Failed to format variable string.");
555 +
556 + DWORD dwAttributes = ::GetFileAttributesW(sczPath);
557 + if (INVALID_FILE_ATTRIBUTES == dwAttributes)
558 + {
559 + hr = HRESULT_FROM_WIN32(::GetLastError());
560 + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
561 + {
562 + hr = S_OK; // didn't find file, fExists still is false.
563 + }
564 + }
565 + else if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)
566 + {
567 + fExists = TRUE;
568 + }
569 +
570 + // else must have found a file.
571 + // What if there is a hidden variable in sczPath?
572 + ExitOnFailure(hr, "Failed while searching directory search: %ls, for path: %ls", pSearch->sczKey, sczPath);
573 +
574 + // set variable
575 + hr = VariableSetNumeric(pVariables, pSearch->sczVariable, fExists, FALSE);
576 + ExitOnFailure(hr, "Failed to set variable.");
577 +
578 +LExit:
579 + StrSecureZeroFreeString(sczPath);
580 +
581 + return hr;
582 +}
583 +
584 +static HRESULT DirectorySearchPath(
585 + __in BURN_SEARCH* pSearch,
586 + __in BURN_VARIABLES* pVariables
587 + )
588 +{
589 + HRESULT hr = S_OK;
590 + LPWSTR sczPath = NULL;
591 +
592 + // format path
593 + hr = VariableFormatString(pVariables, pSearch->DirectorySearch.sczPath, &sczPath, NULL);
594 + ExitOnFailure(hr, "Failed to format variable string.");
595 +
596 + DWORD dwAttributes = ::GetFileAttributesW(sczPath);
597 + if (INVALID_FILE_ATTRIBUTES == dwAttributes)
598 + {
599 + hr = HRESULT_FROM_WIN32(::GetLastError());
600 + }
601 + else if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)
602 + {
603 + hr = VariableSetLiteralString(pVariables, pSearch->sczVariable, sczPath, FALSE);
604 + ExitOnFailure(hr, "Failed to set directory search path variable.");
605 + }
606 + else // must have found a file.
607 + {
608 + hr = E_PATHNOTFOUND;
609 + }
610 +
611 + // What if there is a hidden variable in sczPath?
612 + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
613 + {
614 + LogStringLine(REPORT_STANDARD, "Directory search: %ls, did not find path: %ls, reason: 0x%x", pSearch->sczKey, sczPath, hr);
615 + ExitFunction1(hr = S_OK);
616 + }
617 + ExitOnFailure(hr, "Failed while searching directory search: %ls, for path: %ls", pSearch->sczKey, sczPath);
618 +
619 +LExit:
620 + StrSecureZeroFreeString(sczPath);
621 +
622 + return hr;
623 +}
624 +
625 +static HRESULT FileSearchExists(
626 + __in BURN_SEARCH* pSearch,
627 + __in BURN_VARIABLES* pVariables
628 + )
629 +{
630 + HRESULT hr = S_OK;
631 + DWORD er = ERROR_SUCCESS;
632 + LPWSTR sczPath = NULL;
633 + BOOL fExists = FALSE;
634 +
635 + // format path
636 + hr = VariableFormatString(pVariables, pSearch->FileSearch.sczPath, &sczPath, NULL);
637 + ExitOnFailure(hr, "Failed to format variable string.");
638 +
639 + // find file
640 + DWORD dwAttributes = ::GetFileAttributesW(sczPath);
641 + if (INVALID_FILE_ATTRIBUTES == dwAttributes)
642 + {
643 + er = ::GetLastError();
644 + if (ERROR_FILE_NOT_FOUND == er || ERROR_PATH_NOT_FOUND == er)
645 + {
646 + // What if there is a hidden variable in sczPath?
647 + LogStringLine(REPORT_STANDARD, "File search: %ls, did not find path: %ls", pSearch->sczKey, sczPath);
648 + }
649 + else
650 + {
651 + ExitOnWin32Error(er, hr, "Failed get to file attributes. '%ls'", pSearch->DirectorySearch.sczPath);
652 + }
653 + }
654 + else if (FILE_ATTRIBUTE_DIRECTORY != (dwAttributes & FILE_ATTRIBUTE_DIRECTORY))
655 + {
656 + fExists = TRUE;
657 + }
658 +
659 + // set variable
660 + hr = VariableSetNumeric(pVariables, pSearch->sczVariable, fExists, FALSE);
661 + ExitOnFailure(hr, "Failed to set variable.");
662 +
663 +LExit:
664 + StrSecureZeroFreeString(sczPath);
665 + return hr;
666 +}
667 +
668 +static HRESULT FileSearchVersion(
669 + __in BURN_SEARCH* pSearch,
670 + __in BURN_VARIABLES* pVariables
671 + )
672 +{
673 + HRESULT hr = S_OK;
674 + ULARGE_INTEGER uliVersion = { };
675 + LPWSTR sczPath = NULL;
676 +
677 + // format path
678 + hr = VariableFormatString(pVariables, pSearch->FileSearch.sczPath, &sczPath, NULL);
679 + ExitOnFailure(hr, "Failed to format path string.");
680 +
681 + // get file version
682 + hr = FileVersion(sczPath, &uliVersion.HighPart, &uliVersion.LowPart);
683 + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
684 + {
685 + // What if there is a hidden variable in sczPath?
686 + LogStringLine(REPORT_STANDARD, "File search: %ls, did not find path: %ls", pSearch->sczKey, sczPath);
687 + ExitFunction1(hr = S_OK);
688 + }
689 + ExitOnFailure(hr, "Failed get file version.");
690 +
691 + // set variable
692 + hr = VariableSetVersion(pVariables, pSearch->sczVariable, uliVersion.QuadPart, FALSE);
693 + ExitOnFailure(hr, "Failed to set variable.");
694 +
695 +LExit:
696 + StrSecureZeroFreeString(sczPath);
697 + return hr;
698 +}
699 +
700 +static HRESULT FileSearchPath(
701 + __in BURN_SEARCH* pSearch,
702 + __in BURN_VARIABLES* pVariables
703 + )
704 +{
705 + HRESULT hr = S_OK;
706 + LPWSTR sczPath = NULL;
707 +
708 + // format path
709 + hr = VariableFormatString(pVariables, pSearch->FileSearch.sczPath, &sczPath, NULL);
710 + ExitOnFailure(hr, "Failed to format variable string.");
711 +
712 + DWORD dwAttributes = ::GetFileAttributesW(sczPath);
713 + if (INVALID_FILE_ATTRIBUTES == dwAttributes)
714 + {
715 + hr = HRESULT_FROM_WIN32(::GetLastError());
716 + }
717 + else if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) // found a directory.
718 + {
719 + hr = E_FILENOTFOUND;
720 + }
721 + else // found our file.
722 + {
723 + hr = VariableSetLiteralString(pVariables, pSearch->sczVariable, sczPath, FALSE);
724 + ExitOnFailure(hr, "Failed to set variable to file search path.");
725 + }
726 +
727 + // What if there is a hidden variable in sczPath?
728 + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
729 + {
730 + LogStringLine(REPORT_STANDARD, "File search: %ls, did not find path: %ls", pSearch->sczKey, sczPath);
731 + ExitFunction1(hr = S_OK);
732 + }
733 + ExitOnFailure(hr, "Failed while searching file search: %ls, for path: %ls", pSearch->sczKey, sczPath);
734 +
735 +LExit:
736 + StrSecureZeroFreeString(sczPath);
737 +
738 + return hr;
739 +}
740 +
741 +static HRESULT RegistrySearchExists(
742 + __in BURN_SEARCH* pSearch,
743 + __in BURN_VARIABLES* pVariables
744 + )
745 +{
746 + HRESULT hr = S_OK;
747 + DWORD er = ERROR_SUCCESS;
748 + LPWSTR sczKey = NULL;
749 + LPWSTR sczValue = NULL;
750 + HKEY hKey = NULL;
751 + DWORD dwType = 0;
752 + BOOL fExists = FALSE;
753 + REGSAM samDesired = KEY_QUERY_VALUE;
754 +
755 + if (pSearch->RegistrySearch.fWin64)
756 + {
757 + samDesired = samDesired | KEY_WOW64_64KEY;
758 + }
759 +
760 + // format key string
761 + hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczKey, &sczKey, NULL);
762 + ExitOnFailure(hr, "Failed to format key string.");
763 +
764 + // open key
765 + hr = RegOpen(pSearch->RegistrySearch.hRoot, sczKey, samDesired, &hKey);
766 + if (SUCCEEDED(hr))
767 + {
768 + fExists = TRUE;
769 + }
770 + else if (E_FILENOTFOUND == hr)
771 + {
772 + // What if there is a hidden variable in sczKey?
773 + LogStringLine(REPORT_STANDARD, "Registry key not found. Key = '%ls'", sczKey);
774 + fExists = FALSE;
775 + hr = S_OK;
776 + }
777 + else
778 + {
779 + // What if there is a hidden variable in sczKey?
780 + ExitOnFailure(hr, "Failed to open registry key. Key = '%ls'", sczKey);
781 + }
782 +
783 + if (fExists && pSearch->RegistrySearch.sczValue)
784 + {
785 + // format value string
786 + hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczValue, &sczValue, NULL);
787 + ExitOnFailure(hr, "Failed to format value string.");
788 +
789 + // query value
790 + er = ::RegQueryValueExW(hKey, sczValue, NULL, &dwType, NULL, NULL);
791 + switch (er)
792 + {
793 + case ERROR_SUCCESS:
794 + fExists = TRUE;
795 + break;
796 + case ERROR_FILE_NOT_FOUND:
797 + // What if there is a hidden variable in sczKey or sczValue?
798 + LogStringLine(REPORT_STANDARD, "Registry value not found. Key = '%ls', Value = '%ls'", sczKey, sczValue);
799 + fExists = FALSE;
800 + break;
801 + default:
802 + ExitOnWin32Error(er, hr, "Failed to query registry key value.");
803 + }
804 + }
805 +
806 + // set variable
807 + hr = VariableSetNumeric(pVariables, pSearch->sczVariable, fExists, FALSE);
808 + ExitOnFailure(hr, "Failed to set variable.");
809 +
810 +LExit:
811 + if (FAILED(hr))
812 + {
813 + // What if there is a hidden variable in sczKey?
814 + LogStringLine(REPORT_STANDARD, "RegistrySearchExists failed: ID '%ls', HRESULT 0x%x", sczKey, hr);
815 + }
816 +
817 + StrSecureZeroFreeString(sczKey);
818 + StrSecureZeroFreeString(sczValue);
819 + ReleaseRegKey(hKey);
820 +
821 + return hr;
822 +}
823 +
824 +static HRESULT RegistrySearchValue(
825 + __in BURN_SEARCH* pSearch,
826 + __in BURN_VARIABLES* pVariables
827 + )
828 +{
829 + HRESULT hr = S_OK;
830 + DWORD er = ERROR_SUCCESS;
831 + LPWSTR sczKey = NULL;
832 + LPWSTR sczValue = NULL;
833 + HKEY hKey = NULL;
834 + DWORD dwType = 0;
835 + DWORD cbData = 0;
836 + LPBYTE pData = NULL;
837 + DWORD cch = 0;
838 + BURN_VARIANT value = { };
839 + REGSAM samDesired = KEY_QUERY_VALUE;
840 +
841 + if (pSearch->RegistrySearch.fWin64)
842 + {
843 + samDesired = samDesired | KEY_WOW64_64KEY;
844 + }
845 +
846 + // format key string
847 + hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczKey, &sczKey, NULL);
848 + ExitOnFailure(hr, "Failed to format key string.");
849 +
850 + // format value string
851 + if (pSearch->RegistrySearch.sczValue)
852 + {
853 + hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczValue, &sczValue, NULL);
854 + ExitOnFailure(hr, "Failed to format value string.");
855 + }
856 +
857 + // open key
858 + hr = RegOpen(pSearch->RegistrySearch.hRoot, sczKey, samDesired, &hKey);
859 + if (E_FILENOTFOUND == hr)
860 + {
861 + // What if there is a hidden variable in sczKey?
862 + LogStringLine(REPORT_STANDARD, "Registry key not found. Key = '%ls'", sczKey);
863 + hr = VariableSetLiteralVariant(pVariables, pSearch->sczVariable, &value);
864 + ExitOnFailure(hr, "Failed to clear variable.");
865 + ExitFunction1(hr = S_OK);
866 + }
867 + ExitOnFailure(hr, "Failed to open registry key.");
868 +
869 + // get value
870 + er = ::RegQueryValueExW(hKey, sczValue, NULL, &dwType, NULL, &cbData);
871 + if (ERROR_FILE_NOT_FOUND == er)
872 + {
873 + // What if there is a hidden variable in sczKey or sczValue?
874 + LogStringLine(REPORT_STANDARD, "Registry value not found. Key = '%ls', Value = '%ls'", sczKey, sczValue);
875 + hr = VariableSetLiteralVariant(pVariables, pSearch->sczVariable, &value);
876 + ExitOnFailure(hr, "Failed to clear variable.");
877 + ExitFunction1(hr = S_OK);
878 + }
879 + ExitOnWin32Error(er, hr, "Failed to query registry key value size.");
880 +
881 + pData = (LPBYTE)MemAlloc(cbData + sizeof(WCHAR), TRUE); // + sizeof(WCHAR) here to ensure that we always have a null terminator for REG_SZ
882 + ExitOnNull(pData, hr, E_OUTOFMEMORY, "Failed to allocate memory registry value.");
883 +
884 + er = ::RegQueryValueExW(hKey, sczValue, NULL, &dwType, pData, &cbData);
885 + ExitOnWin32Error(er, hr, "Failed to query registry key value.");
886 +
887 + switch (dwType)
888 + {
889 + case REG_DWORD:
890 + if (sizeof(LONG) != cbData)
891 + {
892 + ExitFunction1(hr = E_UNEXPECTED);
893 + }
894 + hr = BVariantSetNumeric(&value, *((LONG*)pData));
895 + break;
896 + case REG_QWORD:
897 + if (sizeof(LONGLONG) != cbData)
898 + {
899 + ExitFunction1(hr = E_UNEXPECTED);
900 + }
901 + hr = BVariantSetNumeric(&value, *((LONGLONG*)pData));
902 + break;
903 + case REG_EXPAND_SZ:
904 + if (pSearch->RegistrySearch.fExpandEnvironment)
905 + {
906 + hr = StrAlloc(&value.sczValue, cbData);
907 + ExitOnFailure(hr, "Failed to allocate string buffer.");
908 + value.Type = BURN_VARIANT_TYPE_STRING;
909 +
910 + cch = ::ExpandEnvironmentStringsW((LPCWSTR)pData, value.sczValue, cbData);
911 + if (cch > cbData)
912 + {
913 + hr = StrAlloc(&value.sczValue, cch);
914 + ExitOnFailure(hr, "Failed to allocate string buffer.");
915 +
916 + if (cch != ::ExpandEnvironmentStringsW((LPCWSTR)pData, value.sczValue, cch))
917 + {
918 + ExitWithLastError(hr, "Failed to get expand environment string.");
919 + }
920 + }
921 + break;
922 + }
923 + __fallthrough;
924 + case REG_SZ:
925 + hr = BVariantSetString(&value, (LPCWSTR)pData, 0);
926 + break;
927 + default:
928 + ExitOnFailure(hr = E_NOTIMPL, "Unsupported registry key value type. Type = '%u'", dwType);
929 + }
930 + ExitOnFailure(hr, "Failed to read registry value.");
931 +
932 + // change value to requested type
933 + hr = BVariantChangeType(&value, pSearch->RegistrySearch.VariableType);
934 + ExitOnFailure(hr, "Failed to change value type.");
935 +
936 + // Set variable as a literal.
937 + hr = VariableSetLiteralVariant(pVariables, pSearch->sczVariable, &value);
938 + ExitOnFailure(hr, "Failed to set variable.");
939 +
940 +LExit:
941 + if (FAILED(hr))
942 + {
943 + // What if there is a hidden variable in sczKey?
944 + LogStringLine(REPORT_STANDARD, "RegistrySearchValue failed: ID '%ls', HRESULT 0x%x", sczKey, hr);
945 + }
946 +
947 + StrSecureZeroFreeString(sczKey);
948 + StrSecureZeroFreeString(sczValue);
949 + ReleaseRegKey(hKey);
950 + ReleaseMem(pData);
951 + BVariantUninitialize(&value);
952 +
953 + return hr;
954 +}
955 +
956 +static HRESULT MsiComponentSearch(
957 + __in BURN_SEARCH* pSearch,
958 + __in BURN_VARIABLES* pVariables
959 + )
960 +{
961 + HRESULT hr = S_OK;
962 + INSTALLSTATE is = INSTALLSTATE_BROKEN;
963 + LPWSTR sczComponentId = NULL;
964 + LPWSTR sczProductCode = NULL;
965 + LPWSTR sczPath = NULL;
966 +
967 + // format component id string
968 + hr = VariableFormatString(pVariables, pSearch->MsiComponentSearch.sczComponentId, &sczComponentId, NULL);
969 + ExitOnFailure(hr, "Failed to format component id string.");
970 +
971 + if (pSearch->MsiComponentSearch.sczProductCode)
972 + {
973 + // format product code string
974 + hr = VariableFormatString(pVariables, pSearch->MsiComponentSearch.sczProductCode, &sczProductCode, NULL);
975 + ExitOnFailure(hr, "Failed to format product code string.");
976 + }
977 +
978 + if (sczProductCode)
979 + {
980 + hr = WiuGetComponentPath(sczProductCode, sczComponentId, &is, &sczPath);
981 + }
982 + else
983 + {
984 + hr = WiuLocateComponent(sczComponentId, &is, &sczPath);
985 + }
986 +
987 + if (INSTALLSTATE_SOURCEABSENT == is)
988 + {
989 + is = INSTALLSTATE_SOURCE;
990 + }
991 + else if (INSTALLSTATE_UNKNOWN == is || INSTALLSTATE_NOTUSED == is)
992 + {
993 + is = INSTALLSTATE_ABSENT;
994 + }
995 + else if (INSTALLSTATE_ABSENT != is && INSTALLSTATE_LOCAL != is && INSTALLSTATE_SOURCE != is)
996 + {
997 + hr = E_INVALIDARG;
998 + ExitOnFailure(hr, "Failed to get component path: %d", is);
999 + }
1000 +
1001 + // set variable
1002 + switch (pSearch->MsiComponentSearch.Type)
1003 + {
1004 + case BURN_MSI_COMPONENT_SEARCH_TYPE_KEYPATH:
1005 + if (INSTALLSTATE_ABSENT == is || INSTALLSTATE_LOCAL == is || INSTALLSTATE_SOURCE == is)
1006 + {
1007 + hr = VariableSetLiteralString(pVariables, pSearch->sczVariable, sczPath, FALSE);
1008 + }
1009 + break;
1010 + case BURN_MSI_COMPONENT_SEARCH_TYPE_STATE:
1011 + hr = VariableSetNumeric(pVariables, pSearch->sczVariable, is, FALSE);
1012 + break;
1013 + case BURN_MSI_COMPONENT_SEARCH_TYPE_DIRECTORY:
1014 + if (INSTALLSTATE_ABSENT == is || INSTALLSTATE_LOCAL == is || INSTALLSTATE_SOURCE == is)
1015 + {
1016 + // remove file part from path, if any
1017 + LPWSTR wz = wcsrchr(sczPath, L'\\');
1018 + if (wz)
1019 + {
1020 + wz[1] = L'\0';
1021 + }
1022 +
1023 + hr = VariableSetLiteralString(pVariables, pSearch->sczVariable, sczPath, FALSE);
1024 + }
1025 + break;
1026 + }
1027 + ExitOnFailure(hr, "Failed to set variable.");
1028 +
1029 +LExit:
1030 + if (FAILED(hr))
1031 + {
1032 + LogStringLine(REPORT_STANDARD, "MsiComponentSearch failed: ID '%ls', HRESULT 0x%x", pSearch->sczKey, hr);
1033 + }
1034 +
1035 + StrSecureZeroFreeString(sczComponentId);
1036 + StrSecureZeroFreeString(sczProductCode);
1037 + ReleaseStr(sczPath);
1038 + return hr;
1039 +}
1040 +
1041 +static HRESULT MsiProductSearch(
1042 + __in BURN_SEARCH* pSearch,
1043 + __in BURN_VARIABLES* pVariables
1044 + )
1045 +{
1046 + HRESULT hr = S_OK;
1047 + LPWSTR sczGuid = NULL;
1048 + LPCWSTR wzProperty = NULL;
1049 + LPWSTR *rgsczRelatedProductCodes = NULL;
1050 + DWORD dwRelatedProducts = 0;
1051 + BURN_VARIANT_TYPE type = BURN_VARIANT_TYPE_NONE;
1052 + BURN_VARIANT value = { };
1053 + // We're not going to encrypt this value, so can access the value directly.
1054 +
1055 + switch (pSearch->MsiProductSearch.Type)
1056 + {
1057 + case BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION:
1058 + wzProperty = INSTALLPROPERTY_VERSIONSTRING;
1059 + break;
1060 + case BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE:
1061 + wzProperty = INSTALLPROPERTY_LANGUAGE;
1062 + break;
1063 + case BURN_MSI_PRODUCT_SEARCH_TYPE_STATE:
1064 + wzProperty = INSTALLPROPERTY_PRODUCTSTATE;
1065 + break;
1066 + case BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT:
1067 + wzProperty = INSTALLPROPERTY_ASSIGNMENTTYPE;
1068 + break;
1069 + default:
1070 + ExitOnFailure(hr = E_NOTIMPL, "Unsupported product search type: %u", pSearch->MsiProductSearch.Type);
1071 + }
1072 +
1073 + // format guid string
1074 + hr = VariableFormatString(pVariables, pSearch->MsiProductSearch.sczGuid, &sczGuid, NULL);
1075 + ExitOnFailure(hr, "Failed to format GUID string.");
1076 +
1077 + // get product info
1078 + value.Type = BURN_VARIANT_TYPE_STRING;
1079 +
1080 + // if this is an upgrade code then get the product code of the highest versioned related product
1081 + if (BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_UPGRADECODE == pSearch->MsiProductSearch.GuidType)
1082 + {
1083 + // WiuEnumRelatedProductCodes will log sczGuid on errors, what if there's a hidden variable in there?
1084 + hr = WiuEnumRelatedProductCodes(sczGuid, &rgsczRelatedProductCodes, &dwRelatedProducts, TRUE);
1085 + ExitOnFailure(hr, "Failed to enumerate related products for upgrade code.");
1086 +
1087 + // if we actually found a related product then use its upgrade code for the rest of the search
1088 + if (1 == dwRelatedProducts)
1089 + {
1090 + hr = StrAllocStringSecure(&sczGuid, rgsczRelatedProductCodes[0], 0);
1091 + ExitOnFailure(hr, "Failed to copy upgrade code.");
1092 + }
1093 + else
1094 + {
1095 + // set this here so we have a way of knowing that we don't need to bother
1096 + // querying for the product information below
1097 + hr = HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT);
1098 + }
1099 + }
1100 +
1101 + if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) != hr)
1102 + {
1103 + hr = WiuGetProductInfo(sczGuid, wzProperty, &value.sczValue);
1104 + if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) == hr)
1105 + {
1106 + // product state is available only through MsiGetProductInfoEx
1107 + // What if there is a hidden variable in sczGuid?
1108 + LogStringLine(REPORT_VERBOSE, "Trying per-machine extended info for property '%ls' for product: %ls", wzProperty, sczGuid);
1109 + hr = WiuGetProductInfoEx(sczGuid, NULL, MSIINSTALLCONTEXT_MACHINE, wzProperty, &value.sczValue);
1110 +
1111 + // if not in per-machine context, try per-user (unmanaged)
1112 + if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr)
1113 + {
1114 + // What if there is a hidden variable in sczGuid?
1115 + LogStringLine(REPORT_STANDARD, "Trying per-user extended info for property '%ls' for product: %ls", wzProperty, sczGuid);
1116 + hr = WiuGetProductInfoEx(sczGuid, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, wzProperty, &value.sczValue);
1117 + }
1118 + }
1119 + }
1120 +
1121 + if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr)
1122 + {
1123 + // What if there is a hidden variable in sczGuid?
1124 + LogStringLine(REPORT_STANDARD, "Product or related product not found: %ls", sczGuid);
1125 +
1126 + // set value to indicate absent
1127 + switch (pSearch->MsiProductSearch.Type)
1128 + {
1129 + case BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT: __fallthrough;
1130 + case BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION:
1131 + value.Type = BURN_VARIANT_TYPE_NUMERIC;
1132 + value.llValue = 0;
1133 + break;
1134 + case BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE:
1135 + // is supposed to remain empty
1136 + break;
1137 + case BURN_MSI_PRODUCT_SEARCH_TYPE_STATE:
1138 + value.Type = BURN_VARIANT_TYPE_NUMERIC;
1139 + value.llValue = INSTALLSTATE_ABSENT;
1140 + break;
1141 + }
1142 +
1143 + hr = S_OK;
1144 + }
1145 + ExitOnFailure(hr, "Failed to get product info.");
1146 +
1147 + // change value type
1148 + switch (pSearch->MsiProductSearch.Type)
1149 + {
1150 + case BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION:
1151 + type = BURN_VARIANT_TYPE_VERSION;
1152 + break;
1153 + case BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE:
1154 + type = BURN_VARIANT_TYPE_STRING;
1155 + break;
1156 + case BURN_MSI_PRODUCT_SEARCH_TYPE_STATE: __fallthrough;
1157 + case BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT:
1158 + type = BURN_VARIANT_TYPE_NUMERIC;
1159 + break;
1160 + }
1161 + hr = BVariantChangeType(&value, type);
1162 + ExitOnFailure(hr, "Failed to change value type.");
1163 +
1164 + // Set variable as a literal.
1165 + hr = VariableSetLiteralVariant(pVariables, pSearch->sczVariable, &value);
1166 + ExitOnFailure(hr, "Failed to set variable.");
1167 +
1168 +LExit:
1169 + if (FAILED(hr))
1170 + {
1171 + LogStringLine(REPORT_STANDARD, "MsiProductSearch failed: ID '%ls', HRESULT 0x%x", pSearch->sczKey, hr);
1172 + }
1173 +
1174 + StrSecureZeroFreeString(sczGuid);
1175 + ReleaseStrArray(rgsczRelatedProductCodes, dwRelatedProducts);
1176 + BVariantUninitialize(&value);
1177 +
1178 + return hr;
1179 +}
1180 +
1181 +static HRESULT MsiFeatureSearch(
1182 + __in BURN_SEARCH* pSearch,
1183 + __in BURN_VARIABLES* /*pVariables*/
1184 + )
1185 +{
1186 + HRESULT hr = E_NOTIMPL;
1187 +
1188 +//LExit:
1189 + if (FAILED(hr))
1190 + {
1191 + LogStringLine(REPORT_STANDARD, "MsiFeatureSearch failed: ID '%ls', HRESULT 0x%x", pSearch->sczKey, hr);
1192 + }
1193 +
1194 + return hr;
1195 +}
src/engine/search.h new
+152
@@ -0,0 +1,152 @@
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 +#if defined(__cplusplus)
6 +extern "C" {
7 +#endif
8 +
9 +
10 +// constants
11 +
12 +enum BURN_SEARCH_TYPE
13 +{
14 + BURN_SEARCH_TYPE_NONE,
15 + BURN_SEARCH_TYPE_DIRECTORY,
16 + BURN_SEARCH_TYPE_FILE,
17 + BURN_SEARCH_TYPE_REGISTRY,
18 + BURN_SEARCH_TYPE_MSI_COMPONENT,
19 + BURN_SEARCH_TYPE_MSI_PRODUCT,
20 + BURN_SEARCH_TYPE_MSI_FEATURE,
21 +};
22 +
23 +enum BURN_DIRECTORY_SEARCH_TYPE
24 +{
25 + BURN_DIRECTORY_SEARCH_TYPE_NONE,
26 + BURN_DIRECTORY_SEARCH_TYPE_EXISTS,
27 + BURN_DIRECTORY_SEARCH_TYPE_PATH,
28 +};
29 +
30 +enum BURN_FILE_SEARCH_TYPE
31 +{
32 + BURN_FILE_SEARCH_TYPE_NONE,
33 + BURN_FILE_SEARCH_TYPE_EXISTS,
34 + BURN_FILE_SEARCH_TYPE_VERSION,
35 + BURN_FILE_SEARCH_TYPE_PATH,
36 +};
37 +
38 +enum BURN_REGISTRY_SEARCH_TYPE
39 +{
40 + BURN_REGISTRY_SEARCH_TYPE_NONE,
41 + BURN_REGISTRY_SEARCH_TYPE_EXISTS,
42 + BURN_REGISTRY_SEARCH_TYPE_VALUE,
43 +};
44 +
45 +enum BURN_MSI_COMPONENT_SEARCH_TYPE
46 +{
47 + BURN_MSI_COMPONENT_SEARCH_TYPE_NONE,
48 + BURN_MSI_COMPONENT_SEARCH_TYPE_KEYPATH,
49 + BURN_MSI_COMPONENT_SEARCH_TYPE_STATE,
50 + BURN_MSI_COMPONENT_SEARCH_TYPE_DIRECTORY,
51 +};
52 +
53 +enum BURN_MSI_PRODUCT_SEARCH_TYPE
54 +{
55 + BURN_MSI_PRODUCT_SEARCH_TYPE_NONE,
56 + BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION,
57 + BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE,
58 + BURN_MSI_PRODUCT_SEARCH_TYPE_STATE,
59 + BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT,
60 +};
61 +
62 +enum BURN_MSI_PRODUCT_SEARCH_GUID_TYPE
63 +{
64 + BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_NONE,
65 + BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_PRODUCTCODE,
66 + BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_UPGRADECODE
67 +};
68 +
69 +enum BURN_MSI_FEATURE_SEARCH_TYPE
70 +{
71 + BURN_MSI_FEATURE_SEARCH_TYPE_NONE,
72 + BURN_MSI_FEATURE_SEARCH_TYPE_STATE,
73 +};
74 +
75 +
76 +// structs
77 +
78 +typedef struct _BURN_SEARCH
79 +{
80 + LPWSTR sczKey;
81 + LPWSTR sczVariable;
82 + LPWSTR sczCondition;
83 +
84 + BURN_SEARCH_TYPE Type;
85 + union
86 + {
87 + struct
88 + {
89 + BURN_DIRECTORY_SEARCH_TYPE Type;
90 + LPWSTR sczPath;
91 + } DirectorySearch;
92 + struct
93 + {
94 + BURN_FILE_SEARCH_TYPE Type;
95 + LPWSTR sczPath;
96 + } FileSearch;
97 + struct
98 + {
99 + BURN_REGISTRY_SEARCH_TYPE Type;
100 + BURN_VARIANT_TYPE VariableType;
101 + HKEY hRoot;
102 + LPWSTR sczKey;
103 + LPWSTR sczValue;
104 + BOOL fWin64;
105 + BOOL fExpandEnvironment;
106 + } RegistrySearch;
107 + struct
108 + {
109 + BURN_MSI_COMPONENT_SEARCH_TYPE Type;
110 + LPWSTR sczProductCode;
111 + LPWSTR sczComponentId;
112 + } MsiComponentSearch;
113 + struct
114 + {
115 + BURN_MSI_PRODUCT_SEARCH_TYPE Type;
116 + BURN_MSI_PRODUCT_SEARCH_GUID_TYPE GuidType;
117 + LPWSTR sczGuid;
118 + } MsiProductSearch;
119 + struct
120 + {
121 + BURN_MSI_FEATURE_SEARCH_TYPE Type;
122 + LPWSTR sczProductCode;
123 + LPWSTR sczFeatureId;
124 + } MsiFeatureSearch;
125 + };
126 +} BURN_SEARCH;
127 +
128 +typedef struct _BURN_SEARCHES
129 +{
130 + BURN_SEARCH* rgSearches;
131 + DWORD cSearches;
132 +} BURN_SEARCHES;
133 +
134 +
135 +// function declarations
136 +
137 +HRESULT SearchesParseFromXml(
138 + __in BURN_SEARCHES* pSearches,
139 + __in IXMLDOMNode* pixnBundle
140 + );
141 +HRESULT SearchesExecute(
142 + __in BURN_SEARCHES* pSearches,
143 + __in BURN_VARIABLES* pVariables
144 + );
145 +void SearchesUninitialize(
146 + __in BURN_SEARCHES* pSearches
147 + );
148 +
149 +
150 +#if defined(__cplusplus)
151 +}
152 +#endif
src/engine/section.cpp new
+399
@@ -0,0 +1,399 @@
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 +// constants
7 +
8 +// If these defaults ever change, be sure to update constants in burn\stub\StubSection.cpp as well.
9 +#define BURN_SECTION_NAME ".wixburn"
10 +#define BURN_SECTION_MAGIC 0x00f14300
11 +#define BURN_SECTION_VERSION 0x00000002
12 +#define MANIFEST_CABINET_TOKEN L"0"
13 +
14 +// structs
15 +typedef struct _BURN_SECTION_HEADER
16 +{
17 + DWORD dwMagic;
18 + DWORD dwVersion;
19 +
20 + GUID guidBundleId;
21 +
22 + DWORD dwStubSize;
23 + DWORD dwOriginalChecksum;
24 + DWORD dwOriginalSignatureOffset;
25 + DWORD dwOriginalSignatureSize;
26 +
27 + DWORD dwFormat;
28 + DWORD cContainers;
29 + DWORD rgcbContainers[1];
30 +} BURN_SECTION_HEADER;
31 +
32 +static HRESULT VerifySectionMatchesMemoryPEHeader(
33 + __in REFGUID pSection
34 + );
35 +
36 +
37 +extern "C" HRESULT SectionInitialize(
38 + __in BURN_SECTION* pSection,
39 + __in HANDLE hEngineFile,
40 + __in HANDLE hSourceEngineFile
41 + )
42 +{
43 + HRESULT hr = S_OK;
44 + DWORD cbRead = 0;
45 + LARGE_INTEGER li = { };
46 + LONGLONG llSize = 0;
47 + IMAGE_DOS_HEADER dosHeader = { };
48 + IMAGE_NT_HEADERS ntHeader = { };
49 + DWORD dwChecksumOffset = 0;
50 + DWORD dwCertificateTableOffset = 0;
51 + DWORD dwSignatureOffset = 0;
52 + DWORD cbSignature = 0;
53 + IMAGE_SECTION_HEADER sectionHeader = { };
54 + DWORD dwOriginalChecksumAndSignatureOffset = 0;
55 + BURN_SECTION_HEADER* pBurnSectionHeader = NULL;
56 +
57 + pSection->hEngineFile = hEngineFile;
58 + ExitOnInvalidHandleWithLastError(pSection->hEngineFile, hr, "Failed to open handle to engine process path.");
59 +
60 + pSection->hSourceEngineFile = INVALID_HANDLE_VALUE == hSourceEngineFile ? hEngineFile : hSourceEngineFile;
61 +
62 + //
63 + // First, make sure we have a valid DOS signature.
64 + //
65 + if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN))
66 + {
67 + ExitWithLastError(hr, "Failed to seek to start of file.");
68 + }
69 +
70 + // read DOS header
71 + if (!::ReadFile(pSection->hEngineFile, &dosHeader, sizeof(IMAGE_DOS_HEADER), &cbRead, NULL))
72 + {
73 + ExitWithLastError(hr, "Failed to read DOS header.");
74 + }
75 + else if (sizeof(IMAGE_DOS_HEADER) > cbRead || IMAGE_DOS_SIGNATURE != dosHeader.e_magic)
76 + {
77 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
78 + ExitOnRootFailure(hr, "Failed to find valid DOS image header in buffer.");
79 + }
80 +
81 + //
82 + // Now, make sure we have a valid NT signature.
83 + //
84 +
85 + // seek to new header
86 + li.QuadPart = dosHeader.e_lfanew;
87 + if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN))
88 + {
89 + ExitWithLastError(hr, "Failed to seek to NT header.");
90 + }
91 +
92 + // read NT header
93 + if (!::ReadFile(pSection->hEngineFile, &ntHeader, sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER), &cbRead, NULL))
94 + {
95 + ExitWithLastError(hr, "Failed to read NT header.");
96 + }
97 + else if ((sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER)) > cbRead || IMAGE_NT_SIGNATURE != ntHeader.Signature)
98 + {
99 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
100 + ExitOnRootFailure(hr, "Failed to find valid NT image header in buffer.");
101 + }
102 +
103 + // Get the table offsets.
104 + dwChecksumOffset = dosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER) + (sizeof(DWORD) * 16);
105 + dwCertificateTableOffset = dosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS) - (sizeof(IMAGE_DATA_DIRECTORY) * (IMAGE_NUMBEROF_DIRECTORY_ENTRIES - IMAGE_DIRECTORY_ENTRY_SECURITY));
106 +
107 + // Seek into the certificate table to get the signature size.
108 + li.QuadPart = dwCertificateTableOffset;
109 + if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN))
110 + {
111 + ExitWithLastError(hr, "Failed to seek to section info.");
112 + }
113 +
114 + if (!::ReadFile(pSection->hEngineFile, &dwSignatureOffset, sizeof(dwSignatureOffset), &cbRead, NULL))
115 + {
116 + ExitWithLastError(hr, "Failed to read signature offset.");
117 + }
118 +
119 + if (!::ReadFile(pSection->hEngineFile, &cbSignature, sizeof(cbSignature), &cbRead, NULL))
120 + {
121 + ExitWithLastError(hr, "Failed to read signature size.");
122 + }
123 +
124 + //
125 + // Finally, get into the section table and look for the Burn section info.
126 + //
127 +
128 + // seek past optional headers
129 + li.QuadPart = dosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER) + ntHeader.FileHeader.SizeOfOptionalHeader;
130 + if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN))
131 + {
132 + ExitWithLastError(hr, "Failed to seek past optional headers.");
133 + }
134 +
135 + // read sections one by one until we find our section
136 + for (DWORD i = 0; ; ++i)
137 + {
138 + // read section
139 + if (!::ReadFile(pSection->hEngineFile, &sectionHeader, sizeof(IMAGE_SECTION_HEADER), &cbRead, NULL))
140 + {
141 + ExitWithLastError(hr, "Failed to read image section header, index: %u", i);
142 + }
143 + if (sizeof(IMAGE_SECTION_HEADER) > cbRead)
144 + {
145 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
146 + ExitOnRootFailure(hr, "Failed to read complete image section header, index: %u", i);
147 + }
148 +
149 + // compare header name
150 + C_ASSERT(sizeof(sectionHeader.Name) == sizeof(BURN_SECTION_NAME) - 1);
151 + if (0 == memcmp(sectionHeader.Name, BURN_SECTION_NAME, sizeof(sectionHeader.Name)))
152 + {
153 + break;
154 + }
155 +
156 + // fail if we hit the end
157 + if (i + 1 >= ntHeader.FileHeader.NumberOfSections)
158 + {
159 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
160 + ExitOnRootFailure(hr, "Failed to find Burn section.");
161 + }
162 + }
163 +
164 + //
165 + // We've arrived at the section info.
166 + //
167 +
168 + // check size of section
169 + if (sizeof(BURN_SECTION_HEADER) > sectionHeader.SizeOfRawData)
170 + {
171 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
172 + ExitOnRootFailure(hr, "Failed to read section info, data to short: %u", sectionHeader.SizeOfRawData);
173 + }
174 +
175 + // allocate buffer for section info
176 + pBurnSectionHeader = (BURN_SECTION_HEADER*)MemAlloc(sectionHeader.SizeOfRawData, TRUE);
177 + ExitOnNull(pBurnSectionHeader, hr, E_OUTOFMEMORY, "Failed to allocate buffer for section info.");
178 +
179 + // seek to section info
180 + li.QuadPart = sectionHeader.PointerToRawData;
181 + if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN))
182 + {
183 + ExitWithLastError(hr, "Failed to seek to section info.");
184 + }
185 +
186 + // Note the location of original checksum and signature information in the burn section header.
187 + dwOriginalChecksumAndSignatureOffset = sectionHeader.PointerToRawData + (reinterpret_cast<LPBYTE>(&pBurnSectionHeader->dwOriginalChecksum) - reinterpret_cast<LPBYTE>(pBurnSectionHeader));
188 +
189 + // read section info
190 + if (!::ReadFile(pSection->hEngineFile, pBurnSectionHeader, sectionHeader.SizeOfRawData, &cbRead, NULL))
191 + {
192 + ExitWithLastError(hr, "Failed to read section info.");
193 + }
194 + else if (sectionHeader.SizeOfRawData > cbRead)
195 + {
196 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
197 + ExitOnRootFailure(hr, "Failed to read complete section info.");
198 + }
199 +
200 + // validate version of section info
201 + if (BURN_SECTION_VERSION != pBurnSectionHeader->dwVersion)
202 + {
203 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
204 + ExitOnRootFailure(hr, "Failed to read section info, unsupported version: %08x", pBurnSectionHeader->dwVersion);
205 + }
206 +
207 + hr = FileSizeByHandle(pSection->hSourceEngineFile, &llSize);
208 + ExitOnFailure(hr, "Failed to get total size of bundle.");
209 +
210 + pSection->cbStub = pBurnSectionHeader->dwStubSize;
211 +
212 + // If there is an original signature use that to determine the engine size.
213 + if (pBurnSectionHeader->dwOriginalSignatureOffset)
214 + {
215 + pSection->cbEngineSize = pBurnSectionHeader->dwOriginalSignatureOffset + pBurnSectionHeader->dwOriginalSignatureSize;
216 + }
217 + else if (dwSignatureOffset) // if there is a signature, use it.
218 + {
219 + pSection->cbEngineSize = dwSignatureOffset + cbSignature;
220 + }
221 + else // just use the stub and UX container as the size of the engine.
222 + {
223 + pSection->cbEngineSize = pSection->cbStub + pBurnSectionHeader->rgcbContainers[0];
224 + }
225 +
226 + pSection->qwBundleSize = static_cast<DWORD64>(llSize);
227 +
228 + pSection->dwChecksumOffset = dwChecksumOffset;
229 + pSection->dwCertificateTableOffset = dwCertificateTableOffset;
230 + pSection->dwOriginalChecksumAndSignatureOffset = dwOriginalChecksumAndSignatureOffset;
231 +
232 + pSection->dwOriginalChecksum = pBurnSectionHeader->dwOriginalChecksum;
233 + pSection->dwOriginalSignatureOffset = pBurnSectionHeader->dwOriginalSignatureOffset;
234 + pSection->dwOriginalSignatureSize = pBurnSectionHeader->dwOriginalSignatureSize;
235 +
236 + pSection->dwFormat = pBurnSectionHeader->dwFormat;
237 + pSection->cContainers = pBurnSectionHeader->cContainers;
238 + pSection->rgcbContainers = (DWORD*)MemAlloc(sizeof(DWORD) * pSection->cContainers, TRUE);
239 + ExitOnNull(pSection->rgcbContainers, hr, E_OUTOFMEMORY, "Failed to allocate memory for container sizes.");
240 +
241 + memcpy(pSection->rgcbContainers, pBurnSectionHeader->rgcbContainers, sizeof(DWORD) * pSection->cContainers);
242 +
243 + // TODO: verify more than just the GUID.
244 + hr = VerifySectionMatchesMemoryPEHeader(pBurnSectionHeader->guidBundleId);
245 + ExitOnRootFailure(hr, "PE Header from file didn't match PE Header in memory.");
246 +
247 +LExit:
248 + ReleaseMem(pBurnSectionHeader);
249 +
250 + return hr;
251 +}
252 +
253 +extern "C" void SectionUninitialize(
254 + __out BURN_SECTION* pSection
255 + )
256 +{
257 + ReleaseMem(pSection->rgcbContainers);
258 + memset(pSection, 0, sizeof(BURN_SECTION));
259 +}
260 +
261 +extern "C" HRESULT SectionGetAttachedContainerInfo(
262 + __in BURN_SECTION* pSection,
263 + __in DWORD iContainerIndex,
264 + __in DWORD dwExpectedType,
265 + __out DWORD64* pqwOffset,
266 + __out DWORD64* pqwSize,
267 + __out BOOL* pfPresent
268 + )
269 +{
270 + HRESULT hr = S_OK;
271 +
272 + // validate container info
273 + if (iContainerIndex >= pSection->cContainers)
274 + {
275 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
276 + ExitOnRootFailure(hr, "Failed to find container info, too few elements: %u", pSection->cContainers);
277 + }
278 + else if (dwExpectedType != pSection->dwFormat)
279 + {
280 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
281 + ExitOnRootFailure(hr, "Unexpected container format.");
282 + }
283 +
284 + // If we are asking for the UX container, find it right after the stub.
285 + if (0 == iContainerIndex)
286 + {
287 + *pqwOffset = pSection->cbStub;
288 + }
289 + else // attached containers start after the whole engine.
290 + {
291 + *pqwOffset = pSection->cbEngineSize;
292 + for (DWORD i = 1; i < iContainerIndex; ++i)
293 + {
294 + *pqwOffset += pSection->rgcbContainers[i];
295 + }
296 + }
297 +
298 + *pqwSize = pSection->rgcbContainers[iContainerIndex];
299 + *pfPresent = (*pqwOffset + *pqwSize) <= pSection->qwBundleSize;
300 +
301 + AssertSz(*pfPresent || pSection->qwBundleSize <= *pqwOffset, "An attached container should either be present or completely absent from the bundle. Found a case where the attached container is partially present which is wrong.");
302 +
303 +LExit:
304 + return hr;
305 +}
306 +
307 +HRESULT VerifySectionMatchesMemoryPEHeader(
308 + __in REFGUID pBundleId
309 + )
310 +{
311 + HRESULT hr = S_OK;
312 + BYTE* pbPEHeader = NULL;
313 + PIMAGE_DOS_HEADER pDosHeader = NULL;
314 + PIMAGE_NT_HEADERS pNtHeader = NULL;
315 + PIMAGE_SECTION_HEADER pSections = NULL;
316 + PIMAGE_SECTION_HEADER pSectionHeader = NULL;
317 + BURN_SECTION_HEADER* pBurnSectionHeader = NULL;
318 +
319 + pbPEHeader = reinterpret_cast<BYTE*>(::GetModuleHandleW(NULL));
320 + ExitOnNullWithLastError(pbPEHeader, hr, "Failed to get module handle to process.");
321 +
322 + //
323 + // First, make sure we have a valid DOS signature.
324 + //
325 +
326 + pDosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(pbPEHeader);
327 + if (IMAGE_DOS_SIGNATURE != pDosHeader->e_magic)
328 + {
329 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
330 + ExitOnRootFailure(hr, "Failed to find valid DOS image header in buffer.");
331 + }
332 +
333 + //
334 + // Now, make sure we have a valid NT signature.
335 + //
336 +
337 + pNtHeader = reinterpret_cast<PIMAGE_NT_HEADERS>(pbPEHeader + pDosHeader->e_lfanew);
338 + if (IMAGE_NT_SIGNATURE != pNtHeader->Signature)
339 + {
340 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
341 + ExitOnRootFailure(hr, "Failed to find valid NT image header in buffer.");
342 + }
343 +
344 + //
345 + // Finally, get into the section table and look for the Burn section info.
346 + //
347 +
348 + pSections = reinterpret_cast<PIMAGE_SECTION_HEADER>(pbPEHeader + pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER) + pNtHeader->FileHeader.SizeOfOptionalHeader);
349 +
350 + // Read sections one by one until we find our section.
351 + for (DWORD i = 0; ; ++i)
352 + {
353 + pSectionHeader = pSections + i;
354 +
355 + // Compare header name.
356 + C_ASSERT(sizeof(pSectionHeader->Name) == sizeof(BURN_SECTION_NAME) - 1);
357 + if (0 == memcmp(pSectionHeader->Name, BURN_SECTION_NAME, sizeof(pSectionHeader->Name)))
358 + {
359 + break;
360 + }
361 +
362 + // Fail if we hit the end.
363 + if (i + 1 >= pNtHeader->FileHeader.NumberOfSections)
364 + {
365 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
366 + ExitOnRootFailure(hr, "Failed to find Burn section.");
367 + }
368 + }
369 +
370 + //
371 + // We've arrived at the section info.
372 + //
373 +
374 + // Check size of section.
375 + if (sizeof(BURN_SECTION_HEADER) > pSectionHeader->SizeOfRawData)
376 + {
377 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
378 + ExitOnRootFailure(hr, "Failed to read section info, data to short: %u", pSectionHeader->SizeOfRawData);
379 + }
380 +
381 + // Get Burn section info.
382 + pBurnSectionHeader = reinterpret_cast<BURN_SECTION_HEADER*>(pbPEHeader + pSectionHeader->VirtualAddress);
383 +
384 + // Validate version of section info.
385 + if (BURN_SECTION_VERSION != pBurnSectionHeader->dwVersion)
386 + {
387 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
388 + ExitOnRootFailure(hr, "Failed to read section info, unsupported version: %08x", pBurnSectionHeader->dwVersion);
389 + }
390 +
391 + if (!::IsEqualGUID(pBundleId, pBurnSectionHeader->guidBundleId))
392 + {
393 + hr = E_INVALIDDATA;
394 + ExitOnRootFailure(hr, "Bundle guid didn't match the guid in the PE Header in memory.");
395 + }
396 +
397 +LExit:
398 + return hr;
399 +}
src/engine/section.h new
+54
@@ -0,0 +1,54 @@
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 +#if defined(__cplusplus)
6 +extern "C" {
7 +#endif
8 +
9 +
10 +// structs
11 +
12 +typedef struct _BURN_SECTION
13 +{
14 + HANDLE hEngineFile;
15 + HANDLE hSourceEngineFile;
16 +
17 + DWORD cbStub;
18 + DWORD cbEngineSize; // stub + UX container + original certficiate
19 + DWORD64 qwBundleSize; // stub + UX container + original certificate [+ attached containers* + final certificate]
20 +
21 + DWORD dwChecksumOffset;
22 + DWORD dwCertificateTableOffset;
23 + DWORD dwOriginalChecksumAndSignatureOffset;
24 +
25 + DWORD dwOriginalChecksum;
26 + DWORD dwOriginalSignatureOffset;
27 + DWORD dwOriginalSignatureSize;
28 +
29 + DWORD dwFormat;
30 + DWORD cContainers;
31 + DWORD* rgcbContainers;
32 +} BURN_SECTION;
33 +
34 +
35 +HRESULT SectionInitialize(
36 + __in BURN_SECTION* pSection,
37 + __in HANDLE hEngineFile,
38 + __in HANDLE hSourceEngineFile
39 + );
40 +void SectionUninitialize(
41 + __in BURN_SECTION* pSection
42 + );
43 +HRESULT SectionGetAttachedContainerInfo(
44 + __in BURN_SECTION* pSection,
45 + __in DWORD iContainerIndex,
46 + __in DWORD dwExpectedType,
47 + __out DWORD64* pqwOffset,
48 + __out DWORD64* pqwSize,
49 + __out BOOL* pfPresent
50 + );
51 +
52 +#if defined(__cplusplus)
53 +}
54 +#endif
src/engine/splashscreen.cpp new
+316
@@ -0,0 +1,316 @@
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 +using namespace Gdiplus;
6 +
7 +#define BURN_SPLASHSCREEN_CLASS_WINDOW L"WixBurnSplashScreen"
8 +#define IDB_SPLASHSCREEN 1
9 +
10 +// struct
11 +
12 +struct SPLASHSCREEN_INFO
13 +{
14 + Bitmap* pBitmap;
15 + Point pt;
16 + Size size;
17 +};
18 +
19 +struct SPLASHSCREEN_CONTEXT
20 +{
21 + HANDLE hIntializedEvent;
22 + HINSTANCE hInstance;
23 + LPCWSTR wzCaption;
24 +
25 + HWND* pHwnd;
26 +};
27 +
28 +// internal function definitions
29 +
30 +static DWORD WINAPI ThreadProc(
31 + __in LPVOID pvContext
32 + );
33 +static LRESULT CALLBACK WndProc(
34 + __in HWND hWnd,
35 + __in UINT uMsg,
36 + __in WPARAM wParam,
37 + __in LPARAM lParam
38 + );
39 +static void OnPaint(
40 + __in HDC hdc,
41 + __in SPLASHSCREEN_INFO* pSplashScreen
42 + );
43 +static HRESULT LoadSplashScreen(
44 + __in HMODULE hInstance,
45 + __in SPLASHSCREEN_INFO* pSplashScreen
46 + );
47 +
48 +
49 +// function definitions
50 +
51 +extern "C" void SplashScreenCreate(
52 + __in HINSTANCE hInstance,
53 + __in_z_opt LPCWSTR wzCaption,
54 + __out HWND* pHwnd
55 + )
56 +{
57 + HRESULT hr = S_OK;
58 + SPLASHSCREEN_CONTEXT context = { };
59 + HANDLE rgSplashScreenEvents[2] = { };
60 + DWORD dwSplashScreenThreadId = 0;
61 +
62 + rgSplashScreenEvents[0] = ::CreateEventW(NULL, TRUE, FALSE, NULL);
63 + ExitOnNullWithLastError(rgSplashScreenEvents[0], hr, "Failed to create modal event.");
64 +
65 + // create splash screen thread.
66 + context.hIntializedEvent = rgSplashScreenEvents[0];
67 + context.hInstance = hInstance;
68 + context.wzCaption = wzCaption;
69 + context.pHwnd = pHwnd;
70 +
71 + rgSplashScreenEvents[1] = ::CreateThread(NULL, 0, ThreadProc, &context, 0, &dwSplashScreenThreadId);
72 + ExitOnNullWithLastError(rgSplashScreenEvents[1], hr, "Failed to create UI thread.");
73 +
74 + // It doesn't really matter if the thread gets initialized (WAIT_OBJECT_0) or fails and exits
75 + // prematurely (WAIT_OBJECT_0 + 1), we just want to wait long enough for one of those two
76 + // events to happen.
77 + ::WaitForMultipleObjects(countof(rgSplashScreenEvents), rgSplashScreenEvents, FALSE, INFINITE);
78 +
79 +LExit:
80 + ReleaseHandle(rgSplashScreenEvents[1]);
81 + ReleaseHandle(rgSplashScreenEvents[0]);
82 +}
83 +
84 +extern "C" HRESULT SplashScreenDisplayError(
85 + __in BOOTSTRAPPER_DISPLAY display,
86 + __in_z LPCWSTR wzBundleName,
87 + __in HRESULT hrError
88 + )
89 +{
90 + HRESULT hr = S_OK;
91 + LPWSTR sczDisplayString = NULL;
92 +
93 + hr = StrAllocFromError(&sczDisplayString, hrError, NULL);
94 + ExitOnFailure(hr, "Failed to allocate string to display error message");
95 +
96 + Trace(REPORT_STANDARD, "Error message displayed because: %ls", sczDisplayString);
97 +
98 + if (BOOTSTRAPPER_DISPLAY_NONE == display || BOOTSTRAPPER_DISPLAY_PASSIVE == display || BOOTSTRAPPER_DISPLAY_EMBEDDED == display)
99 + {
100 + // Don't display the error dialog in these modes
101 + ExitFunction1(hr = S_OK);
102 + }
103 +
104 + ::MessageBoxW(NULL, sczDisplayString, wzBundleName, MB_OK | MB_ICONERROR | MB_SYSTEMMODAL);
105 +
106 +LExit:
107 + ReleaseStr(sczDisplayString);
108 +
109 + return hr;
110 +}
111 +
112 +
113 +static DWORD WINAPI ThreadProc(
114 + __in LPVOID pvContext
115 + )
116 +{
117 + HRESULT hr = S_OK;
118 +
119 + ULONG_PTR token = 0;
120 + GdiplusStartupInput input;
121 + GdiplusStartupOutput output = { };
122 +
123 + SPLASHSCREEN_CONTEXT* pContext = static_cast<SPLASHSCREEN_CONTEXT*>(pvContext);
124 + SPLASHSCREEN_INFO splashScreen = { };
125 +
126 + WNDCLASSW wc = { };
127 + BOOL fRegistered = TRUE;
128 + HWND hWnd = NULL;
129 +
130 + BOOL fRet = FALSE;
131 + MSG msg = { };
132 +
133 + input.GdiplusVersion = 1;
134 +
135 + hr = GdipInitialize(&input, &token, &output);
136 + ExitOnFailure(hr, "Failed to initialize GDI+.");
137 +
138 + hr = LoadSplashScreen(pContext->hInstance, &splashScreen);
139 + ExitOnFailure(hr, "Failed to load splash screen.");
140 +
141 + // Register the window class and create the window.
142 + wc.lpfnWndProc = WndProc;
143 + wc.hInstance = pContext->hInstance;
144 + wc.hCursor = ::LoadCursorW(NULL, (LPCWSTR)IDC_ARROW);
145 + wc.lpszClassName = BURN_SPLASHSCREEN_CLASS_WINDOW;
146 + if (!::RegisterClassW(&wc))
147 + {
148 + ExitWithLastError(hr, "Failed to register window.");
149 + }
150 +
151 + fRegistered = TRUE;
152 +
153 + hWnd = ::CreateWindowExW(WS_EX_TOOLWINDOW, wc.lpszClassName, pContext->wzCaption, WS_POPUP | WS_VISIBLE, splashScreen.pt.X, splashScreen.pt.Y, splashScreen.size.Width, splashScreen.size.Height, HWND_DESKTOP, NULL, pContext->hInstance, &splashScreen);
154 + ExitOnNullWithLastError(hWnd, hr, "Failed to create window.");
155 +
156 + // Return the splash screen window and free the main thread waiting for us to be initialized.
157 + *pContext->pHwnd = hWnd;
158 + ::SetEvent(pContext->hIntializedEvent);
159 +
160 + // Pump messages until the bootstrapper application destroys the window.
161 + while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0)))
162 + {
163 + if (-1 == fRet)
164 + {
165 + hr = E_UNEXPECTED;
166 + ExitOnFailure(hr, "Unexpected return value from message pump.");
167 + }
168 + else if (!::IsDialogMessageW(hWnd, &msg))
169 + {
170 + ::TranslateMessage(&msg);
171 + ::DispatchMessageW(&msg);
172 + }
173 + }
174 +
175 +LExit:
176 + if (fRegistered)
177 + {
178 + ::UnregisterClassW(BURN_SPLASHSCREEN_CLASS_WINDOW, pContext->hInstance);
179 + }
180 +
181 + if (splashScreen.pBitmap)
182 + {
183 + delete splashScreen.pBitmap;
184 + }
185 +
186 + if (token)
187 + {
188 + GdipUninitialize(token);
189 + }
190 +
191 + return hr;
192 +}
193 +
194 +static LRESULT CALLBACK WndProc(
195 + __in HWND hWnd,
196 + __in UINT uMsg,
197 + __in WPARAM wParam,
198 + __in LPARAM lParam
199 + )
200 +{
201 + LRESULT lres = 0;
202 + SPLASHSCREEN_INFO* pSplashScreen = reinterpret_cast<SPLASHSCREEN_INFO*>(::GetWindowLongW(hWnd, GWLP_USERDATA));
203 +
204 + switch (uMsg)
205 + {
206 + case WM_NCCREATE:
207 + {
208 + LPCREATESTRUCTW lpcs = reinterpret_cast<LPCREATESTRUCTW>(lParam);
209 + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(lpcs->lpCreateParams));
210 + }
211 + break;
212 +
213 + case WM_NCDESTROY:
214 + lres = ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
215 + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0);
216 + return lres;
217 +
218 + case WM_NCHITTEST:
219 + return HTCAPTION; // allow window to be moved by grabbing any pixel.
220 +
221 + case WM_DESTROY:
222 + ::PostQuitMessage(0);
223 + return 0;
224 +
225 + case WM_ERASEBKGND:
226 + // The splash screen image will be repainted in its entirety.
227 + return 1;
228 +
229 + case WM_PAINT:
230 + {
231 + PAINTSTRUCT ps = { };
232 +
233 + HDC hdc = BeginPaint(hWnd, &ps);
234 + OnPaint(hdc, pSplashScreen);
235 + EndPaint(hWnd, &ps);
236 + }
237 + return 0;
238 + }
239 +
240 + return ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
241 +}
242 +
243 +static void OnPaint(
244 + __in HDC hdc,
245 + __in SPLASHSCREEN_INFO* pSplashScreen
246 + )
247 +{
248 + // Use high-quality bicubuc stretching from GDI+ which looks better than GDI.
249 + Graphics graphics(hdc);
250 + graphics.SetInterpolationMode(InterpolationModeHighQualityBicubic);
251 +
252 + Rect dst(0, 0, pSplashScreen->size.Width, pSplashScreen->size.Height);
253 + Status status = graphics.DrawImage(pSplashScreen->pBitmap, dst);
254 +
255 +#if DEBUG
256 + HRESULT hr = GdipHresultFromStatus(status);
257 + TraceError(hr, "Failed to draw splash screen bitmap.");
258 +#else
259 + UNREFERENCED_PARAMETER(status);
260 +#endif
261 +}
262 +
263 +static HRESULT LoadSplashScreen(
264 + __in HMODULE hInstance,
265 + __in SPLASHSCREEN_INFO* pSplashScreen
266 + )
267 +{
268 + HRESULT hr = S_OK;
269 + POINT ptCursor = { };
270 + HMONITOR hMonitor = NULL;
271 + MONITORINFOEXW mi;
272 + HDC hdc = NULL;
273 + UINT dpiX = 0;
274 + UINT dpiY = 0;
275 +
276 + pSplashScreen->pBitmap = Bitmap::FromResource(hInstance, MAKEINTRESOURCEW(IDB_SPLASHSCREEN));
277 + ExitOnNull(pSplashScreen->pBitmap, hr, E_INVALIDDATA, "Failed to find the splash screen bitmap.");
278 + ExitOnGdipFailure(pSplashScreen->pBitmap->GetLastStatus(), hr, "Failed to load the splash screen bitmap.");
279 +
280 + pSplashScreen->pt.X = CW_USEDEFAULT;
281 + pSplashScreen->pt.Y = CW_USEDEFAULT;
282 + pSplashScreen->size.Width = pSplashScreen->pBitmap->GetWidth();
283 + pSplashScreen->size.Height = pSplashScreen->pBitmap->GetHeight();
284 +
285 + // Stretch and center the window on the monitor with the mouse.
286 + if (::GetCursorPos(&ptCursor))
287 + {
288 + hMonitor = ::MonitorFromPoint(ptCursor, MONITOR_DEFAULTTONEAREST);
289 + if (hMonitor)
290 + {
291 + ZeroMemory(&mi, sizeof(mi));
292 + mi.cbSize = sizeof(mi);
293 +
294 + if (::GetMonitorInfoW(hMonitor, &mi))
295 + {
296 + hdc = ::CreateDCW(L"DISPLAY", mi.szDevice, NULL, NULL);
297 + if (hdc)
298 + {
299 + dpiX = ::GetDeviceCaps(hdc, LOGPIXELSX);
300 + dpiY = ::GetDeviceCaps(hdc, LOGPIXELSY);
301 +
302 + pSplashScreen->size.Width = pSplashScreen->size.Width * dpiX / 96;
303 + pSplashScreen->size.Height = pSplashScreen->size.Height * dpiY / 96;
304 +
305 + ::ReleaseDC(NULL, hdc);
306 + }
307 +
308 + pSplashScreen->pt.X = mi.rcWork.left + (mi.rcWork.right - mi.rcWork.left - pSplashScreen->size.Width) / 2;
309 + pSplashScreen->pt.Y = mi.rcWork.top + (mi.rcWork.bottom - mi.rcWork.top - pSplashScreen->size.Height) / 2;
310 + }
311 + }
312 + }
313 +
314 +LExit:
315 + return hr;
316 +}
src/engine/splashscreen.h new
+31
@@ -0,0 +1,31 @@
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 +#if defined(__cplusplus)
6 +extern "C" {
7 +#endif
8 +
9 +
10 +// constants
11 +
12 +
13 +// structs
14 +
15 +
16 +// functions
17 +
18 +void SplashScreenCreate(
19 + __in HINSTANCE hInstance,
20 + __in_z_opt LPCWSTR wzCaption,
21 + __out HWND* pHwnd
22 + );
23 +HRESULT SplashScreenDisplayError(
24 + __in BOOTSTRAPPER_DISPLAY display,
25 + __in_z LPCWSTR wzBundleName,
26 + __in HRESULT hrError
27 + );
28 +
29 +#if defined(__cplusplus)
30 +}
31 +#endif
src/engine/uithread.cpp new
+220
@@ -0,0 +1,220 @@
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 +#define BURN_UITHREAD_CLASS_WINDOW L"WixBurnMessageWindow"
6 +
7 +
8 +// structs
9 +
10 +struct UITHREAD_CONTEXT
11 +{
12 + HANDLE hInitializedEvent;
13 + HINSTANCE hInstance;
14 + BURN_ENGINE_STATE* pEngineState;
15 +};
16 +
17 +struct UITHREAD_INFO
18 +{
19 + BOOL fElevated;
20 + BURN_USER_EXPERIENCE* pUserExperience;
21 +};
22 +
23 +
24 +// internal function declarations
25 +
26 +static DWORD WINAPI ThreadProc(
27 + __in LPVOID pvContext
28 + );
29 +
30 +static LRESULT CALLBACK WndProc(
31 + __in HWND hWnd,
32 + __in UINT uMsg,
33 + __in WPARAM wParam,
34 + __in LPARAM lParam
35 + );
36 +
37 +
38 +// function definitions
39 +
40 +HRESULT UiCreateMessageWindow(
41 + __in HINSTANCE hInstance,
42 + __in BURN_ENGINE_STATE* pEngineState
43 + )
44 +{
45 + HRESULT hr = S_OK;
46 + HANDLE rgWaitHandles[2] = { };
47 + UITHREAD_CONTEXT context = { };
48 +
49 + // Create event to signal after the UI thread / window is initialized.
50 + rgWaitHandles[0] = ::CreateEventW(NULL, TRUE, FALSE, NULL);
51 + ExitOnNullWithLastError(rgWaitHandles[0], hr, "Failed to create initialization event.");
52 +
53 + // Pass necessary information to create the window.
54 + context.hInitializedEvent = rgWaitHandles[0];
55 + context.hInstance = hInstance;
56 + context.pEngineState = pEngineState;
57 +
58 + // Create our separate UI thread.
59 + rgWaitHandles[1] = ::CreateThread(NULL, 0, ThreadProc, &context, 0, NULL);
60 + ExitOnNullWithLastError(rgWaitHandles[1], hr, "Failed to create the UI thread.");
61 +
62 + // Wait for either the thread to be initialized or the window to exit / fail prematurely.
63 + ::WaitForMultipleObjects(countof(rgWaitHandles), rgWaitHandles, FALSE, INFINITE);
64 +
65 + pEngineState->hMessageWindowThread = rgWaitHandles[1];
66 + rgWaitHandles[1] = NULL;
67 +
68 +LExit:
69 + ReleaseHandle(rgWaitHandles[1]);
70 + ReleaseHandle(rgWaitHandles[0]);
71 +
72 + return hr;
73 +}
74 +
75 +void UiCloseMessageWindow(
76 + __in BURN_ENGINE_STATE* pEngineState
77 + )
78 +{
79 + if (::IsWindow(pEngineState->hMessageWindow))
80 + {
81 + ::PostMessageW(pEngineState->hMessageWindow, WM_CLOSE, 0, 0);
82 +
83 + // Give the window 15 seconds to close because if it stays open it can prevent
84 + // the engine from starting a reboot (should a reboot actually be necessary).
85 + ::WaitForSingleObject(pEngineState->hMessageWindowThread, 15 * 1000);
86 + }
87 +}
88 +
89 +
90 +// internal function definitions
91 +
92 +static DWORD WINAPI ThreadProc(
93 + __in LPVOID pvContext
94 + )
95 +{
96 + HRESULT hr = S_OK;
97 + UITHREAD_CONTEXT* pContext = static_cast<UITHREAD_CONTEXT*>(pvContext);
98 + UITHREAD_INFO info = { };
99 +
100 + WNDCLASSW wc = { };
101 + BOOL fRegistered = TRUE;
102 + HWND hWnd = NULL;
103 +
104 + BOOL fRet = FALSE;
105 + MSG msg = { };
106 +
107 + BURN_ENGINE_STATE* pEngineState = pContext->pEngineState;
108 + BOOL fElevated = BURN_MODE_ELEVATED == pContext->pEngineState->mode;
109 +
110 + // If elevated, set up the thread local storage to store the correct pipe to communicate logging.
111 + if (fElevated)
112 + {
113 + Assert(TLS_OUT_OF_INDEXES != pEngineState->dwElevatedLoggingTlsId);
114 +
115 + if (!::TlsSetValue(pEngineState->dwElevatedLoggingTlsId, pEngineState->companionConnection.hPipe))
116 + {
117 + // If the function failed we cannot write to the pipe so just terminate.
118 + ExitFunction1(hr = E_INVALIDSTATE);
119 + }
120 + }
121 +
122 + wc.lpfnWndProc = WndProc;
123 + wc.hInstance = pContext->hInstance;
124 + wc.lpszClassName = BURN_UITHREAD_CLASS_WINDOW;
125 +
126 + if (!::RegisterClassW(&wc))
127 + {
128 + ExitWithLastError(hr, "Failed to register window.");
129 + }
130 +
131 + fRegistered = TRUE;
132 +
133 + info.fElevated = fElevated;
134 + info.pUserExperience = &pEngineState->userExperience;
135 +
136 + // Create the window to handle reboots without activating it.
137 + hWnd = ::CreateWindowExW(WS_EX_TOOLWINDOW, wc.lpszClassName, NULL, WS_POPUP | WS_VISIBLE, CW_USEDEFAULT, SW_SHOWNA, 0, 0, HWND_DESKTOP, NULL, pContext->hInstance, &info);
138 + ExitOnNullWithLastError(hWnd, hr, "Failed to create window.");
139 +
140 + // Persist the window handle and let the caller know we've initialized.
141 + pEngineState->hMessageWindow = hWnd;
142 + ::SetEvent(pContext->hInitializedEvent);
143 +
144 + // Pump messages until the window is closed.
145 + while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0)))
146 + {
147 + if (-1 == fRet)
148 + {
149 + hr = E_UNEXPECTED;
150 + ExitOnFailure(hr, "Unexpected return value from message pump.");
151 + }
152 + else if (!::IsDialogMessageW(msg.hwnd, &msg))
153 + {
154 + ::TranslateMessage(&msg);
155 + ::DispatchMessageW(&msg);
156 + }
157 + }
158 +
159 +LExit:
160 + if (fRegistered)
161 + {
162 + ::UnregisterClassW(BURN_UITHREAD_CLASS_WINDOW, pContext->hInstance);
163 + }
164 +
165 + return hr;
166 +}
167 +
168 +static LRESULT CALLBACK WndProc(
169 + __in HWND hWnd,
170 + __in UINT uMsg,
171 + __in WPARAM wParam,
172 + __in LPARAM lParam
173 + )
174 +{
175 + switch (uMsg)
176 + {
177 + case WM_NCCREATE:
178 + {
179 + LPCREATESTRUCTW lpcs = reinterpret_cast<LPCREATESTRUCTW>(lParam);
180 + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(lpcs->lpCreateParams));
181 + break;
182 + }
183 +
184 + case WM_NCDESTROY:
185 + {
186 + LRESULT lRes = ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
187 + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0);
188 + return lRes;
189 + }
190 +
191 + case WM_QUERYENDSESSION:
192 + {
193 + DWORD dwEndSession = static_cast<DWORD>(lParam);
194 + BOOL fCritical = ENDSESSION_CRITICAL & dwEndSession;
195 + BOOL fCancel = TRUE;
196 + BOOL fRet = FALSE;
197 +
198 + // Always block shutdown in the elevated process, but ask the BA in the non-elevated.
199 + UITHREAD_INFO* pInfo = reinterpret_cast<UITHREAD_INFO*>(::GetWindowLongW(hWnd, GWLP_USERDATA));
200 + if (!pInfo->fElevated)
201 + {
202 + // TODO: instead of recommending canceling all non-critical shutdowns, maybe we should only recommend cancel
203 + // when the engine is doing work?
204 + fCancel = !fCritical;
205 + // TODO: There's a race condition here where the BA may not have been loaded, or already was unloaded.
206 + UserExperienceOnSystemShutdown(pInfo->pUserExperience, dwEndSession, &fCancel);
207 + }
208 +
209 + fRet = !fCancel;
210 + LogId(REPORT_STANDARD, MSG_SYSTEM_SHUTDOWN, LoggingBoolToString(fCritical), LoggingBoolToString(pInfo->fElevated), LoggingBoolToString(fRet));
211 + return fRet;
212 + }
213 +
214 + case WM_DESTROY:
215 + ::PostQuitMessage(0);
216 + return 0;
217 + }
218 +
219 + return ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
220 +}
src/engine/uithread.h new
+23
@@ -0,0 +1,23 @@
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 +#if defined(__cplusplus)
6 +extern "C" {
7 +#endif
8 +
9 +
10 +// functions
11 +
12 +HRESULT UiCreateMessageWindow(
13 + __in HINSTANCE hInstance,
14 + __in BURN_ENGINE_STATE* pEngineState
15 + );
16 +
17 +void UiCloseMessageWindow(
18 + __in BURN_ENGINE_STATE* pEngineState
19 + );
20 +
21 +#if defined(__cplusplus)
22 +}
23 +#endif
src/engine/update.cpp new
+44
@@ -0,0 +1,44 @@
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 +// internal function declarations
7 +
8 +
9 +// function definitions
10 +
11 +extern "C" HRESULT UpdateParseFromXml(
12 + __in BURN_UPDATE* pUpdate,
13 + __in IXMLDOMNode* pixnBundle
14 + )
15 +{
16 + HRESULT hr = S_OK;
17 + IXMLDOMNode* pixnUpdateNode = NULL;
18 +
19 + hr = XmlSelectSingleNode(pixnBundle, L"Update", &pixnUpdateNode);
20 + if (S_FALSE == hr)
21 + {
22 + ExitFunction1(hr = S_OK);
23 + }
24 + ExitOnFailure(hr, "Failed to select Bundle/Update node.");
25 +
26 + // @Location
27 + hr = XmlGetAttributeEx(pixnUpdateNode, L"Location", &pUpdate->sczUpdateSource);
28 + ExitOnFailure(hr, "Failed to get Update@Location.");
29 +
30 +LExit:
31 + ReleaseObject(pixnUpdateNode);
32 +
33 + return hr;
34 +}
35 +
36 +extern "C" void UpdateUninitialize(
37 + __in BURN_UPDATE* pUpdate
38 + )
39 +{
40 + PackageUninitialize(&pUpdate->package);
41 +
42 + ReleaseStr(pUpdate->sczUpdateSource);
43 + memset(pUpdate, 0, sizeof(BURN_UPDATE));
44 +}
src/engine/update.h new
+33
@@ -0,0 +1,33 @@
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 +#if defined(__cplusplus)
6 +extern "C" {
7 +#endif
8 +
9 +
10 +// structs
11 +
12 +typedef struct _BURN_UPDATE
13 +{
14 + BOOL fUpdateAvailable;
15 + LPWSTR sczUpdateSource;
16 +
17 + BURN_PACKAGE package;
18 +} BURN_UPDATE;
19 +
20 +
21 +// function declarations
22 +
23 +HRESULT UpdateParseFromXml(
24 + __in BURN_UPDATE* pUpdate,
25 + __in IXMLDOMNode* pixnBundle
26 + );
27 +void UpdateUninitialize(
28 + __in BURN_UPDATE* pUpdate
29 + );
30 +
31 +#if defined(__cplusplus)
32 +}
33 +#endif
src/engine/userexperience.cpp new
+2122
@@ -0,0 +1,2122 @@
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 +// internal function declarations
6 +
7 +static int FilterResult(
8 + __in DWORD dwAllowedResults,
9 + __in int nResult
10 + );
11 +
12 +static HRESULT FilterExecuteResult(
13 + __in BURN_USER_EXPERIENCE* pUserExperience,
14 + __in HRESULT hrStatus,
15 + __in BOOL fRollback,
16 + __in BOOL fCancel,
17 + __in LPCWSTR sczEventName
18 + );
19 +
20 +
21 +// function definitions
22 +
23 +/*******************************************************************
24 + UserExperienceParseFromXml -
25 +
26 +*******************************************************************/
27 +extern "C" HRESULT UserExperienceParseFromXml(
28 + __in BURN_USER_EXPERIENCE* pUserExperience,
29 + __in IXMLDOMNode* pixnBundle
30 + )
31 +{
32 + HRESULT hr = S_OK;
33 + IXMLDOMNode* pixnUserExperienceNode = NULL;
34 +
35 + // select UX node
36 + hr = XmlSelectSingleNode(pixnBundle, L"UX", &pixnUserExperienceNode);
37 + if (S_FALSE == hr)
38 + {
39 + hr = E_NOTFOUND;
40 + }
41 + ExitOnFailure(hr, "Failed to select user experience node.");
42 +
43 + // parse splash screen
44 + hr = XmlGetYesNoAttribute(pixnUserExperienceNode, L"SplashScreen", &pUserExperience->fSplashScreen);
45 + if (E_NOTFOUND != hr)
46 + {
47 + ExitOnFailure(hr, "Failed to to get UX/@SplashScreen");
48 + }
49 +
50 + // parse payloads
51 + hr = PayloadsParseFromXml(&pUserExperience->payloads, NULL, NULL, pixnUserExperienceNode);
52 + ExitOnFailure(hr, "Failed to parse user experience payloads.");
53 +
54 + // make sure we have at least one payload
55 + if (0 == pUserExperience->payloads.cPayloads)
56 + {
57 + hr = E_UNEXPECTED;
58 + ExitOnFailure(hr, "Too few UX payloads.");
59 + }
60 +
61 +LExit:
62 + ReleaseObject(pixnUserExperienceNode);
63 +
64 + return hr;
65 +}
66 +
67 +/*******************************************************************
68 + UserExperienceUninitialize -
69 +
70 +*******************************************************************/
71 +extern "C" void UserExperienceUninitialize(
72 + __in BURN_USER_EXPERIENCE* pUserExperience
73 + )
74 +{
75 + ReleaseStr(pUserExperience->sczTempDirectory);
76 + PayloadsUninitialize(&pUserExperience->payloads);
77 +
78 + // clear struct
79 + memset(pUserExperience, 0, sizeof(BURN_USER_EXPERIENCE));
80 +}
81 +
82 +/*******************************************************************
83 + UserExperienceLoad -
84 +
85 +*******************************************************************/
86 +extern "C" HRESULT UserExperienceLoad(
87 + __in BURN_USER_EXPERIENCE* pUserExperience,
88 + __in BOOTSTRAPPER_ENGINE_CONTEXT* pEngineContext,
89 + __in BOOTSTRAPPER_COMMAND* pCommand
90 + )
91 +{
92 + HRESULT hr = S_OK;
93 + BOOTSTRAPPER_CREATE_ARGS args = { };
94 + BOOTSTRAPPER_CREATE_RESULTS results = { };
95 +
96 + args.cbSize = sizeof(BOOTSTRAPPER_CREATE_ARGS);
97 + args.pCommand = pCommand;
98 + args.pfnBootstrapperEngineProc = EngineForApplicationProc;
99 + args.pvBootstrapperEngineProcContext = pEngineContext;
100 + args.qwEngineAPIVersion = MAKEQWORDVERSION(0, 0, 0, 5); // TODO: need to decide whether to keep this, and if so when to update it.
101 +
102 + results.cbSize = sizeof(BOOTSTRAPPER_CREATE_RESULTS);
103 +
104 + // Load BA DLL.
105 + pUserExperience->hUXModule = ::LoadLibraryExW(pUserExperience->payloads.rgPayloads[0].sczLocalFilePath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
106 + ExitOnNullWithLastError(pUserExperience->hUXModule, hr, "Failed to load UX DLL.");
107 +
108 + // Get BootstrapperApplicationCreate entry-point.
109 + PFN_BOOTSTRAPPER_APPLICATION_CREATE pfnCreate = (PFN_BOOTSTRAPPER_APPLICATION_CREATE)::GetProcAddress(pUserExperience->hUXModule, "BootstrapperApplicationCreate");
110 + ExitOnNullWithLastError(pfnCreate, hr, "Failed to get BootstrapperApplicationCreate entry-point");
111 +
112 + // Create BA.
113 + hr = pfnCreate(&args, &results);
114 + ExitOnFailure(hr, "Failed to create BA.");
115 +
116 + pUserExperience->pfnBAProc = results.pfnBootstrapperApplicationProc;
117 + pUserExperience->pvBAProcContext = results.pvBootstrapperApplicationProcContext;
118 +
119 +LExit:
120 + return hr;
121 +}
122 +
123 +/*******************************************************************
124 + UserExperienceUnload -
125 +
126 +*******************************************************************/
127 +extern "C" HRESULT UserExperienceUnload(
128 + __in BURN_USER_EXPERIENCE* pUserExperience
129 + )
130 +{
131 + HRESULT hr = S_OK;
132 +
133 + if (pUserExperience->hUXModule)
134 + {
135 + // Get BootstrapperApplicationDestroy entry-point and call it if it exists.
136 + PFN_BOOTSTRAPPER_APPLICATION_DESTROY pfnDestroy = (PFN_BOOTSTRAPPER_APPLICATION_DESTROY)::GetProcAddress(pUserExperience->hUXModule, "BootstrapperApplicationDestroy");
137 + if (pfnDestroy)
138 + {
139 + pfnDestroy();
140 + }
141 +
142 + // Free BA DLL.
143 + if (!::FreeLibrary(pUserExperience->hUXModule))
144 + {
145 + hr = HRESULT_FROM_WIN32(::GetLastError());
146 + TraceError(hr, "Failed to unload BA DLL.");
147 + }
148 + pUserExperience->hUXModule = NULL;
149 + }
150 +
151 +//LExit:
152 + return hr;
153 +}
154 +
155 +extern "C" HRESULT UserExperienceEnsureWorkingFolder(
156 + __in LPCWSTR wzBundleId,
157 + __deref_out_z LPWSTR* psczUserExperienceWorkingFolder
158 + )
159 +{
160 + HRESULT hr = S_OK;
161 + LPWSTR sczWorkingFolder = NULL;
162 +
163 + hr = CacheEnsureWorkingFolder(wzBundleId, &sczWorkingFolder);
164 + ExitOnFailure(hr, "Failed to create working folder.");
165 +
166 + hr = StrAllocFormatted(psczUserExperienceWorkingFolder, L"%ls%ls\\", sczWorkingFolder, L".ba");
167 + ExitOnFailure(hr, "Failed to calculate the bootstrapper application working path.");
168 +
169 + hr = DirEnsureExists(*psczUserExperienceWorkingFolder, NULL);
170 + ExitOnFailure(hr, "Failed create bootstrapper application working folder.");
171 +
172 +LExit:
173 + ReleaseStr(sczWorkingFolder);
174 +
175 + return hr;
176 +}
177 +
178 +
179 +extern "C" HRESULT UserExperienceRemove(
180 + __in BURN_USER_EXPERIENCE* pUserExperience
181 + )
182 +{
183 + HRESULT hr = S_OK;
184 +
185 + // Remove temporary UX directory
186 + if (pUserExperience->sczTempDirectory)
187 + {
188 + hr = DirEnsureDeleteEx(pUserExperience->sczTempDirectory, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE);
189 + TraceError(hr, "Could not delete bootstrapper application folder. Some files will be left in the temp folder.");
190 + }
191 +
192 +//LExit:
193 + return hr;
194 +}
195 +
196 +extern "C" int UserExperienceSendError(
197 + __in BURN_USER_EXPERIENCE* pUserExperience,
198 + __in BOOTSTRAPPER_ERROR_TYPE errorType,
199 + __in_z_opt LPCWSTR wzPackageId,
200 + __in HRESULT hrCode,
201 + __in_z_opt LPCWSTR wzError,
202 + __in DWORD uiFlags,
203 + __in int nRecommendation
204 + )
205 +{
206 + int nResult = nRecommendation;
207 + DWORD dwCode = HRESULT_CODE(hrCode);
208 + LPWSTR sczError = NULL;
209 +
210 + // If no error string was provided, try to get the error string from the HRESULT.
211 + if (!wzError)
212 + {
213 + if (SUCCEEDED(StrAllocFromError(&sczError, hrCode, NULL)))
214 + {
215 + wzError = sczError;
216 + }
217 + }
218 +
219 + UserExperienceOnError(pUserExperience, errorType, wzPackageId, dwCode, wzError, uiFlags, 0, NULL, &nResult); // ignore return value.
220 +
221 + ReleaseStr(sczError);
222 + return nResult;
223 +}
224 +
225 +extern "C" HRESULT UserExperienceActivateEngine(
226 + __in BURN_USER_EXPERIENCE* pUserExperience,
227 + __out_opt BOOL* pfActivated
228 + )
229 +{
230 + HRESULT hr = S_OK;
231 + BOOL fActivated;
232 +
233 + ::EnterCriticalSection(&pUserExperience->csEngineActive);
234 + if (InterlockedCompareExchange(reinterpret_cast<LONG*>(&pUserExperience->fEngineActive), TRUE, FALSE))
235 + {
236 + AssertSz(FALSE, "Engine should have been deactivated before activating it.");
237 +
238 + fActivated = FALSE;
239 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_STATE);
240 + }
241 + else
242 + {
243 + fActivated = TRUE;
244 + }
245 + ::LeaveCriticalSection(&pUserExperience->csEngineActive);
246 +
247 + if (pfActivated)
248 + {
249 + *pfActivated = fActivated;
250 + }
251 + ExitOnRootFailure(hr, "Engine active cannot be changed because it was already in that state.");
252 +
253 +LExit:
254 + return hr;
255 +}
256 +
257 +extern "C" void UserExperienceDeactivateEngine(
258 + __in BURN_USER_EXPERIENCE* pUserExperience
259 + )
260 +{
261 + BOOL fActive = InterlockedExchange(reinterpret_cast<LONG*>(&pUserExperience->fEngineActive), FALSE);
262 + fActive = fActive; // prevents warning in "ship" build.
263 + AssertSz(fActive, "Engine should have be active before deactivating it.");
264 +}
265 +
266 +extern "C" HRESULT UserExperienceEnsureEngineInactive(
267 + __in BURN_USER_EXPERIENCE* pUserExperience
268 + )
269 +{
270 + HRESULT hr = pUserExperience->fEngineActive ? HRESULT_FROM_WIN32(ERROR_BUSY) : S_OK;
271 + ExitOnRootFailure(hr, "Engine is active, cannot proceed.");
272 +
273 +LExit:
274 + return hr;
275 +}
276 +
277 +extern "C" void UserExperienceExecuteReset(
278 + __in BURN_USER_EXPERIENCE* pUserExperience
279 + )
280 +{
281 + pUserExperience->hrApplyError = S_OK;
282 + pUserExperience->hwndApply = NULL;
283 +}
284 +
285 +extern "C" void UserExperienceExecutePhaseComplete(
286 + __in BURN_USER_EXPERIENCE* pUserExperience,
287 + __in HRESULT hrResult
288 + )
289 +{
290 + if (FAILED(hrResult))
291 + {
292 + pUserExperience->hrApplyError = hrResult;
293 + }
294 +}
295 +
296 +EXTERN_C BAAPI UserExperienceOnApplyBegin(
297 + __in BURN_USER_EXPERIENCE* pUserExperience,
298 + __in DWORD dwPhaseCount
299 + )
300 +{
301 + HRESULT hr = S_OK;
302 + BA_ONAPPLYBEGIN_ARGS args = { };
303 + BA_ONAPPLYBEGIN_RESULTS results = { };
304 +
305 + args.cbSize = sizeof(args);
306 + args.dwPhaseCount = dwPhaseCount;
307 +
308 + results.cbSize = sizeof(results);
309 +
310 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYBEGIN, &args, &results, pUserExperience->pvBAProcContext);
311 + ExitOnFailure(hr, "BA OnApplyBegin failed.");
312 +
313 + if (results.fCancel)
314 + {
315 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
316 + }
317 +
318 +LExit:
319 + return hr;
320 +}
321 +
322 +EXTERN_C BAAPI UserExperienceOnApplyComplete(
323 + __in BURN_USER_EXPERIENCE* pUserExperience,
324 + __in HRESULT hrStatus,
325 + __in BOOTSTRAPPER_APPLY_RESTART restart,
326 + __inout BOOTSTRAPPER_APPLYCOMPLETE_ACTION* pAction
327 + )
328 +{
329 + HRESULT hr = S_OK;
330 + BA_ONAPPLYCOMPLETE_ARGS args = { };
331 + BA_ONAPPLYCOMPLETE_RESULTS results = { };
332 +
333 + args.cbSize = sizeof(args);
334 + args.hrStatus = hrStatus;
335 + args.restart = restart;
336 + args.recommendation = *pAction;
337 +
338 + results.cbSize = sizeof(results);
339 + results.action = *pAction;
340 +
341 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYCOMPLETE, &args, &results, pUserExperience->pvBAProcContext);
342 + ExitOnFailure(hr, "BA OnApplyComplete failed.");
343 +
344 + *pAction = results.action;
345 +
346 +LExit:
347 + return hr;
348 +}
349 +
350 +EXTERN_C BAAPI UserExperienceOnCacheAcquireBegin(
351 + __in BURN_USER_EXPERIENCE* pUserExperience,
352 + __in_z_opt LPCWSTR wzPackageOrContainerId,
353 + __in_z_opt LPCWSTR wzPayloadId,
354 + __in BOOTSTRAPPER_CACHE_OPERATION operation,
355 + __in_z LPCWSTR wzSource
356 + )
357 +{
358 + HRESULT hr = S_OK;
359 + BA_ONCACHEACQUIREBEGIN_ARGS args = { };
360 + BA_ONCACHEACQUIREBEGIN_RESULTS results = { };
361 +
362 + args.cbSize = sizeof(args);
363 + args.wzPackageOrContainerId = wzPackageOrContainerId;
364 + args.wzPayloadId = wzPayloadId;
365 + args.operation = operation;
366 + args.wzSource = wzSource;
367 +
368 + results.cbSize = sizeof(results);
369 +
370 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIREBEGIN, &args, &results, pUserExperience->pvBAProcContext);
371 + ExitOnFailure(hr, "BA OnCacheAcquireBegin failed.");
372 +
373 + if (results.fCancel)
374 + {
375 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
376 + }
377 +
378 +LExit:
379 + return hr;
380 +}
381 +
382 +EXTERN_C BAAPI UserExperienceOnCacheAcquireComplete(
383 + __in BURN_USER_EXPERIENCE* pUserExperience,
384 + __in_z_opt LPCWSTR wzPackageOrContainerId,
385 + __in_z_opt LPCWSTR wzPayloadId,
386 + __in HRESULT hrStatus,
387 + __inout BOOL* pfRetry
388 + )
389 +{
390 + HRESULT hr = S_OK;
391 + BA_ONCACHEACQUIRECOMPLETE_ARGS args = { };
392 + BA_ONCACHEACQUIRECOMPLETE_RESULTS results = { };
393 +
394 + args.cbSize = sizeof(args);
395 + args.wzPackageOrContainerId = wzPackageOrContainerId;
396 + args.wzPayloadId = wzPayloadId;
397 + args.hrStatus = hrStatus;
398 + args.recommendation = *pfRetry ? BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION_RETRY : BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION_NONE;
399 +
400 + results.cbSize = sizeof(results);
401 + results.action = args.recommendation;
402 +
403 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIRECOMPLETE, &args, &results, pUserExperience->pvBAProcContext);
404 + ExitOnFailure(hr, "BA OnCacheAcquireComplete failed.");
405 +
406 + if (FAILED(hrStatus))
407 + {
408 + *pfRetry = BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION_RETRY == results.action;
409 + }
410 +
411 +LExit:
412 + return hr;
413 +}
414 +
415 +EXTERN_C BAAPI UserExperienceOnCacheAcquireProgress(
416 + __in BURN_USER_EXPERIENCE* pUserExperience,
417 + __in_z_opt LPCWSTR wzPackageOrContainerId,
418 + __in_z_opt LPCWSTR wzPayloadId,
419 + __in DWORD64 dw64Progress,
420 + __in DWORD64 dw64Total,
421 + __in DWORD dwOverallPercentage
422 + )
423 +{
424 + HRESULT hr = S_OK;
425 + BA_ONCACHEACQUIREPROGRESS_ARGS args = { };
426 + BA_ONCACHEACQUIREPROGRESS_RESULTS results = { };
427 +
428 + args.cbSize = sizeof(args);
429 + args.wzPackageOrContainerId = wzPackageOrContainerId;
430 + args.wzPayloadId = wzPayloadId;
431 + args.dw64Progress = dw64Progress;
432 + args.dw64Total = dw64Total;
433 + args.dwOverallPercentage = dwOverallPercentage;
434 +
435 + results.cbSize = sizeof(results);
436 +
437 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIREPROGRESS, &args, &results, pUserExperience->pvBAProcContext);
438 + ExitOnFailure(hr, "BA OnCacheAcquireProgress failed.");
439 +
440 + if (results.fCancel)
441 + {
442 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
443 + }
444 +
445 +LExit:
446 + return hr;
447 +}
448 +
449 +EXTERN_C BAAPI UserExperienceOnCacheBegin(
450 + __in BURN_USER_EXPERIENCE* pUserExperience
451 + )
452 +{
453 + HRESULT hr = S_OK;
454 + BA_ONCACHEBEGIN_ARGS args = { };
455 + BA_ONCACHEBEGIN_RESULTS results = { };
456 +
457 + args.cbSize = sizeof(args);
458 +
459 + results.cbSize = sizeof(results);
460 +
461 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEBEGIN, &args, &results, pUserExperience->pvBAProcContext);
462 + ExitOnFailure(hr, "BA OnCacheBegin failed.");
463 +
464 + if (results.fCancel)
465 + {
466 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
467 + }
468 +
469 +LExit:
470 + return hr;
471 +}
472 +
473 +EXTERN_C BAAPI UserExperienceOnCacheComplete(
474 + __in BURN_USER_EXPERIENCE* pUserExperience,
475 + __in HRESULT hrStatus
476 + )
477 +{
478 + HRESULT hr = S_OK;
479 + BA_ONCACHECOMPLETE_ARGS args = { };
480 + BA_ONCACHECOMPLETE_RESULTS results = { };
481 +
482 + args.cbSize = sizeof(args);
483 + args.hrStatus = hrStatus;
484 +
485 + results.cbSize = sizeof(results);
486 +
487 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHECOMPLETE, &args, &results, pUserExperience->pvBAProcContext);
488 + ExitOnFailure(hr, "BA OnCacheComplete failed.");
489 +
490 +LExit:
491 + return hr;
492 +}
493 +
494 +EXTERN_C BAAPI UserExperienceOnCachePackageBegin(
495 + __in BURN_USER_EXPERIENCE* pUserExperience,
496 + __in_z LPCWSTR wzPackageId,
497 + __in DWORD cCachePayloads,
498 + __in DWORD64 dw64PackageCacheSize
499 + )
500 +{
501 + HRESULT hr = S_OK;
502 + BA_ONCACHEPACKAGEBEGIN_ARGS args = { };
503 + BA_ONCACHEPACKAGEBEGIN_RESULTS results = { };
504 +
505 + args.cbSize = sizeof(args);
506 + args.wzPackageId = wzPackageId;
507 + args.cCachePayloads = cCachePayloads;
508 + args.dw64PackageCacheSize = dw64PackageCacheSize;
509 +
510 + results.cbSize = sizeof(results);
511 +
512 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPACKAGEBEGIN, &args, &results, pUserExperience->pvBAProcContext);
513 + ExitOnFailure(hr, "BA OnCachePackageBegin failed.");
514 +
515 + if (results.fCancel)
516 + {
517 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
518 + }
519 +
520 +LExit:
521 + return hr;
522 +}
523 +
524 +EXTERN_C BAAPI UserExperienceOnCachePackageComplete(
525 + __in BURN_USER_EXPERIENCE* pUserExperience,
526 + __in_z LPCWSTR wzPackageId,
527 + __in HRESULT hrStatus,
528 + __inout BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION* pAction
529 + )
530 +{
531 + HRESULT hr = S_OK;
532 + BA_ONCACHEPACKAGECOMPLETE_ARGS args = { };
533 + BA_ONCACHEPACKAGECOMPLETE_RESULTS results = { };
534 +
535 + args.cbSize = sizeof(args);
536 + args.wzPackageId = wzPackageId;
537 + args.hrStatus = hrStatus;
538 + args.recommendation = *pAction;
539 +
540 + results.cbSize = sizeof(results);
541 + results.action = *pAction;
542 +
543 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPACKAGECOMPLETE, &args, &results, pUserExperience->pvBAProcContext);
544 + ExitOnFailure(hr, "BA OnCachePackageComplete failed.");
545 +
546 + if (FAILED(hrStatus))
547 + {
548 + *pAction = results.action;
549 + }
550 +
551 +LExit:
552 + return hr;
553 +}
554 +
555 +EXTERN_C BAAPI UserExperienceOnCacheVerifyBegin(
556 + __in BURN_USER_EXPERIENCE* pUserExperience,
557 + __in_z_opt LPCWSTR wzPackageOrContainerId,
558 + __in_z_opt LPCWSTR wzPayloadId
559 + )
560 +{
561 + HRESULT hr = S_OK;
562 + BA_ONCACHEVERIFYBEGIN_ARGS args = { };
563 + BA_ONCACHEVERIFYBEGIN_RESULTS results = { };
564 +
565 + args.cbSize = sizeof(args);
566 + args.wzPackageOrContainerId = wzPackageOrContainerId;
567 + args.wzPayloadId = wzPayloadId;
568 +
569 + results.cbSize = sizeof(results);
570 +
571 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEVERIFYBEGIN, &args, &results, pUserExperience->pvBAProcContext);
572 + ExitOnFailure(hr, "BA OnCacheVerifyBegin failed.");
573 +
574 + if (results.fCancel)
575 + {
576 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
577 + }
578 +
579 +LExit:
580 + return hr;
581 +}
582 +
583 +EXTERN_C BAAPI UserExperienceOnCacheVerifyComplete(
584 + __in BURN_USER_EXPERIENCE* pUserExperience,
585 + __in_z_opt LPCWSTR wzPackageOrContainerId,
586 + __in_z_opt LPCWSTR wzPayloadId,
587 + __in HRESULT hrStatus,
588 + __inout BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION* pAction
589 + )
590 +{
591 + HRESULT hr = S_OK;
592 + BA_ONCACHEVERIFYCOMPLETE_ARGS args = { };
593 + BA_ONCACHEVERIFYCOMPLETE_RESULTS results = { };
594 +
595 + args.cbSize = sizeof(args);
596 + args.wzPackageOrContainerId = wzPackageOrContainerId;
597 + args.wzPayloadId = wzPayloadId;
598 + args.hrStatus = hrStatus;
599 + args.recommendation = *pAction;
600 +
601 + results.cbSize = sizeof(results);
602 + results.action = *pAction;
603 +
604 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEVERIFYCOMPLETE, &args, &results, pUserExperience->pvBAProcContext);
605 + ExitOnFailure(hr, "BA OnCacheVerifyComplete failed.");
606 +
607 + if (FAILED(hrStatus))
608 + {
609 + *pAction = results.action;
610 + }
611 +
612 +LExit:
613 + return hr;
614 +}
615 +
616 +EXTERN_C BAAPI UserExperienceOnDetectBegin(
617 + __in BURN_USER_EXPERIENCE* pUserExperience,
618 + __in BOOL fInstalled,
619 + __in DWORD cPackages
620 + )
621 +{
622 + HRESULT hr = S_OK;
623 + BA_ONDETECTBEGIN_ARGS args = { };
624 + BA_ONDETECTBEGIN_RESULTS results = { };
625 +
626 + args.cbSize = sizeof(args);
627 + args.cPackages = cPackages;
628 + args.fInstalled = fInstalled;
629 +
630 + results.cbSize = sizeof(results);
631 +
632 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTBEGIN, &args, &results, pUserExperience->pvBAProcContext);
633 + ExitOnFailure(hr, "BA OnDetectBegin failed.");
634 +
635 + if (results.fCancel)
636 + {
637 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
638 + }
639 +
640 +LExit:
641 + return hr;
642 +}
643 +
644 +EXTERN_C BAAPI UserExperienceOnDetectCompatibleMsiPackage(
645 + __in BURN_USER_EXPERIENCE* pUserExperience,
646 + __in_z LPCWSTR wzPackageId,
647 + __in_z LPCWSTR wzCompatiblePackageId,
648 + __in DWORD64 dw64CompatiblePackageVersion
649 + )
650 +{
651 + HRESULT hr = S_OK;
652 + BA_ONDETECTCOMPATIBLEMSIPACKAGE_ARGS args = { };
653 + BA_ONDETECTCOMPATIBLEMSIPACKAGE_RESULTS results = { };
654 +
655 + args.cbSize = sizeof(args);
656 + args.wzPackageId = wzPackageId;
657 + args.wzCompatiblePackageId = wzCompatiblePackageId;
658 + args.dw64CompatiblePackageVersion = dw64CompatiblePackageVersion;
659 +
660 + results.cbSize = sizeof(results);
661 +
662 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTCOMPATIBLEMSIPACKAGE, &args, &results, pUserExperience->pvBAProcContext);
663 + ExitOnFailure(hr, "BA OnDetectCompatibleMsiPackage failed.");
664 +
665 + if (results.fCancel)
666 + {
667 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
668 + }
669 +
670 +LExit:
671 + return hr;
672 +}
673 +
674 +EXTERN_C BAAPI UserExperienceOnDetectComplete(
675 + __in BURN_USER_EXPERIENCE* pUserExperience,
676 + __in HRESULT hrStatus
677 + )
678 +{
679 + HRESULT hr = S_OK;
680 + BA_ONDETECTCOMPLETE_ARGS args = { };
681 + BA_ONDETECTCOMPLETE_RESULTS results = { };
682 +
683 + args.cbSize = sizeof(args);
684 + args.hrStatus = hrStatus;
685 +
686 + results.cbSize = sizeof(results);
687 +
688 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTCOMPLETE, &args, &results, pUserExperience->pvBAProcContext);
689 + ExitOnFailure(hr, "BA OnDetectComplete failed.");
690 +
691 +LExit:
692 + return hr;
693 +}
694 +
695 +EXTERN_C BAAPI UserExperienceOnDetectForwardCompatibleBundle(
696 + __in BURN_USER_EXPERIENCE* pUserExperience,
697 + __in_z LPCWSTR wzBundleId,
698 + __in BOOTSTRAPPER_RELATION_TYPE relationType,
699 + __in_z LPCWSTR wzBundleTag,
700 + __in BOOL fPerMachine,
701 + __in DWORD64 dw64Version,
702 + __inout BOOL* pfIgnoreBundle
703 + )
704 +{
705 + HRESULT hr = S_OK;
706 + BA_ONDETECTFORWARDCOMPATIBLEBUNDLE_ARGS args = { };
707 + BA_ONDETECTFORWARDCOMPATIBLEBUNDLE_RESULTS results = { };
708 +
709 + args.cbSize = sizeof(args);
710 + args.wzBundleId = wzBundleId;
711 + args.relationType = relationType;
712 + args.wzBundleTag = wzBundleTag;
713 + args.fPerMachine = fPerMachine;
714 + args.dw64Version = dw64Version;
715 +
716 + results.cbSize = sizeof(results);
717 + results.fIgnoreBundle = *pfIgnoreBundle;
718 +
719 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTFORWARDCOMPATIBLEBUNDLE, &args, &results, pUserExperience->pvBAProcContext);
720 + ExitOnFailure(hr, "BA OnDetectForwardCompatibleBundle failed.");
721 +
722 + if (results.fCancel)
723 + {
724 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
725 + }
726 + *pfIgnoreBundle = results.fIgnoreBundle;
727 +
728 +LExit:
729 + return hr;
730 +}
731 +
732 +EXTERN_C BAAPI UserExperienceOnDetectMsiFeature(
733 + __in BURN_USER_EXPERIENCE* pUserExperience,
734 + __in_z LPCWSTR wzPackageId,
735 + __in_z LPCWSTR wzFeatureId,
736 + __in BOOTSTRAPPER_FEATURE_STATE state
737 + )
738 +{
739 + HRESULT hr = S_OK;
740 + BA_ONDETECTMSIFEATURE_ARGS args = { };
741 + BA_ONDETECTMSIFEATURE_RESULTS results = { };
742 +
743 + args.cbSize = sizeof(args);
744 + args.wzPackageId = wzPackageId;
745 + args.wzFeatureId = wzFeatureId;
746 + args.state = state;
747 +
748 + results.cbSize = sizeof(results);
749 +
750 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTMSIFEATURE, &args, &results, pUserExperience->pvBAProcContext);
751 + ExitOnFailure(hr, "BA OnDetectMsiFeature failed.");
752 +
753 + if (results.fCancel)
754 + {
755 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
756 + }
757 +
758 +LExit:
759 + return hr;
760 +}
761 +
762 +EXTERN_C BAAPI UserExperienceOnDetectPackageBegin(
763 + __in BURN_USER_EXPERIENCE* pUserExperience,
764 + __in_z LPCWSTR wzPackageId
765 + )
766 +{
767 + HRESULT hr = S_OK;
768 + BA_ONDETECTPACKAGEBEGIN_ARGS args = { };
769 + BA_ONDETECTPACKAGEBEGIN_RESULTS results = { };
770 +
771 + args.cbSize = sizeof(args);
772 + args.wzPackageId = wzPackageId;
773 +
774 + results.cbSize = sizeof(results);
775 +
776 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTPACKAGEBEGIN, &args, &results, pUserExperience->pvBAProcContext);
777 + ExitOnFailure(hr, "BA OnDetectPackageBegin failed.");
778 +
779 + if (results.fCancel)
780 + {
781 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
782 + }
783 +
784 +LExit:
785 + return hr;
786 +}
787 +
788 +EXTERN_C BAAPI UserExperienceOnDetectPackageComplete(
789 + __in BURN_USER_EXPERIENCE* pUserExperience,
790 + __in_z LPCWSTR wzPackageId,
791 + __in HRESULT hrStatus,
792 + __in BOOTSTRAPPER_PACKAGE_STATE state
793 + )
794 +{
795 + HRESULT hr = S_OK;
796 + BA_ONDETECTPACKAGECOMPLETE_ARGS args = { };
797 + BA_ONDETECTPACKAGECOMPLETE_RESULTS results = { };
798 +
799 + args.cbSize = sizeof(args);
800 + args.wzPackageId = wzPackageId;
801 + args.hrStatus = hrStatus;
802 + args.state = state;
803 +
804 + results.cbSize = sizeof(results);
805 +
806 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTPACKAGECOMPLETE, &args, &results, pUserExperience->pvBAProcContext);
807 + ExitOnFailure(hr, "BA OnDetectPackageComplete failed.");
808 +
809 +LExit:
810 + return hr;
811 +}
812 +
813 +EXTERN_C BAAPI UserExperienceOnDetectRelatedBundle(
814 + __in BURN_USER_EXPERIENCE* pUserExperience,
815 + __in_z LPCWSTR wzBundleId,
816 + __in BOOTSTRAPPER_RELATION_TYPE relationType,
817 + __in_z LPCWSTR wzBundleTag,
818 + __in BOOL fPerMachine,
819 + __in DWORD64 dw64Version,
820 + __in BOOTSTRAPPER_RELATED_OPERATION operation
821 + )
822 +{
823 + HRESULT hr = S_OK;
824 + BA_ONDETECTRELATEDBUNDLE_ARGS args = { };
825 + BA_ONDETECTRELATEDBUNDLE_RESULTS results = { };
826 +
827 + args.cbSize = sizeof(args);
828 + args.wzBundleId = wzBundleId;
829 + args.relationType = relationType;
830 + args.wzBundleTag = wzBundleTag;
831 + args.fPerMachine = fPerMachine;
832 + args.dw64Version = dw64Version;
833 + args.operation = operation;
834 +
835 + results.cbSize = sizeof(results);
836 +
837 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTRELATEDBUNDLE, &args, &results, pUserExperience->pvBAProcContext);
838 + ExitOnFailure(hr, "BA OnDetectRelatedBundle failed.");
839 +
840 + if (results.fCancel)
841 + {
842 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
843 + }
844 +
845 +LExit:
846 + return hr;
847 +}
848 +
849 +EXTERN_C BAAPI UserExperienceOnDetectRelatedMsiPackage(
850 + __in BURN_USER_EXPERIENCE* pUserExperience,
851 + __in_z LPCWSTR wzPackageId,
852 + __in_z LPCWSTR wzUpgradeCode,
853 + __in_z LPCWSTR wzProductCode,
854 + __in BOOL fPerMachine,
855 + __in DWORD64 dw64Version,
856 + __in BOOTSTRAPPER_RELATED_OPERATION operation
857 + )
858 +{
859 + HRESULT hr = S_OK;
860 + BA_ONDETECTRELATEDMSIPACKAGE_ARGS args = { };
861 + BA_ONDETECTRELATEDMSIPACKAGE_RESULTS results = { };
862 +
863 + args.cbSize = sizeof(args);
864 + args.wzPackageId = wzPackageId;
865 + args.wzUpgradeCode = wzUpgradeCode;
866 + args.wzProductCode = wzProductCode;
867 + args.fPerMachine = fPerMachine;
868 + args.dw64Version = dw64Version;
869 + args.operation = operation;
870 +
871 + results.cbSize = sizeof(results);
872 +
873 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTRELATEDMSIPACKAGE, &args, &results, pUserExperience->pvBAProcContext);
874 + ExitOnFailure(hr, "BA OnDetectRelatedMsiPackage failed.");
875 +
876 + if (results.fCancel)
877 + {
878 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
879 + }
880 +
881 +LExit:
882 + return hr;
883 +}
884 +
885 +EXTERN_C BAAPI UserExperienceOnDetectTargetMsiPackage(
886 + __in BURN_USER_EXPERIENCE* pUserExperience,
887 + __in_z LPCWSTR wzPackageId,
888 + __in_z LPCWSTR wzProductCode,
889 + __in BOOTSTRAPPER_PACKAGE_STATE patchState
890 + )
891 +{
892 + HRESULT hr = S_OK;
893 + BA_ONDETECTTARGETMSIPACKAGE_ARGS args = { };
894 + BA_ONDETECTTARGETMSIPACKAGE_RESULTS results = { };
895 +
896 + args.cbSize = sizeof(args);
897 + args.wzPackageId = wzPackageId;
898 + args.wzProductCode = wzProductCode;
899 + args.patchState = patchState;
900 +
901 + results.cbSize = sizeof(results);
902 +
903 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTTARGETMSIPACKAGE, &args, &results, pUserExperience->pvBAProcContext);
904 + ExitOnFailure(hr, "BA OnDetectTargetMsiPackage failed.");
905 +
906 + if (results.fCancel)
907 + {
908 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
909 + }
910 +
911 +LExit:
912 + return hr;
913 +}
914 +
915 +EXTERN_C BAAPI UserExperienceOnDetectUpdate(
916 + __in BURN_USER_EXPERIENCE* pUserExperience,
917 + __in_z LPCWSTR wzUpdateLocation,
918 + __in DWORD64 dw64Size,
919 + __in DWORD64 dw64Version,
920 + __in_z_opt LPCWSTR wzTitle,
921 + __in_z_opt LPCWSTR wzSummary,
922 + __in_z_opt LPCWSTR wzContentType,
923 + __in_z_opt LPCWSTR wzContent,
924 + __inout BOOL* pfStopProcessingUpdates
925 + )
926 +{
927 + HRESULT hr = S_OK;
928 + BA_ONDETECTUPDATE_ARGS args = { };
929 + BA_ONDETECTUPDATE_RESULTS results = { };
930 +
931 + args.cbSize = sizeof(args);
932 + args.wzUpdateLocation = wzUpdateLocation;
933 + args.dw64Size = dw64Size;
934 + args.dw64Version = dw64Version;
935 + args.wzTitle = wzTitle;
936 + args.wzSummary = wzSummary;
937 + args.wzContentType = wzContentType;
938 + args.wzContent = wzContent;
939 +
940 + results.cbSize = sizeof(results);
941 + results.fStopProcessingUpdates = *pfStopProcessingUpdates;
942 +
943 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATE, &args, &results, pUserExperience->pvBAProcContext);
944 + ExitOnFailure(hr, "BA OnDetectUpdate failed.");
945 +
946 + if (results.fCancel)
947 + {
948 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
949 + }
950 + *pfStopProcessingUpdates = results.fStopProcessingUpdates;
951 +
952 +LExit:
953 + return hr;
954 +}
955 +
956 +EXTERN_C BAAPI UserExperienceOnDetectUpdateBegin(
957 + __in BURN_USER_EXPERIENCE* pUserExperience,
958 + __in_z LPCWSTR wzUpdateLocation,
959 + __inout BOOL* pfSkip
960 + )
961 +{
962 + HRESULT hr = S_OK;
963 + BA_ONDETECTUPDATEBEGIN_ARGS args = { };
964 + BA_ONDETECTUPDATEBEGIN_RESULTS results = { };
965 +
966 + args.cbSize = sizeof(args);
967 + args.wzUpdateLocation = wzUpdateLocation;
968 +
969 + results.cbSize = sizeof(results);
970 + results.fSkip = *pfSkip;
971 +
972 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATEBEGIN, &args, &results, pUserExperience->pvBAProcContext);
973 + ExitOnFailure(hr, "BA OnDetectUpdateBegin failed.");
974 +
975 + if (results.fCancel)
976 + {
977 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
978 + }
979 + *pfSkip = results.fSkip;
980 +
981 +LExit:
982 + return hr;
983 +}
984 +
985 +EXTERN_C BAAPI UserExperienceOnDetectUpdateComplete(
986 + __in BURN_USER_EXPERIENCE* pUserExperience,
987 + __in HRESULT hrStatus,
988 + __inout BOOL* pfIgnoreError
989 + )
990 +{
991 + HRESULT hr = S_OK;
992 + BA_ONDETECTUPDATECOMPLETE_ARGS args = { };
993 + BA_ONDETECTUPDATECOMPLETE_RESULTS results = { };
994 +
995 + args.cbSize = sizeof(args);
996 + args.hrStatus = hrStatus;
997 +
998 + results.cbSize = sizeof(results);
999 + results.fIgnoreError = *pfIgnoreError;
1000 +
1001 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATECOMPLETE, &args, &results, pUserExperience->pvBAProcContext);
1002 + ExitOnFailure(hr, "BA OnDetectUpdateComplete failed.");
1003 +
1004 + if (FAILED(hrStatus))
1005 + {
1006 + *pfIgnoreError = results.fIgnoreError;
1007 + }
1008 +
1009 +LExit:
1010 + return hr;
1011 +}
1012 +
1013 +EXTERN_C BAAPI UserExperienceOnElevateBegin(
1014 + __in BURN_USER_EXPERIENCE* pUserExperience
1015 + )
1016 +{
1017 + HRESULT hr = S_OK;
1018 + BA_ONELEVATEBEGIN_ARGS args = { };
1019 + BA_ONELEVATEBEGIN_RESULTS results = { };
1020 +
1021 + args.cbSize = sizeof(args);
1022 +
1023 + results.cbSize = sizeof(results);
1024 +
1025 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONELEVATEBEGIN, &args, &results, pUserExperience->pvBAProcContext);
1026 + ExitOnFailure(hr, "BA OnElevateBegin failed.");
1027 +
1028 + if (results.fCancel)
1029 + {
1030 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1031 + }
1032 +
1033 +LExit:
1034 + return hr;
1035 +}
1036 +
1037 +EXTERN_C BAAPI UserExperienceOnElevateComplete(
1038 + __in BURN_USER_EXPERIENCE* pUserExperience,
1039 + __in HRESULT hrStatus
1040 + )
1041 +{
1042 + HRESULT hr = S_OK;
1043 + BA_ONELEVATECOMPLETE_ARGS args = { };
1044 + BA_ONELEVATECOMPLETE_RESULTS results = { };
1045 +
1046 + args.cbSize = sizeof(args);
1047 + args.hrStatus = hrStatus;
1048 +
1049 + results.cbSize = sizeof(results);
1050 +
1051 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONELEVATECOMPLETE, &args, &results, pUserExperience->pvBAProcContext);
1052 + ExitOnFailure(hr, "BA OnElevateComplete failed.");
1053 +
1054 +LExit:
1055 + return hr;
1056 +}
1057 +
1058 +EXTERN_C BAAPI UserExperienceOnError(
1059 + __in BURN_USER_EXPERIENCE* pUserExperience,
1060 + __in BOOTSTRAPPER_ERROR_TYPE errorType,
1061 + __in_z_opt LPCWSTR wzPackageId,
1062 + __in DWORD dwCode,
1063 + __in_z_opt LPCWSTR wzError,
1064 + __in DWORD dwUIHint,
1065 + __in DWORD cData,
1066 + __in_ecount_z_opt(cData) LPCWSTR* rgwzData,
1067 + __inout int* pnResult
1068 + )
1069 +{
1070 + HRESULT hr = S_OK;
1071 + BA_ONERROR_ARGS args = { };
1072 + BA_ONERROR_RESULTS results = { };
1073 +
1074 + args.cbSize = sizeof(args);
1075 + args.errorType = errorType;
1076 + args.wzPackageId = wzPackageId;
1077 + args.dwCode = dwCode;
1078 + args.wzError = wzError;
1079 + args.dwUIHint = dwUIHint;
1080 + args.cData = cData;
1081 + args.rgwzData = rgwzData;
1082 + args.nRecommendation = *pnResult;
1083 +
1084 + results.cbSize = sizeof(results);
1085 + results.nResult = *pnResult;
1086 +
1087 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONERROR, &args, &results, pUserExperience->pvBAProcContext);
1088 + ExitOnFailure(hr, "BA OnError failed.");
1089 +
1090 + *pnResult = results.nResult;
1091 +
1092 +LExit:
1093 + return hr;
1094 +}
1095 +
1096 +EXTERN_C BAAPI UserExperienceOnExecuteBegin(
1097 + __in BURN_USER_EXPERIENCE* pUserExperience,
1098 + __in DWORD cExecutingPackages
1099 + )
1100 +{
1101 + HRESULT hr = S_OK;
1102 + BA_ONEXECUTEBEGIN_ARGS args = { };
1103 + BA_ONEXECUTEBEGIN_RESULTS results = { };
1104 +
1105 + args.cbSize = sizeof(args);
1106 + args.cExecutingPackages = cExecutingPackages;
1107 +
1108 + results.cbSize = sizeof(results);
1109 +
1110 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEBEGIN, &args, &results, pUserExperience->pvBAProcContext);
1111 + ExitOnFailure(hr, "BA OnExecuteBegin failed.");
1112 +
1113 + if (results.fCancel)
1114 + {
1115 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1116 + }
1117 +
1118 +LExit:
1119 + return hr;
1120 +}
1121 +
1122 +EXTERN_C BAAPI UserExperienceOnExecuteComplete(
1123 + __in BURN_USER_EXPERIENCE* pUserExperience,
1124 + __in HRESULT hrStatus
1125 + )
1126 +{
1127 + HRESULT hr = S_OK;
1128 + BA_ONEXECUTECOMPLETE_ARGS args = { };
1129 + BA_ONEXECUTECOMPLETE_RESULTS results = { };
1130 +
1131 + args.cbSize = sizeof(args);
1132 + args.hrStatus = hrStatus;
1133 +
1134 + results.cbSize = sizeof(results);
1135 +
1136 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTECOMPLETE, &args, &results, pUserExperience->pvBAProcContext);
1137 + ExitOnFailure(hr, "BA OnExecuteComplete failed.");
1138 +
1139 +LExit:
1140 + return hr;
1141 +}
1142 +
1143 +EXTERN_C BAAPI UserExperienceOnExecuteFilesInUse(
1144 + __in BURN_USER_EXPERIENCE* pUserExperience,
1145 + __in_z LPCWSTR wzPackageId,
1146 + __in DWORD cFiles,
1147 + __in_ecount_z_opt(cFiles) LPCWSTR* rgwzFiles,
1148 + __inout int* pnResult
1149 + )
1150 +{
1151 + HRESULT hr = S_OK;
1152 + BA_ONEXECUTEFILESINUSE_ARGS args = { };
1153 + BA_ONEXECUTEFILESINUSE_RESULTS results = { };
1154 +
1155 + args.cbSize = sizeof(args);
1156 + args.wzPackageId = wzPackageId;
1157 + args.cFiles = cFiles;
1158 + args.rgwzFiles = rgwzFiles;
1159 + args.nRecommendation = *pnResult;
1160 +
1161 + results.cbSize = sizeof(results);
1162 + results.nResult = *pnResult;
1163 +
1164 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEFILESINUSE, &args, &results, pUserExperience->pvBAProcContext);
1165 + ExitOnFailure(hr, "BA OnExecuteFilesInUse failed.");
1166 +
1167 + *pnResult = results.nResult;
1168 +
1169 +LExit:
1170 + return hr;
1171 +}
1172 +
1173 +EXTERN_C BAAPI UserExperienceOnExecuteMsiMessage(
1174 + __in BURN_USER_EXPERIENCE* pUserExperience,
1175 + __in_z LPCWSTR wzPackageId,
1176 + __in INSTALLMESSAGE messageType,
1177 + __in DWORD dwUIHint,
1178 + __in_z LPCWSTR wzMessage,
1179 + __in DWORD cData,
1180 + __in_ecount_z_opt(cData) LPCWSTR* rgwzData,
1181 + __inout int* pnResult
1182 + )
1183 +{
1184 + HRESULT hr = S_OK;
1185 + BA_ONEXECUTEMSIMESSAGE_ARGS args = { };
1186 + BA_ONEXECUTEMSIMESSAGE_RESULTS results = { };
1187 +
1188 + args.cbSize = sizeof(args);
1189 + args.wzPackageId = wzPackageId;
1190 + args.messageType = messageType;
1191 + args.dwUIHint = dwUIHint;
1192 + args.wzMessage = wzMessage;
1193 + args.cData = cData;
1194 + args.rgwzData = rgwzData;
1195 + args.nRecommendation = *pnResult;
1196 +
1197 + results.cbSize = sizeof(results);
1198 + results.nResult = *pnResult;
1199 +
1200 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEMSIMESSAGE, &args, &results, pUserExperience->pvBAProcContext);
1201 + ExitOnFailure(hr, "BA OnExecuteMsiMessage failed.");
1202 +
1203 + *pnResult = results.nResult;
1204 +
1205 +LExit:
1206 + return hr;
1207 +}
1208 +
1209 +EXTERN_C BAAPI UserExperienceOnExecutePackageBegin(
1210 + __in BURN_USER_EXPERIENCE* pUserExperience,
1211 + __in_z LPCWSTR wzPackageId,
1212 + __in BOOL fExecute
1213 + )
1214 +{
1215 + HRESULT hr = S_OK;
1216 + BA_ONEXECUTEPACKAGEBEGIN_ARGS args = { };
1217 + BA_ONEXECUTEPACKAGEBEGIN_RESULTS results = { };
1218 +
1219 + args.cbSize = sizeof(args);
1220 + args.wzPackageId = wzPackageId;
1221 + args.fExecute = fExecute;
1222 +
1223 + results.cbSize = sizeof(results);
1224 +
1225 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPACKAGEBEGIN, &args, &results, pUserExperience->pvBAProcContext);
1226 + ExitOnFailure(hr, "BA OnExecutePackageBegin failed.");
1227 +
1228 + if (results.fCancel)
1229 + {
1230 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1231 + }
1232 +
1233 +LExit:
1234 + return hr;
1235 +}
1236 +
1237 +EXTERN_C BAAPI UserExperienceOnExecutePackageComplete(
1238 + __in BURN_USER_EXPERIENCE* pUserExperience,
1239 + __in_z LPCWSTR wzPackageId,
1240 + __in HRESULT hrStatus,
1241 + __in BOOTSTRAPPER_APPLY_RESTART restart,
1242 + __inout BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION* pAction
1243 + )
1244 +{
1245 + HRESULT hr = S_OK;
1246 + BA_ONEXECUTEPACKAGECOMPLETE_ARGS args = { };
1247 + BA_ONEXECUTEPACKAGECOMPLETE_RESULTS results = { };
1248 +
1249 + args.cbSize = sizeof(args);
1250 + args.wzPackageId = wzPackageId;
1251 + args.hrStatus = hrStatus;
1252 + args.restart = restart;
1253 + args.recommendation = *pAction;
1254 +
1255 + results.cbSize = sizeof(results);
1256 + results.action = *pAction;
1257 +
1258 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPACKAGECOMPLETE, &args, &results, pUserExperience->pvBAProcContext);
1259 + ExitOnFailure(hr, "BA OnExecutePackageComplete failed.");
1260 +
1261 + *pAction = results.action;
1262 +
1263 +LExit:
1264 + return hr;
1265 +}
1266 +
1267 +EXTERN_C BAAPI UserExperienceOnExecutePatchTarget(
1268 + __in BURN_USER_EXPERIENCE* pUserExperience,
1269 + __in_z LPCWSTR wzPackageId,
1270 + __in_z LPCWSTR wzTargetProductCode
1271 + )
1272 +{
1273 + HRESULT hr = S_OK;
1274 + BA_ONEXECUTEPATCHTARGET_ARGS args = { };
1275 + BA_ONEXECUTEPATCHTARGET_RESULTS results = { };
1276 +
1277 + args.cbSize = sizeof(args);
1278 + args.wzPackageId = wzPackageId;
1279 + args.wzTargetProductCode = wzTargetProductCode;
1280 +
1281 + results.cbSize = sizeof(results);
1282 +
1283 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPATCHTARGET, &args, &results, pUserExperience->pvBAProcContext);
1284 + ExitOnFailure(hr, "BA OnExecutePatchTarget failed.");
1285 +
1286 + if (results.fCancel)
1287 + {
1288 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1289 + }
1290 +
1291 +LExit:
1292 + return hr;
1293 +}
1294 +
1295 +EXTERN_C BAAPI UserExperienceOnExecuteProgress(
1296 + __in BURN_USER_EXPERIENCE* pUserExperience,
1297 + __in_z LPCWSTR wzPackageId,
1298 + __in DWORD dwProgressPercentage,
1299 + __in DWORD dwOverallPercentage,
1300 + __out int* pnResult
1301 + )
1302 +{
1303 + HRESULT hr = S_OK;
1304 + BA_ONEXECUTEPROGRESS_ARGS args = { };
1305 + BA_ONEXECUTEPROGRESS_RESULTS results = { };
1306 +
1307 + args.cbSize = sizeof(args);
1308 + args.wzPackageId = wzPackageId;
1309 + args.dwProgressPercentage = dwProgressPercentage;
1310 + args.dwOverallPercentage = dwOverallPercentage;
1311 +
1312 + results.cbSize = sizeof(results);
1313 +
1314 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPROGRESS, &args, &results, pUserExperience->pvBAProcContext);
1315 + ExitOnFailure(hr, "BA OnExecuteProgress failed.");
1316 +
1317 +LExit:
1318 + if (FAILED(hr))
1319 + {
1320 + *pnResult = IDERROR;
1321 + }
1322 + else if (results.fCancel)
1323 + {
1324 + *pnResult = IDCANCEL;
1325 + }
1326 + else
1327 + {
1328 + *pnResult = IDNOACTION;
1329 + }
1330 + return hr;
1331 +}
1332 +
1333 +EXTERN_C BAAPI UserExperienceOnLaunchApprovedExeBegin(
1334 + __in BURN_USER_EXPERIENCE* pUserExperience
1335 + )
1336 +{
1337 + HRESULT hr = S_OK;
1338 + BA_ONLAUNCHAPPROVEDEXEBEGIN_ARGS args = { };
1339 + BA_ONLAUNCHAPPROVEDEXEBEGIN_RESULTS results = { };
1340 +
1341 + args.cbSize = sizeof(args);
1342 +
1343 + results.cbSize = sizeof(results);
1344 +
1345 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONLAUNCHAPPROVEDEXEBEGIN, &args, &results, pUserExperience->pvBAProcContext);
1346 + ExitOnFailure(hr, "BA OnLaunchApprovedExeBegin failed.");
1347 +
1348 + if (results.fCancel)
1349 + {
1350 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1351 + }
1352 +
1353 +LExit:
1354 + return hr;
1355 +}
1356 +
1357 +EXTERN_C BAAPI UserExperienceOnLaunchApprovedExeComplete(
1358 + __in BURN_USER_EXPERIENCE* pUserExperience,
1359 + __in HRESULT hrStatus,
1360 + __in DWORD dwProcessId
1361 + )
1362 +{
1363 + HRESULT hr = S_OK;
1364 + BA_ONLAUNCHAPPROVEDEXECOMPLETE_ARGS args = { };
1365 + BA_ONLAUNCHAPPROVEDEXECOMPLETE_RESULTS results = { };
1366 +
1367 + args.cbSize = sizeof(args);
1368 + args.hrStatus = hrStatus;
1369 + args.dwProcessId = dwProcessId;
1370 +
1371 + results.cbSize = sizeof(results);
1372 +
1373 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONLAUNCHAPPROVEDEXECOMPLETE, &args, &results, pUserExperience->pvBAProcContext);
1374 + ExitOnFailure(hr, "BA OnLaunchApprovedExeComplete failed.");
1375 +
1376 +LExit:
1377 + return hr;
1378 +}
1379 +
1380 +EXTERN_C BAAPI UserExperienceOnPlanBegin(
1381 + __in BURN_USER_EXPERIENCE* pUserExperience,
1382 + __in DWORD cPackages
1383 + )
1384 +{
1385 + HRESULT hr = S_OK;
1386 + BA_ONPLANBEGIN_ARGS args = { };
1387 + BA_ONPLANBEGIN_RESULTS results = { };
1388 +
1389 + args.cbSize = sizeof(args);
1390 + args.cPackages = cPackages;
1391 +
1392 + results.cbSize = sizeof(results);
1393 +
1394 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANBEGIN, &args, &results, pUserExperience->pvBAProcContext);
1395 + ExitOnFailure(hr, "BA OnPlanBegin failed.");
1396 +
1397 + if (results.fCancel)
1398 + {
1399 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1400 + }
1401 +
1402 +LExit:
1403 + return hr;
1404 +}
1405 +
1406 +EXTERN_C BAAPI UserExperienceOnPlanCompatibleMsiPackageBegin(
1407 + __in BURN_USER_EXPERIENCE* pUserExperience,
1408 + __in_z LPCWSTR wzPackageId,
1409 + __in_z LPCWSTR wzCompatiblePackageId,
1410 + __in DWORD64 dw64CompatiblePackageVersion,
1411 + __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState
1412 + )
1413 +{
1414 + HRESULT hr = S_OK;
1415 + BA_ONPLANCOMPATIBLEMSIPACKAGEBEGIN_ARGS args = { };
1416 + BA_ONPLANCOMPATIBLEMSIPACKAGEBEGIN_RESULTS results = { };
1417 +
1418 + args.cbSize = sizeof(args);
1419 + args.wzPackageId = wzPackageId;
1420 + args.wzCompatiblePackageId = wzCompatiblePackageId;
1421 + args.dw64CompatiblePackageVersion = dw64CompatiblePackageVersion;
1422 + args.recommendedState = *pRequestedState;
1423 +
1424 + results.cbSize = sizeof(results);
1425 + results.requestedState = *pRequestedState;
1426 +
1427 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGEBEGIN, &args, &results, pUserExperience->pvBAProcContext);
1428 + ExitOnFailure(hr, "BA OnPlanCompatibleMsiPackageBegin failed.");
1429 +
1430 + if (results.fCancel)
1431 + {
1432 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1433 + }
1434 + *pRequestedState = results.requestedState;
1435 +
1436 +LExit:
1437 + return hr;
1438 +}
1439 +
1440 +EXTERN_C BAAPI UserExperienceOnPlanCompatibleMsiPackageComplete(
1441 + __in BURN_USER_EXPERIENCE* pUserExperience,
1442 + __in_z LPCWSTR wzPackageId,
1443 + __in_z LPCWSTR wzCompatiblePackageId,
1444 + __in HRESULT hrStatus,
1445 + __in BOOTSTRAPPER_PACKAGE_STATE state,
1446 + __in BOOTSTRAPPER_REQUEST_STATE requested,
1447 + __in BOOTSTRAPPER_ACTION_STATE execute,
1448 + __in BOOTSTRAPPER_ACTION_STATE rollback
1449 + )
1450 +{
1451 + HRESULT hr = S_OK;
1452 + BA_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE_ARGS args = { };
1453 + BA_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE_RESULTS results = { };
1454 +
1455 + args.cbSize = sizeof(args);
1456 + args.wzPackageId = wzPackageId;
1457 + args.wzCompatiblePackageId = wzCompatiblePackageId;
1458 + args.hrStatus = hrStatus;
1459 + args.state = state;
1460 + args.requested = requested;
1461 + args.execute = execute;
1462 + args.rollback = rollback;
1463 +
1464 + results.cbSize = sizeof(results);
1465 +
1466 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE, &args, &results, pUserExperience->pvBAProcContext);
1467 + ExitOnFailure(hr, "BA OnPlanCompatibleMsiPackageComplete failed.");
1468 +
1469 +LExit:
1470 + return hr;
1471 +}
1472 +
1473 +EXTERN_C BAAPI UserExperienceOnPlanMsiFeature(
1474 + __in BURN_USER_EXPERIENCE* pUserExperience,
1475 + __in_z LPCWSTR wzPackageId,
1476 + __in_z LPCWSTR wzFeatureId,
1477 + __inout BOOTSTRAPPER_FEATURE_STATE* pRequestedState
1478 + )
1479 +{
1480 + HRESULT hr = S_OK;
1481 + BA_ONPLANMSIFEATURE_ARGS args = { };
1482 + BA_ONPLANMSIFEATURE_RESULTS results = { };
1483 +
1484 + args.cbSize = sizeof(args);
1485 + args.wzPackageId = wzPackageId;
1486 + args.wzFeatureId = wzFeatureId;
1487 + args.recommendedState = *pRequestedState;
1488 +
1489 + results.cbSize = sizeof(results);
1490 + results.requestedState = *pRequestedState;
1491 +
1492 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANMSIFEATURE, &args, &results, pUserExperience->pvBAProcContext);
1493 + ExitOnFailure(hr, "BA OnPlanMsiFeature failed.");
1494 +
1495 + if (results.fCancel)
1496 + {
1497 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1498 + }
1499 + *pRequestedState = results.requestedState;
1500 +
1501 +LExit:
1502 + return hr;
1503 +}
1504 +
1505 +EXTERN_C BAAPI UserExperienceOnPlanComplete(
1506 + __in BURN_USER_EXPERIENCE* pUserExperience,
1507 + __in HRESULT hrStatus
1508 + )
1509 +{
1510 + HRESULT hr = S_OK;
1511 + BA_ONPLANCOMPLETE_ARGS args = { };
1512 + BA_ONPLANCOMPLETE_RESULTS results = { };
1513 +
1514 + args.cbSize = sizeof(args);
1515 + args.hrStatus = hrStatus;
1516 +
1517 + results.cbSize = sizeof(results);
1518 +
1519 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPLETE, &args, &results, pUserExperience->pvBAProcContext);
1520 + ExitOnFailure(hr, "BA OnPlanComplete failed.");
1521 +
1522 +LExit:
1523 + return hr;
1524 +}
1525 +
1526 +EXTERN_C BAAPI UserExperienceOnPlanPackageBegin(
1527 + __in BURN_USER_EXPERIENCE* pUserExperience,
1528 + __in_z LPCWSTR wzPackageId,
1529 + __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState
1530 + )
1531 +{
1532 + HRESULT hr = S_OK;
1533 + BA_ONPLANPACKAGEBEGIN_ARGS args = { };
1534 + BA_ONPLANPACKAGEBEGIN_RESULTS results = { };
1535 +
1536 + args.cbSize = sizeof(args);
1537 + args.wzPackageId = wzPackageId;
1538 + args.recommendedState = *pRequestedState;
1539 +
1540 + results.cbSize = sizeof(results);
1541 + results.requestedState = *pRequestedState;
1542 +
1543 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANPACKAGEBEGIN, &args, &results, pUserExperience->pvBAProcContext);
1544 + ExitOnFailure(hr, "BA OnPlanPackageBegin failed.");
1545 +
1546 + if (results.fCancel)
1547 + {
1548 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1549 + }
1550 + *pRequestedState = results.requestedState;
1551 +
1552 +LExit:
1553 + return hr;
1554 +}
1555 +
1556 +EXTERN_C BAAPI UserExperienceOnPlanPackageComplete(
1557 + __in BURN_USER_EXPERIENCE* pUserExperience,
1558 + __in_z LPCWSTR wzPackageId,
1559 + __in HRESULT hrStatus,
1560 + __in BOOTSTRAPPER_PACKAGE_STATE state,
1561 + __in BOOTSTRAPPER_REQUEST_STATE requested,
1562 + __in BOOTSTRAPPER_ACTION_STATE execute,
1563 + __in BOOTSTRAPPER_ACTION_STATE rollback
1564 + )
1565 +{
1566 + HRESULT hr = S_OK;
1567 + BA_ONPLANPACKAGECOMPLETE_ARGS args = { };
1568 + BA_ONPLANPACKAGECOMPLETE_RESULTS results = { };
1569 +
1570 + args.cbSize = sizeof(args);
1571 + args.wzPackageId = wzPackageId;
1572 + args.hrStatus = hrStatus;
1573 + args.state = state;
1574 + args.requested = requested;
1575 + args.execute = execute;
1576 + args.rollback = rollback;
1577 +
1578 + results.cbSize = sizeof(results);
1579 +
1580 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANPACKAGECOMPLETE, &args, &results, pUserExperience->pvBAProcContext);
1581 + ExitOnFailure(hr, "BA OnPlanPackageComplete failed.");
1582 +
1583 +LExit:
1584 + return hr;
1585 +}
1586 +
1587 +EXTERN_C BAAPI UserExperienceOnPlanRelatedBundle(
1588 + __in BURN_USER_EXPERIENCE* pUserExperience,
1589 + __in_z LPCWSTR wzBundleId,
1590 + __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState
1591 + )
1592 +{
1593 + HRESULT hr = S_OK;
1594 + BA_ONPLANRELATEDBUNDLE_ARGS args = { };
1595 + BA_ONPLANRELATEDBUNDLE_RESULTS results = { };
1596 +
1597 + args.cbSize = sizeof(args);
1598 + args.wzBundleId = wzBundleId;
1599 + args.recommendedState = *pRequestedState;
1600 +
1601 + results.cbSize = sizeof(results);
1602 + results.requestedState = *pRequestedState;
1603 +
1604 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLE, &args, &results, pUserExperience->pvBAProcContext);
1605 + ExitOnFailure(hr, "BA OnPlanRelatedBundle failed.");
1606 +
1607 + if (results.fCancel)
1608 + {
1609 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1610 + }
1611 + *pRequestedState = results.requestedState;
1612 +
1613 +LExit:
1614 + return hr;
1615 +}
1616 +
1617 +EXTERN_C BAAPI UserExperienceOnPlanTargetMsiPackage(
1618 + __in BURN_USER_EXPERIENCE* pUserExperience,
1619 + __in_z LPCWSTR wzPackageId,
1620 + __in_z LPCWSTR wzProductCode,
1621 + __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState
1622 + )
1623 +{
1624 + HRESULT hr = S_OK;
1625 + BA_ONPLANTARGETMSIPACKAGE_ARGS args = { };
1626 + BA_ONPLANTARGETMSIPACKAGE_RESULTS results = { };
1627 +
1628 + args.cbSize = sizeof(args);
1629 + args.wzPackageId = wzPackageId;
1630 + args.wzProductCode = wzProductCode;
1631 + args.recommendedState = *pRequestedState;
1632 +
1633 + results.cbSize = sizeof(results);
1634 + results.requestedState = *pRequestedState;
1635 +
1636 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANTARGETMSIPACKAGE, &args, &results, pUserExperience->pvBAProcContext);
1637 + ExitOnFailure(hr, "BA OnPlanTargetMsiPackage failed.");
1638 +
1639 + if (results.fCancel)
1640 + {
1641 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1642 + }
1643 + *pRequestedState = results.requestedState;
1644 +
1645 +LExit:
1646 + return hr;
1647 +}
1648 +
1649 +EXTERN_C BAAPI UserExperienceOnProgress(
1650 + __in BURN_USER_EXPERIENCE* pUserExperience,
1651 + __in BOOL fRollback,
1652 + __in DWORD dwProgressPercentage,
1653 + __in DWORD dwOverallPercentage
1654 + )
1655 +{
1656 + HRESULT hr = S_OK;
1657 + BA_ONPROGRESS_ARGS args = { };
1658 + BA_ONPROGRESS_RESULTS results = { };
1659 +
1660 + args.cbSize = sizeof(args);
1661 + args.dwProgressPercentage = dwProgressPercentage;
1662 + args.dwOverallPercentage = dwOverallPercentage;
1663 +
1664 + results.cbSize = sizeof(results);
1665 +
1666 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPROGRESS, &args, &results, pUserExperience->pvBAProcContext);
1667 + hr = FilterExecuteResult(pUserExperience, hr, fRollback, results.fCancel, L"OnProgress");
1668 +
1669 + return hr;
1670 +}
1671 +
1672 +EXTERN_C BAAPI UserExperienceOnRegisterBegin(
1673 + __in BURN_USER_EXPERIENCE* pUserExperience
1674 + )
1675 +{
1676 + HRESULT hr = S_OK;
1677 + BA_ONREGISTERBEGIN_ARGS args = { };
1678 + BA_ONREGISTERBEGIN_RESULTS results = { };
1679 +
1680 + args.cbSize = sizeof(args);
1681 +
1682 + results.cbSize = sizeof(results);
1683 +
1684 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONREGISTERBEGIN, &args, &results, pUserExperience->pvBAProcContext);
1685 + ExitOnFailure(hr, "BA OnRegisterBegin failed.");
1686 +
1687 + if (results.fCancel)
1688 + {
1689 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1690 + }
1691 +
1692 +LExit:
1693 + return hr;
1694 +}
1695 +
1696 +EXTERN_C BAAPI UserExperienceOnRegisterComplete(
1697 + __in BURN_USER_EXPERIENCE* pUserExperience,
1698 + __in HRESULT hrStatus
1699 + )
1700 +{
1701 + HRESULT hr = S_OK;
1702 + BA_ONREGISTERCOMPLETE_ARGS args = { };
1703 + BA_ONREGISTERCOMPLETE_RESULTS results = { };
1704 +
1705 + args.cbSize = sizeof(args);
1706 + args.hrStatus = hrStatus;
1707 +
1708 + results.cbSize = sizeof(results);
1709 +
1710 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONREGISTERCOMPLETE, &args, &results, pUserExperience->pvBAProcContext);
1711 + ExitOnFailure(hr, "BA OnRegisterComplete failed.");
1712 +
1713 +LExit:
1714 + return hr;
1715 +}
1716 +
1717 +EXTERN_C BAAPI UserExperienceOnResolveSource(
1718 + __in BURN_USER_EXPERIENCE* pUserExperience,
1719 + __in_z LPCWSTR wzPackageOrContainerId,
1720 + __in_z_opt LPCWSTR wzPayloadId,
1721 + __in_z LPCWSTR wzLocalSource,
1722 + __in_z_opt LPCWSTR wzDownloadSource,
1723 + __inout BOOTSTRAPPER_RESOLVESOURCE_ACTION* pAction
1724 + )
1725 +{
1726 + HRESULT hr = S_OK;
1727 + BA_ONRESOLVESOURCE_ARGS args = { };
1728 + BA_ONRESOLVESOURCE_RESULTS results = { };
1729 +
1730 + args.cbSize = sizeof(args);
1731 + args.wzPackageOrContainerId = wzPackageOrContainerId;
1732 + args.wzPayloadId = wzPayloadId;
1733 + args.wzLocalSource = wzLocalSource;
1734 + args.wzDownloadSource = wzDownloadSource;
1735 + args.recommendation = *pAction;
1736 +
1737 + results.cbSize = sizeof(results);
1738 + results.action = *pAction;
1739 +
1740 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONRESOLVESOURCE, &args, &results, pUserExperience->pvBAProcContext);
1741 + ExitOnFailure(hr, "BA OnResolveSource failed.");
1742 +
1743 + if (results.fCancel)
1744 + {
1745 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1746 + }
1747 + else
1748 + {
1749 + *pAction = results.action;
1750 + }
1751 +
1752 +LExit:
1753 + return hr;
1754 +}
1755 +
1756 +EXTERN_C BAAPI UserExperienceOnShutdown(
1757 + __in BURN_USER_EXPERIENCE* pUserExperience,
1758 + __inout BOOTSTRAPPER_SHUTDOWN_ACTION* pAction
1759 + )
1760 +{
1761 + HRESULT hr = S_OK;
1762 + BA_ONSHUTDOWN_ARGS args = { };
1763 + BA_ONSHUTDOWN_RESULTS results = { };
1764 +
1765 + args.cbSize = sizeof(args);
1766 +
1767 + results.cbSize = sizeof(results);
1768 + results.action = *pAction;
1769 +
1770 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONSHUTDOWN, &args, &results, pUserExperience->pvBAProcContext);
1771 + ExitOnFailure(hr, "BA OnShutdown failed.");
1772 +
1773 + *pAction = results.action;
1774 +
1775 +LExit:
1776 + return hr;
1777 +}
1778 +
1779 +EXTERN_C BAAPI UserExperienceOnStartup(
1780 + __in BURN_USER_EXPERIENCE* pUserExperience
1781 + )
1782 +{
1783 + HRESULT hr = S_OK;
1784 + BA_ONSTARTUP_ARGS args = { };
1785 + BA_ONSTARTUP_RESULTS results = { };
1786 +
1787 + args.cbSize = sizeof(args);
1788 +
1789 + results.cbSize = sizeof(results);
1790 +
1791 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONSTARTUP, &args, &results, pUserExperience->pvBAProcContext);
1792 + ExitOnFailure(hr, "BA OnStartup failed.");
1793 +
1794 +LExit:
1795 + return hr;
1796 +}
1797 +
1798 +EXTERN_C BAAPI UserExperienceOnSystemShutdown(
1799 + __in BURN_USER_EXPERIENCE* pUserExperience,
1800 + __in DWORD dwEndSession,
1801 + __inout BOOL* pfCancel
1802 + )
1803 +{
1804 + HRESULT hr = S_OK;
1805 + BA_ONSYSTEMSHUTDOWN_ARGS args = { };
1806 + BA_ONSYSTEMSHUTDOWN_RESULTS results = { };
1807 +
1808 + args.cbSize = sizeof(args);
1809 + args.dwEndSession = dwEndSession;
1810 +
1811 + results.cbSize = sizeof(results);
1812 + results.fCancel = *pfCancel;
1813 +
1814 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONSYSTEMSHUTDOWN, &args, &results, pUserExperience->pvBAProcContext);
1815 + ExitOnFailure(hr, "BA OnSystemShutdown failed.");
1816 +
1817 + *pfCancel = results.fCancel;
1818 +
1819 +LExit:
1820 + return hr;
1821 +}
1822 +
1823 +EXTERN_C BAAPI UserExperienceOnUnregisterBegin(
1824 + __in BURN_USER_EXPERIENCE* pUserExperience
1825 + )
1826 +{
1827 + HRESULT hr = S_OK;
1828 + BA_ONUNREGISTERBEGIN_ARGS args = { };
1829 + BA_ONUNREGISTERBEGIN_RESULTS results = { };
1830 +
1831 + args.cbSize = sizeof(args);
1832 +
1833 + results.cbSize = sizeof(results);
1834 +
1835 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONUNREGISTERBEGIN, &args, &results, pUserExperience->pvBAProcContext);
1836 + ExitOnFailure(hr, "BA OnUnregisterBegin failed.");
1837 +
1838 + if (results.fCancel)
1839 + {
1840 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1841 + }
1842 +
1843 +LExit:
1844 + return hr;
1845 +}
1846 +
1847 +EXTERN_C BAAPI UserExperienceOnUnregisterComplete(
1848 + __in BURN_USER_EXPERIENCE* pUserExperience,
1849 + __in HRESULT hrStatus
1850 + )
1851 +{
1852 + HRESULT hr = S_OK;
1853 + BA_ONUNREGISTERCOMPLETE_ARGS args = { };
1854 + BA_ONUNREGISTERCOMPLETE_RESULTS results = { };
1855 +
1856 + args.cbSize = sizeof(args);
1857 + args.hrStatus = hrStatus;
1858 +
1859 + results.cbSize = sizeof(results);
1860 +
1861 + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONUNREGISTERCOMPLETE, &args, &results, pUserExperience->pvBAProcContext);
1862 + ExitOnFailure(hr, "BA OnUnregisterComplete failed.");
1863 +
1864 +LExit:
1865 + return hr;
1866 +}
1867 +
1868 +extern "C" int UserExperienceCheckExecuteResult(
1869 + __in BURN_USER_EXPERIENCE* pUserExperience,
1870 + __in BOOL fRollback,
1871 + __in DWORD dwAllowedResults,
1872 + __in int nResult
1873 + )
1874 +{
1875 + // Do not allow canceling while rolling back.
1876 + if (fRollback && (IDCANCEL == nResult || IDABORT == nResult))
1877 + {
1878 + nResult = IDNOACTION;
1879 + }
1880 + else if (FAILED(pUserExperience->hrApplyError) && !fRollback) // if we failed cancel except not during rollback.
1881 + {
1882 + nResult = IDCANCEL;
1883 + }
1884 +
1885 + nResult = FilterResult(dwAllowedResults, nResult);
1886 + return nResult;
1887 +}
1888 +
1889 +extern "C" HRESULT UserExperienceInterpretResult(
1890 + __in BURN_USER_EXPERIENCE* /*pUserExperience*/,
1891 + __in DWORD dwAllowedResults,
1892 + __in int nResult
1893 + )
1894 +{
1895 + int nFilteredResult = FilterResult(dwAllowedResults, nResult);
1896 + return IDOK == nFilteredResult || IDNOACTION == nFilteredResult ? S_OK : IDCANCEL == nFilteredResult || IDABORT == nFilteredResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE);
1897 +}
1898 +
1899 +extern "C" HRESULT UserExperienceInterpretExecuteResult(
1900 + __in BURN_USER_EXPERIENCE* pUserExperience,
1901 + __in BOOL fRollback,
1902 + __in DWORD dwAllowedResults,
1903 + __in int nResult
1904 + )
1905 +{
1906 + HRESULT hr = S_OK;
1907 +
1908 + // If we failed return that error unless this is rollback which should roll on.
1909 + if (FAILED(pUserExperience->hrApplyError) && !fRollback)
1910 + {
1911 + hr = pUserExperience->hrApplyError;
1912 + }
1913 + else
1914 + {
1915 + int nCheckedResult = UserExperienceCheckExecuteResult(pUserExperience, fRollback, dwAllowedResults, nResult);
1916 + hr = IDOK == nCheckedResult || IDNOACTION == nCheckedResult ? S_OK : IDCANCEL == nCheckedResult || IDABORT == nCheckedResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE);
1917 + }
1918 +
1919 + return hr;
1920 +}
1921 +
1922 +
1923 +// internal functions
1924 +
1925 +static int FilterResult(
1926 + __in DWORD dwAllowedResults,
1927 + __in int nResult
1928 + )
1929 +{
1930 + if (IDNOACTION == nResult || IDERROR == nResult) // do nothing and errors pass through.
1931 + {
1932 + }
1933 + else
1934 + {
1935 + switch (dwAllowedResults)
1936 + {
1937 + case MB_OK:
1938 + nResult = IDOK;
1939 + break;
1940 +
1941 + case MB_OKCANCEL:
1942 + if (IDOK == nResult || IDYES == nResult)
1943 + {
1944 + nResult = IDOK;
1945 + }
1946 + else if (IDCANCEL == nResult || IDABORT == nResult || IDNO == nResult)
1947 + {
1948 + nResult = IDCANCEL;
1949 + }
1950 + else
1951 + {
1952 + nResult = IDNOACTION;
1953 + }
1954 + break;
1955 +
1956 + case MB_ABORTRETRYIGNORE:
1957 + if (IDCANCEL == nResult || IDABORT == nResult)
1958 + {
1959 + nResult = IDABORT;
1960 + }
1961 + else if (IDRETRY == nResult || IDTRYAGAIN == nResult)
1962 + {
1963 + nResult = IDRETRY;
1964 + }
1965 + else if (IDIGNORE == nResult)
1966 + {
1967 + nResult = IDIGNORE;
1968 + }
1969 + else
1970 + {
1971 + nResult = IDNOACTION;
1972 + }
1973 + break;
1974 +
1975 + case MB_YESNO:
1976 + if (IDOK == nResult || IDYES == nResult)
1977 + {
1978 + nResult = IDYES;
1979 + }
1980 + else if (IDCANCEL == nResult || IDABORT == nResult || IDNO == nResult)
1981 + {
1982 + nResult = IDNO;
1983 + }
1984 + else
1985 + {
1986 + nResult = IDNOACTION;
1987 + }
1988 + break;
1989 +
1990 + case MB_YESNOCANCEL:
1991 + if (IDOK == nResult || IDYES == nResult)
1992 + {
1993 + nResult = IDYES;
1994 + }
1995 + else if (IDNO == nResult)
1996 + {
1997 + nResult = IDNO;
1998 + }
1999 + else if (IDCANCEL == nResult || IDABORT == nResult)
2000 + {
2001 + nResult = IDCANCEL;
2002 + }
2003 + else
2004 + {
2005 + nResult = IDNOACTION;
2006 + }
2007 + break;
2008 +
2009 + case MB_RETRYCANCEL:
2010 + if (IDRETRY == nResult || IDTRYAGAIN == nResult)
2011 + {
2012 + nResult = IDRETRY;
2013 + }
2014 + else if (IDCANCEL == nResult || IDABORT == nResult)
2015 + {
2016 + nResult = IDABORT;
2017 + }
2018 + else
2019 + {
2020 + nResult = IDNOACTION;
2021 + }
2022 + break;
2023 +
2024 + case MB_CANCELTRYCONTINUE:
2025 + if (IDCANCEL == nResult || IDABORT == nResult)
2026 + {
2027 + nResult = IDABORT;
2028 + }
2029 + else if (IDRETRY == nResult || IDTRYAGAIN == nResult)
2030 + {
2031 + nResult = IDRETRY;
2032 + }
2033 + else if (IDCONTINUE == nResult || IDIGNORE == nResult)
2034 + {
2035 + nResult = IDCONTINUE;
2036 + }
2037 + else
2038 + {
2039 + nResult = IDNOACTION;
2040 + }
2041 + break;
2042 +
2043 + case WIU_MB_OKIGNORECANCELRETRY: // custom Windows Installer utility return code.
2044 + if (IDOK == nResult || IDYES == nResult)
2045 + {
2046 + nResult = IDOK;
2047 + }
2048 + else if (IDCONTINUE == nResult || IDIGNORE == nResult)
2049 + {
2050 + nResult = IDIGNORE;
2051 + }
2052 + else if (IDCANCEL == nResult || IDABORT == nResult)
2053 + {
2054 + nResult = IDCANCEL;
2055 + }
2056 + else if (IDRETRY == nResult || IDTRYAGAIN == nResult || IDNO == nResult)
2057 + {
2058 + nResult = IDRETRY;
2059 + }
2060 + else
2061 + {
2062 + nResult = IDNOACTION;
2063 + }
2064 + break;
2065 +
2066 + case MB_RETRYTRYAGAIN: // custom return code.
2067 + if (IDRETRY != nResult && IDTRYAGAIN != nResult)
2068 + {
2069 + nResult = IDNOACTION;
2070 + }
2071 + break;
2072 +
2073 + default:
2074 + AssertSz(FALSE, "Unknown allowed results.");
2075 + break;
2076 + }
2077 + }
2078 +
2079 + return nResult;
2080 +}
2081 +
2082 +// This filters the BA's responses to events during apply.
2083 +// If an apply thread failed, then return its error so this thread will bail out.
2084 +// During rollback, the BA can't cancel.
2085 +static HRESULT FilterExecuteResult(
2086 + __in BURN_USER_EXPERIENCE* pUserExperience,
2087 + __in HRESULT hrStatus,
2088 + __in BOOL fRollback,
2089 + __in BOOL fCancel,
2090 + __in LPCWSTR sczEventName
2091 + )
2092 +{
2093 + HRESULT hr = hrStatus;
2094 + HRESULT hrApplyError = pUserExperience->hrApplyError; // make sure to use the same value for the whole method, since it can be changed in other threads.
2095 +
2096 + // If we failed return that error unless this is rollback which should roll on.
2097 + if (FAILED(hrApplyError) && !fRollback)
2098 + {
2099 + hr = hrApplyError;
2100 + }
2101 + else if (fRollback)
2102 + {
2103 + if (fCancel)
2104 + {
2105 + LogId(REPORT_STANDARD, MSG_APPLY_CANCEL_IGNORED_DURING_ROLLBACK, sczEventName);
2106 + }
2107 + // TODO: since cancel isn't allowed, should the BA's HRESULT be ignored as well?
2108 + // In the previous code, they could still alter rollback by returning IDERROR.
2109 + }
2110 + else
2111 + {
2112 + ExitOnFailure(hr, "BA %ls failed.", sczEventName);
2113 +
2114 + if (fCancel)
2115 + {
2116 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
2117 + }
2118 + }
2119 +
2120 +LExit:
2121 + return hr;
2122 +}
src/engine/userexperience.h new
+439
@@ -0,0 +1,439 @@
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 +#define BAAPI HRESULT __stdcall
5 +
6 +#if defined(__cplusplus)
7 +extern "C" {
8 +#endif
9 +
10 +
11 +// constants
12 +
13 +const DWORD MB_RETRYTRYAGAIN = 0xF;
14 +
15 +
16 +// structs
17 +
18 +struct BOOTSTRAPPER_ENGINE_CONTEXT;
19 +
20 +typedef struct _BURN_USER_EXPERIENCE
21 +{
22 + BOOL fSplashScreen;
23 + BURN_PAYLOADS payloads;
24 +
25 + HMODULE hUXModule;
26 + PFN_BOOTSTRAPPER_APPLICATION_PROC pfnBAProc;
27 + LPVOID pvBAProcContext;
28 + LPWSTR sczTempDirectory;
29 +
30 + CRITICAL_SECTION csEngineActive; // Changing the engine active state in the user experience must be
31 + // syncronized through this critical section.
32 + // Note: The engine must never do a UX callback while in this critical section.
33 +
34 + BOOL fEngineActive; // Indicates that the engine is currently active with one of the execution
35 + // steps (detect, plan, apply), and cannot accept requests from the UX.
36 + // This flag should be cleared by the engine prior to UX callbacks that
37 + // allows altering of the engine state.
38 +
39 + HRESULT hrApplyError; // Tracks is an error occurs during apply that requires the cache or
40 + // execute threads to bail.
41 +
42 + HWND hwndApply; // The window handle provided at the beginning of Apply(). Only valid
43 + // during apply.
44 +
45 + HWND hwndDetect; // The window handle provided at the beginning of Detect(). Only valid
46 + // during Detect.
47 +
48 + DWORD dwExitCode; // Exit code returned by the user experience for the engine overall.
49 +} BURN_USER_EXPERIENCE;
50 +
51 +// functions
52 +
53 +HRESULT UserExperienceParseFromXml(
54 + __in BURN_USER_EXPERIENCE* pUserExperience,
55 + __in IXMLDOMNode* pixnBundle
56 + );
57 +void UserExperienceUninitialize(
58 + __in BURN_USER_EXPERIENCE* pUserExperience
59 + );
60 +HRESULT UserExperienceLoad(
61 + __in BURN_USER_EXPERIENCE* pUserExperience,
62 + __in BOOTSTRAPPER_ENGINE_CONTEXT* pEngineContext,
63 + __in BOOTSTRAPPER_COMMAND* pCommand
64 + );
65 +HRESULT UserExperienceUnload(
66 + __in BURN_USER_EXPERIENCE* pUserExperience
67 + );
68 +HRESULT UserExperienceEnsureWorkingFolder(
69 + __in LPCWSTR wzBundleId,
70 + __deref_out_z LPWSTR* psczUserExperienceWorkingFolder
71 + );
72 +HRESULT UserExperienceRemove(
73 + __in BURN_USER_EXPERIENCE* pUserExperience
74 + );
75 +int UserExperienceSendError(
76 + __in BURN_USER_EXPERIENCE* pUserExperience,
77 + __in BOOTSTRAPPER_ERROR_TYPE errorType,
78 + __in_z_opt LPCWSTR wzPackageId,
79 + __in HRESULT hrCode,
80 + __in_z_opt LPCWSTR wzError,
81 + __in DWORD uiFlags,
82 + __in int nRecommendation
83 + );
84 +HRESULT UserExperienceActivateEngine(
85 + __in BURN_USER_EXPERIENCE* pUserExperience,
86 + __out_opt BOOL* pfActivated
87 + );
88 +void UserExperienceDeactivateEngine(
89 + __in BURN_USER_EXPERIENCE* pUserExperience
90 + );
91 +HRESULT UserExperienceEnsureEngineInactive(
92 + __in BURN_USER_EXPERIENCE* pUserExperience
93 + );
94 +void UserExperienceExecuteReset(
95 + __in BURN_USER_EXPERIENCE* pUserExperience
96 + );
97 +void UserExperienceExecutePhaseComplete(
98 + __in BURN_USER_EXPERIENCE* pUserExperience,
99 + __in HRESULT hrResult
100 + );
101 +BAAPI UserExperienceOnApplyBegin(
102 + __in BURN_USER_EXPERIENCE* pUserExperience,
103 + __in DWORD dwPhaseCount
104 + );
105 +BAAPI UserExperienceOnApplyComplete(
106 + __in BURN_USER_EXPERIENCE* pUserExperience,
107 + __in HRESULT hrStatus,
108 + __in BOOTSTRAPPER_APPLY_RESTART restart,
109 + __inout BOOTSTRAPPER_APPLYCOMPLETE_ACTION* pAction
110 +);
111 +BAAPI UserExperienceOnCacheAcquireBegin(
112 + __in BURN_USER_EXPERIENCE* pUserExperience,
113 + __in_z_opt LPCWSTR wzPackageOrContainerId,
114 + __in_z_opt LPCWSTR wzPayloadId,
115 + __in BOOTSTRAPPER_CACHE_OPERATION operation,
116 + __in_z LPCWSTR wzSource
117 + );
118 +BAAPI UserExperienceOnCacheAcquireComplete(
119 + __in BURN_USER_EXPERIENCE* pUserExperience,
120 + __in_z_opt LPCWSTR wzPackageOrContainerId,
121 + __in_z_opt LPCWSTR wzPayloadId,
122 + __in HRESULT hrStatus,
123 + __inout BOOL* pfRetry
124 + );
125 +BAAPI UserExperienceOnCacheAcquireProgress(
126 + __in BURN_USER_EXPERIENCE* pUserExperience,
127 + __in_z_opt LPCWSTR wzPackageOrContainerId,
128 + __in_z_opt LPCWSTR wzPayloadId,
129 + __in DWORD64 dw64Progress,
130 + __in DWORD64 dw64Total,
131 + __in DWORD dwOverallPercentage
132 + );
133 +BAAPI UserExperienceOnCacheBegin(
134 + __in BURN_USER_EXPERIENCE* pUserExperience
135 + );
136 +BAAPI UserExperienceOnCacheComplete(
137 + __in BURN_USER_EXPERIENCE* pUserExperience,
138 + __in HRESULT hrStatus
139 + );
140 +BAAPI UserExperienceOnCachePackageBegin(
141 + __in BURN_USER_EXPERIENCE* pUserExperience,
142 + __in_z LPCWSTR wzPackageId,
143 + __in DWORD cCachePayloads,
144 + __in DWORD64 dw64PackageCacheSize
145 + );
146 +BAAPI UserExperienceOnCachePackageComplete(
147 + __in BURN_USER_EXPERIENCE* pUserExperience,
148 + __in_z LPCWSTR wzPackageId,
149 + __in HRESULT hrStatus,
150 + __inout BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION* pAction
151 + );
152 +BAAPI UserExperienceOnCacheVerifyBegin(
153 + __in BURN_USER_EXPERIENCE* pUserExperience,
154 + __in_z_opt LPCWSTR wzPackageOrContainerId,
155 + __in_z_opt LPCWSTR wzPayloadId
156 + );
157 +BAAPI UserExperienceOnCacheVerifyComplete(
158 + __in BURN_USER_EXPERIENCE* pUserExperience,
159 + __in_z_opt LPCWSTR wzPackageOrContainerId,
160 + __in_z_opt LPCWSTR wzPayloadId,
161 + __in HRESULT hrStatus,
162 + __inout BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION* pAction
163 + );
164 +BAAPI UserExperienceOnDetectBegin(
165 + __in BURN_USER_EXPERIENCE* pUserExperience,
166 + __in BOOL fInstalled,
167 + __in DWORD cPackages
168 + );
169 +BAAPI UserExperienceOnDetectCompatibleMsiPackage(
170 + __in BURN_USER_EXPERIENCE* pUserExperience,
171 + __in_z LPCWSTR wzPackageId,
172 + __in_z LPCWSTR wzCompatiblePackageId,
173 + __in DWORD64 dw64CompatiblePackageVersion
174 + );
175 +BAAPI UserExperienceOnDetectComplete(
176 + __in BURN_USER_EXPERIENCE* pUserExperience,
177 + __in HRESULT hrStatus
178 + );
179 +BAAPI UserExperienceOnDetectForwardCompatibleBundle(
180 + __in BURN_USER_EXPERIENCE* pUserExperience,
181 + __in_z LPCWSTR wzBundleId,
182 + __in BOOTSTRAPPER_RELATION_TYPE relationType,
183 + __in_z LPCWSTR wzBundleTag,
184 + __in BOOL fPerMachine,
185 + __in DWORD64 dw64Version,
186 + __inout BOOL* pfIgnoreBundle
187 + );
188 +BAAPI UserExperienceOnDetectMsiFeature(
189 + __in BURN_USER_EXPERIENCE* pUserExperience,
190 + __in_z LPCWSTR wzPackageId,
191 + __in_z LPCWSTR wzFeatureId,
192 + __in BOOTSTRAPPER_FEATURE_STATE state
193 + );
194 +BAAPI UserExperienceOnDetectPackageBegin(
195 + __in BURN_USER_EXPERIENCE* pUserExperience,
196 + __in_z LPCWSTR wzPackageId
197 + );
198 +BAAPI UserExperienceOnDetectPackageComplete(
199 + __in BURN_USER_EXPERIENCE* pUserExperience,
200 + __in_z LPCWSTR wzPackageId,
201 + __in HRESULT hrStatus,
202 + __in BOOTSTRAPPER_PACKAGE_STATE state
203 + );
204 +BAAPI UserExperienceOnDetectRelatedBundle(
205 + __in BURN_USER_EXPERIENCE* pUserExperience,
206 + __in_z LPCWSTR wzBundleId,
207 + __in BOOTSTRAPPER_RELATION_TYPE relationType,
208 + __in_z LPCWSTR wzBundleTag,
209 + __in BOOL fPerMachine,
210 + __in DWORD64 dw64Version,
211 + __in BOOTSTRAPPER_RELATED_OPERATION operation
212 + );
213 +BAAPI UserExperienceOnDetectRelatedMsiPackage(
214 + __in BURN_USER_EXPERIENCE* pUserExperience,
215 + __in_z LPCWSTR wzPackageId,
216 + __in_z LPCWSTR wzUpgradeCode,
217 + __in_z LPCWSTR wzProductCode,
218 + __in BOOL fPerMachine,
219 + __in DWORD64 dw64Version,
220 + __in BOOTSTRAPPER_RELATED_OPERATION operation
221 + );
222 +BAAPI UserExperienceOnDetectTargetMsiPackage(
223 + __in BURN_USER_EXPERIENCE* pUserExperience,
224 + __in_z LPCWSTR wzPackageId,
225 + __in_z LPCWSTR wzProductCode,
226 + __in BOOTSTRAPPER_PACKAGE_STATE patchState
227 + );
228 +BAAPI UserExperienceOnDetectUpdate(
229 + __in BURN_USER_EXPERIENCE* pUserExperience,
230 + __in_z LPCWSTR wzUpdateLocation,
231 + __in DWORD64 dw64Size,
232 + __in DWORD64 dw64Version,
233 + __in_z_opt LPCWSTR wzTitle,
234 + __in_z_opt LPCWSTR wzSummary,
235 + __in_z_opt LPCWSTR wzContentType,
236 + __in_z_opt LPCWSTR wzContent,
237 + __inout BOOL* pfStopProcessingUpdates
238 + );
239 +BAAPI UserExperienceOnDetectUpdateBegin(
240 + __in BURN_USER_EXPERIENCE* pUserExperience,
241 + __in_z LPCWSTR wzUpdateLocation,
242 + __inout BOOL* pfSkip
243 + );
244 +BAAPI UserExperienceOnDetectUpdateComplete(
245 + __in BURN_USER_EXPERIENCE* pUserExperience,
246 + __in HRESULT hrStatus,
247 + __inout BOOL* pfIgnoreError
248 + );
249 +BAAPI UserExperienceOnElevateBegin(
250 + __in BURN_USER_EXPERIENCE* pUserExperience
251 + );
252 +BAAPI UserExperienceOnElevateComplete(
253 + __in BURN_USER_EXPERIENCE* pUserExperience,
254 + __in HRESULT hrStatus
255 + );
256 +BAAPI UserExperienceOnError(
257 + __in BURN_USER_EXPERIENCE* pUserExperience,
258 + __in BOOTSTRAPPER_ERROR_TYPE errorType,
259 + __in_z_opt LPCWSTR wzPackageId,
260 + __in DWORD dwCode,
261 + __in_z_opt LPCWSTR wzError,
262 + __in DWORD dwUIHint,
263 + __in DWORD cData,
264 + __in_ecount_z_opt(cData) LPCWSTR* rgwzData,
265 + __inout int* pnResult
266 + );
267 +BAAPI UserExperienceOnExecuteBegin(
268 + __in BURN_USER_EXPERIENCE* pUserExperience,
269 + __in DWORD cExecutingPackages
270 + );
271 +BAAPI UserExperienceOnExecuteComplete(
272 + __in BURN_USER_EXPERIENCE* pUserExperience,
273 + __in HRESULT hrStatus
274 +);
275 +BAAPI UserExperienceOnExecuteFilesInUse(
276 + __in BURN_USER_EXPERIENCE* pUserExperience,
277 + __in_z LPCWSTR wzPackageId,
278 + __in DWORD cFiles,
279 + __in_ecount_z_opt(cFiles) LPCWSTR* rgwzFiles,
280 + __inout int* pnResult
281 + );
282 +BAAPI UserExperienceOnExecuteMsiMessage(
283 + __in BURN_USER_EXPERIENCE* pUserExperience,
284 + __in_z LPCWSTR wzPackageId,
285 + __in INSTALLMESSAGE messageType,
286 + __in DWORD dwUIHint,
287 + __in_z LPCWSTR wzMessage,
288 + __in DWORD cData,
289 + __in_ecount_z_opt(cData) LPCWSTR* rgwzData,
290 + __inout int* pnResult
291 + );
292 +BAAPI UserExperienceOnExecutePackageBegin(
293 + __in BURN_USER_EXPERIENCE* pUserExperience,
294 + __in_z LPCWSTR wzPackageId,
295 + __in BOOL fExecute
296 + );
297 +BAAPI UserExperienceOnExecutePackageComplete(
298 + __in BURN_USER_EXPERIENCE* pUserExperience,
299 + __in_z LPCWSTR wzPackageId,
300 + __in HRESULT hrStatus,
301 + __in BOOTSTRAPPER_APPLY_RESTART restart,
302 + __inout BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION* pAction
303 + );
304 +BAAPI UserExperienceOnExecutePatchTarget(
305 + __in BURN_USER_EXPERIENCE* pUserExperience,
306 + __in_z LPCWSTR wzPackageId,
307 + __in_z LPCWSTR wzTargetProductCode
308 + );
309 +BAAPI UserExperienceOnExecuteProgress(
310 + __in BURN_USER_EXPERIENCE* pUserExperience,
311 + __in_z LPCWSTR wzPackageId,
312 + __in DWORD dwProgressPercentage,
313 + __in DWORD dwOverallPercentage,
314 + __inout int* pnResult
315 + );
316 +BAAPI UserExperienceOnLaunchApprovedExeBegin(
317 + __in BURN_USER_EXPERIENCE* pUserExperience
318 + );
319 +BAAPI UserExperienceOnLaunchApprovedExeComplete(
320 + __in BURN_USER_EXPERIENCE* pUserExperience,
321 + __in HRESULT hrStatus,
322 + __in DWORD dwProcessId
323 + );
324 +BAAPI UserExperienceOnPlanBegin(
325 + __in BURN_USER_EXPERIENCE* pUserExperience,
326 + __in DWORD cPackages
327 + );
328 +BAAPI UserExperienceOnPlanCompatibleMsiPackageBegin(
329 + __in BURN_USER_EXPERIENCE* pUserExperience,
330 + __in_z LPCWSTR wzPackageId,
331 + __in_z LPCWSTR wzCompatiblePackageId,
332 + __in DWORD64 dw64CompatiblePackageVersion,
333 + __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState
334 + );
335 +BAAPI UserExperienceOnPlanCompatibleMsiPackageComplete(
336 + __in BURN_USER_EXPERIENCE* pUserExperience,
337 + __in_z LPCWSTR wzPackageId,
338 + __in_z LPCWSTR wzCompatiblePackageId,
339 + __in HRESULT hrStatus,
340 + __in BOOTSTRAPPER_PACKAGE_STATE state,
341 + __in BOOTSTRAPPER_REQUEST_STATE requested,
342 + __in BOOTSTRAPPER_ACTION_STATE execute,
343 + __in BOOTSTRAPPER_ACTION_STATE rollback
344 + );
345 +BAAPI UserExperienceOnPlanComplete(
346 + __in BURN_USER_EXPERIENCE* pUserExperience,
347 + __in HRESULT hrStatus
348 + );
349 +BAAPI UserExperienceOnPlanMsiFeature(
350 + __in BURN_USER_EXPERIENCE* pUserExperience,
351 + __in_z LPCWSTR wzPackageId,
352 + __in_z LPCWSTR wzFeatureId,
353 + __inout BOOTSTRAPPER_FEATURE_STATE* pRequestedState
354 + );
355 +BAAPI UserExperienceOnPlanPackageBegin(
356 + __in BURN_USER_EXPERIENCE* pUserExperience,
357 + __in_z LPCWSTR wzPackageId,
358 + __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState
359 + );
360 +BAAPI UserExperienceOnPlanPackageComplete(
361 + __in BURN_USER_EXPERIENCE* pUserExperience,
362 + __in_z LPCWSTR wzPackageId,
363 + __in HRESULT hrStatus,
364 + __in BOOTSTRAPPER_PACKAGE_STATE state,
365 + __in BOOTSTRAPPER_REQUEST_STATE requested,
366 + __in BOOTSTRAPPER_ACTION_STATE execute,
367 + __in BOOTSTRAPPER_ACTION_STATE rollback
368 + );
369 +BAAPI UserExperienceOnPlanRelatedBundle(
370 + __in BURN_USER_EXPERIENCE* pUserExperience,
371 + __in_z LPCWSTR wzBundleId,
372 + __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState
373 + );
374 +BAAPI UserExperienceOnPlanTargetMsiPackage(
375 + __in BURN_USER_EXPERIENCE* pUserExperience,
376 + __in_z LPCWSTR wzPackageId,
377 + __in_z LPCWSTR wzProductCode,
378 + __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState
379 + );
380 +BAAPI UserExperienceOnProgress(
381 + __in BURN_USER_EXPERIENCE* pUserExperience,
382 + __in BOOL fRollback,
383 + __in DWORD dwProgressPercentage,
384 + __in DWORD dwOverallPercentage
385 + );
386 +BAAPI UserExperienceOnRegisterBegin(
387 + __in BURN_USER_EXPERIENCE* pUserExperience
388 + );
389 +BAAPI UserExperienceOnRegisterComplete(
390 + __in BURN_USER_EXPERIENCE* pUserExperience,
391 + __in HRESULT hrStatus
392 + );
393 +BAAPI UserExperienceOnResolveSource(
394 + __in BURN_USER_EXPERIENCE* pUserExperience,
395 + __in_z LPCWSTR wzPackageOrContainerId,
396 + __in_z_opt LPCWSTR wzPayloadId,
397 + __in_z LPCWSTR wzLocalSource,
398 + __in_z_opt LPCWSTR wzDownloadSource,
399 + __inout BOOTSTRAPPER_RESOLVESOURCE_ACTION* pAction
400 + );
401 +BAAPI UserExperienceOnShutdown(
402 + __in BURN_USER_EXPERIENCE* pUserExperience,
403 + __inout BOOTSTRAPPER_SHUTDOWN_ACTION* pAction
404 + );
405 +BAAPI UserExperienceOnStartup(
406 + __in BURN_USER_EXPERIENCE* pUserExperience
407 + );
408 +BAAPI UserExperienceOnSystemShutdown(
409 + __in BURN_USER_EXPERIENCE* pUserExperience,
410 + __in DWORD dwEndSession,
411 + __inout BOOL* pfCancel
412 + );
413 +BAAPI UserExperienceOnUnregisterBegin(
414 + __in BURN_USER_EXPERIENCE* pUserExperience
415 + );
416 +BAAPI UserExperienceOnUnregisterComplete(
417 + __in BURN_USER_EXPERIENCE* pUserExperience,
418 + __in HRESULT hrStatus
419 + );
420 +HRESULT UserExperienceInterpretResult(
421 + __in BURN_USER_EXPERIENCE* pUserExperience,
422 + __in DWORD dwAllowedResults,
423 + __in int nResult
424 + );
425 +int UserExperienceCheckExecuteResult(
426 + __in BURN_USER_EXPERIENCE* pUserExperience,
427 + __in BOOL fRollback,
428 + __in DWORD dwAllowedResults,
429 + __in int nResult
430 + );
431 +HRESULT UserExperienceInterpretExecuteResult(
432 + __in BURN_USER_EXPERIENCE* pUserExperience,
433 + __in BOOL fRollback,
434 + __in DWORD dwAllowedResults,
435 + __in int nResult
436 + );
437 +#if defined(__cplusplus)
438 +}
439 +#endif
src/engine/variable.cpp new
+2345
@@ -0,0 +1,2345 @@
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 +// structs
7 +
8 +typedef const struct _BUILT_IN_VARIABLE_DECLARATION
9 +{
10 + LPCWSTR wzVariable;
11 + PFN_INITIALIZEVARIABLE pfnInitialize;
12 + DWORD_PTR dwpInitializeData;
13 + BOOL fPersist;
14 + BOOL fOverridable;
15 +} BUILT_IN_VARIABLE_DECLARATION;
16 +
17 +
18 +// constants
19 +
20 +const DWORD GROW_VARIABLE_ARRAY = 3;
21 +
22 +enum OS_INFO_VARIABLE
23 +{
24 + OS_INFO_VARIABLE_NONE,
25 + OS_INFO_VARIABLE_VersionNT,
26 + OS_INFO_VARIABLE_VersionNT64,
27 + OS_INFO_VARIABLE_ServicePackLevel,
28 + OS_INFO_VARIABLE_NTProductType,
29 + OS_INFO_VARIABLE_NTSuiteBackOffice,
30 + OS_INFO_VARIABLE_NTSuiteDataCenter,
31 + OS_INFO_VARIABLE_NTSuiteEnterprise,
32 + OS_INFO_VARIABLE_NTSuitePersonal,
33 + OS_INFO_VARIABLE_NTSuiteSmallBusiness,
34 + OS_INFO_VARIABLE_NTSuiteSmallBusinessRestricted,
35 + OS_INFO_VARIABLE_NTSuiteWebServer,
36 + OS_INFO_VARIABLE_CompatibilityMode,
37 + OS_INFO_VARIABLE_TerminalServer,
38 + OS_INFO_VARIABLE_ProcessorArchitecture,
39 +};
40 +
41 +enum SET_VARIABLE
42 +{
43 + SET_VARIABLE_NOT_BUILTIN,
44 + SET_VARIABLE_OVERRIDE_BUILTIN,
45 + SET_VARIABLE_OVERRIDE_PERSISTED_BUILTINS,
46 + SET_VARIABLE_ANY,
47 +};
48 +
49 +// internal function declarations
50 +
51 +static HRESULT FormatString(
52 + __in BURN_VARIABLES* pVariables,
53 + __in_z LPCWSTR wzIn,
54 + __out_z_opt LPWSTR* psczOut,
55 + __out_opt DWORD* pcchOut,
56 + __in BOOL fObfuscateHiddenVariables
57 + );
58 +static HRESULT AddBuiltInVariable(
59 + __in BURN_VARIABLES* pVariables,
60 + __in LPCWSTR wzVariable,
61 + __in PFN_INITIALIZEVARIABLE pfnInitialize,
62 + __in DWORD_PTR dwpInitializeData,
63 + __in BOOL fPersist,
64 + __in BOOL fOverridable
65 + );
66 +static HRESULT GetVariable(
67 + __in BURN_VARIABLES* pVariables,
68 + __in_z LPCWSTR wzVariable,
69 + __out BURN_VARIABLE** ppVariable
70 + );
71 +static HRESULT FindVariableIndexByName(
72 + __in BURN_VARIABLES* pVariables,
73 + __in_z LPCWSTR wzVariable,
74 + __out DWORD* piVariable
75 + );
76 +static HRESULT InsertVariable(
77 + __in BURN_VARIABLES* pVariables,
78 + __in_z LPCWSTR wzVariable,
79 + __in DWORD iPosition
80 + );
81 +static HRESULT SetVariableValue(
82 + __in BURN_VARIABLES* pVariables,
83 + __in_z LPCWSTR wzVariable,
84 + __in BURN_VARIANT* pVariant,
85 + __in BOOL fLiteral,
86 + __in SET_VARIABLE setBuiltin,
87 + __in BOOL fLog
88 + );
89 +static HRESULT InitializeVariableVersionNT(
90 + __in DWORD_PTR dwpData,
91 + __inout BURN_VARIANT* pValue
92 + );
93 +static HRESULT InitializeVariableOsInfo(
94 + __in DWORD_PTR dwpData,
95 + __inout BURN_VARIANT* pValue
96 + );
97 +static HRESULT InitializeVariableSystemInfo(
98 + __in DWORD_PTR dwpData,
99 + __inout BURN_VARIANT* pValue
100 + );
101 +static HRESULT InitializeVariableComputerName(
102 + __in DWORD_PTR dwpData,
103 + __inout BURN_VARIANT* pValue
104 + );
105 +static HRESULT InitializeVariableVersionMsi(
106 + __in DWORD_PTR dwpData,
107 + __inout BURN_VARIANT* pValue
108 + );
109 +static HRESULT InitializeVariableCsidlFolder(
110 + __in DWORD_PTR dwpData,
111 + __inout BURN_VARIANT* pValue
112 + );
113 +static HRESULT InitializeVariableWindowsVolumeFolder(
114 + __in DWORD_PTR dwpData,
115 + __inout BURN_VARIANT* pValue
116 + );
117 +static HRESULT InitializeVariableTempFolder(
118 + __in DWORD_PTR dwpData,
119 + __inout BURN_VARIANT* pValue
120 + );
121 +static HRESULT InitializeVariableSystemFolder(
122 + __in DWORD_PTR dwpData,
123 + __inout BURN_VARIANT* pValue
124 + );
125 +static HRESULT InitializeVariablePrivileged(
126 + __in DWORD_PTR dwpData,
127 + __inout BURN_VARIANT* pValue
128 + );
129 +static HRESULT InitializeVariableRebootPending(
130 + __in DWORD_PTR dwpData,
131 + __inout BURN_VARIANT* pValue
132 + );
133 +static HRESULT InitializeSystemLanguageID(
134 + __in DWORD_PTR dwpData,
135 + __inout BURN_VARIANT* pValue
136 + );
137 +static HRESULT InitializeUserUILanguageID(
138 + __in DWORD_PTR dwpData,
139 + __inout BURN_VARIANT* pValue
140 + );
141 +static HRESULT InitializeUserLanguageID(
142 + __in DWORD_PTR dwpData,
143 + __inout BURN_VARIANT* pValue
144 + );
145 +static HRESULT InitializeVariableString(
146 + __in DWORD_PTR dwpData,
147 + __inout BURN_VARIANT* pValue
148 + );
149 +static HRESULT InitializeVariableNumeric(
150 + __in DWORD_PTR dwpData,
151 + __inout BURN_VARIANT* pValue
152 + );
153 +static HRESULT InitializeVariableRegistryFolder(
154 + __in DWORD_PTR dwpData,
155 + __inout BURN_VARIANT* pValue
156 + );
157 +static HRESULT InitializeVariable6432Folder(
158 + __in DWORD_PTR dwpData,
159 + __inout BURN_VARIANT* pValue
160 + );
161 +static HRESULT InitializeVariableDate(
162 + __in DWORD_PTR dwpData,
163 + __inout BURN_VARIANT* pValue
164 + );
165 +static HRESULT InitializeVariableInstallerName(
166 + __in DWORD_PTR dwpData,
167 + __inout BURN_VARIANT* pValue
168 + );
169 +static HRESULT InitializeVariableInstallerVersion(
170 + __in DWORD_PTR dwpData,
171 + __inout BURN_VARIANT* pValue
172 + );
173 +static HRESULT InitializeVariableVersion(
174 + __in DWORD_PTR dwpData,
175 + __inout BURN_VARIANT* pValue
176 + );
177 +static HRESULT InitializeVariableLogonUser(
178 + __in DWORD_PTR dwpData,
179 + __inout BURN_VARIANT* pValue
180 + );
181 +static HRESULT Get64bitFolderFromRegistry(
182 + __in int nFolder,
183 + __deref_out_z LPWSTR* psczPath
184 + );
185 +
186 +
187 +// function definitions
188 +
189 +extern "C" HRESULT VariableInitialize(
190 + __in BURN_VARIABLES* pVariables
191 + )
192 +{
193 + HRESULT hr = S_OK;
194 +
195 + ::InitializeCriticalSection(&pVariables->csAccess);
196 +
197 + const BUILT_IN_VARIABLE_DECLARATION vrgBuiltInVariables[] = {
198 + {L"AdminToolsFolder", InitializeVariableCsidlFolder, CSIDL_ADMINTOOLS},
199 + {L"AppDataFolder", InitializeVariableCsidlFolder, CSIDL_APPDATA},
200 + {L"CommonAppDataFolder", InitializeVariableCsidlFolder, CSIDL_COMMON_APPDATA},
201 +#if defined(_WIN64)
202 + {L"CommonFiles64Folder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES_COMMON},
203 + {L"CommonFilesFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES_COMMONX86},
204 +#else
205 + {L"CommonFiles64Folder", InitializeVariableRegistryFolder, CSIDL_PROGRAM_FILES_COMMON},
206 + {L"CommonFilesFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES_COMMON},
207 +#endif
208 + {L"CommonFiles6432Folder", InitializeVariable6432Folder, CSIDL_PROGRAM_FILES_COMMON},
209 + {L"CompatibilityMode", InitializeVariableOsInfo, OS_INFO_VARIABLE_CompatibilityMode},
210 + {VARIABLE_DATE, InitializeVariableDate, 0},
211 + {L"ComputerName", InitializeVariableComputerName, 0},
212 + {L"DesktopFolder", InitializeVariableCsidlFolder, CSIDL_DESKTOP},
213 + {L"FavoritesFolder", InitializeVariableCsidlFolder, CSIDL_FAVORITES},
214 + {L"FontsFolder", InitializeVariableCsidlFolder, CSIDL_FONTS},
215 + {VARIABLE_INSTALLERNAME, InitializeVariableInstallerName, 0},
216 + {VARIABLE_INSTALLERVERSION, InitializeVariableInstallerVersion, 0},
217 + {L"LocalAppDataFolder", InitializeVariableCsidlFolder, CSIDL_LOCAL_APPDATA},
218 + {VARIABLE_LOGONUSER, InitializeVariableLogonUser, 0},
219 + {L"MyPicturesFolder", InitializeVariableCsidlFolder, CSIDL_MYPICTURES},
220 + {L"NTProductType", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTProductType},
221 + {L"NTSuiteBackOffice", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteBackOffice},
222 + {L"NTSuiteDataCenter", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteDataCenter},
223 + {L"NTSuiteEnterprise", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteEnterprise},
224 + {L"NTSuitePersonal", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuitePersonal},
225 + {L"NTSuiteSmallBusiness", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteSmallBusiness},
226 + {L"NTSuiteSmallBusinessRestricted", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteSmallBusinessRestricted},
227 + {L"NTSuiteWebServer", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteWebServer},
228 + {L"PersonalFolder", InitializeVariableCsidlFolder, CSIDL_PERSONAL},
229 + {L"Privileged", InitializeVariablePrivileged, 0},
230 + {L"ProcessorArchitecture", InitializeVariableSystemInfo, OS_INFO_VARIABLE_ProcessorArchitecture},
231 +#if defined(_WIN64)
232 + {L"ProgramFiles64Folder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES},
233 + {L"ProgramFilesFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILESX86},
234 +#else
235 + {L"ProgramFiles64Folder", InitializeVariableRegistryFolder, CSIDL_PROGRAM_FILES},
236 + {L"ProgramFilesFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES},
237 +#endif
238 + {L"ProgramFiles6432Folder", InitializeVariable6432Folder, CSIDL_PROGRAM_FILES},
239 + {L"ProgramMenuFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAMS},
240 + {L"RebootPending", InitializeVariableRebootPending, 0},
241 + {L"SendToFolder", InitializeVariableCsidlFolder, CSIDL_SENDTO},
242 + {L"ServicePackLevel", InitializeVariableVersionNT, OS_INFO_VARIABLE_ServicePackLevel},
243 + {L"StartMenuFolder", InitializeVariableCsidlFolder, CSIDL_STARTMENU},
244 + {L"StartupFolder", InitializeVariableCsidlFolder, CSIDL_STARTUP},
245 + {L"SystemFolder", InitializeVariableSystemFolder, FALSE},
246 + {L"System64Folder", InitializeVariableSystemFolder, TRUE},
247 + {L"SystemLanguageID", InitializeSystemLanguageID, 0},
248 + {L"TempFolder", InitializeVariableTempFolder, 0},
249 + {L"TemplateFolder", InitializeVariableCsidlFolder, CSIDL_TEMPLATES},
250 + {L"TerminalServer", InitializeVariableOsInfo, OS_INFO_VARIABLE_TerminalServer},
251 + {L"UserUILanguageID", InitializeUserUILanguageID, 0},
252 + {L"UserLanguageID", InitializeUserLanguageID, 0},
253 + {L"VersionMsi", InitializeVariableVersionMsi, 0},
254 + {L"VersionNT", InitializeVariableVersionNT, OS_INFO_VARIABLE_VersionNT},
255 + {L"VersionNT64", InitializeVariableVersionNT, OS_INFO_VARIABLE_VersionNT64},
256 + {L"WindowsFolder", InitializeVariableCsidlFolder, CSIDL_WINDOWS},
257 + {L"WindowsVolume", InitializeVariableWindowsVolumeFolder, 0},
258 + {BURN_BUNDLE_ACTION, InitializeVariableNumeric, 0, FALSE, TRUE},
259 + {BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, InitializeVariableString, NULL, FALSE, TRUE},
260 + {BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, InitializeVariableString, NULL, FALSE, TRUE},
261 + {BURN_BUNDLE_FORCED_RESTART_PACKAGE, InitializeVariableString, NULL, TRUE, TRUE},
262 + {BURN_BUNDLE_INSTALLED, InitializeVariableNumeric, 0, FALSE, TRUE},
263 + {BURN_BUNDLE_ELEVATED, InitializeVariableNumeric, 0, FALSE, TRUE},
264 + {BURN_BUNDLE_ACTIVE_PARENT, InitializeVariableString, NULL, FALSE, TRUE},
265 + {BURN_BUNDLE_PROVIDER_KEY, InitializeVariableString, (DWORD_PTR)L"", FALSE, TRUE},
266 + {BURN_BUNDLE_SOURCE_PROCESS_PATH, InitializeVariableString, NULL, FALSE, TRUE},
267 + {BURN_BUNDLE_SOURCE_PROCESS_FOLDER, InitializeVariableString, NULL, FALSE, TRUE},
268 + {BURN_BUNDLE_TAG, InitializeVariableString, (DWORD_PTR)L"", FALSE, TRUE},
269 + {BURN_BUNDLE_UILEVEL, InitializeVariableNumeric, 0, FALSE, TRUE},
270 + {BURN_BUNDLE_VERSION, InitializeVariableVersion, 0, FALSE, TRUE},
271 + };
272 +
273 + for (DWORD i = 0; i < countof(vrgBuiltInVariables); ++i)
274 + {
275 + BUILT_IN_VARIABLE_DECLARATION* pBuiltInVariable = &vrgBuiltInVariables[i];
276 +
277 + hr = AddBuiltInVariable(pVariables, pBuiltInVariable->wzVariable, pBuiltInVariable->pfnInitialize, pBuiltInVariable->dwpInitializeData, pBuiltInVariable->fPersist, pBuiltInVariable->fOverridable);
278 + ExitOnFailure(hr, "Failed to add built-in variable: %ls.", pBuiltInVariable->wzVariable);
279 + }
280 +
281 +LExit:
282 + return hr;
283 +}
284 +
285 +extern "C" HRESULT VariablesParseFromXml(
286 + __in BURN_VARIABLES* pVariables,
287 + __in IXMLDOMNode* pixnBundle
288 + )
289 +{
290 + HRESULT hr = S_OK;
291 + IXMLDOMNodeList* pixnNodes = NULL;
292 + IXMLDOMNode* pixnNode = NULL;
293 + DWORD cNodes = 0;
294 + LPWSTR sczId = NULL;
295 + LPWSTR scz = NULL;
296 + BURN_VARIANT value = { };
297 + BURN_VARIANT_TYPE valueType = BURN_VARIANT_TYPE_NONE;
298 + BOOL fHidden = FALSE;
299 + BOOL fPersisted = FALSE;
300 + DWORD iVariable = 0;
301 +
302 + ::EnterCriticalSection(&pVariables->csAccess);
303 +
304 + // select variable nodes
305 + hr = XmlSelectNodes(pixnBundle, L"Variable", &pixnNodes);
306 + ExitOnFailure(hr, "Failed to select variable nodes.");
307 +
308 + // get variable node count
309 + hr = pixnNodes->get_length((long*)&cNodes);
310 + ExitOnFailure(hr, "Failed to get variable node count.");
311 +
312 + // parse package elements
313 + for (DWORD i = 0; i < cNodes; ++i)
314 + {
315 + hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
316 + ExitOnFailure(hr, "Failed to get next node.");
317 +
318 + // @Id
319 + hr = XmlGetAttributeEx(pixnNode, L"Id", &sczId);
320 + ExitOnFailure(hr, "Failed to get @Id.");
321 +
322 + // @Hidden
323 + hr = XmlGetYesNoAttribute(pixnNode, L"Hidden", &fHidden);
324 + ExitOnFailure(hr, "Failed to get @Hidden.");
325 +
326 + // @Persisted
327 + hr = XmlGetYesNoAttribute(pixnNode, L"Persisted", &fPersisted);
328 + ExitOnFailure(hr, "Failed to get @Persisted.");
329 +
330 + // @Value
331 + hr = XmlGetAttributeEx(pixnNode, L"Value", &scz);
332 + if (E_NOTFOUND != hr)
333 + {
334 + ExitOnFailure(hr, "Failed to get @Value.");
335 +
336 + hr = BVariantSetString(&value, scz, 0);
337 + ExitOnFailure(hr, "Failed to set variant value.");
338 +
339 + // @Type
340 + hr = XmlGetAttributeEx(pixnNode, L"Type", &scz);
341 + ExitOnFailure(hr, "Failed to get @Type.");
342 +
343 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"numeric", -1))
344 + {
345 + if (!fHidden)
346 + {
347 + LogStringLine(REPORT_STANDARD, "Initializing numeric variable '%ls' to value '%ls'", sczId, value.sczValue);
348 + }
349 + valueType = BURN_VARIANT_TYPE_NUMERIC;
350 + }
351 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"string", -1))
352 + {
353 + if (!fHidden)
354 + {
355 + LogStringLine(REPORT_STANDARD, "Initializing string variable '%ls' to value '%ls'", sczId, value.sczValue);
356 + }
357 + valueType = BURN_VARIANT_TYPE_STRING;
358 + }
359 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1))
360 + {
361 + if (!fHidden)
362 + {
363 + LogStringLine(REPORT_STANDARD, "Initializing version variable '%ls' to value '%ls'", sczId, value.sczValue);
364 + }
365 + valueType = BURN_VARIANT_TYPE_VERSION;
366 + }
367 + else
368 + {
369 + hr = E_INVALIDARG;
370 + ExitOnFailure(hr, "Invalid value for @Type: %ls", scz);
371 + }
372 + }
373 + else
374 + {
375 + valueType = BURN_VARIANT_TYPE_NONE;
376 + }
377 +
378 + if (fHidden)
379 + {
380 + LogStringLine(REPORT_STANDARD, "Initializing hidden variable '%ls'", sczId);
381 + }
382 +
383 + // change value variant to correct type
384 + hr = BVariantChangeType(&value, valueType);
385 + ExitOnFailure(hr, "Failed to change variant type.");
386 +
387 + // find existing variable
388 + hr = FindVariableIndexByName(pVariables, sczId, &iVariable);
389 + ExitOnFailure(hr, "Failed to find variable value '%ls'.", sczId);
390 +
391 + // insert element if not found
392 + if (S_FALSE == hr)
393 + {
394 + hr = InsertVariable(pVariables, sczId, iVariable);
395 + ExitOnFailure(hr, "Failed to insert variable '%ls'.", sczId);
396 + }
397 + else if (BURN_VARIABLE_INTERNAL_TYPE_NORMAL < pVariables->rgVariables[iVariable].internalType)
398 + {
399 + hr = E_INVALIDARG;
400 + ExitOnRootFailure(hr, "Attempt to set built-in variable value: %ls", sczId);
401 + }
402 + pVariables->rgVariables[iVariable].fHidden = fHidden;
403 + pVariables->rgVariables[iVariable].fPersisted = fPersisted;
404 +
405 + // update variable value
406 + hr = BVariantSetValue(&pVariables->rgVariables[iVariable].Value, &value);
407 + ExitOnFailure(hr, "Failed to set value of variable: %ls", sczId);
408 +
409 + hr = BVariantSetEncryption(&pVariables->rgVariables[iVariable].Value, fHidden);
410 + ExitOnFailure(hr, "Failed to set variant encryption");
411 +
412 + // prepare next iteration
413 + ReleaseNullObject(pixnNode);
414 + BVariantUninitialize(&value);
415 + ReleaseNullStrSecure(scz);
416 + }
417 +
418 +LExit:
419 + ::LeaveCriticalSection(&pVariables->csAccess);
420 +
421 + ReleaseObject(pixnNodes);
422 + ReleaseObject(pixnNode);
423 + ReleaseStr(scz);
424 + ReleaseStr(sczId);
425 + BVariantUninitialize(&value);
426 +
427 + return hr;
428 +}
429 +
430 +extern "C" void VariablesUninitialize(
431 + __in BURN_VARIABLES* pVariables
432 + )
433 +{
434 + ::DeleteCriticalSection(&pVariables->csAccess);
435 +
436 + if (pVariables->rgVariables)
437 + {
438 + for (DWORD i = 0; i < pVariables->cVariables; ++i)
439 + {
440 + BURN_VARIABLE* pVariable = &pVariables->rgVariables[i];
441 + if (pVariable)
442 + {
443 + ReleaseStr(pVariable->sczName);
444 + BVariantUninitialize(&pVariable->Value);
445 + }
446 + }
447 + MemFree(pVariables->rgVariables);
448 + }
449 +}
450 +
451 +extern "C" void VariablesDump(
452 + __in BURN_VARIABLES* pVariables
453 + )
454 +{
455 + HRESULT hr = S_OK;
456 + LPWSTR sczValue = NULL;
457 +
458 + for (DWORD i = 0; i < pVariables->cVariables; ++i)
459 + {
460 + BURN_VARIABLE* pVariable = &pVariables->rgVariables[i];
461 + if (pVariable && BURN_VARIANT_TYPE_NONE != pVariable->Value.Type)
462 + {
463 + hr = StrAllocFormatted(&sczValue, L"%ls = [%ls]", pVariable->sczName, pVariable->sczName);
464 + if (SUCCEEDED(hr))
465 + {
466 + if (pVariable->fHidden)
467 + {
468 + hr = VariableFormatStringObfuscated(pVariables, sczValue, &sczValue, NULL);
469 + }
470 + else
471 + {
472 + hr = VariableFormatString(pVariables, sczValue, &sczValue, NULL);
473 + }
474 + }
475 +
476 + if (FAILED(hr))
477 + {
478 + // already logged; best-effort to dump the rest on our way out the door
479 + continue;
480 + }
481 +
482 + LogId(REPORT_VERBOSE, MSG_VARIABLE_DUMP, sczValue);
483 +
484 + ReleaseNullStrSecure(sczValue);
485 + }
486 + }
487 +
488 + StrSecureZeroFreeString(sczValue);
489 +}
490 +
491 +// The contents of pllValue may be sensitive, if variable is hidden should keep value encrypted and SecureZeroMemory.
492 +extern "C" HRESULT VariableGetNumeric(
493 + __in BURN_VARIABLES* pVariables,
494 + __in_z LPCWSTR wzVariable,
495 + __out LONGLONG* pllValue
496 + )
497 +{
498 + HRESULT hr = S_OK;
499 + BURN_VARIABLE* pVariable = NULL;
500 +
501 + ::EnterCriticalSection(&pVariables->csAccess);
502 +
503 + hr = GetVariable(pVariables, wzVariable, &pVariable);
504 + if (SUCCEEDED(hr) && BURN_VARIANT_TYPE_NONE == pVariable->Value.Type)
505 + {
506 + ExitFunction1(hr = E_NOTFOUND);
507 + }
508 + else if (E_NOTFOUND == hr)
509 + {
510 + ExitFunction();
511 + }
512 + ExitOnFailure(hr, "Failed to get value of variable: %ls", wzVariable);
513 +
514 + hr = BVariantGetNumeric(&pVariable->Value, pllValue);
515 + ExitOnFailure(hr, "Failed to get value as numeric for variable: %ls", wzVariable);
516 +
517 +LExit:
518 + ::LeaveCriticalSection(&pVariables->csAccess);
519 +
520 + return hr;
521 +}
522 +
523 +// The contents of psczValue may be sensitive, if variable is hidden should keep encrypted and SecureZeroFree.
524 +extern "C" HRESULT VariableGetString(
525 + __in BURN_VARIABLES* pVariables,
526 + __in_z LPCWSTR wzVariable,
527 + __out_z LPWSTR* psczValue
528 + )
529 +{
530 + HRESULT hr = S_OK;
531 + BURN_VARIABLE* pVariable = NULL;
532 +
533 + ::EnterCriticalSection(&pVariables->csAccess);
534 +
535 + hr = GetVariable(pVariables, wzVariable, &pVariable);
536 + if (SUCCEEDED(hr) && BURN_VARIANT_TYPE_NONE == pVariable->Value.Type)
537 + {
538 + ExitFunction1(hr = E_NOTFOUND);
539 + }
540 + else if (E_NOTFOUND == hr)
541 + {
542 + ExitFunction();
543 + }
544 + ExitOnFailure(hr, "Failed to get value of variable: %ls", wzVariable);
545 +
546 + hr = BVariantGetString(&pVariable->Value, psczValue);
547 + ExitOnFailure(hr, "Failed to get value as string for variable: %ls", wzVariable);
548 +
549 +LExit:
550 + ::LeaveCriticalSection(&pVariables->csAccess);
551 +
552 + return hr;
553 +}
554 +
555 +// The contents of pqwValue may be sensitive, if variable is hidden should keep value encrypted and SecureZeroMemory.
556 +extern "C" HRESULT VariableGetVersion(
557 + __in BURN_VARIABLES* pVariables,
558 + __in_z LPCWSTR wzVariable,
559 + __in DWORD64* pqwValue
560 + )
561 +{
562 + HRESULT hr = S_OK;
563 + BURN_VARIABLE* pVariable = NULL;
564 +
565 + ::EnterCriticalSection(&pVariables->csAccess);
566 +
567 + hr = GetVariable(pVariables, wzVariable, &pVariable);
568 + if (SUCCEEDED(hr) && BURN_VARIANT_TYPE_NONE == pVariable->Value.Type)
569 + {
570 + ExitFunction1(hr = E_NOTFOUND);
571 + }
572 + else if (E_NOTFOUND == hr)
573 + {
574 + ExitFunction();
575 + }
576 + ExitOnFailure(hr, "Failed to get value of variable: %ls", wzVariable);
577 +
578 + hr = BVariantGetVersion(&pVariable->Value, pqwValue);
579 + ExitOnFailure(hr, "Failed to get value as version for variable: %ls", wzVariable);
580 +
581 +LExit:
582 + ::LeaveCriticalSection(&pVariables->csAccess);
583 +
584 + return hr;
585 +}
586 +
587 +extern "C" HRESULT VariableGetVariant(
588 + __in BURN_VARIABLES* pVariables,
589 + __in_z LPCWSTR wzVariable,
590 + __in BURN_VARIANT* pValue
591 + )
592 +{
593 + HRESULT hr = S_OK;
594 + BURN_VARIABLE* pVariable = NULL;
595 +
596 + ::EnterCriticalSection(&pVariables->csAccess);
597 +
598 + hr = GetVariable(pVariables, wzVariable, &pVariable);
599 + if (E_NOTFOUND == hr)
600 + {
601 + ExitFunction();
602 + }
603 + ExitOnFailure(hr, "Failed to get value of variable: %ls", wzVariable);
604 +
605 + hr = BVariantCopy(&pVariable->Value, pValue);
606 + ExitOnFailure(hr, "Failed to copy value of variable: %ls", wzVariable);
607 +
608 +LExit:
609 + ::LeaveCriticalSection(&pVariables->csAccess);
610 +
611 + return hr;
612 +}
613 +
614 +// The contents of psczValue may be sensitive, should keep encrypted and SecureZeroFree.
615 +extern "C" HRESULT VariableGetFormatted(
616 + __in BURN_VARIABLES* pVariables,
617 + __in_z LPCWSTR wzVariable,
618 + __out_z LPWSTR* psczValue
619 + )
620 +{
621 + HRESULT hr = S_OK;
622 + BURN_VARIABLE* pVariable = NULL;
623 + LPWSTR scz = NULL;
624 +
625 + ::EnterCriticalSection(&pVariables->csAccess);
626 +
627 + hr = GetVariable(pVariables, wzVariable, &pVariable);
628 + if (SUCCEEDED(hr) && BURN_VARIANT_TYPE_NONE == pVariable->Value.Type)
629 + {
630 + ExitFunction1(hr = E_NOTFOUND);
631 + }
632 + else if (E_NOTFOUND == hr)
633 + {
634 + ExitFunction();
635 + }
636 + ExitOnFailure(hr, "Failed to get variable: %ls", wzVariable);
637 +
638 + // Strings need to get expanded unless they're built-in or literal because they're guaranteed not to have embedded variables.
639 + if (BURN_VARIANT_TYPE_STRING == pVariable->Value.Type &&
640 + BURN_VARIABLE_INTERNAL_TYPE_NORMAL == pVariable->internalType &&
641 + !pVariable->fLiteral)
642 + {
643 + hr = BVariantGetString(&pVariable->Value, &scz);
644 + ExitOnFailure(hr, "Failed to get unformatted string.");
645 +
646 + hr = VariableFormatString(pVariables, scz, psczValue, NULL);
647 + ExitOnFailure(hr, "Failed to format value '%ls' of variable: %ls", pVariable->fHidden ? L"*****" : pVariable->Value.sczValue, wzVariable);
648 + }
649 + else
650 + {
651 + hr = BVariantGetString(&pVariable->Value, psczValue);
652 + ExitOnFailure(hr, "Failed to get value as string for variable: %ls", wzVariable);
653 + }
654 +
655 +LExit:
656 + ::LeaveCriticalSection(&pVariables->csAccess);
657 + StrSecureZeroFreeString(scz);
658 +
659 + return hr;
660 +}
661 +
662 +extern "C" HRESULT VariableSetNumeric(
663 + __in BURN_VARIABLES* pVariables,
664 + __in_z LPCWSTR wzVariable,
665 + __in LONGLONG llValue,
666 + __in BOOL fOverwriteBuiltIn
667 + )
668 +{
669 + BURN_VARIANT variant = { };
670 +
671 + // We're not going to encrypt this value, so can access the value directly.
672 + variant.llValue = llValue;
673 + variant.Type = BURN_VARIANT_TYPE_NUMERIC;
674 +
675 + return SetVariableValue(pVariables, wzVariable, &variant, FALSE, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE);
676 +}
677 +
678 +extern "C" HRESULT VariableSetLiteralString(
679 + __in BURN_VARIABLES* pVariables,
680 + __in_z LPCWSTR wzVariable,
681 + __in_z_opt LPCWSTR wzValue,
682 + __in BOOL fOverwriteBuiltIn
683 + )
684 +{
685 + BURN_VARIANT variant = { };
686 +
687 + // We're not going to encrypt this value, so can access the value directly.
688 + variant.sczValue = (LPWSTR)wzValue;
689 + variant.Type = BURN_VARIANT_TYPE_STRING;
690 +
691 + return SetVariableValue(pVariables, wzVariable, &variant, TRUE, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE);
692 +}
693 +
694 +extern "C" HRESULT VariableSetString(
695 + __in BURN_VARIABLES* pVariables,
696 + __in_z LPCWSTR wzVariable,
697 + __in_z_opt LPCWSTR wzValue,
698 + __in BOOL fOverwriteBuiltIn
699 + )
700 +{
701 + BURN_VARIANT variant = { };
702 +
703 + // We're not going to encrypt this value, so can access the value directly.
704 + variant.sczValue = (LPWSTR)wzValue;
705 + variant.Type = BURN_VARIANT_TYPE_STRING;
706 +
707 + return SetVariableValue(pVariables, wzVariable, &variant, FALSE, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE);
708 +}
709 +
710 +extern "C" HRESULT VariableSetVersion(
711 + __in BURN_VARIABLES* pVariables,
712 + __in_z LPCWSTR wzVariable,
713 + __in DWORD64 qwValue,
714 + __in BOOL fOverwriteBuiltIn
715 + )
716 +{
717 + BURN_VARIANT variant = { };
718 +
719 + // We're not going to encrypt this value, so can access the value directly.
720 + variant.qwValue = qwValue;
721 + variant.Type = BURN_VARIANT_TYPE_VERSION;
722 +
723 + return SetVariableValue(pVariables, wzVariable, &variant, FALSE, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE);
724 +}
725 +
726 +extern "C" HRESULT VariableSetLiteralVariant(
727 + __in BURN_VARIABLES* pVariables,
728 + __in_z LPCWSTR wzVariable,
729 + __in BURN_VARIANT* pVariant
730 + )
731 +{
732 + return SetVariableValue(pVariables, wzVariable, pVariant, TRUE, SET_VARIABLE_NOT_BUILTIN, TRUE);
733 +}
734 +
735 +// The contents of psczOut may be sensitive, should keep encrypted and SecureZeroFree
736 +extern "C" HRESULT VariableFormatString(
737 + __in BURN_VARIABLES* pVariables,
738 + __in_z LPCWSTR wzIn,
739 + __out_z_opt LPWSTR* psczOut,
740 + __out_opt DWORD* pcchOut
741 + )
742 +{
743 + return FormatString(pVariables, wzIn, psczOut, pcchOut, FALSE);
744 +}
745 +
746 +extern "C" HRESULT VariableFormatStringObfuscated(
747 + __in BURN_VARIABLES* pVariables,
748 + __in_z LPCWSTR wzIn,
749 + __out_z_opt LPWSTR* psczOut,
750 + __out_opt DWORD* pcchOut
751 + )
752 +{
753 + return FormatString(pVariables, wzIn, psczOut, pcchOut, TRUE);
754 +}
755 +
756 +extern "C" HRESULT VariableEscapeString(
757 + __in_z LPCWSTR wzIn,
758 + __out_z LPWSTR* psczOut
759 + )
760 +{
761 + HRESULT hr = S_OK;
762 + LPCWSTR wzRead = NULL;
763 + LPWSTR pwzEscaped = NULL;
764 + LPWSTR pwz = NULL;
765 + SIZE_T i = 0;
766 +
767 + // allocate buffer for escaped string
768 + hr = StrAlloc(&pwzEscaped, lstrlenW(wzIn) + 1);
769 + ExitOnFailure(hr, "Failed to allocate buffer for escaped string.");
770 +
771 + // read through string and move characters, inserting escapes as needed
772 + wzRead = wzIn;
773 + for (;;)
774 + {
775 + // find next character needing escaping
776 + i = wcscspn(wzRead, L"[]{}");
777 +
778 + // copy skipped characters
779 + if (0 < i)
780 + {
781 + hr = StrAllocConcat(&pwzEscaped, wzRead, i);
782 + ExitOnFailure(hr, "Failed to append characters.");
783 + }
784 +
785 + if (L'\0' == wzRead[i])
786 + {
787 + break; // end reached
788 + }
789 +
790 + // escape character
791 + hr = StrAllocFormatted(&pwz, L"[\\%c]", wzRead[i]);
792 + ExitOnFailure(hr, "Failed to format escape sequence.");
793 +
794 + hr = StrAllocConcat(&pwzEscaped, pwz, 0);
795 + ExitOnFailure(hr, "Failed to append escape sequence.");
796 +
797 + // update read pointer
798 + wzRead += i + 1;
799 + }
800 +
801 + // return value
802 + hr = StrAllocString(psczOut, pwzEscaped, 0);
803 + ExitOnFailure(hr, "Failed to copy string.");
804 +
805 +LExit:
806 + ReleaseStr(pwzEscaped);
807 + ReleaseStr(pwz);
808 + return hr;
809 +}
810 +
811 +extern "C" HRESULT VariableSerialize(
812 + __in BURN_VARIABLES* pVariables,
813 + __in BOOL fPersisting,
814 + __inout BYTE** ppbBuffer,
815 + __inout SIZE_T* piBuffer
816 + )
817 +{
818 + HRESULT hr = S_OK;
819 + BOOL fIncluded = FALSE;
820 + LONGLONG ll = 0;
821 + LPWSTR scz = NULL;
822 + DWORD64 qw = 0;
823 +
824 + ::EnterCriticalSection(&pVariables->csAccess);
825 +
826 + // Write variable count.
827 + hr = BuffWriteNumber(ppbBuffer, piBuffer, pVariables->cVariables);
828 + ExitOnFailure(hr, "Failed to write variable count.");
829 +
830 + // Write variables.
831 + for (DWORD i = 0; i < pVariables->cVariables; ++i)
832 + {
833 + BURN_VARIABLE* pVariable = &pVariables->rgVariables[i];
834 +
835 + // If we aren't persisting, include only variables that aren't rejected by the elevated process.
836 + // If we are persisting, include only variables that should be persisted.
837 + fIncluded = (!fPersisting && BURN_VARIABLE_INTERNAL_TYPE_BUILTIN != pVariable->internalType) ||
838 + (fPersisting && pVariable->fPersisted);
839 +
840 + // Write included flag.
841 + hr = BuffWriteNumber(ppbBuffer, piBuffer, (DWORD)fIncluded);
842 + ExitOnFailure(hr, "Failed to write included flag.");
843 +
844 + if (!fIncluded)
845 + {
846 + continue;
847 + }
848 +
849 + // Write variable name.
850 + hr = BuffWriteString(ppbBuffer, piBuffer, pVariable->sczName);
851 + ExitOnFailure(hr, "Failed to write variable name.");
852 +
853 + // Write variable value type.
854 + hr = BuffWriteNumber(ppbBuffer, piBuffer, (DWORD)pVariable->Value.Type);
855 + ExitOnFailure(hr, "Failed to write variable value type.");
856 +
857 + // Write variable value.
858 + switch (pVariable->Value.Type)
859 + {
860 + case BURN_VARIANT_TYPE_NONE:
861 + break;
862 + case BURN_VARIANT_TYPE_NUMERIC:
863 + hr = BVariantGetNumeric(&pVariable->Value, &ll);
864 + ExitOnFailure(hr, "Failed to get numeric.");
865 +
866 + hr = BuffWriteNumber64(ppbBuffer, piBuffer, static_cast<DWORD64>(ll));
867 + ExitOnFailure(hr, "Failed to write variable value as number.");
868 +
869 + SecureZeroMemory(&ll, sizeof(ll));
870 + break;
871 + case BURN_VARIANT_TYPE_VERSION:
872 + hr = BVariantGetVersion(&pVariable->Value, &qw);
873 + ExitOnFailure(hr, "Failed to get version.");
874 +
875 + hr = BuffWriteNumber64(ppbBuffer, piBuffer, qw);
876 + ExitOnFailure(hr, "Failed to write variable value as number.");
877 +
878 + SecureZeroMemory(&qw, sizeof(qw));
879 + break;
880 + case BURN_VARIANT_TYPE_STRING:
881 + hr = BVariantGetString(&pVariable->Value, &scz);
882 + ExitOnFailure(hr, "Failed to get string.");
883 +
884 + hr = BuffWriteString(ppbBuffer, piBuffer, scz);
885 + ExitOnFailure(hr, "Failed to write variable value as string.");
886 +
887 + ReleaseNullStrSecure(scz);
888 + break;
889 + default:
890 + hr = E_INVALIDARG;
891 + ExitOnFailure(hr, "Unsupported variable type.");
892 + }
893 +
894 + // Write literal flag.
895 + hr = BuffWriteNumber(ppbBuffer, piBuffer, (DWORD)pVariable->fLiteral);
896 + ExitOnFailure(hr, "Failed to write literal flag.");
897 + }
898 +
899 +LExit:
900 + ::LeaveCriticalSection(&pVariables->csAccess);
901 + SecureZeroMemory(&ll, sizeof(ll));
902 + SecureZeroMemory(&qw, sizeof(qw));
903 + StrSecureZeroFreeString(scz);
904 +
905 + return hr;
906 +}
907 +
908 +extern "C" HRESULT VariableDeserialize(
909 + __in BURN_VARIABLES* pVariables,
910 + __in BOOL fWasPersisted,
911 + __in_bcount(cbBuffer) BYTE* pbBuffer,
912 + __in SIZE_T cbBuffer,
913 + __inout SIZE_T* piBuffer
914 + )
915 +{
916 + HRESULT hr = S_OK;
917 + DWORD cVariables = 0;
918 + LPWSTR sczName = NULL;
919 + BOOL fIncluded = FALSE;
920 + BOOL fLiteral = FALSE;
921 + BURN_VARIANT value = { };
922 + LPWSTR scz = NULL;
923 + DWORD64 qw = 0;
924 +
925 + ::EnterCriticalSection(&pVariables->csAccess);
926 +
927 + // Read variable count.
928 + hr = BuffReadNumber(pbBuffer, cbBuffer, piBuffer, &cVariables);
929 + ExitOnFailure(hr, "Failed to read variable count.");
930 +
931 + // Read variables.
932 + for (DWORD i = 0; i < cVariables; ++i)
933 + {
934 + // Read variable included flag.
935 + hr = BuffReadNumber(pbBuffer, cbBuffer, piBuffer, (DWORD*)&fIncluded);
936 + ExitOnFailure(hr, "Failed to read variable included flag.");
937 +
938 + if (!fIncluded)
939 + {
940 + continue; // if variable is not included, skip.
941 + }
942 +
943 + // Read variable name.
944 + hr = BuffReadString(pbBuffer, cbBuffer, piBuffer, &sczName);
945 + ExitOnFailure(hr, "Failed to read variable name.");
946 +
947 + // Read variable value type.
948 + hr = BuffReadNumber(pbBuffer, cbBuffer, piBuffer, (DWORD*)&value.Type);
949 + ExitOnFailure(hr, "Failed to read variable value type.");
950 +
951 + // Read variable value.
952 + switch (value.Type)
953 + {
954 + case BURN_VARIANT_TYPE_NONE:
955 + break;
956 + case BURN_VARIANT_TYPE_NUMERIC:
957 + hr = BuffReadNumber64(pbBuffer, cbBuffer, piBuffer, &qw);
958 + ExitOnFailure(hr, "Failed to read variable value as number.");
959 +
960 + hr = BVariantSetNumeric(&value, static_cast<LONGLONG>(qw));
961 + ExitOnFailure(hr, "Failed to set variable value.");
962 +
963 + SecureZeroMemory(&qw, sizeof(qw));
964 + break;
965 + case BURN_VARIANT_TYPE_VERSION:
966 + hr = BuffReadNumber64(pbBuffer, cbBuffer, piBuffer, &qw);
967 + ExitOnFailure(hr, "Failed to read variable value as number.");
968 +
969 + hr = BVariantSetVersion(&value, qw);
970 + ExitOnFailure(hr, "Failed to set variable value.");
971 +
972 + SecureZeroMemory(&qw, sizeof(qw));
973 + break;
974 + case BURN_VARIANT_TYPE_STRING:
975 + hr = BuffReadString(pbBuffer, cbBuffer, piBuffer, &scz);
976 + ExitOnFailure(hr, "Failed to read variable value as string.");
977 +
978 + hr = BVariantSetString(&value, scz, NULL);
979 + ExitOnFailure(hr, "Failed to set variable value.");
980 +
981 + ReleaseNullStrSecure(scz);
982 + break;
983 + default:
984 + hr = E_INVALIDARG;
985 + ExitOnFailure(hr, "Unsupported variable type.");
986 + }
987 +
988 + // Read variable literal flag.
989 + hr = BuffReadNumber(pbBuffer, cbBuffer, piBuffer, (DWORD*)&fLiteral);
990 + ExitOnFailure(hr, "Failed to read variable literal flag.");
991 +
992 + // Set variable.
993 + hr = SetVariableValue(pVariables, sczName, &value, fLiteral, fWasPersisted ? SET_VARIABLE_OVERRIDE_PERSISTED_BUILTINS : SET_VARIABLE_ANY, FALSE);
994 + ExitOnFailure(hr, "Failed to set variable.");
995 +
996 + // Clean up.
997 + BVariantUninitialize(&value);
998 + }
999 +
1000 +LExit:
1001 + ::LeaveCriticalSection(&pVariables->csAccess);
1002 +
1003 + ReleaseStr(sczName);
1004 + BVariantUninitialize(&value);
1005 + SecureZeroMemory(&qw, sizeof(qw));
1006 + StrSecureZeroFreeString(scz);
1007 +
1008 + return hr;
1009 +}
1010 +
1011 +extern "C" HRESULT VariableStrAlloc(
1012 + __in BOOL fZeroOnRealloc,
1013 + __deref_out_ecount_part(cch, 0) LPWSTR* ppwz,
1014 + __in DWORD_PTR cch
1015 + )
1016 +{
1017 + HRESULT hr = S_OK;
1018 +
1019 + if (fZeroOnRealloc)
1020 + {
1021 + hr = StrAllocSecure(ppwz, cch);
1022 + }
1023 + else
1024 + {
1025 + hr = StrAlloc(ppwz, cch);
1026 + }
1027 +
1028 + return hr;
1029 +}
1030 +
1031 +extern "C" HRESULT VariableStrAllocString(
1032 + __in BOOL fZeroOnRealloc,
1033 + __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz,
1034 + __in_z LPCWSTR wzSource,
1035 + __in DWORD_PTR cchSource
1036 + )
1037 +{
1038 + HRESULT hr = S_OK;
1039 +
1040 + if (fZeroOnRealloc)
1041 + {
1042 + hr = StrAllocStringSecure(ppwz, wzSource, cchSource);
1043 + }
1044 + else
1045 + {
1046 + hr = StrAllocString(ppwz, wzSource, cchSource);
1047 + }
1048 +
1049 + return hr;
1050 +}
1051 +
1052 +extern "C" HRESULT VariableStrAllocConcat(
1053 + __in BOOL fZeroOnRealloc,
1054 + __deref_out_z LPWSTR* ppwz,
1055 + __in_z LPCWSTR wzSource,
1056 + __in DWORD_PTR cchSource
1057 + )
1058 +{
1059 + HRESULT hr = S_OK;
1060 +
1061 + if (fZeroOnRealloc)
1062 + {
1063 + hr = StrAllocConcatSecure(ppwz, wzSource, cchSource);
1064 + }
1065 + else
1066 + {
1067 + hr = StrAllocConcat(ppwz, wzSource, cchSource);
1068 + }
1069 +
1070 + return hr;
1071 +}
1072 +
1073 +extern "C" HRESULT __cdecl VariableStrAllocFormatted(
1074 + __in BOOL fZeroOnRealloc,
1075 + __deref_out_z LPWSTR* ppwz,
1076 + __in __format_string LPCWSTR wzFormat,
1077 + ...
1078 + )
1079 +{
1080 + HRESULT hr = S_OK;
1081 + va_list args;
1082 +
1083 + va_start(args, wzFormat);
1084 + if (fZeroOnRealloc)
1085 + {
1086 + hr = StrAllocFormattedArgsSecure(ppwz, wzFormat, args);
1087 + }
1088 + else
1089 + {
1090 + hr = StrAllocFormattedArgs(ppwz, wzFormat, args);
1091 + }
1092 + va_end(args);
1093 +
1094 + return hr;
1095 +}
1096 +
1097 +extern "C" HRESULT VariableIsHidden(
1098 + __in BURN_VARIABLES* pVariables,
1099 + __in_z LPCWSTR wzVariable,
1100 + __out BOOL* pfHidden
1101 + )
1102 +{
1103 + HRESULT hr = S_OK;
1104 + BURN_VARIABLE* pVariable = NULL;
1105 +
1106 + ::EnterCriticalSection(&pVariables->csAccess);
1107 +
1108 + hr = GetVariable(pVariables, wzVariable, &pVariable);
1109 + if (E_NOTFOUND == hr)
1110 + {
1111 + // A missing variable does not need its data hidden.
1112 + *pfHidden = FALSE;
1113 +
1114 + ExitFunction1(hr = S_OK);
1115 + }
1116 + ExitOnFailure(hr, "Failed to get visibility of variable: %ls", wzVariable);
1117 +
1118 + *pfHidden = pVariable->fHidden;
1119 +
1120 +LExit:
1121 + ::LeaveCriticalSection(&pVariables->csAccess);
1122 +
1123 + return hr;
1124 +}
1125 +
1126 +
1127 +// internal function definitions
1128 +
1129 +// The contents of psczOut may be sensitive, should keep encrypted and SecureZeroFree.
1130 +static HRESULT FormatString(
1131 + __in BURN_VARIABLES* pVariables,
1132 + __in_z LPCWSTR wzIn,
1133 + __out_z_opt LPWSTR* psczOut,
1134 + __out_opt DWORD* pcchOut,
1135 + __in BOOL fObfuscateHiddenVariables
1136 + )
1137 +{
1138 + HRESULT hr = S_OK;
1139 + DWORD er = ERROR_SUCCESS;
1140 + LPWSTR sczUnformatted = NULL;
1141 + LPWSTR sczFormat = NULL;
1142 + LPCWSTR wzRead = NULL;
1143 + LPCWSTR wzOpen = NULL;
1144 + LPCWSTR wzClose = NULL;
1145 + LPWSTR scz = NULL;
1146 + LPWSTR* rgVariables = NULL;
1147 + DWORD cVariables = 0;
1148 + DWORD cch = 0;
1149 + BOOL fHidden = FALSE;
1150 + MSIHANDLE hRecord = NULL;
1151 +
1152 + ::EnterCriticalSection(&pVariables->csAccess);
1153 +
1154 + // allocate buffer for format string
1155 + hr = StrAlloc(&sczFormat, lstrlenW(wzIn) + 1);
1156 + ExitOnFailure(hr, "Failed to allocate buffer for format string.");
1157 +
1158 + // read out variables from the unformatted string and build a format string
1159 + wzRead = wzIn;
1160 + for (;;)
1161 + {
1162 + // scan for opening '['
1163 + wzOpen = wcschr(wzRead, L'[');
1164 + if (!wzOpen)
1165 + {
1166 + // end reached, append the remainder of the string and end loop
1167 + hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, wzRead, 0);
1168 + ExitOnFailure(hr, "Failed to append string.");
1169 + break;
1170 + }
1171 +
1172 + // scan for closing ']'
1173 + wzClose = wcschr(wzOpen + 1, L']');
1174 + if (!wzClose)
1175 + {
1176 + // end reached, treat unterminated expander as literal
1177 + hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, wzRead, 0);
1178 + ExitOnFailure(hr, "Failed to append string.");
1179 + break;
1180 + }
1181 + cch = wzClose - wzOpen - 1;
1182 +
1183 + if (0 == cch)
1184 + {
1185 + // blank, copy all text including the terminator
1186 + hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, wzRead, (DWORD_PTR)(wzClose - wzRead) + 1);
1187 + ExitOnFailure(hr, "Failed to append string.");
1188 + }
1189 + else
1190 + {
1191 + // append text preceding expander
1192 + if (wzOpen > wzRead)
1193 + {
1194 + hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, wzRead, (DWORD_PTR)(wzOpen - wzRead));
1195 + ExitOnFailure(hr, "Failed to append string.");
1196 + }
1197 +
1198 + // get variable name
1199 + hr = VariableStrAllocString(!fObfuscateHiddenVariables, &scz, wzOpen + 1, cch);
1200 + ExitOnFailure(hr, "Failed to get variable name.");
1201 +
1202 + // allocate space in variable array
1203 + if (rgVariables)
1204 + {
1205 + LPVOID pv = MemReAlloc(rgVariables, sizeof(LPWSTR) * (cVariables + 1), TRUE);
1206 + ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate variable array.");
1207 + rgVariables = (LPWSTR*)pv;
1208 + }
1209 + else
1210 + {
1211 + rgVariables = (LPWSTR*)MemAlloc(sizeof(LPWSTR) * (cVariables + 1), TRUE);
1212 + ExitOnNull(rgVariables, hr, E_OUTOFMEMORY, "Failed to allocate variable array.");
1213 + }
1214 +
1215 + // set variable value
1216 + if (2 <= cch && L'\\' == wzOpen[1])
1217 + {
1218 + // escape sequence, copy character
1219 + hr = VariableStrAllocString(!fObfuscateHiddenVariables, &rgVariables[cVariables], &wzOpen[2], 1);
1220 + }
1221 + else
1222 + {
1223 + if (fObfuscateHiddenVariables)
1224 + {
1225 + hr = VariableIsHidden(pVariables, scz, &fHidden);
1226 + ExitOnFailure(hr, "Failed to determine variable visibility: '%ls'.", scz);
1227 + }
1228 +
1229 + if (fHidden)
1230 + {
1231 + hr = StrAllocString(&rgVariables[cVariables], L"*****", 0);
1232 + }
1233 + else
1234 + {
1235 + // get formatted variable value
1236 + hr = VariableGetFormatted(pVariables, scz, &rgVariables[cVariables]);
1237 + if (E_NOTFOUND == hr) // variable not found
1238 + {
1239 + hr = StrAllocStringSecure(&rgVariables[cVariables], L"", 0);
1240 + }
1241 + }
1242 + }
1243 + ExitOnFailure(hr, "Failed to set variable value.");
1244 + ++cVariables;
1245 +
1246 + // append placeholder to format string
1247 + hr = VariableStrAllocFormatted(!fObfuscateHiddenVariables, &scz, L"[%d]", cVariables);
1248 + ExitOnFailure(hr, "Failed to format placeholder string.");
1249 +
1250 + hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, scz, 0);
1251 + ExitOnFailure(hr, "Failed to append placeholder.");
1252 + }
1253 +
1254 + // update read pointer
1255 + wzRead = wzClose + 1;
1256 + }
1257 +
1258 + // create record
1259 + hRecord = ::MsiCreateRecord(cVariables);
1260 + ExitOnNull(hRecord, hr, E_OUTOFMEMORY, "Failed to allocate record.");
1261 +
1262 + // set format string
1263 + er = ::MsiRecordSetStringW(hRecord, 0, sczFormat);
1264 + ExitOnWin32Error(er, hr, "Failed to set record format string.");
1265 +
1266 + // copy record fields
1267 + for (DWORD i = 0; i < cVariables; ++i)
1268 + {
1269 + if (*rgVariables[i]) // not setting if blank
1270 + {
1271 + er = ::MsiRecordSetStringW(hRecord, i + 1, rgVariables[i]);
1272 + ExitOnWin32Error(er, hr, "Failed to set record string.");
1273 + }
1274 + }
1275 +
1276 + // get formatted character count
1277 + cch = 0;
1278 +#pragma prefast(push)
1279 +#pragma prefast(disable:6298)
1280 + er = ::MsiFormatRecordW(NULL, hRecord, L"", &cch);
1281 +#pragma prefast(pop)
1282 + if (ERROR_MORE_DATA != er)
1283 + {
1284 + ExitOnWin32Error(er, hr, "Failed to get formatted length.");
1285 + }
1286 +
1287 + // return formatted string
1288 + if (psczOut)
1289 + {
1290 + hr = VariableStrAlloc(!fObfuscateHiddenVariables, &scz, ++cch);
1291 + ExitOnFailure(hr, "Failed to allocate string.");
1292 +
1293 + er = ::MsiFormatRecordW(NULL, hRecord, scz, &cch);
1294 + ExitOnWin32Error(er, hr, "Failed to format record.");
1295 +
1296 + hr = VariableStrAllocString(!fObfuscateHiddenVariables, psczOut, scz, 0);
1297 + ExitOnFailure(hr, "Failed to copy string.");
1298 + }
1299 +
1300 + // return character count
1301 + if (pcchOut)
1302 + {
1303 + *pcchOut = cch;
1304 + }
1305 +
1306 +LExit:
1307 + ::LeaveCriticalSection(&pVariables->csAccess);
1308 +
1309 + if (rgVariables)
1310 + {
1311 + for (DWORD i = 0; i < cVariables; ++i)
1312 + {
1313 + if (fObfuscateHiddenVariables)
1314 + {
1315 + ReleaseStr(rgVariables[i]);
1316 + }
1317 + else
1318 + {
1319 + StrSecureZeroFreeString(rgVariables[i]);
1320 + }
1321 + }
1322 + MemFree(rgVariables);
1323 + }
1324 +
1325 + if (hRecord)
1326 + {
1327 + ::MsiCloseHandle(hRecord);
1328 + }
1329 +
1330 + if (fObfuscateHiddenVariables)
1331 + {
1332 + ReleaseStr(sczUnformatted);
1333 + ReleaseStr(sczFormat);
1334 + ReleaseStr(scz);
1335 + }
1336 + else
1337 + {
1338 + StrSecureZeroFreeString(sczUnformatted);
1339 + StrSecureZeroFreeString(sczFormat);
1340 + StrSecureZeroFreeString(scz);
1341 + }
1342 +
1343 + return hr;
1344 +}
1345 +
1346 +static HRESULT AddBuiltInVariable(
1347 + __in BURN_VARIABLES* pVariables,
1348 + __in LPCWSTR wzVariable,
1349 + __in PFN_INITIALIZEVARIABLE pfnInitialize,
1350 + __in DWORD_PTR dwpInitializeData,
1351 + __in BOOL fPersist,
1352 + __in BOOL fOverridable
1353 + )
1354 +{
1355 + HRESULT hr = S_OK;
1356 + DWORD iVariable = 0;
1357 + BURN_VARIABLE* pVariable = NULL;
1358 +
1359 + hr = FindVariableIndexByName(pVariables, wzVariable, &iVariable);
1360 + ExitOnFailure(hr, "Failed to find variable value.");
1361 +
1362 + // insert element if not found
1363 + if (S_FALSE == hr)
1364 + {
1365 + hr = InsertVariable(pVariables, wzVariable, iVariable);
1366 + ExitOnFailure(hr, "Failed to insert variable.");
1367 + }
1368 +
1369 + // set variable values
1370 + pVariable = &pVariables->rgVariables[iVariable];
1371 + pVariable->fPersisted = fPersist;
1372 + pVariable->internalType = fOverridable ? BURN_VARIABLE_INTERNAL_TYPE_OVERRIDABLE_BUILTIN : BURN_VARIABLE_INTERNAL_TYPE_BUILTIN;
1373 + pVariable->pfnInitialize = pfnInitialize;
1374 + pVariable->dwpInitializeData = dwpInitializeData;
1375 +
1376 +LExit:
1377 + return hr;
1378 +}
1379 +
1380 +static HRESULT GetVariable(
1381 + __in BURN_VARIABLES* pVariables,
1382 + __in_z LPCWSTR wzVariable,
1383 + __out BURN_VARIABLE** ppVariable
1384 + )
1385 +{
1386 + HRESULT hr = S_OK;
1387 + DWORD iVariable = 0;
1388 + BURN_VARIABLE* pVariable = NULL;
1389 +
1390 + hr = FindVariableIndexByName(pVariables, wzVariable, &iVariable);
1391 + ExitOnFailure(hr, "Failed to find variable value '%ls'.", wzVariable);
1392 +
1393 + if (S_FALSE == hr)
1394 + {
1395 + ExitFunction1(hr = E_NOTFOUND);
1396 + }
1397 +
1398 + pVariable = &pVariables->rgVariables[iVariable];
1399 +
1400 + // initialize built-in variable
1401 + if (BURN_VARIANT_TYPE_NONE == pVariable->Value.Type && BURN_VARIABLE_INTERNAL_TYPE_NORMAL < pVariable->internalType)
1402 + {
1403 + hr = pVariable->pfnInitialize(pVariable->dwpInitializeData, &pVariable->Value);
1404 + ExitOnFailure(hr, "Failed to initialize built-in variable value '%ls'.", wzVariable);
1405 + }
1406 +
1407 + *ppVariable = pVariable;
1408 +
1409 +LExit:
1410 + return hr;
1411 +}
1412 +
1413 +static HRESULT FindVariableIndexByName(
1414 + __in BURN_VARIABLES* pVariables,
1415 + __in_z LPCWSTR wzVariable,
1416 + __out DWORD* piVariable
1417 + )
1418 +{
1419 + HRESULT hr = S_OK;
1420 + DWORD iRangeFirst = 0;
1421 + DWORD cRangeLength = pVariables->cVariables;
1422 +
1423 + while (cRangeLength)
1424 + {
1425 + // get variable in middle of range
1426 + DWORD iPosition = cRangeLength / 2;
1427 + BURN_VARIABLE* pVariable = &pVariables->rgVariables[iRangeFirst + iPosition];
1428 +
1429 + switch (::CompareStringW(LOCALE_INVARIANT, SORT_STRINGSORT, wzVariable, -1, pVariable->sczName, -1))
1430 + {
1431 + case CSTR_LESS_THAN:
1432 + // restrict range to elements before the current
1433 + cRangeLength = iPosition;
1434 + break;
1435 + case CSTR_EQUAL:
1436 + // variable found
1437 + *piVariable = iRangeFirst + iPosition;
1438 + ExitFunction1(hr = S_OK);
1439 + case CSTR_GREATER_THAN:
1440 + // restrict range to elements after the current
1441 + iRangeFirst += iPosition + 1;
1442 + cRangeLength -= iPosition + 1;
1443 + break;
1444 + default:
1445 + ExitWithLastError(hr, "Failed to compare strings.");
1446 + }
1447 + }
1448 +
1449 + *piVariable = iRangeFirst;
1450 + hr = S_FALSE; // variable not found
1451 +
1452 +LExit:
1453 + return hr;
1454 +}
1455 +
1456 +static HRESULT InsertVariable(
1457 + __in BURN_VARIABLES* pVariables,
1458 + __in_z LPCWSTR wzVariable,
1459 + __in DWORD iPosition
1460 + )
1461 +{
1462 + HRESULT hr = S_OK;
1463 + size_t cbAllocSize = 0;
1464 +
1465 + // ensure there is room in the variable array
1466 + if (pVariables->cVariables == pVariables->dwMaxVariables)
1467 + {
1468 + hr = ::DWordAdd(pVariables->dwMaxVariables, GROW_VARIABLE_ARRAY, &(pVariables->dwMaxVariables));
1469 + ExitOnRootFailure(hr, "Overflow while growing variable array size");
1470 +
1471 + if (pVariables->rgVariables)
1472 + {
1473 + hr = ::SizeTMult(sizeof(BURN_VARIABLE), pVariables->dwMaxVariables, &cbAllocSize);
1474 + ExitOnRootFailure(hr, "Overflow while calculating size of variable array buffer");
1475 +
1476 + LPVOID pv = MemReAlloc(pVariables->rgVariables, cbAllocSize, FALSE);
1477 + ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate room for more variables.");
1478 +
1479 + // Prefast claims it's possible to hit this. Putting the check in just in case.
1480 + if (pVariables->dwMaxVariables < pVariables->cVariables)
1481 + {
1482 + hr = INTSAFE_E_ARITHMETIC_OVERFLOW;
1483 + ExitOnRootFailure(hr, "Overflow while dealing with variable array buffer allocation");
1484 + }
1485 +
1486 + pVariables->rgVariables = (BURN_VARIABLE*)pv;
1487 + memset(&pVariables->rgVariables[pVariables->cVariables], 0, sizeof(BURN_VARIABLE) * (pVariables->dwMaxVariables - pVariables->cVariables));
1488 + }
1489 + else
1490 + {
1491 + pVariables->rgVariables = (BURN_VARIABLE*)MemAlloc(sizeof(BURN_VARIABLE) * pVariables->dwMaxVariables, TRUE);
1492 + ExitOnNull(pVariables->rgVariables, hr, E_OUTOFMEMORY, "Failed to allocate room for variables.");
1493 + }
1494 + }
1495 +
1496 + // move variables
1497 + if (0 < pVariables->cVariables - iPosition)
1498 + {
1499 + memmove(&pVariables->rgVariables[iPosition + 1], &pVariables->rgVariables[iPosition], sizeof(BURN_VARIABLE) * (pVariables->cVariables - iPosition));
1500 + memset(&pVariables->rgVariables[iPosition], 0, sizeof(BURN_VARIABLE));
1501 + }
1502 +
1503 + ++pVariables->cVariables;
1504 +
1505 + // allocate name
1506 + hr = StrAllocString(&pVariables->rgVariables[iPosition].sczName, wzVariable, 0);
1507 + ExitOnFailure(hr, "Failed to copy variable name.");
1508 +
1509 +LExit:
1510 + return hr;
1511 +}
1512 +
1513 +static HRESULT SetVariableValue(
1514 + __in BURN_VARIABLES* pVariables,
1515 + __in_z LPCWSTR wzVariable,
1516 + __in BURN_VARIANT* pVariant,
1517 + __in BOOL fLiteral,
1518 + __in SET_VARIABLE setBuiltin,
1519 + __in BOOL fLog
1520 + )
1521 +{
1522 + HRESULT hr = S_OK;
1523 + DWORD iVariable = 0;
1524 +
1525 + ::EnterCriticalSection(&pVariables->csAccess);
1526 +
1527 + hr = FindVariableIndexByName(pVariables, wzVariable, &iVariable);
1528 + ExitOnFailure(hr, "Failed to find variable value '%ls'.", wzVariable);
1529 +
1530 + // Insert element if not found.
1531 + if (S_FALSE == hr)
1532 + {
1533 + hr = InsertVariable(pVariables, wzVariable, iVariable);
1534 + ExitOnFailure(hr, "Failed to insert variable '%ls'.", wzVariable);
1535 + }
1536 + else if (BURN_VARIABLE_INTERNAL_TYPE_NORMAL < pVariables->rgVariables[iVariable].internalType) // built-in variables must be overridden.
1537 + {
1538 + if (SET_VARIABLE_OVERRIDE_BUILTIN == setBuiltin ||
1539 + (SET_VARIABLE_OVERRIDE_PERSISTED_BUILTINS == setBuiltin && pVariables->rgVariables[iVariable].fPersisted) ||
1540 + SET_VARIABLE_ANY == setBuiltin && BURN_VARIABLE_INTERNAL_TYPE_BUILTIN != pVariables->rgVariables[iVariable].internalType)
1541 + {
1542 + hr = S_OK;
1543 + }
1544 + else
1545 + {
1546 + hr = E_INVALIDARG;
1547 + ExitOnRootFailure(hr, "Attempt to set built-in variable value: %ls", wzVariable);
1548 + }
1549 + }
1550 + else // must *not* be a built-in variable so caller should not have tried to override it as a built-in.
1551 + {
1552 + // Not possible from external callers so just assert.
1553 + AssertSz(SET_VARIABLE_OVERRIDE_BUILTIN != setBuiltin, "Intent to overwrite non-built-in variable.");
1554 + }
1555 +
1556 + // Log value when not overwriting a built-in variable.
1557 + if (fLog && BURN_VARIABLE_INTERNAL_TYPE_NORMAL == pVariables->rgVariables[iVariable].internalType)
1558 + {
1559 + if (pVariables->rgVariables[iVariable].fHidden)
1560 + {
1561 + LogStringLine(REPORT_STANDARD, "Setting hidden variable '%ls'", wzVariable);
1562 + }
1563 + else
1564 + {
1565 + // Assume value isn't encrypted since it's not hidden.
1566 + switch (pVariant->Type)
1567 + {
1568 + case BURN_VARIANT_TYPE_NONE:
1569 + if (BURN_VARIANT_TYPE_NONE != pVariables->rgVariables[iVariable].Value.Type)
1570 + {
1571 + LogStringLine(REPORT_STANDARD, "Unsetting variable '%ls'", wzVariable, pVariant->sczValue);
1572 + }
1573 + break;
1574 +
1575 + case BURN_VARIANT_TYPE_NUMERIC:
1576 + LogStringLine(REPORT_STANDARD, "Setting numeric variable '%ls' to value %lld", wzVariable, pVariant->llValue);
1577 + break;
1578 +
1579 + case BURN_VARIANT_TYPE_STRING:
1580 + if (!pVariant->sczValue)
1581 + {
1582 + LogStringLine(REPORT_STANDARD, "Unsetting variable '%ls'", wzVariable, pVariant->sczValue);
1583 + }
1584 + else
1585 + {
1586 + LogStringLine(REPORT_STANDARD, "Setting string variable '%ls' to value '%ls'", wzVariable, pVariant->sczValue);
1587 + }
1588 + break;
1589 +
1590 + case BURN_VARIANT_TYPE_VERSION:
1591 + LogStringLine(REPORT_STANDARD, "Setting version variable '%ls' to value '%hu.%hu.%hu.%hu'", wzVariable, (WORD)(pVariant->qwValue >> 48), (WORD)(pVariant->qwValue >> 32), (WORD)(pVariant->qwValue >> 16), (WORD)(pVariant->qwValue));
1592 + break;
1593 +
1594 + default:
1595 + AssertSz(FALSE, "Unknown variant type.");
1596 + break;
1597 + }
1598 + }
1599 + }
1600 +
1601 + // Update variable value.
1602 + hr = BVariantSetValue(&pVariables->rgVariables[iVariable].Value, pVariant);
1603 + ExitOnFailure(hr, "Failed to set value of variable: %ls", wzVariable);
1604 +
1605 + // Update variable literal flag.
1606 + pVariables->rgVariables[iVariable].fLiteral = fLiteral;
1607 +
1608 +LExit:
1609 + ::LeaveCriticalSection(&pVariables->csAccess);
1610 +
1611 + if (FAILED(hr) && fLog)
1612 + {
1613 + LogStringLine(REPORT_STANDARD, "Setting variable failed: ID '%ls', HRESULT 0x%x", wzVariable, hr);
1614 + }
1615 +
1616 + return hr;
1617 +}
1618 +
1619 +extern "C" typedef NTSTATUS (NTAPI *RTL_GET_VERSION)(_Out_ PRTL_OSVERSIONINFOEXW lpVersionInformation);
1620 +
1621 +static HRESULT InitializeVariableVersionNT(
1622 + __in DWORD_PTR dwpData,
1623 + __inout BURN_VARIANT* pValue
1624 + )
1625 +{
1626 + HRESULT hr = S_OK;
1627 + HMODULE ntdll = NULL;
1628 + RTL_GET_VERSION rtlGetVersion = NULL;
1629 + RTL_OSVERSIONINFOEXW ovix = { };
1630 + BURN_VARIANT value = { };
1631 +
1632 + if (!::GetModuleHandleExW(0, L"ntdll", &ntdll))
1633 + {
1634 + ExitWithLastError(hr, "Failed to locate NTDLL.");
1635 + }
1636 +
1637 + rtlGetVersion = reinterpret_cast<RTL_GET_VERSION>(::GetProcAddress(ntdll, "RtlGetVersion"));
1638 + if (NULL == rtlGetVersion)
1639 + {
1640 + ExitWithLastError(hr, "Failed to locate RtlGetVersion.");
1641 + }
1642 +
1643 + ovix.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
1644 + hr = static_cast<HRESULT>(rtlGetVersion(&ovix));
1645 + ExitOnFailure(hr, "Failed to get OS info.");
1646 +
1647 + switch ((OS_INFO_VARIABLE)dwpData)
1648 + {
1649 + case OS_INFO_VARIABLE_ServicePackLevel:
1650 + if (0 != ovix.wServicePackMajor)
1651 + {
1652 + value.qwValue = static_cast<DWORD64>(ovix.wServicePackMajor);
1653 + value.Type = BURN_VARIANT_TYPE_NUMERIC;
1654 + }
1655 + break;
1656 + case OS_INFO_VARIABLE_VersionNT:
1657 + value.qwValue = MAKEQWORDVERSION(ovix.dwMajorVersion, ovix.dwMinorVersion, 0, 0);
1658 + value.Type = BURN_VARIANT_TYPE_VERSION;
1659 + break;
1660 + case OS_INFO_VARIABLE_VersionNT64:
1661 + {
1662 +#if !defined(_WIN64)
1663 + BOOL fIsWow64 = FALSE;
1664 +
1665 + ProcWow64(::GetCurrentProcess(), &fIsWow64);
1666 + if (fIsWow64)
1667 +#endif
1668 + {
1669 + value.qwValue = MAKEQWORDVERSION(ovix.dwMajorVersion, ovix.dwMinorVersion, 0, 0);
1670 + value.Type = BURN_VARIANT_TYPE_VERSION;
1671 + }
1672 + }
1673 + break;
1674 + default:
1675 + AssertSz(FALSE, "Unknown OS info type.");
1676 + break;
1677 + }
1678 +
1679 + hr = BVariantCopy(&value, pValue);
1680 + ExitOnFailure(hr, "Failed to set variant value.");
1681 +
1682 +LExit:
1683 + if (NULL != ntdll)
1684 + {
1685 + FreeLibrary(ntdll);
1686 + }
1687 +
1688 + return hr;
1689 +}
1690 +
1691 +static HRESULT InitializeVariableOsInfo(
1692 + __in DWORD_PTR dwpData,
1693 + __inout BURN_VARIANT* pValue
1694 + )
1695 +{
1696 + HRESULT hr = S_OK;
1697 + OSVERSIONINFOEXW ovix = { };
1698 + BURN_VARIANT value = { };
1699 +
1700 + ovix.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
1701 + if (!::GetVersionExW((LPOSVERSIONINFOW)&ovix))
1702 + {
1703 + ExitWithLastError(hr, "Failed to get OS info.");
1704 + }
1705 +
1706 + switch ((OS_INFO_VARIABLE)dwpData)
1707 + {
1708 + case OS_INFO_VARIABLE_NTProductType:
1709 + value.llValue = ovix.wProductType;
1710 + value.Type = BURN_VARIANT_TYPE_NUMERIC;
1711 + break;
1712 + case OS_INFO_VARIABLE_NTSuiteBackOffice:
1713 + value.llValue = VER_SUITE_BACKOFFICE & ovix.wSuiteMask ? 1 : 0;
1714 + value.Type = BURN_VARIANT_TYPE_NUMERIC;
1715 + break;
1716 + case OS_INFO_VARIABLE_NTSuiteDataCenter:
1717 + value.llValue = VER_SUITE_DATACENTER & ovix.wSuiteMask ? 1 : 0;
1718 + value.Type = BURN_VARIANT_TYPE_NUMERIC;
1719 + break;
1720 + case OS_INFO_VARIABLE_NTSuiteEnterprise:
1721 + value.llValue = VER_SUITE_ENTERPRISE & ovix.wSuiteMask ? 1 : 0;
1722 + value.Type = BURN_VARIANT_TYPE_NUMERIC;
1723 + break;
1724 + case OS_INFO_VARIABLE_NTSuitePersonal:
1725 + value.llValue = VER_SUITE_PERSONAL & ovix.wSuiteMask ? 1 : 0;
1726 + value.Type = BURN_VARIANT_TYPE_NUMERIC;
1727 + break;
1728 + case OS_INFO_VARIABLE_NTSuiteSmallBusiness:
1729 + value.llValue = VER_SUITE_SMALLBUSINESS & ovix.wSuiteMask ? 1 : 0;
1730 + value.Type = BURN_VARIANT_TYPE_NUMERIC;
1731 + break;
1732 + case OS_INFO_VARIABLE_NTSuiteSmallBusinessRestricted:
1733 + value.llValue = VER_SUITE_SMALLBUSINESS_RESTRICTED & ovix.wSuiteMask ? 1 : 0;
1734 + value.Type = BURN_VARIANT_TYPE_NUMERIC;
1735 + break;
1736 + case OS_INFO_VARIABLE_NTSuiteWebServer:
1737 + value.llValue = VER_SUITE_BLADE & ovix.wSuiteMask ? 1 : 0;
1738 + value.Type = BURN_VARIANT_TYPE_NUMERIC;
1739 + break;
1740 + case OS_INFO_VARIABLE_CompatibilityMode:
1741 + {
1742 + DWORDLONG dwlConditionMask = 0;
1743 + VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_EQUAL);
1744 + VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_EQUAL);
1745 + VER_SET_CONDITION(dwlConditionMask, VER_SERVICEPACKMAJOR, VER_EQUAL);
1746 + VER_SET_CONDITION(dwlConditionMask, VER_SERVICEPACKMINOR, VER_EQUAL);
1747 +
1748 + value.llValue = ::VerifyVersionInfoW(&ovix, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, dwlConditionMask);
1749 + value.Type = BURN_VARIANT_TYPE_NUMERIC;
1750 + }
1751 + break;
1752 + case OS_INFO_VARIABLE_TerminalServer:
1753 + value.llValue = (VER_SUITE_TERMINAL == (ovix.wSuiteMask & VER_SUITE_TERMINAL)) && (VER_SUITE_SINGLEUSERTS != (ovix.wSuiteMask & VER_SUITE_SINGLEUSERTS)) ? 1 : 0;
1754 + value.Type = BURN_VARIANT_TYPE_NUMERIC;
1755 + break;
1756 + default:
1757 + AssertSz(FALSE, "Unknown OS info type.");
1758 + break;
1759 + }
1760 +
1761 + hr = BVariantCopy(&value, pValue);
1762 + ExitOnFailure(hr, "Failed to set variant value.");
1763 +
1764 +LExit:
1765 + return hr;
1766 +}
1767 +
1768 +static HRESULT InitializeVariableSystemInfo(
1769 + __in DWORD_PTR dwpData,
1770 + __inout BURN_VARIANT* pValue
1771 + )
1772 +{
1773 + HRESULT hr = S_OK;
1774 + SYSTEM_INFO si = { };
1775 + BURN_VARIANT value = { };
1776 +
1777 + ::GetNativeSystemInfo(&si);
1778 +
1779 + switch ((OS_INFO_VARIABLE)dwpData)
1780 + {
1781 + case OS_INFO_VARIABLE_ProcessorArchitecture:
1782 + value.llValue = si.wProcessorArchitecture;
1783 + value.Type = BURN_VARIANT_TYPE_NUMERIC;
1784 + break;
1785 + default:
1786 + AssertSz(FALSE, "Unknown OS info type.");
1787 + break;
1788 + }
1789 +
1790 + hr = BVariantCopy(&value, pValue);
1791 + ExitOnFailure(hr, "Failed to set variant value.");
1792 +
1793 +LExit:
1794 + return hr;
1795 +}
1796 +
1797 +static HRESULT InitializeVariableComputerName(
1798 + __in DWORD_PTR dwpData,
1799 + __inout BURN_VARIANT* pValue
1800 + )
1801 +{
1802 + UNREFERENCED_PARAMETER(dwpData);
1803 +
1804 + HRESULT hr = S_OK;
1805 + WCHAR wzComputerName[MAX_COMPUTERNAME_LENGTH + 1] = { };
1806 + DWORD cchComputerName = countof(wzComputerName);
1807 +
1808 + // get computer name
1809 + if (!::GetComputerNameW(wzComputerName, &cchComputerName))
1810 + {
1811 + ExitWithLastError(hr, "Failed to get computer name.");
1812 + }
1813 +
1814 + // set value
1815 + hr = BVariantSetString(pValue, wzComputerName, 0);
1816 + ExitOnFailure(hr, "Failed to set variant value.");
1817 +
1818 +LExit:
1819 + return hr;
1820 +}
1821 +
1822 +static HRESULT InitializeVariableVersionMsi(
1823 + __in DWORD_PTR dwpData,
1824 + __inout BURN_VARIANT* pValue
1825 + )
1826 +{
1827 + UNREFERENCED_PARAMETER(dwpData);
1828 +
1829 + HRESULT hr = S_OK;
1830 + DLLGETVERSIONPROC pfnMsiDllGetVersion = NULL;
1831 + DLLVERSIONINFO msiVersionInfo = { };
1832 +
1833 + // get DllGetVersion proc address
1834 + pfnMsiDllGetVersion = (DLLGETVERSIONPROC)::GetProcAddress(::GetModuleHandleW(L"msi"), "DllGetVersion");
1835 + ExitOnNullWithLastError(pfnMsiDllGetVersion, hr, "Failed to find DllGetVersion entry point in msi.dll.");
1836 +
1837 + // get msi.dll version info
1838 + msiVersionInfo.cbSize = sizeof(DLLVERSIONINFO);
1839 + hr = pfnMsiDllGetVersion(&msiVersionInfo);
1840 + ExitOnFailure(hr, "Failed to get msi.dll version info.");
1841 +
1842 + hr = BVariantSetVersion(pValue, MAKEQWORDVERSION(msiVersionInfo.dwMajorVersion, msiVersionInfo.dwMinorVersion, 0, 0));
1843 + ExitOnFailure(hr, "Failed to set variant value.");
1844 +
1845 +LExit:
1846 + return hr;
1847 +}
1848 +
1849 +static HRESULT InitializeVariableCsidlFolder(
1850 + __in DWORD_PTR dwpData,
1851 + __inout BURN_VARIANT* pValue
1852 + )
1853 +{
1854 + HRESULT hr = S_OK;
1855 + LPWSTR sczPath = NULL;
1856 + int nFolder = (int)dwpData;
1857 +
1858 + // get folder path
1859 + hr = ShelGetFolder(&sczPath, nFolder);
1860 + ExitOnRootFailure(hr, "Failed to get shell folder.");
1861 +
1862 + // set value
1863 + hr = BVariantSetString(pValue, sczPath, 0);
1864 + ExitOnFailure(hr, "Failed to set variant value.");
1865 +
1866 +LExit:
1867 + ReleaseStr(sczPath);
1868 +
1869 + return hr;
1870 +}
1871 +
1872 +static HRESULT InitializeVariableTempFolder(
1873 + __in DWORD_PTR dwpData,
1874 + __inout BURN_VARIANT* pValue
1875 + )
1876 +{
1877 + UNREFERENCED_PARAMETER(dwpData);
1878 +
1879 + HRESULT hr = S_OK;
1880 + WCHAR wzPath[MAX_PATH] = { };
1881 +
1882 + // get volume path name
1883 + if (!::GetTempPathW(MAX_PATH, wzPath))
1884 + {
1885 + ExitWithLastError(hr, "Failed to get temp path.");
1886 + }
1887 +
1888 + // set value
1889 + hr = BVariantSetString(pValue, wzPath, 0);
1890 + ExitOnFailure(hr, "Failed to set variant value.");
1891 +
1892 +LExit:
1893 + return hr;
1894 +}
1895 +
1896 +static HRESULT InitializeVariableSystemFolder(
1897 + __in DWORD_PTR dwpData,
1898 + __inout BURN_VARIANT* pValue
1899 + )
1900 +{
1901 + HRESULT hr = S_OK;
1902 + BOOL f64 = (BOOL)dwpData;
1903 + WCHAR wzSystemFolder[MAX_PATH] = { };
1904 +
1905 +#if !defined(_WIN64)
1906 + BOOL fIsWow64 = FALSE;
1907 + ProcWow64(::GetCurrentProcess(), &fIsWow64);
1908 +
1909 + if (fIsWow64)
1910 + {
1911 + if (f64)
1912 + {
1913 + if (!::GetSystemDirectoryW(wzSystemFolder, countof(wzSystemFolder)))
1914 + {
1915 + ExitWithLastError(hr, "Failed to get 64-bit system folder.");
1916 + }
1917 + }
1918 + else
1919 + {
1920 + if (!::GetSystemWow64DirectoryW(wzSystemFolder, countof(wzSystemFolder)))
1921 + {
1922 + ExitWithLastError(hr, "Failed to get 32-bit system folder.");
1923 + }
1924 + }
1925 + }
1926 + else
1927 + {
1928 + if (!f64)
1929 + {
1930 + if (!::GetSystemDirectoryW(wzSystemFolder, countof(wzSystemFolder)))
1931 + {
1932 + ExitWithLastError(hr, "Failed to get 32-bit system folder.");
1933 + }
1934 + }
1935 + }
1936 +#else
1937 + if (f64)
1938 + {
1939 + if (!::GetSystemDirectoryW(wzSystemFolder, countof(wzSystemFolder)))
1940 + {
1941 + ExitWithLastError(hr, "Failed to get 64-bit system folder.");
1942 + }
1943 + }
1944 + else
1945 + {
1946 + if (!::GetSystemWow64DirectoryW(wzSystemFolder, countof(wzSystemFolder)))
1947 + {
1948 + ExitWithLastError(hr, "Failed to get 32-bit system folder.");
1949 + }
1950 + }
1951 +#endif
1952 +
1953 + if (*wzSystemFolder)
1954 + {
1955 + hr = PathFixedBackslashTerminate(wzSystemFolder, countof(wzSystemFolder));
1956 + ExitOnFailure(hr, "Failed to backslash terminate system folder.");
1957 + }
1958 +
1959 + // set value
1960 + hr = BVariantSetString(pValue, wzSystemFolder, 0);
1961 + ExitOnFailure(hr, "Failed to set system folder variant value.");
1962 +
1963 +LExit:
1964 + return hr;
1965 +}
1966 +
1967 +static HRESULT InitializeVariableWindowsVolumeFolder(
1968 + __in DWORD_PTR dwpData,
1969 + __inout BURN_VARIANT* pValue
1970 + )
1971 +{
1972 + UNREFERENCED_PARAMETER(dwpData);
1973 +
1974 + HRESULT hr = S_OK;
1975 + WCHAR wzWindowsPath[MAX_PATH] = { };
1976 + WCHAR wzVolumePath[MAX_PATH] = { };
1977 +
1978 + // get windows directory
1979 + if (!::GetWindowsDirectoryW(wzWindowsPath, countof(wzWindowsPath)))
1980 + {
1981 + ExitWithLastError(hr, "Failed to get windows directory.");
1982 + }
1983 +
1984 + // get volume path name
1985 + if (!::GetVolumePathNameW(wzWindowsPath, wzVolumePath, MAX_PATH))
1986 + {
1987 + ExitWithLastError(hr, "Failed to get volume path name.");
1988 + }
1989 +
1990 + // set value
1991 + hr = BVariantSetString(pValue, wzVolumePath, 0);
1992 + ExitOnFailure(hr, "Failed to set variant value.");
1993 +
1994 +LExit:
1995 + return hr;
1996 +}
1997 +
1998 +static HRESULT InitializeVariablePrivileged(
1999 + __in DWORD_PTR dwpData,
2000 + __inout BURN_VARIANT* pValue
2001 + )
2002 +{
2003 + UNREFERENCED_PARAMETER(dwpData);
2004 +
2005 + HRESULT hr = S_OK;
2006 + BOOL fPrivileged = FALSE;
2007 +
2008 + // check if process could run privileged.
2009 + hr = OsCouldRunPrivileged(&fPrivileged);
2010 + ExitOnFailure(hr, "Failed to check if process could run privileged.");
2011 +
2012 + // set value
2013 + hr = BVariantSetNumeric(pValue, fPrivileged);
2014 + ExitOnFailure(hr, "Failed to set variant value.");
2015 +
2016 +LExit:
2017 + return hr;
2018 +}
2019 +
2020 +static HRESULT InitializeVariableRebootPending(
2021 + __in DWORD_PTR dwpData,
2022 + __inout BURN_VARIANT* pValue
2023 + )
2024 +{
2025 + UNREFERENCED_PARAMETER(dwpData);
2026 +
2027 + HRESULT hr = S_OK;
2028 + BOOL fRebootPending = FALSE;
2029 + BOOL fComInitialized = FALSE;
2030 +
2031 + // Do a best effort to ask WU if a reboot is required. If anything goes
2032 + // wrong then let's pretend a reboot is not required.
2033 + hr = ::CoInitialize(NULL);
2034 + if (SUCCEEDED(hr) || RPC_E_CHANGED_MODE == hr)
2035 + {
2036 + fComInitialized = TRUE;
2037 +
2038 + hr = WuaRestartRequired(&fRebootPending);
2039 + if (FAILED(hr))
2040 + {
2041 + fRebootPending = FALSE;
2042 + hr = S_OK;
2043 + }
2044 + }
2045 +
2046 + hr = BVariantSetNumeric(pValue, fRebootPending);
2047 + ExitOnFailure(hr, "Failed to set reboot pending variant value.");
2048 +
2049 +LExit:
2050 + if (fComInitialized)
2051 + {
2052 + ::CoUninitialize();
2053 + }
2054 +
2055 + return hr;
2056 +}
2057 +
2058 +static HRESULT InitializeSystemLanguageID(
2059 + __in DWORD_PTR dwpData,
2060 + __inout BURN_VARIANT* pValue
2061 + )
2062 +{
2063 + UNREFERENCED_PARAMETER(dwpData);
2064 +
2065 + HRESULT hr = S_OK;
2066 + LANGID langid = ::GetSystemDefaultLangID();
2067 +
2068 + hr = BVariantSetNumeric(pValue, langid);
2069 + ExitOnFailure(hr, "Failed to set variant value.");
2070 +
2071 +LExit:
2072 + return hr;
2073 +}
2074 +
2075 +static HRESULT InitializeUserUILanguageID(
2076 + __in DWORD_PTR dwpData,
2077 + __inout BURN_VARIANT* pValue
2078 + )
2079 +{
2080 + UNREFERENCED_PARAMETER(dwpData);
2081 +
2082 + HRESULT hr = S_OK;
2083 + LANGID langid = ::GetUserDefaultUILanguage();
2084 +
2085 + hr = BVariantSetNumeric(pValue, langid);
2086 + ExitOnFailure(hr, "Failed to set variant value.");
2087 +
2088 +LExit:
2089 + return hr;
2090 +}
2091 +
2092 +static HRESULT InitializeUserLanguageID(
2093 + __in DWORD_PTR dwpData,
2094 + __inout BURN_VARIANT* pValue
2095 + )
2096 +{
2097 + UNREFERENCED_PARAMETER(dwpData);
2098 +
2099 + HRESULT hr = S_OK;
2100 + LANGID langid = ::GetUserDefaultLangID();
2101 +
2102 + hr = BVariantSetNumeric(pValue, langid);
2103 + ExitOnFailure(hr, "Failed to set variant value.");
2104 +
2105 +LExit:
2106 + return hr;
2107 +}
2108 +
2109 +static HRESULT InitializeVariableString(
2110 + __in DWORD_PTR dwpData,
2111 + __inout BURN_VARIANT* pValue
2112 + )
2113 +{
2114 + HRESULT hr = S_OK;
2115 + LPCWSTR wzValue = (LPCWSTR)dwpData;
2116 +
2117 + // set value
2118 + hr = BVariantSetString(pValue, wzValue, 0);
2119 + ExitOnFailure(hr, "Failed to set variant value.");
2120 +
2121 +LExit:
2122 + return hr;
2123 +}
2124 +
2125 +static HRESULT InitializeVariableNumeric(
2126 + __in DWORD_PTR dwpData,
2127 + __inout BURN_VARIANT* pValue
2128 + )
2129 +{
2130 + HRESULT hr = S_OK;
2131 + LONGLONG llValue = (LONGLONG)dwpData;
2132 +
2133 + // set value
2134 + hr = BVariantSetNumeric(pValue, llValue);
2135 + ExitOnFailure(hr, "Failed to set variant value.");
2136 +
2137 +LExit:
2138 + return hr;
2139 +}
2140 +
2141 +static HRESULT InitializeVariableRegistryFolder(
2142 + __in DWORD_PTR dwpData,
2143 + __inout BURN_VARIANT* pValue
2144 + )
2145 +{
2146 + HRESULT hr = S_OK;
2147 + int nFolder = (int)dwpData;
2148 + LPWSTR sczPath = NULL;
2149 +
2150 +#if !defined(_WIN64)
2151 + BOOL fIsWow64 = FALSE;
2152 +
2153 + ProcWow64(::GetCurrentProcess(), &fIsWow64);
2154 + if (!fIsWow64) // on 32-bit machines, variables aren't set
2155 + {
2156 + ExitFunction();
2157 + }
2158 +#endif
2159 +
2160 + hr = Get64bitFolderFromRegistry(nFolder, &sczPath);
2161 + ExitOnFailure(hr, "Failed to get 64-bit folder.");
2162 +
2163 + // set value
2164 + hr = BVariantSetString(pValue, sczPath, 0);
2165 + ExitOnFailure(hr, "Failed to set variant value.");
2166 +
2167 +LExit:
2168 + ReleaseStr(sczPath);
2169 +
2170 + return hr;
2171 +}
2172 +
2173 +static HRESULT InitializeVariable6432Folder(
2174 + __in DWORD_PTR dwpData,
2175 + __inout BURN_VARIANT* pValue
2176 + )
2177 +{
2178 + HRESULT hr = S_OK;
2179 + int nFolder = (int)dwpData;
2180 + LPWSTR sczPath = NULL;
2181 +
2182 +#if !defined(_WIN64)
2183 + BOOL fIsWow64 = FALSE;
2184 +
2185 + // If 32-bit use shell-folder.
2186 + ProcWow64(::GetCurrentProcess(), &fIsWow64);
2187 + if (!fIsWow64)
2188 + {
2189 + hr = ShelGetFolder(&sczPath, nFolder);
2190 + ExitOnRootFailure(hr, "Failed to get shell folder.");
2191 + }
2192 + else
2193 +#endif
2194 + {
2195 + hr = Get64bitFolderFromRegistry(nFolder, &sczPath);
2196 + ExitOnFailure(hr, "Failed to get 64-bit folder.");
2197 + }
2198 +
2199 + // set value
2200 + hr = BVariantSetString(pValue, sczPath, 0);
2201 + ExitOnFailure(hr, "Failed to set variant value.");
2202 +
2203 +LExit:
2204 + ReleaseStr(sczPath);
2205 +
2206 + return hr;
2207 +}
2208 +
2209 +// Get the date in the same format as Windows Installer.
2210 +static HRESULT InitializeVariableDate(
2211 + __in DWORD_PTR /*dwpData*/,
2212 + __inout BURN_VARIANT* pValue
2213 + )
2214 +{
2215 + HRESULT hr = S_OK;
2216 + SYSTEMTIME systime = { };
2217 + LPWSTR sczDate = NULL;
2218 + int cchDate = 0;
2219 +
2220 + ::GetSystemTime(&systime);
2221 +
2222 + cchDate = ::GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &systime, NULL, NULL, cchDate);
2223 + if (!cchDate)
2224 + {
2225 + ExitOnLastError(hr, "Failed to get the required buffer length for the Date.");
2226 + }
2227 +
2228 + hr = StrAlloc(&sczDate, cchDate);
2229 + ExitOnFailure(hr, "Failed to allocate the buffer for the Date.");
2230 +
2231 + if (!::GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &systime, NULL, sczDate, cchDate))
2232 + {
2233 + ExitOnLastError(hr, "Failed to get the Date.");
2234 + }
2235 +
2236 + // set value
2237 + hr = BVariantSetString(pValue, sczDate, cchDate);
2238 + ExitOnFailure(hr, "Failed to set variant value.");
2239 +
2240 +LExit:
2241 + ReleaseStr(sczDate);
2242 +
2243 + return hr;
2244 +}
2245 +
2246 +static HRESULT InitializeVariableInstallerName(
2247 + __in DWORD_PTR /*dwpData*/,
2248 + __inout BURN_VARIANT* pValue
2249 + )
2250 +{
2251 + HRESULT hr = S_OK;
2252 +
2253 + // set value
2254 + hr = BVariantSetString(pValue, L"WiX Burn", 0);
2255 + ExitOnFailure(hr, "Failed to set variant value.");
2256 +
2257 +LExit:
2258 + return hr;
2259 +}
2260 +
2261 +static HRESULT InitializeVariableInstallerVersion(
2262 + __in DWORD_PTR /*dwpData*/,
2263 + __inout BURN_VARIANT* pValue
2264 + )
2265 +{
2266 + HRESULT hr = S_OK;
2267 + LPWSTR sczVersion = NULL;
2268 +
2269 + hr = StrAllocStringAnsi(&sczVersion, szVerMajorMinorBuild, 0, CP_ACP);
2270 + ExitOnFailure(hr, "Failed to copy the engine version.");
2271 +
2272 + // set value
2273 + hr = BVariantSetString(pValue, sczVersion, 0);
2274 + ExitOnFailure(hr, "Failed to set variant value.");
2275 +
2276 +LExit:
2277 + ReleaseStr(sczVersion);
2278 +
2279 + return hr;
2280 +}
2281 +
2282 +static HRESULT InitializeVariableVersion(
2283 + __in DWORD_PTR dwpData,
2284 + __inout BURN_VARIANT* pValue
2285 + )
2286 +{
2287 + HRESULT hr = S_OK;
2288 +
2289 + // set value
2290 + hr = BVariantSetVersion(pValue, static_cast<DWORD64>(dwpData));
2291 + ExitOnFailure(hr, "Failed to set variant value.");
2292 +
2293 +LExit:
2294 + return hr;
2295 +}
2296 +
2297 +// Get the current user the same as Windows Installer.
2298 +static HRESULT InitializeVariableLogonUser(
2299 + __in DWORD_PTR /*dwpData*/,
2300 + __inout BURN_VARIANT* pValue
2301 + )
2302 +{
2303 + HRESULT hr = S_OK;
2304 + WCHAR wzUserName[UNLEN + 1];
2305 + DWORD cchUserName = countof(wzUserName);
2306 +
2307 + if (!::GetUserNameW(wzUserName, &cchUserName))
2308 + {
2309 + ExitOnLastError(hr, "Failed to get the user name.");
2310 + }
2311 +
2312 + // set value
2313 + hr = BVariantSetString(pValue, wzUserName, 0);
2314 + ExitOnFailure(hr, "Failed to set variant value.");
2315 +
2316 +LExit:
2317 + return hr;
2318 +}
2319 +
2320 +static HRESULT Get64bitFolderFromRegistry(
2321 + __in int nFolder,
2322 + __deref_out_z LPWSTR* psczPath
2323 + )
2324 +{
2325 + HRESULT hr = S_OK;
2326 + HKEY hkFolders = NULL;
2327 +
2328 + AssertSz(CSIDL_PROGRAM_FILES == nFolder || CSIDL_PROGRAM_FILES_COMMON == nFolder, "Unknown folder CSIDL.");
2329 + LPCWSTR wzFolderValue = CSIDL_PROGRAM_FILES_COMMON == nFolder ? L"CommonFilesDir" : L"ProgramFilesDir";
2330 +
2331 + hr = RegOpen(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion", KEY_READ | KEY_WOW64_64KEY, &hkFolders);
2332 + ExitOnFailure(hr, "Failed to open Windows folder key.");
2333 +
2334 + hr = RegReadString(hkFolders, wzFolderValue, psczPath);
2335 + ExitOnFailure(hr, "Failed to read folder path for '%ls'.", wzFolderValue);
2336 +
2337 + hr = PathBackslashTerminate(psczPath);
2338 + ExitOnFailure(hr, "Failed to ensure path was backslash terminated.");
2339 +
2340 +LExit:
2341 + ReleaseRegKey(hkFolders);
2342 +
2343 + return hr;
2344 +}
2345 +
src/engine/variable.h new
+190
@@ -0,0 +1,190 @@
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 +#if defined(__cplusplus)
6 +extern "C" {
7 +#endif
8 +
9 +
10 +// constants
11 +
12 +const LPCWSTR VARIABLE_DATE = L"Date";
13 +const LPCWSTR VARIABLE_LOGONUSER = L"LogonUser";
14 +const LPCWSTR VARIABLE_INSTALLERNAME = L"InstallerName";
15 +const LPCWSTR VARIABLE_INSTALLERVERSION = L"InstallerVersion";
16 +
17 +
18 +// typedefs
19 +
20 +typedef HRESULT (*PFN_INITIALIZEVARIABLE)(
21 + __in DWORD_PTR dwpData,
22 + __inout BURN_VARIANT* pValue
23 + );
24 +
25 +
26 +// constants
27 +
28 +enum BURN_VARIABLE_INTERNAL_TYPE
29 +{
30 + BURN_VARIABLE_INTERNAL_TYPE_NORMAL, // the BA can set this variable.
31 + BURN_VARIABLE_INTERNAL_TYPE_OVERRIDABLE_BUILTIN, // the BA can't set this variable, but the unelevated process can serialize it to the elevated process.
32 + BURN_VARIABLE_INTERNAL_TYPE_BUILTIN, // the BA can't set this variable, and the unelevated process can't serialize it to the elevated process.
33 +};
34 +
35 +
36 +// structs
37 +
38 +typedef struct _BURN_VARIABLE
39 +{
40 + LPWSTR sczName;
41 + BURN_VARIANT Value;
42 + BOOL fHidden;
43 + BOOL fLiteral; // if fLiteral, then when formatting this variable its value should be used as is (don't continue recursively formatting).
44 + BOOL fPersisted;
45 +
46 + // used for late initialization of built-in variables
47 + BURN_VARIABLE_INTERNAL_TYPE internalType;
48 + PFN_INITIALIZEVARIABLE pfnInitialize;
49 + DWORD_PTR dwpInitializeData;
50 +} BURN_VARIABLE;
51 +
52 +typedef struct _BURN_VARIABLES
53 +{
54 + CRITICAL_SECTION csAccess;
55 + DWORD dwMaxVariables;
56 + DWORD cVariables;
57 + BURN_VARIABLE* rgVariables;
58 +} BURN_VARIABLES;
59 +
60 +
61 +// function declarations
62 +
63 +HRESULT VariableInitialize(
64 + __in BURN_VARIABLES* pVariables
65 + );
66 +HRESULT VariablesParseFromXml(
67 + __in BURN_VARIABLES* pVariables,
68 + __in IXMLDOMNode* pixnBundle
69 + );
70 +void VariablesUninitialize(
71 + __in BURN_VARIABLES* pVariables
72 + );
73 +void VariablesDump(
74 + __in BURN_VARIABLES* pVariables
75 + );
76 +HRESULT VariableGetNumeric(
77 + __in BURN_VARIABLES* pVariables,
78 + __in_z LPCWSTR wzVariable,
79 + __out LONGLONG* pllValue
80 + );
81 +HRESULT VariableGetString(
82 + __in BURN_VARIABLES* pVariables,
83 + __in_z LPCWSTR wzVariable,
84 + __out_z LPWSTR* psczValue
85 + );
86 +HRESULT VariableGetVersion(
87 + __in BURN_VARIABLES* pVariables,
88 + __in_z LPCWSTR wzVariable,
89 + __in DWORD64* pqwValue
90 + );
91 +HRESULT VariableGetVariant(
92 + __in BURN_VARIABLES* pVariables,
93 + __in_z LPCWSTR wzVariable,
94 + __in BURN_VARIANT* pValue
95 + );
96 +HRESULT VariableGetFormatted(
97 + __in BURN_VARIABLES* pVariables,
98 + __in_z LPCWSTR wzVariable,
99 + __out_z LPWSTR* psczValue
100 + );
101 +HRESULT VariableSetNumeric(
102 + __in BURN_VARIABLES* pVariables,
103 + __in_z LPCWSTR wzVariable,
104 + __in LONGLONG llValue,
105 + __in BOOL fOverwriteBuiltIn
106 + );
107 +HRESULT VariableSetLiteralString(
108 + __in BURN_VARIABLES* pVariables,
109 + __in_z LPCWSTR wzVariable,
110 + __in_z_opt LPCWSTR wzValue,
111 + __in BOOL fOverwriteBuiltIn
112 + );
113 +HRESULT VariableSetString(
114 + __in BURN_VARIABLES* pVariables,
115 + __in_z LPCWSTR wzVariable,
116 + __in_z_opt LPCWSTR wzValue,
117 + __in BOOL fOverwriteBuiltIn
118 + );
119 +HRESULT VariableSetVersion(
120 + __in BURN_VARIABLES* pVariables,
121 + __in_z LPCWSTR wzVariable,
122 + __in DWORD64 qwValue,
123 + __in BOOL fOverwriteBuiltIn
124 + );
125 +HRESULT VariableSetLiteralVariant(
126 + __in BURN_VARIABLES* pVariables,
127 + __in_z LPCWSTR wzVariable,
128 + __in BURN_VARIANT* pVariant
129 + );
130 +HRESULT VariableFormatString(
131 + __in BURN_VARIABLES* pVariables,
132 + __in_z LPCWSTR wzIn,
133 + __out_z_opt LPWSTR* psczOut,
134 + __out_opt DWORD* pcchOut
135 + );
136 +HRESULT VariableFormatStringObfuscated(
137 + __in BURN_VARIABLES* pVariables,
138 + __in_z LPCWSTR wzIn,
139 + __out_z_opt LPWSTR* psczOut,
140 + __out_opt DWORD* pcchOut
141 + );
142 +HRESULT VariableEscapeString(
143 + __in_z LPCWSTR wzIn,
144 + __out_z LPWSTR* psczOut
145 + );
146 +HRESULT VariableSerialize(
147 + __in BURN_VARIABLES* pVariables,
148 + __in BOOL fPersisting,
149 + __inout BYTE** ppbBuffer,
150 + __inout SIZE_T* piBuffer
151 + );
152 +HRESULT VariableDeserialize(
153 + __in BURN_VARIABLES* pVariables,
154 + __in BOOL fWasPersisted,
155 + __in_bcount(cbBuffer) BYTE* pbBuffer,
156 + __in SIZE_T cbBuffer,
157 + __inout SIZE_T* piBuffer
158 + );
159 +HRESULT VariableStrAlloc(
160 + __in BOOL fZeroOnRealloc,
161 + __deref_out_ecount_part(cch, 0) LPWSTR* ppwz,
162 + __in DWORD_PTR cch
163 + );
164 +HRESULT VariableStrAllocString(
165 + __in BOOL fZeroOnRealloc,
166 + __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz,
167 + __in_z LPCWSTR wzSource,
168 + __in DWORD_PTR cchSource
169 + );
170 +HRESULT VariableStrAllocConcat(
171 + __in BOOL fZeroOnRealloc,
172 + __deref_out_z LPWSTR* ppwz,
173 + __in_z LPCWSTR wzSource,
174 + __in DWORD_PTR cchSource
175 + );
176 +HRESULT __cdecl VariableStrAllocFormatted(
177 + __in BOOL fZeroOnRealloc,
178 + __deref_out_z LPWSTR* ppwz,
179 + __in __format_string LPCWSTR wzFormat,
180 + ...
181 + );
182 +HRESULT VariableIsHidden(
183 + __in BURN_VARIABLES* pVariables,
184 + __in_z LPCWSTR wzVariable,
185 + __out BOOL* pfHidden
186 + );
187 +
188 +#if defined(__cplusplus)
189 +}
190 +#endif
src/engine/variant.cpp new
+601
@@ -0,0 +1,601 @@
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 +#define VARIANT_ENCRYPTION_SCOPE CRYPTPROTECTMEMORY_SAME_PROCESS
6 +
7 +// internal function declarations
8 +
9 +static HRESULT BVariantEncryptNumeric(
10 + __in BURN_VARIANT* pVariant,
11 + __in BOOL fEncrypt
12 + );
13 +
14 +static HRESULT BVariantEncryptString(
15 + __in BURN_VARIANT* pVariant,
16 + __in BOOL fEncrypt
17 + );
18 +
19 +static HRESULT BVariantEncryptVersion(
20 + __in BURN_VARIANT* pVariant,
21 + __in BOOL fEncrypt
22 + );
23 +
24 +static HRESULT BVariantRetrieveDecryptedNumeric(
25 + __in BURN_VARIANT* pVariant,
26 + __out LONGLONG* pllValue
27 + );
28 +
29 +static HRESULT BVariantRetrieveDecryptedString(
30 + __in BURN_VARIANT* pVariant,
31 + __out LPWSTR* psczValue
32 + );
33 +
34 +static HRESULT BVariantRetrieveDecryptedVersion(
35 + __in BURN_VARIANT* pVariant,
36 + __out DWORD64* pqwValue
37 + );
38 +
39 +// function definitions
40 +
41 +extern "C" void BVariantUninitialize(
42 + __in BURN_VARIANT* pVariant
43 + )
44 +{
45 + if (BURN_VARIANT_TYPE_STRING == pVariant->Type)
46 + {
47 + StrSecureZeroFreeString(pVariant->sczValue);
48 + }
49 + SecureZeroMemory(pVariant, sizeof(BURN_VARIANT));
50 +}
51 +
52 +// The contents of pllValue may be sensitive, should keep encrypted and SecureZeroMemory.
53 +extern "C" HRESULT BVariantGetNumeric(
54 + __in BURN_VARIANT* pVariant,
55 + __out LONGLONG* pllValue
56 + )
57 +{
58 + HRESULT hr = S_OK;
59 + LPWSTR sczValue = NULL;
60 +
61 + switch (pVariant->Type)
62 + {
63 + case BURN_VARIANT_TYPE_NUMERIC:
64 + BVariantRetrieveDecryptedNumeric(pVariant, pllValue);
65 + break;
66 + case BURN_VARIANT_TYPE_STRING:
67 + hr = BVariantRetrieveDecryptedString(pVariant, &sczValue);
68 + if (SUCCEEDED(hr))
69 + {
70 + hr = StrStringToInt64(sczValue, 0, pllValue);
71 + if (FAILED(hr))
72 + {
73 + hr = DISP_E_TYPEMISMATCH;
74 + }
75 + }
76 + StrSecureZeroFreeString(sczValue);
77 + break;
78 + case BURN_VARIANT_TYPE_VERSION:
79 + BVariantRetrieveDecryptedVersion(pVariant, (DWORD64*)pllValue);
80 + break;
81 + default:
82 + hr = E_INVALIDARG;
83 + break;
84 + }
85 +
86 + return hr;
87 +}
88 +
89 +// The contents of psczValue may be sensitive, should keep encrypted and SecureZeroFree.
90 +extern "C" HRESULT BVariantGetString(
91 + __in BURN_VARIANT* pVariant,
92 + __out_z LPWSTR* psczValue
93 + )
94 +{
95 + HRESULT hr = S_OK;
96 + LONGLONG llValue = 0;
97 + DWORD64 qwValue = 0;
98 +
99 + switch (pVariant->Type)
100 + {
101 + case BURN_VARIANT_TYPE_NUMERIC:
102 + hr = BVariantRetrieveDecryptedNumeric(pVariant, &llValue);
103 + if (SUCCEEDED(hr))
104 + {
105 + hr = StrAllocFormattedSecure(psczValue, L"%I64d", llValue);
106 + ExitOnFailure(hr, "Failed to convert int64 to string.");
107 + }
108 + SecureZeroMemory(&llValue, sizeof(llValue));
109 + break;
110 + case BURN_VARIANT_TYPE_STRING:
111 + hr = BVariantRetrieveDecryptedString(pVariant, psczValue);
112 + break;
113 + case BURN_VARIANT_TYPE_VERSION:
114 + hr = BVariantRetrieveDecryptedVersion(pVariant, &qwValue);
115 + if (SUCCEEDED(hr))
116 + {
117 + hr = StrAllocFormattedSecure(psczValue, L"%hu.%hu.%hu.%hu",
118 + (WORD)(qwValue >> 48),
119 + (WORD)(qwValue >> 32),
120 + (WORD)(qwValue >> 16),
121 + (WORD)qwValue);
122 + ExitOnFailure(hr, "Failed to convert version to string.");
123 + }
124 + SecureZeroMemory(&qwValue, sizeof(qwValue));
125 + break;
126 + default:
127 + hr = E_INVALIDARG;
128 + break;
129 + }
130 +
131 +LExit:
132 + return hr;
133 +}
134 +
135 +// The contents of pqwValue may be sensitive, should keep encrypted and SecureZeroMemory.
136 +extern "C" HRESULT BVariantGetVersion(
137 + __in BURN_VARIANT* pVariant,
138 + __out DWORD64* pqwValue
139 + )
140 +{
141 + HRESULT hr = S_OK;
142 + LPWSTR sczValue = NULL;
143 +
144 + switch (pVariant->Type)
145 + {
146 + case BURN_VARIANT_TYPE_NUMERIC:
147 + BVariantRetrieveDecryptedNumeric(pVariant, (LONGLONG*)pqwValue);
148 + break;
149 + case BURN_VARIANT_TYPE_STRING:
150 + hr = BVariantRetrieveDecryptedString(pVariant, &sczValue);
151 + if (SUCCEEDED(hr))
152 + {
153 + hr = FileVersionFromStringEx(sczValue, 0, pqwValue);
154 + if (FAILED(hr))
155 + {
156 + hr = DISP_E_TYPEMISMATCH;
157 + }
158 + }
159 + StrSecureZeroFreeString(sczValue);
160 + break;
161 + case BURN_VARIANT_TYPE_VERSION:
162 + BVariantRetrieveDecryptedVersion(pVariant, pqwValue);
163 + break;
164 + default:
165 + hr = E_INVALIDARG;
166 + break;
167 + }
168 +
169 + return hr;
170 +}
171 +
172 +extern "C" HRESULT BVariantSetNumeric(
173 + __in BURN_VARIANT* pVariant,
174 + __in LONGLONG llValue
175 + )
176 +{
177 + HRESULT hr = S_OK;
178 + BOOL fEncryptValue = pVariant->fEncryptValue;
179 +
180 + if (BURN_VARIANT_TYPE_STRING == pVariant->Type)
181 + {
182 + StrSecureZeroFreeString(pVariant->sczValue);
183 + }
184 + memset(pVariant, 0, sizeof(BURN_VARIANT));
185 + pVariant->llValue = llValue;
186 + pVariant->Type = BURN_VARIANT_TYPE_NUMERIC;
187 + BVariantSetEncryption(pVariant, fEncryptValue);
188 +
189 + return hr;
190 +}
191 +
192 +extern "C" HRESULT BVariantSetString(
193 + __in BURN_VARIANT* pVariant,
194 + __in_z_opt LPCWSTR wzValue,
195 + __in DWORD_PTR cchValue
196 + )
197 +{
198 + HRESULT hr = S_OK;
199 + BOOL fEncryptValue = pVariant->fEncryptValue;
200 +
201 + if (!wzValue) // if we're nulling out the string, make the variable NONE.
202 + {
203 + BVariantUninitialize(pVariant);
204 + }
205 + else // assign the value.
206 + {
207 + if (BURN_VARIANT_TYPE_STRING != pVariant->Type)
208 + {
209 + memset(pVariant, 0, sizeof(BURN_VARIANT));
210 + }
211 + else
212 + {
213 + // We're about to copy an unencrypted value.
214 + pVariant->fEncryptValue = FALSE;
215 + }
216 +
217 + hr = StrAllocStringSecure(&pVariant->sczValue, wzValue, cchValue);
218 + ExitOnFailure(hr, "Failed to copy string.");
219 +
220 + pVariant->Type = BURN_VARIANT_TYPE_STRING;
221 + }
222 +
223 +LExit:
224 + BVariantSetEncryption(pVariant, fEncryptValue);
225 + return hr;
226 +}
227 +
228 +extern "C" HRESULT BVariantSetVersion(
229 + __in BURN_VARIANT* pVariant,
230 + __in DWORD64 qwValue
231 + )
232 +{
233 + HRESULT hr = S_OK;
234 + BOOL fEncryptValue = pVariant->fEncryptValue;
235 +
236 + if (BURN_VARIANT_TYPE_STRING == pVariant->Type)
237 + {
238 + StrSecureZeroFreeString(pVariant->sczValue);
239 + }
240 + memset(pVariant, 0, sizeof(BURN_VARIANT));
241 + pVariant->qwValue = qwValue;
242 + pVariant->Type = BURN_VARIANT_TYPE_VERSION;
243 + BVariantSetEncryption(pVariant, fEncryptValue);
244 +
245 + return hr;
246 +}
247 +
248 +extern "C" HRESULT BVariantSetValue(
249 + __in BURN_VARIANT* pVariant,
250 + __in BURN_VARIANT* pValue
251 + )
252 +{
253 + HRESULT hr = S_OK;
254 + LONGLONG llValue = 0;
255 + LPWSTR sczValue = NULL;
256 + DWORD64 qwValue = 0;
257 + BOOL fEncrypt = pVariant->fEncryptValue;
258 +
259 + switch (pValue->Type)
260 + {
261 + case BURN_VARIANT_TYPE_NONE:
262 + BVariantUninitialize(pVariant);
263 + break;
264 + case BURN_VARIANT_TYPE_NUMERIC:
265 + hr = BVariantGetNumeric(pValue, &llValue);
266 + if (SUCCEEDED(hr))
267 + {
268 + hr = BVariantSetNumeric(pVariant, llValue);
269 + }
270 + SecureZeroMemory(&llValue, sizeof(llValue));
271 + break;
272 + case BURN_VARIANT_TYPE_STRING:
273 + hr = BVariantGetString(pValue, &sczValue);
274 + if (SUCCEEDED(hr))
275 + {
276 + hr = BVariantSetString(pVariant, sczValue, 0);
277 + }
278 + StrSecureZeroFreeString(sczValue);
279 + break;
280 + case BURN_VARIANT_TYPE_VERSION:
281 + hr = BVariantGetVersion(pValue, &qwValue);
282 + if (SUCCEEDED(hr))
283 + {
284 + hr = BVariantSetVersion(pVariant, qwValue);
285 + }
286 + SecureZeroMemory(&qwValue, sizeof(qwValue));
287 + break;
288 + default:
289 + hr = E_INVALIDARG;
290 + }
291 + ExitOnFailure(hr, "Failed to copy variant.");
292 +
293 + hr = BVariantSetEncryption(pVariant, fEncrypt);
294 +
295 +LExit:
296 + return hr;
297 +}
298 +
299 +extern "C" HRESULT BVariantCopy(
300 + __in BURN_VARIANT* pSource,
301 + __out BURN_VARIANT* pTarget
302 + )
303 +{
304 + HRESULT hr = S_OK;
305 + LONGLONG llValue = 0;
306 + LPWSTR sczValue = NULL;
307 + DWORD64 qwValue = 0;
308 +
309 + BVariantUninitialize(pTarget);
310 +
311 + switch (pSource->Type)
312 + {
313 + case BURN_VARIANT_TYPE_NONE:
314 + break;
315 + case BURN_VARIANT_TYPE_NUMERIC:
316 + hr = BVariantGetNumeric(pSource, &llValue);
317 + if (SUCCEEDED(hr))
318 + {
319 + hr = BVariantSetNumeric(pTarget, llValue);
320 + }
321 + SecureZeroMemory(&llValue, sizeof(llValue));
322 + break;
323 + case BURN_VARIANT_TYPE_STRING:
324 + hr = BVariantGetString(pSource, &sczValue);
325 + if (SUCCEEDED(hr))
326 + {
327 + hr = BVariantSetString(pTarget, sczValue, 0);
328 + }
329 + StrSecureZeroFreeString(sczValue);
330 + break;
331 + case BURN_VARIANT_TYPE_VERSION:
332 + hr = BVariantGetVersion(pSource, &qwValue);
333 + if (SUCCEEDED(hr))
334 + {
335 + hr = BVariantSetVersion(pTarget, qwValue);
336 + }
337 + SecureZeroMemory(&qwValue, sizeof(qwValue));
338 + break;
339 + default:
340 + hr = E_INVALIDARG;
341 + }
342 + ExitOnFailure(hr, "Failed to copy variant.");
343 +
344 + hr = BVariantSetEncryption(pTarget, pSource->fEncryptValue);
345 +
346 +LExit:
347 + return hr;
348 +}
349 +
350 +extern "C" HRESULT BVariantChangeType(
351 + __in BURN_VARIANT* pVariant,
352 + __in BURN_VARIANT_TYPE type
353 + )
354 +{
355 + HRESULT hr = S_OK;
356 + BURN_VARIANT variant = { };
357 + BOOL fEncryptValue = pVariant->fEncryptValue;
358 +
359 + if (pVariant->Type == type)
360 + {
361 + ExitFunction(); // variant already is of the requested type
362 + }
363 +
364 + switch (type)
365 + {
366 + case BURN_VARIANT_TYPE_NONE:
367 + hr = S_OK;
368 + break;
369 + case BURN_VARIANT_TYPE_NUMERIC:
370 + hr = BVariantGetNumeric(pVariant, &variant.llValue);
371 + break;
372 + case BURN_VARIANT_TYPE_STRING:
373 + hr = BVariantGetString(pVariant, &variant.sczValue);
374 + break;
375 + case BURN_VARIANT_TYPE_VERSION:
376 + hr = BVariantGetVersion(pVariant, &variant.qwValue);
377 + break;
378 + default:
379 + ExitFunction1(hr = E_INVALIDARG);
380 + }
381 + ExitOnFailure(hr, "Failed to copy variant value.");
382 + variant.Type = type;
383 +
384 + BVariantUninitialize(pVariant);
385 + memcpy_s(pVariant, sizeof(BURN_VARIANT), &variant, sizeof(BURN_VARIANT));
386 + SecureZeroMemory(&variant, sizeof(BURN_VARIANT));
387 + BVariantSetEncryption(pVariant, fEncryptValue);
388 +
389 +LExit:
390 + return hr;
391 +}
392 +
393 +extern "C" HRESULT BVariantSetEncryption(
394 + __in BURN_VARIANT* pVariant,
395 + __in BOOL fEncrypt
396 + )
397 +{
398 + HRESULT hr = S_OK;
399 +
400 + if (pVariant->fEncryptValue == fEncrypt)
401 + {
402 + // The requested encryption state is already applied.
403 + ExitFunction();
404 + }
405 +
406 + switch (pVariant->Type)
407 + {
408 + case BURN_VARIANT_TYPE_NONE:
409 + hr = S_OK;
410 + break;
411 + case BURN_VARIANT_TYPE_NUMERIC:
412 + hr = BVariantEncryptNumeric(pVariant, fEncrypt);
413 + break;
414 + case BURN_VARIANT_TYPE_STRING:
415 + hr = BVariantEncryptString(pVariant, fEncrypt);
416 + break;
417 + case BURN_VARIANT_TYPE_VERSION:
418 + hr = BVariantEncryptVersion(pVariant, fEncrypt);
419 + break;
420 + default:
421 + hr = E_INVALIDARG;
422 + }
423 + ExitOnFailure(hr, "Failed to set the variant's encryption state");
424 + pVariant->fEncryptValue = fEncrypt;
425 +
426 +LExit:
427 + return hr;
428 +}
429 +
430 +static HRESULT BVariantEncryptNumeric(
431 + __in BURN_VARIANT* pVariant,
432 + __in BOOL fEncrypt
433 + )
434 +{
435 + HRESULT hr = S_OK;
436 +
437 + if (fEncrypt)
438 + {
439 + hr = CrypEncryptMemory(&pVariant->llValue, sizeof(pVariant->encryptionPadding), VARIANT_ENCRYPTION_SCOPE);
440 + }
441 + else
442 + {
443 + hr = CrypDecryptMemory(&pVariant->llValue, sizeof(pVariant->encryptionPadding), VARIANT_ENCRYPTION_SCOPE);
444 + }
445 +
446 +//LExit:
447 + return hr;
448 +}
449 +
450 +static HRESULT BVariantEncryptString(
451 + __in BURN_VARIANT* pVariant,
452 + __in BOOL fEncrypt
453 + )
454 +{
455 + HRESULT hr = S_OK;
456 + SIZE_T cbData = 0;
457 +
458 + if (NULL == pVariant->sczValue)
459 + {
460 + ExitFunction();
461 + }
462 +
463 + cbData = MemSize(pVariant->sczValue);
464 + if (-1 == cbData)
465 + {
466 + hr = E_INVALIDARG;
467 + ExitOnFailure(hr, "Failed to get the size of the string");
468 + }
469 +
470 + DWORD remainder = fEncrypt ? cbData % CRYP_ENCRYPT_MEMORY_SIZE : 0;
471 + DWORD extraNeeded = 0 < remainder ? CRYP_ENCRYPT_MEMORY_SIZE - remainder : 0;
472 + if ((MAXDWORD - extraNeeded) < cbData)
473 + {
474 + hr = E_INVALIDDATA;
475 + ExitOnFailure(hr, "The string is too big: size %u", cbData);
476 + }
477 + else if (0 < extraNeeded)
478 + {
479 + cbData += extraNeeded;
480 + LPVOID pvNew = NULL;
481 + hr = MemReAllocSecure(static_cast<LPVOID>(pVariant->sczValue), cbData, TRUE, &pvNew);
482 + ExitOnFailure(hr, "Failed to resize the string so it could be encrypted");
483 + pVariant->sczValue = static_cast<LPWSTR>(pvNew);
484 + }
485 +
486 + if (fEncrypt)
487 + {
488 + hr = CrypEncryptMemory(pVariant->sczValue, static_cast<DWORD>(cbData), VARIANT_ENCRYPTION_SCOPE);
489 + }
490 + else
491 + {
492 + hr = CrypDecryptMemory(pVariant->sczValue, static_cast<DWORD>(cbData), VARIANT_ENCRYPTION_SCOPE);
493 + }
494 +
495 +LExit:
496 + return hr;
497 +}
498 +
499 +static HRESULT BVariantEncryptVersion(
500 + __in BURN_VARIANT* pVariant,
501 + __in BOOL fEncrypt
502 + )
503 +{
504 + HRESULT hr = S_OK;
505 +
506 + if (fEncrypt)
507 + {
508 + hr = CrypEncryptMemory(&pVariant->qwValue, sizeof(pVariant->encryptionPadding), VARIANT_ENCRYPTION_SCOPE);
509 + }
510 + else
511 + {
512 + hr = CrypDecryptMemory(&pVariant->qwValue, sizeof(pVariant->encryptionPadding), VARIANT_ENCRYPTION_SCOPE);
513 + }
514 +
515 +//LExit:
516 + return hr;
517 +}
518 +
519 +// The contents of pllValue may be sensitive, should keep encrypted and SecureZeroMemory.
520 +static HRESULT BVariantRetrieveDecryptedNumeric(
521 + __in BURN_VARIANT* pVariant,
522 + __out LONGLONG* pllValue
523 + )
524 +{
525 + HRESULT hr = S_OK;
526 +
527 + Assert(NULL != pllValue);
528 + if (pVariant->fEncryptValue)
529 + {
530 + hr = BVariantEncryptNumeric(pVariant, FALSE);
531 + ExitOnFailure(hr, "Failed to decrypt numeric");
532 + }
533 +
534 + *pllValue = pVariant->llValue;
535 +
536 + if (pVariant->fEncryptValue)
537 + {
538 + hr = BVariantEncryptNumeric(pVariant, TRUE);
539 + }
540 +
541 +LExit:
542 + return hr;
543 +}
544 +
545 +// The contents of psczValue may be sensitive, should keep encrypted and SecureZeroFree.
546 +static HRESULT BVariantRetrieveDecryptedString(
547 + __in BURN_VARIANT* pVariant,
548 + __out LPWSTR* psczValue
549 + )
550 +{
551 + HRESULT hr = S_OK;
552 +
553 + if (NULL == pVariant->sczValue)
554 + {
555 + *psczValue = NULL;
556 + ExitFunction();
557 + }
558 +
559 + if (pVariant->fEncryptValue)
560 + {
561 + hr = BVariantEncryptString(pVariant, FALSE);
562 + ExitOnFailure(hr, "Failed to decrypt string");
563 + }
564 +
565 + hr = StrAllocStringSecure(psczValue, pVariant->sczValue, 0);
566 + ExitOnFailure(hr, "Failed to copy value.");
567 +
568 + if (pVariant->fEncryptValue)
569 + {
570 + hr = BVariantEncryptString(pVariant, TRUE);
571 + }
572 +
573 +LExit:
574 + return hr;
575 +}
576 +
577 +// The contents of pqwValue may be sensitive, should keep encrypted and SecureZeroMemory.
578 +static HRESULT BVariantRetrieveDecryptedVersion(
579 + __in BURN_VARIANT* pVariant,
580 + __out DWORD64* pqwValue
581 + )
582 +{
583 + HRESULT hr = S_OK;
584 +
585 + Assert(NULL != pqwValue);
586 + if (pVariant->fEncryptValue)
587 + {
588 + hr = BVariantEncryptVersion(pVariant, FALSE);
589 + ExitOnFailure(hr, "Failed to decrypt version");
590 + }
591 +
592 + *pqwValue = pVariant->qwValue;
593 +
594 + if (pVariant->fEncryptValue)
595 + {
596 + hr = BVariantEncryptVersion(pVariant, TRUE);
597 + }
598 +
599 +LExit:
600 + return hr;
601 +}
src/engine/variant.h new
+102
@@ -0,0 +1,102 @@
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 +#if defined(__cplusplus)
6 +extern "C" {
7 +#endif
8 +
9 +
10 +// constants
11 +
12 +enum BURN_VARIANT_TYPE
13 +{
14 + BURN_VARIANT_TYPE_NONE,
15 + BURN_VARIANT_TYPE_NUMERIC,
16 + BURN_VARIANT_TYPE_STRING,
17 + BURN_VARIANT_TYPE_VERSION,
18 +};
19 +
20 +
21 +// struct
22 +
23 +typedef struct _BURN_VARIANT
24 +{
25 + union
26 + {
27 + LONGLONG llValue;
28 + DWORD64 qwValue;
29 + LPWSTR sczValue;
30 + BYTE encryptionPadding[CRYP_ENCRYPT_MEMORY_SIZE];
31 + };
32 + BURN_VARIANT_TYPE Type;
33 + BOOL fEncryptValue;
34 +} BURN_VARIANT;
35 +
36 +
37 +// function declarations
38 +
39 +void BVariantUninitialize(
40 + __in BURN_VARIANT* pVariant
41 + );
42 +HRESULT BVariantGetNumeric(
43 + __in BURN_VARIANT* pVariant,
44 + __out LONGLONG* pllValue
45 + );
46 +HRESULT BVariantGetString(
47 + __in BURN_VARIANT* pVariant,
48 + __out_z LPWSTR* psczValue
49 + );
50 +HRESULT BVariantGetVersion(
51 + __in BURN_VARIANT* pVariant,
52 + __out DWORD64* pqwValue
53 + );
54 +HRESULT BVariantSetNumeric(
55 + __in BURN_VARIANT* pVariant,
56 + __in LONGLONG llValue
57 + );
58 +HRESULT BVariantSetString(
59 + __in BURN_VARIANT* pVariant,
60 + __in_z_opt LPCWSTR wzValue,
61 + __in DWORD_PTR cchValue
62 + );
63 +HRESULT BVariantSetVersion(
64 + __in BURN_VARIANT* pVariant,
65 + __in DWORD64 qwValue
66 + );
67 +/********************************************************************
68 +BVariantSetValue - Convenience function that calls BVariantUninitialize,
69 + BVariantSetNumeric, BVariantSetString, or
70 + BVariantSetVersion based on the type of pValue.
71 + The encryption state of pVariant is preserved.
72 +********************************************************************/
73 +HRESULT BVariantSetValue(
74 + __in BURN_VARIANT* pVariant,
75 + __in BURN_VARIANT* pValue
76 + );
77 +/********************************************************************
78 +BVariantCopy - creates a copy of pSource.
79 + The encryption state of pTarget is set to
80 + the encryption state of pSource.
81 +********************************************************************/
82 +HRESULT BVariantCopy(
83 + __in BURN_VARIANT* pSource,
84 + __out BURN_VARIANT* pTarget
85 + );
86 +HRESULT BVariantChangeType(
87 + __in BURN_VARIANT* pVariant,
88 + __in BURN_VARIANT_TYPE type
89 + );
90 +/********************************************************************
91 +BVariantSetEncryption - sets the encryption state of pVariant.
92 + If the encryption state matches the requested
93 + state, this function does nothing.
94 +********************************************************************/
95 +HRESULT BVariantSetEncryption(
96 + __in BURN_VARIANT* pVariant,
97 + __in BOOL fEncrypt
98 + );
99 +
100 +#if defined(__cplusplus)
101 +}
102 +#endif
src/stub/StubSection.cpp new
+23
@@ -0,0 +1,23 @@
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 +#pragma section(".wixburn",read)
6 +
7 +// If these defaults ever change, be sure to update constants in burn\engine\section.cpp as well.
8 +#pragma data_seg(push, ".wixburn")
9 +static DWORD dwMagic = 0x00f14300;
10 +static DWORD dwVersion = 0x00000002;
11 +
12 +static GUID guidBundleId = { };
13 +
14 +static DWORD dwStubSize = 0;
15 +static DWORD dwOriginalChecksum = 0;
16 +static DWORD dwOriginalSignatureOffset = 0;
17 +static DWORD dwOriginalSignatureSize = 0;
18 +
19 +static DWORD dwContainerFormat = 1;
20 +static DWORD dwContainerCount = 0;
21 +static DWORD qwBootstrapperApplicationContainerSize = 0;
22 +static DWORD qwAttachedContainerSize = 0;
23 +#pragma data_seg(pop)
src/stub/precomp.h new
+13
@@ -0,0 +1,13 @@
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 +#include <windows.h>
6 +
7 +#include <dutil.h>
8 +#include <apputil.h>
9 +#include <strutil.h>
10 +#include <fileutil.h>
11 +#include <pathutil.h>
12 +
13 +#include "engine.h"
src/stub/stub.cpp new
+64
@@ -0,0 +1,64 @@
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 +int WINAPI wWinMain(
7 + __in HINSTANCE hInstance,
8 + __in_opt HINSTANCE /* hPrevInstance */,
9 + __in_z_opt LPWSTR lpCmdLine,
10 + __in int nCmdShow
11 + )
12 +{
13 + HRESULT hr = S_OK;
14 + DWORD dwExitCode = 0;
15 + LPWSTR sczPath = NULL;
16 + HANDLE hEngineFile = INVALID_HANDLE_VALUE;
17 +
18 + LPCWSTR rgsczSafelyLoadSystemDlls[] =
19 + {
20 + L"cabinet.dll", // required by Burn.
21 + L"msi.dll", // required by Burn.
22 + L"version.dll", // required by Burn.
23 + L"wininet.dll", // required by Burn.
24 +
25 + L"comres.dll", // required by CLSIDFromProgID() when loading clbcatq.dll.
26 + L"clbcatq.dll", // required by CLSIDFromProgID() when loading msxml?.dll.
27 +
28 + L"msasn1.dll", // required by DecryptFile() when loading crypt32.dll.
29 + L"crypt32.dll", // required by DecryptFile() when loading feclient.dll.
30 + L"feclient.dll", // unsafely loaded by DecryptFile().
31 + };
32 +
33 + // Best effort attempt to get our file handle as soon as possible.
34 + hr = PathForCurrentProcess(&sczPath, NULL);
35 + if (SUCCEEDED(hr))
36 + {
37 + hEngineFile = ::CreateFileW(sczPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
38 + }
39 +
40 + // If the engine is in the clean room, we'll do the unsafe initialization
41 + // because some systems in Windows (namely GDI+) will fail when run in
42 + // a process that protects against DLL hijacking. Since we know the clean
43 + // room is in a clean folder and not subject to DLL hijacking we won't
44 + // make ourselves perfectly secure so that we can load BAs that still
45 + // depend on those parts of Windows that are insecure to DLL hijacking.
46 + if (EngineInCleanRoom(lpCmdLine))
47 + {
48 + AppInitializeUnsafe();
49 + }
50 + else
51 + {
52 + AppInitialize(rgsczSafelyLoadSystemDlls, countof(rgsczSafelyLoadSystemDlls));
53 + }
54 +
55 + // call run
56 + hr = EngineRun(hInstance, hEngineFile, lpCmdLine, nCmdShow, &dwExitCode);
57 + ExitOnFailure(hr, "Failed to run application.");
58 +
59 +LExit:
60 + ReleaseFileHandle(hEngineFile);
61 + ReleaseStr(sczPath);
62 +
63 + return FAILED(hr) ? (int)hr : (int)dwExitCode;
64 +}
src/stub/stub.ico
Binary files /dev/null and b/src/stub/stub.ico differ
src/stub/stub.manifest new
+18
@@ -0,0 +1,18 @@
1 +<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
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 +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" manifestVersion="1.0">
6 + <assemblyIdentity name="setup.exe" version="1.0.0.0" processorArchitecture="x86" type="win32"/>
7 + <description>WiX Toolset Bootstrapper</description>
8 + <asmv3:application><asmv3:windowsSettings><ws:dpiAware xmlns:ws="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</ws:dpiAware></asmv3:windowsSettings></asmv3:application>
9 + <dependency><dependentAssembly><assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="X86" publicKeyToken="6595b64144ccf1df" language="*" /></dependentAssembly></dependency>
10 + <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"><application>
11 + <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
12 + <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
13 + <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
14 + <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
15 + <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
16 + </application></compatibility>
17 + <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"><security><requestedPrivileges><requestedExecutionLevel level="asInvoker" uiAccess="false"/></requestedPrivileges></security></trustInfo>
18 +</assembly>
src/stub/stub.rc new
+14
@@ -0,0 +1,14 @@
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 +#define VER_APP
4 +#define VER_ORIGINAL_FILENAME "setup.exe"
5 +#define VER_INTERNAL_NAME "setup"
6 +#define VER_FILE_DESCRIPTION "WiX Toolset Bootstrapper"
7 +#include "wix.rc"
8 +
9 +1 ICON "stub.ico"
10 +
11 +//#define MANIFEST_RESOURCE_ID 1
12 +#ifndef ARM // the ARM manifest is automatically injected but other platforms need it done manually.
13 +//MANIFEST_RESOURCE_ID RT_MANIFEST "stub.manifest"
14 +#endif