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, §ionHeader, 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