aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2020-03-29 19:14:06 +1000
committerSean Hall <r.sean.hall@gmail.com>2020-03-30 21:40:34 +1000
commit0354a00e74492ad8d930c5bf499bc8606e48b1c9 (patch)
tree5640d653449287699b4cd08cb6b64fe27c4fa8af
parent6ce359752afac0d3d70c2cf5fabd7d92859564ee (diff)
downloadwix-0354a00e74492ad8d930c5bf499bc8606e48b1c9.tar.gz
wix-0354a00e74492ad8d930c5bf499bc8606e48b1c9.tar.bz2
wix-0354a00e74492ad8d930c5bf499bc8606e48b1c9.zip
Add support for BundleExtensions.
-rw-r--r--src/engine/EngineForApplication.cpp2
-rw-r--r--src/engine/EngineForApplication.h4
-rw-r--r--src/engine/EngineForExtension.cpp405
-rw-r--r--src/engine/EngineForExtension.h27
-rw-r--r--src/engine/burnextension.cpp184
-rw-r--r--src/engine/burnextension.h51
-rw-r--r--src/engine/core.h1
-rw-r--r--src/engine/engine.cpp12
-rw-r--r--src/engine/engine.vcxproj13
-rw-r--r--src/engine/manifest.cpp4
-rw-r--r--src/engine/packages.config2
-rw-r--r--src/engine/precomp.h4
-rw-r--r--src/engine/userexperience.cpp2
-rw-r--r--src/engine/userexperience.h2
-rw-r--r--src/engine/variable.cpp2
15 files changed, 701 insertions, 14 deletions
diff --git a/src/engine/EngineForApplication.cpp b/src/engine/EngineForApplication.cpp
index eda5fc64..c3600c7b 100644
--- a/src/engine/EngineForApplication.cpp
+++ b/src/engine/EngineForApplication.cpp
@@ -593,7 +593,7 @@ static HRESULT BAEngineSetVariableString(
593 if (wzVariable && *wzVariable) 593 if (wzVariable && *wzVariable)
594 { 594 {
595 hr = VariableSetString(&pContext->pEngineState->variables, wzVariable, wzValue, FALSE); 595 hr = VariableSetString(&pContext->pEngineState->variables, wzVariable, wzValue, FALSE);
596 ExitOnFailure(hr, "Failed to set numeric variable."); 596 ExitOnFailure(hr, "Failed to set string variable.");
597 } 597 }
598 else 598 else
599 { 599 {
diff --git a/src/engine/EngineForApplication.h b/src/engine/EngineForApplication.h
index 1b755acc..e5e8f6d7 100644
--- a/src/engine/EngineForApplication.h
+++ b/src/engine/EngineForApplication.h
@@ -24,11 +24,11 @@ enum WM_BURN
24 24
25// structs 25// structs
26 26
27struct BOOTSTRAPPER_ENGINE_CONTEXT 27typedef struct _BOOTSTRAPPER_ENGINE_CONTEXT
28{ 28{
29 BURN_ENGINE_STATE* pEngineState; 29 BURN_ENGINE_STATE* pEngineState;
30 DWORD dwThreadId; 30 DWORD dwThreadId;
31}; 31} BOOTSTRAPPER_ENGINE_CONTEXT;
32 32
33// function declarations 33// function declarations
34 34
diff --git a/src/engine/EngineForExtension.cpp b/src/engine/EngineForExtension.cpp
new file mode 100644
index 00000000..9667dd18
--- /dev/null
+++ b/src/engine/EngineForExtension.cpp
@@ -0,0 +1,405 @@
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
5static HRESULT BEEngineEscapeString(
6 __in BURN_EXTENSION_ENGINE_CONTEXT* /*pContext*/,
7 __in BUNDLE_EXTENSION_ENGINE_ESCAPESTRING_ARGS* pArgs,
8 __in BUNDLE_EXTENSION_ENGINE_ESCAPESTRING_RESULTS* pResults
9 )
10{
11 HRESULT hr = S_OK;
12 LPWSTR sczValue = NULL;
13 size_t cchRemaining = 0;
14 LPCWSTR wzIn = pArgs->wzIn;
15 LPWSTR wzOut = pResults->wzOut;
16 DWORD* pcchOut = &pResults->cchOut;
17
18 if (wzIn && *wzIn)
19 {
20 hr = VariableEscapeString(wzIn, &sczValue);
21 if (SUCCEEDED(hr))
22 {
23 if (wzOut)
24 {
25 hr = ::StringCchCopyExW(wzOut, *pcchOut, sczValue, NULL, &cchRemaining, STRSAFE_FILL_BEHIND_NULL);
26 if (STRSAFE_E_INSUFFICIENT_BUFFER == hr)
27 {
28 hr = E_MOREDATA;
29 ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, &cchRemaining);
30 *pcchOut = cchRemaining;
31 }
32 }
33 else
34 {
35 ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, &cchRemaining);
36 *pcchOut = cchRemaining;
37 }
38 }
39 }
40 else
41 {
42 hr = E_INVALIDARG;
43 }
44
45 StrSecureZeroFreeString(sczValue);
46 return hr;
47}
48
49static HRESULT BEEngineEvaluateCondition(
50 __in BURN_EXTENSION_ENGINE_CONTEXT* pContext,
51 __in BUNDLE_EXTENSION_ENGINE_EVALUATECONDITION_ARGS* pArgs,
52 __in BUNDLE_EXTENSION_ENGINE_EVALUATECONDITION_RESULTS* pResults
53 )
54{
55 HRESULT hr = S_OK;
56 LPCWSTR wzCondition = pArgs->wzCondition;
57 BOOL* pf = &pResults->f;
58
59 if (wzCondition && *wzCondition)
60 {
61 hr = ConditionEvaluate(&pContext->pEngineState->variables, wzCondition, pf);
62 }
63 else
64 {
65 hr = E_INVALIDARG;
66 }
67
68 return hr;
69}
70
71static HRESULT BEEngineFormatString(
72 __in BURN_EXTENSION_ENGINE_CONTEXT* pContext,
73 __in BUNDLE_EXTENSION_ENGINE_FORMATSTRING_ARGS* pArgs,
74 __in BUNDLE_EXTENSION_ENGINE_FORMATSTRING_RESULTS* pResults
75 )
76{
77 HRESULT hr = S_OK;
78 LPWSTR sczValue = NULL;
79 DWORD cchValue = 0;
80 LPCWSTR wzIn = pArgs->wzIn;
81 LPWSTR wzOut = pResults->wzOut;
82 DWORD* pcchOut = &pResults->cchOut;
83
84 if (wzIn && *wzIn)
85 {
86 hr = VariableFormatString(&pContext->pEngineState->variables, wzIn, &sczValue, &cchValue);
87 if (SUCCEEDED(hr))
88 {
89 if (wzOut)
90 {
91 hr = ::StringCchCopyExW(wzOut, *pcchOut, sczValue, NULL, NULL, STRSAFE_FILL_BEHIND_NULL);
92 if (FAILED(hr))
93 {
94 *pcchOut = cchValue;
95 if (STRSAFE_E_INSUFFICIENT_BUFFER == hr)
96 {
97 hr = E_MOREDATA;
98 }
99 }
100 }
101 else
102 {
103 hr = E_MOREDATA;
104 *pcchOut = cchValue;
105 }
106 }
107 }
108 else
109 {
110 hr = E_INVALIDARG;
111 }
112
113 StrSecureZeroFreeString(sczValue);
114 return hr;
115}
116
117static HRESULT BEEngineGetVariableNumeric(
118 __in BURN_EXTENSION_ENGINE_CONTEXT* pContext,
119 __in BUNDLE_EXTENSION_ENGINE_GETVARIABLENUMERIC_ARGS* pArgs,
120 __in BUNDLE_EXTENSION_ENGINE_GETVARIABLENUMERIC_RESULTS* pResults
121 )
122{
123 HRESULT hr = S_OK;
124 LPCWSTR wzVariable = pArgs->wzVariable;
125 LONGLONG* pllValue = &pResults->llValue;
126
127 if (wzVariable && *wzVariable)
128 {
129 hr = VariableGetNumeric(&pContext->pEngineState->variables, wzVariable, pllValue);
130 }
131 else
132 {
133 hr = E_INVALIDARG;
134 }
135
136 return hr;
137}
138
139static HRESULT BEEngineGetVariableString(
140 __in BURN_EXTENSION_ENGINE_CONTEXT* pContext,
141 __in BUNDLE_EXTENSION_ENGINE_GETVARIABLESTRING_ARGS* pArgs,
142 __in BUNDLE_EXTENSION_ENGINE_GETVARIABLESTRING_RESULTS* pResults
143 )
144{
145 HRESULT hr = S_OK;
146 LPWSTR sczValue = NULL;
147 size_t cchRemaining = 0;
148 LPCWSTR wzVariable = pArgs->wzVariable;
149 LPWSTR wzValue = pResults->wzValue;
150 DWORD* pcchValue = &pResults->cchValue;
151
152 if (wzVariable && *wzVariable)
153 {
154 hr = VariableGetString(&pContext->pEngineState->variables, wzVariable, &sczValue);
155 if (SUCCEEDED(hr))
156 {
157 if (wzValue)
158 {
159 hr = ::StringCchCopyExW(wzValue, *pcchValue, sczValue, NULL, &cchRemaining, STRSAFE_FILL_BEHIND_NULL);
160 if (STRSAFE_E_INSUFFICIENT_BUFFER == hr)
161 {
162 hr = E_MOREDATA;
163
164 ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, &cchRemaining);
165 *pcchValue = cchRemaining + 1;
166 }
167 }
168 else
169 {
170 hr = E_MOREDATA;
171
172 ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, &cchRemaining);
173 *pcchValue = cchRemaining + 1;
174 }
175 }
176 }
177 else
178 {
179 hr = E_INVALIDARG;
180 }
181
182 StrSecureZeroFreeString(sczValue);
183 return hr;
184}
185
186static HRESULT BEEngineGetVariableVersion(
187 __in BURN_EXTENSION_ENGINE_CONTEXT* pContext,
188 __in BUNDLE_EXTENSION_ENGINE_GETVARIABLEVERSION_ARGS* pArgs,
189 __in BUNDLE_EXTENSION_ENGINE_GETVARIABLEVERSION_RESULTS* pResults
190 )
191{
192 HRESULT hr = S_OK;
193 LPCWSTR wzVariable = pArgs->wzVariable;
194 DWORD64* pqwValue = &pResults->qwValue;
195
196 if (wzVariable && *wzVariable)
197 {
198 hr = VariableGetVersion(&pContext->pEngineState->variables, wzVariable, pqwValue);
199 }
200 else
201 {
202 hr = E_INVALIDARG;
203 }
204
205 return hr;
206}
207
208static HRESULT BEEngineLog(
209 __in BURN_EXTENSION_ENGINE_CONTEXT* /*pContext*/,
210 __in BUNDLE_EXTENSION_ENGINE_LOG_ARGS* pArgs,
211 __in BUNDLE_EXTENSION_ENGINE_LOG_RESULTS* /*pResults*/
212 )
213{
214 HRESULT hr = S_OK;
215 REPORT_LEVEL rl = REPORT_NONE;
216 BUNDLE_EXTENSION_LOG_LEVEL level = pArgs->level;
217 LPCWSTR wzMessage = pArgs->wzMessage;
218
219 switch (level)
220 {
221 case BUNDLE_EXTENSION_LOG_LEVEL_STANDARD:
222 rl = REPORT_STANDARD;
223 break;
224
225 case BUNDLE_EXTENSION_LOG_LEVEL_VERBOSE:
226 rl = REPORT_VERBOSE;
227 break;
228
229 case BUNDLE_EXTENSION_LOG_LEVEL_DEBUG:
230 rl = REPORT_DEBUG;
231 break;
232
233 case BUNDLE_EXTENSION_LOG_LEVEL_ERROR:
234 rl = REPORT_ERROR;
235 break;
236
237 default:
238 ExitFunction1(hr = E_INVALIDARG);
239 }
240
241 hr = LogStringLine(rl, "%ls", wzMessage);
242 ExitOnFailure(hr, "Failed to log Bundle Extension message.");
243
244LExit:
245 return hr;
246}
247
248static HRESULT BEEngineSetVariableLiteralString(
249 __in BURN_EXTENSION_ENGINE_CONTEXT* pContext,
250 __in const BUNDLE_EXTENSION_ENGINE_SETVARIABLELITERALSTRING_ARGS* pArgs,
251 __in BUNDLE_EXTENSION_ENGINE_SETVARIABLELITERALSTRING_RESULTS* /*pResults*/
252 )
253{
254 HRESULT hr = S_OK;
255 LPCWSTR wzVariable = pArgs->wzVariable;
256 LPCWSTR wzValue = pArgs->wzValue;
257
258 if (wzVariable && *wzVariable)
259 {
260 hr = VariableSetLiteralString(&pContext->pEngineState->variables, wzVariable, wzValue, FALSE);
261 ExitOnFailure(hr, "Failed to set literal string variable.");
262 }
263 else
264 {
265 hr = E_INVALIDARG;
266 ExitOnFailure(hr, "Bundle Extension did not provide variable name.");
267 }
268
269LExit:
270 return hr;
271}
272
273static HRESULT BEEngineSetVariableNumeric(
274 __in BURN_EXTENSION_ENGINE_CONTEXT* pContext,
275 __in const BUNDLE_EXTENSION_ENGINE_SETVARIABLENUMERIC_ARGS* pArgs,
276 __in BUNDLE_EXTENSION_ENGINE_SETVARIABLENUMERIC_RESULTS* /*pResults*/
277 )
278{
279 HRESULT hr = S_OK;
280 LPCWSTR wzVariable = pArgs->wzVariable;
281 LONGLONG llValue = pArgs->llValue;
282
283 if (wzVariable && *wzVariable)
284 {
285 hr = VariableSetNumeric(&pContext->pEngineState->variables, wzVariable, llValue, FALSE);
286 ExitOnFailure(hr, "Failed to set numeric variable.");
287 }
288 else
289 {
290 hr = E_INVALIDARG;
291 ExitOnFailure(hr, "Bundle Extension did not provide variable name.");
292 }
293
294LExit:
295 return hr;
296}
297
298static HRESULT BEEngineSetVariableString(
299 __in BURN_EXTENSION_ENGINE_CONTEXT* pContext,
300 __in const BUNDLE_EXTENSION_ENGINE_SETVARIABLESTRING_ARGS* pArgs,
301 __in BUNDLE_EXTENSION_ENGINE_SETVARIABLESTRING_RESULTS* /*pResults*/
302 )
303{
304 HRESULT hr = S_OK;
305 LPCWSTR wzVariable = pArgs->wzVariable;
306 LPCWSTR wzValue = pArgs->wzValue;
307
308 if (wzVariable && *wzVariable)
309 {
310 hr = VariableSetString(&pContext->pEngineState->variables, wzVariable, wzValue, FALSE);
311 ExitOnFailure(hr, "Failed to set string variable.");
312 }
313 else
314 {
315 hr = E_INVALIDARG;
316 ExitOnFailure(hr, "Bundle Extension did not provide variable name.");
317 }
318
319LExit:
320 return hr;
321}
322
323static HRESULT BEEngineSetVariableVersion(
324 __in BURN_EXTENSION_ENGINE_CONTEXT* pContext,
325 __in const BUNDLE_EXTENSION_ENGINE_SETVARIABLEVERSION_ARGS* pArgs,
326 __in BUNDLE_EXTENSION_ENGINE_SETVARIABLEVERSION_RESULTS* /*pResults*/
327 )
328{
329 HRESULT hr = S_OK;
330 LPCWSTR wzVariable = pArgs->wzVariable;
331 DWORD64 qwValue = pArgs->qwValue;
332
333 if (wzVariable && *wzVariable)
334 {
335 hr = VariableSetVersion(&pContext->pEngineState->variables, wzVariable, qwValue, FALSE);
336 ExitOnFailure(hr, "Failed to set version variable.");
337 }
338 else
339 {
340 hr = E_INVALIDARG;
341 ExitOnFailure(hr, "Bundle Extension did not provide variable name.");
342 }
343
344LExit:
345 return hr;
346}
347
348HRESULT WINAPI EngineForExtensionProc(
349 __in BUNDLE_EXTENSION_ENGINE_MESSAGE message,
350 __in const LPVOID pvArgs,
351 __inout LPVOID pvResults,
352 __in_opt LPVOID pvContext
353 )
354{
355 HRESULT hr = S_OK;
356 BURN_EXTENSION_ENGINE_CONTEXT* pContext = reinterpret_cast<BURN_EXTENSION_ENGINE_CONTEXT*>(pvContext);
357
358 if (!pContext || !pvArgs || !pvResults)
359 {
360 ExitFunction1(hr = E_INVALIDARG);
361 }
362
363 switch (message)
364 {
365 case BUNDLE_EXTENSION_ENGINE_MESSAGE_ESCAPESTRING:
366 hr = BEEngineEscapeString(pContext, reinterpret_cast<BUNDLE_EXTENSION_ENGINE_ESCAPESTRING_ARGS*>(pvArgs), reinterpret_cast<BUNDLE_EXTENSION_ENGINE_ESCAPESTRING_RESULTS*>(pvResults));
367 break;
368 case BUNDLE_EXTENSION_ENGINE_MESSAGE_EVALUATECONDITION:
369 hr = BEEngineEvaluateCondition(pContext, reinterpret_cast<BUNDLE_EXTENSION_ENGINE_EVALUATECONDITION_ARGS*>(pvArgs), reinterpret_cast<BUNDLE_EXTENSION_ENGINE_EVALUATECONDITION_RESULTS*>(pvResults));
370 break;
371 case BUNDLE_EXTENSION_ENGINE_MESSAGE_FORMATSTRING:
372 hr = BEEngineFormatString(pContext, reinterpret_cast<BUNDLE_EXTENSION_ENGINE_FORMATSTRING_ARGS*>(pvArgs), reinterpret_cast<BUNDLE_EXTENSION_ENGINE_FORMATSTRING_RESULTS*>(pvResults));
373 break;
374 case BUNDLE_EXTENSION_ENGINE_MESSAGE_GETVARIABLENUMERIC:
375 hr = BEEngineGetVariableNumeric(pContext, reinterpret_cast<BUNDLE_EXTENSION_ENGINE_GETVARIABLENUMERIC_ARGS*>(pvArgs), reinterpret_cast<BUNDLE_EXTENSION_ENGINE_GETVARIABLENUMERIC_RESULTS*>(pvResults));
376 break;
377 case BUNDLE_EXTENSION_ENGINE_MESSAGE_GETVARIABLESTRING:
378 hr = BEEngineGetVariableString(pContext, reinterpret_cast<BUNDLE_EXTENSION_ENGINE_GETVARIABLESTRING_ARGS*>(pvArgs), reinterpret_cast<BUNDLE_EXTENSION_ENGINE_GETVARIABLESTRING_RESULTS*>(pvResults));
379 break;
380 case BUNDLE_EXTENSION_ENGINE_MESSAGE_GETVARIABLEVERSION:
381 hr = BEEngineGetVariableVersion(pContext, reinterpret_cast<BUNDLE_EXTENSION_ENGINE_GETVARIABLEVERSION_ARGS*>(pvArgs), reinterpret_cast<BUNDLE_EXTENSION_ENGINE_GETVARIABLEVERSION_RESULTS*>(pvResults));
382 break;
383 case BUNDLE_EXTENSION_ENGINE_MESSAGE_LOG:
384 hr = BEEngineLog(pContext, reinterpret_cast<BUNDLE_EXTENSION_ENGINE_LOG_ARGS*>(pvArgs), reinterpret_cast<BUNDLE_EXTENSION_ENGINE_LOG_RESULTS*>(pvResults));
385 break;
386 case BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLELITERALSTRING:
387 hr = BEEngineSetVariableLiteralString(pContext, reinterpret_cast<BUNDLE_EXTENSION_ENGINE_SETVARIABLELITERALSTRING_ARGS*>(pvArgs), reinterpret_cast<BUNDLE_EXTENSION_ENGINE_SETVARIABLELITERALSTRING_RESULTS*>(pvResults));
388 break;
389 case BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLENUMERIC:
390 hr = BEEngineSetVariableNumeric(pContext, reinterpret_cast<BUNDLE_EXTENSION_ENGINE_SETVARIABLENUMERIC_ARGS*>(pvArgs), reinterpret_cast<BUNDLE_EXTENSION_ENGINE_SETVARIABLENUMERIC_RESULTS*>(pvResults));
391 break;
392 case BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLESTRING:
393 hr = BEEngineSetVariableString(pContext, reinterpret_cast<BUNDLE_EXTENSION_ENGINE_SETVARIABLESTRING_ARGS*>(pvArgs), reinterpret_cast<BUNDLE_EXTENSION_ENGINE_SETVARIABLESTRING_RESULTS*>(pvResults));
394 break;
395 case BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLEVERSION:
396 hr = BEEngineSetVariableVersion(pContext, reinterpret_cast<BUNDLE_EXTENSION_ENGINE_SETVARIABLEVERSION_ARGS*>(pvArgs), reinterpret_cast<BUNDLE_EXTENSION_ENGINE_SETVARIABLEVERSION_RESULTS*>(pvResults));
397 break;
398 default:
399 hr = E_NOTIMPL;
400 break;
401 }
402
403LExit:
404 return hr;
405}
diff --git a/src/engine/EngineForExtension.h b/src/engine/EngineForExtension.h
new file mode 100644
index 00000000..bad5f08a
--- /dev/null
+++ b/src/engine/EngineForExtension.h
@@ -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)
6extern "C" {
7#endif
8
9// structs
10
11typedef struct _BURN_EXTENSION_ENGINE_CONTEXT
12{
13 BURN_ENGINE_STATE* pEngineState;
14} BURN_EXTENSION_ENGINE_CONTEXT;
15
16// function declarations
17
18HRESULT WINAPI EngineForExtensionProc(
19 __in BUNDLE_EXTENSION_ENGINE_MESSAGE message,
20 __in const LPVOID pvArgs,
21 __inout LPVOID pvResults,
22 __in_opt LPVOID pvContext
23 );
24
25#if defined(__cplusplus)
26}
27#endif
diff --git a/src/engine/burnextension.cpp b/src/engine/burnextension.cpp
new file mode 100644
index 00000000..99673cd9
--- /dev/null
+++ b/src/engine/burnextension.cpp
@@ -0,0 +1,184 @@
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// function definitions
6
7/*******************************************************************
8 BurnExtensionParseFromXml -
9
10*******************************************************************/
11EXTERN_C HRESULT BurnExtensionParseFromXml(
12 __in BURN_EXTENSIONS* pBurnExtensions,
13 __in BURN_PAYLOADS* pBaPayloads,
14 __in IXMLDOMNode* pixnBundle
15 )
16{
17 HRESULT hr = S_OK;
18 IXMLDOMNodeList* pixnNodes = NULL;
19 IXMLDOMNode* pixnNode = NULL;
20 DWORD cNodes = 0;
21
22 // Select BundleExtension nodes.
23 hr = XmlSelectNodes(pixnBundle, L"BundleExtension", &pixnNodes);
24 ExitOnFailure(hr, "Failed to select BundleExtension nodes.");
25
26 // Get BundleExtension node count.
27 hr = pixnNodes->get_length((long*)&cNodes);
28 ExitOnFailure(hr, "Failed to get BundleExtension node count.");
29
30 if (!cNodes)
31 {
32 ExitFunction();
33 }
34
35 // Allocate memory for BundleExtensions.
36 pBurnExtensions->rgExtensions = (BURN_EXTENSION*)MemAlloc(sizeof(BURN_EXTENSION) * cNodes, TRUE);
37 ExitOnNull(pBurnExtensions->rgExtensions, hr, E_OUTOFMEMORY, "Failed to allocate memory for BundleExtension structs.");
38
39 pBurnExtensions->cExtensions = cNodes;
40
41 // parse search elements
42 for (DWORD i = 0; i < cNodes; ++i)
43 {
44 BURN_EXTENSION* pExtension = &pBurnExtensions->rgExtensions[i];
45
46 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
47 ExitOnFailure(hr, "Failed to get next node.");
48
49 // @Id
50 hr = XmlGetAttributeEx(pixnNode, L"Id", &pExtension->sczId);
51 ExitOnFailure(hr, "Failed to get @Id.");
52
53 // @EntryPayloadId
54 hr = XmlGetAttributeEx(pixnNode, L"EntryPayloadId", &pExtension->sczEntryPayloadId);
55 ExitOnFailure(hr, "Failed to get @EntryPayloadId.");
56
57 hr = PayloadFindById(pBaPayloads, pExtension->sczEntryPayloadId, &pExtension->pEntryPayload);
58 ExitOnFailure(hr, "Failed to find BundleExtension EntryPayload '%ls'.", pExtension->sczEntryPayloadId);
59
60 // prepare next iteration
61 ReleaseNullObject(pixnNode);
62 }
63
64 hr = S_OK;
65
66LExit:
67 ReleaseObject(pixnNode);
68 ReleaseObject(pixnNodes);
69
70 return hr;
71}
72
73/*******************************************************************
74 BurnExtensionUninitialize -
75
76*******************************************************************/
77EXTERN_C void BurnExtensionUninitialize(
78 __in BURN_EXTENSIONS* pBurnExtensions
79 )
80{
81 if (pBurnExtensions->rgExtensions)
82 {
83 for (DWORD i = 0; i < pBurnExtensions->cExtensions; ++i)
84 {
85 BURN_EXTENSION* pExtension = &pBurnExtensions->rgExtensions[i];
86
87 ReleaseStr(pExtension->sczEntryPayloadId);
88 ReleaseStr(pExtension->sczId);
89 }
90 MemFree(pBurnExtensions->rgExtensions);
91 }
92
93 // clear struct
94 memset(pBurnExtensions, 0, sizeof(BURN_EXTENSIONS));
95}
96
97/*******************************************************************
98 BurnExtensionLoad -
99
100*******************************************************************/
101EXTERN_C HRESULT BurnExtensionLoad(
102 __in BURN_EXTENSIONS * pBurnExtensions,
103 __in BURN_EXTENSION_ENGINE_CONTEXT* pEngineContext
104 )
105{
106 HRESULT hr = S_OK;
107 BUNDLE_EXTENSION_CREATE_ARGS args = { };
108 BUNDLE_EXTENSION_CREATE_RESULTS results = { };
109
110 if (!pBurnExtensions->rgExtensions || !pBurnExtensions->cExtensions)
111 {
112 ExitFunction();
113 }
114
115 for (DWORD i = 0; i < pBurnExtensions->cExtensions; ++i)
116 {
117 BURN_EXTENSION* pExtension = &pBurnExtensions->rgExtensions[i];
118
119 memset(&args, 0, sizeof(BUNDLE_EXTENSION_CREATE_ARGS));
120 memset(&results, 0, sizeof(BUNDLE_EXTENSION_CREATE_RESULTS));
121
122 args.cbSize = sizeof(BUNDLE_EXTENSION_CREATE_ARGS);
123 args.pfnBundleExtensionEngineProc = EngineForExtensionProc;
124 args.pvBundleExtensionEngineProcContext = pEngineContext;
125 args.qwEngineAPIVersion = MAKEQWORDVERSION(0, 0, 0, 1); // TODO: need to decide whether to keep this, and if so when to update it.
126
127 results.cbSize = sizeof(BUNDLE_EXTENSION_CREATE_RESULTS);
128
129 // Load BundleExtension DLL.
130 pExtension->hBextModule = ::LoadLibraryExW(pExtension->pEntryPayload->sczLocalFilePath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
131 ExitOnNullWithLastError(pExtension->hBextModule, hr, "Failed to load BundleExtension DLL '%ls': '%ls'.", pExtension->sczId, pExtension->pEntryPayload->sczLocalFilePath);
132
133 // Get BundleExtensionCreate entry-point.
134 PFN_BUNDLE_EXTENSION_CREATE pfnCreate = (PFN_BUNDLE_EXTENSION_CREATE)::GetProcAddress(pExtension->hBextModule, "BundleExtensionCreate");
135 ExitOnNullWithLastError(pfnCreate, hr, "Failed to get BundleExtensionCreate entry-point '%ls'.", pExtension->sczId);
136
137 // Create BundleExtension.
138 hr = pfnCreate(&args, &results);
139 ExitOnFailure(hr, "Failed to create BundleExtension '%ls'.", pExtension->sczId);
140
141 pExtension->pfnBurnExtensionProc = results.pfnBundleExtensionProc;
142 pExtension->pvBurnExtensionProcContext = results.pvBundleExtensionProcContext;
143 }
144
145LExit:
146 return hr;
147}
148
149/*******************************************************************
150 BurnExtensionUnload -
151
152*******************************************************************/
153EXTERN_C void BurnExtensionUnload(
154 __in BURN_EXTENSIONS * pBurnExtensions
155 )
156{
157 HRESULT hr = S_OK;
158
159 if (pBurnExtensions->rgExtensions)
160 {
161 for (DWORD i = 0; i < pBurnExtensions->cExtensions; ++i)
162 {
163 BURN_EXTENSION* pExtension = &pBurnExtensions->rgExtensions[i];
164
165 if (pExtension->hBextModule)
166 {
167 // Get BundleExtensionDestroy entry-point and call it if it exists.
168 PFN_BUNDLE_EXTENSION_DESTROY pfnDestroy = (PFN_BUNDLE_EXTENSION_DESTROY)::GetProcAddress(pExtension->hBextModule, "BundleExtensionDestroy");
169 if (pfnDestroy)
170 {
171 pfnDestroy();
172 }
173
174 // Free BundleExtension DLL.
175 if (!::FreeLibrary(pExtension->hBextModule))
176 {
177 hr = HRESULT_FROM_WIN32(::GetLastError());
178 TraceError(hr, "Failed to unload BundleExtension DLL.");
179 }
180 pExtension->hBextModule = NULL;
181 }
182 }
183 }
184}
diff --git a/src/engine/burnextension.h b/src/engine/burnextension.h
new file mode 100644
index 00000000..43c8afe6
--- /dev/null
+++ b/src/engine/burnextension.h
@@ -0,0 +1,51 @@
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 BEEAPI HRESULT __stdcall
5
6#if defined(__cplusplus)
7extern "C" {
8#endif
9
10// structs
11
12typedef struct _BURN_EXTENSION_ENGINE_CONTEXT BURN_EXTENSION_ENGINE_CONTEXT;
13
14typedef struct _BURN_EXTENSION
15{
16 LPWSTR sczEntryPayloadId;
17 LPWSTR sczId;
18
19 BURN_PAYLOAD* pEntryPayload;
20
21 HMODULE hBextModule;
22 PFN_BUNDLE_EXTENSION_PROC pfnBurnExtensionProc;
23 LPVOID pvBurnExtensionProcContext;
24} BURN_EXTENSION;
25
26typedef struct _BURN_EXTENSIONS
27{
28 BURN_EXTENSION* rgExtensions;
29 DWORD cExtensions;
30} BURN_EXTENSIONS;
31
32// functions
33
34HRESULT BurnExtensionParseFromXml(
35 __in BURN_EXTENSIONS* pBurnExtensions,
36 __in BURN_PAYLOADS* pBaPayloads,
37 __in IXMLDOMNode* pixnBundle
38 );
39void BurnExtensionUninitialize(
40 __in BURN_EXTENSIONS* pBurnExtensions
41 );
42HRESULT BurnExtensionLoad(
43 __in BURN_EXTENSIONS* pBurnExtensions,
44 __in BURN_EXTENSION_ENGINE_CONTEXT* pEngineContext
45 );
46void BurnExtensionUnload(
47 __in BURN_EXTENSIONS* pBurnExtensions
48 );
49#if defined(__cplusplus)
50}
51#endif
diff --git a/src/engine/core.h b/src/engine/core.h
index 6a6da2b1..544c1786 100644
--- a/src/engine/core.h
+++ b/src/engine/core.h
@@ -103,6 +103,7 @@ typedef struct _BURN_ENGINE_STATE
103 BURN_PACKAGES packages; 103 BURN_PACKAGES packages;
104 BURN_UPDATE update; 104 BURN_UPDATE update;
105 BURN_APPROVED_EXES approvedExes; 105 BURN_APPROVED_EXES approvedExes;
106 BURN_EXTENSIONS extensions;
106 107
107 HWND hMessageWindow; 108 HWND hMessageWindow;
108 HANDLE hMessageWindowThread; 109 HANDLE hMessageWindowThread;
diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp
index 341fd471..488dbfe8 100644
--- a/src/engine/engine.cpp
+++ b/src/engine/engine.cpp
@@ -352,6 +352,8 @@ static void UninitializeEngineState(
352 352
353 ReleaseHandle(pEngineState->hMessageWindowThread); 353 ReleaseHandle(pEngineState->hMessageWindowThread);
354 354
355 BurnExtensionUninitialize(&pEngineState->extensions);
356
355 ::DeleteCriticalSection(&pEngineState->userExperience.csEngineActive); 357 ::DeleteCriticalSection(&pEngineState->userExperience.csEngineActive);
356 UserExperienceUninitialize(&pEngineState->userExperience); 358 UserExperienceUninitialize(&pEngineState->userExperience);
357 359
@@ -493,6 +495,7 @@ static HRESULT RunNormal(
493 HANDLE hPipesCreatedEvent = NULL; 495 HANDLE hPipesCreatedEvent = NULL;
494 BOOL fContinueExecution = TRUE; 496 BOOL fContinueExecution = TRUE;
495 BOOL fReloadApp = FALSE; 497 BOOL fReloadApp = FALSE;
498 BURN_EXTENSION_ENGINE_CONTEXT extensionEngineContext = { };
496 499
497 // Initialize logging. 500 // Initialize logging.
498 hr = LoggingOpen(&pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->registration.sczDisplayName); 501 hr = LoggingOpen(&pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->registration.sczDisplayName);
@@ -537,6 +540,13 @@ static HRESULT RunNormal(
537 ExitOnFailure(hr, "Failed to set layout directory variable to value provided from command-line."); 540 ExitOnFailure(hr, "Failed to set layout directory variable to value provided from command-line.");
538 } 541 }
539 542
543 // Setup the extension engine.
544 extensionEngineContext.pEngineState = pEngineState;
545
546 // Load the extensions.
547 hr = BurnExtensionLoad(&pEngineState->extensions, &extensionEngineContext);
548 ExitOnFailure(hr, "Failed to load BundleExtensions.");
549
540 do 550 do
541 { 551 {
542 fReloadApp = FALSE; 552 fReloadApp = FALSE;
@@ -546,6 +556,8 @@ static HRESULT RunNormal(
546 } while (fReloadApp); 556 } while (fReloadApp);
547 557
548LExit: 558LExit:
559 BurnExtensionUnload(&pEngineState->extensions);
560
549 // If the message window is still around, close it. 561 // If the message window is still around, close it.
550 UiCloseMessageWindow(pEngineState); 562 UiCloseMessageWindow(pEngineState);
551 563
diff --git a/src/engine/engine.vcxproj b/src/engine/engine.vcxproj
index 499fcd4d..c2e8f34c 100644
--- a/src/engine/engine.vcxproj
+++ b/src/engine/engine.vcxproj
@@ -2,7 +2,7 @@
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. --> 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 3
4<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> 4<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
5 <Import Project="..\..\packages\WixToolset.BootstrapperCore.Native.4.0.12\build\WixToolset.BootstrapperCore.Native.props" Condition="Exists('..\..\packages\WixToolset.BootstrapperCore.Native.4.0.12\build\WixToolset.BootstrapperCore.Native.props')" /> 5 <Import Project="..\..\packages\WixToolset.BootstrapperCore.Native.4.0.13\build\WixToolset.BootstrapperCore.Native.props" Condition="Exists('..\..\packages\WixToolset.BootstrapperCore.Native.4.0.13\build\WixToolset.BootstrapperCore.Native.props')" />
6 <Import Project="..\..\packages\WixToolset.DUtil.4.0.18\build\WixToolset.DUtil.props" Condition="Exists('..\..\packages\WixToolset.DUtil.4.0.18\build\WixToolset.DUtil.props')" /> 6 <Import Project="..\..\packages\WixToolset.DUtil.4.0.18\build\WixToolset.DUtil.props" Condition="Exists('..\..\packages\WixToolset.DUtil.4.0.18\build\WixToolset.DUtil.props')" />
7 7
8 <ItemGroup Label="ProjectConfigurations"> 8 <ItemGroup Label="ProjectConfigurations">
@@ -50,10 +50,12 @@
50 <ClCompile Include="apply.cpp" /> 50 <ClCompile Include="apply.cpp" />
51 <ClCompile Include="approvedexe.cpp" /> 51 <ClCompile Include="approvedexe.cpp" />
52 <ClCompile Include="bitsengine.cpp" /> 52 <ClCompile Include="bitsengine.cpp" />
53 <ClCompile Include="burnextension.cpp" />
53 <ClCompile Include="catalog.cpp" /> 54 <ClCompile Include="catalog.cpp" />
54 <ClCompile Include="detect.cpp" /> 55 <ClCompile Include="detect.cpp" />
55 <ClCompile Include="embedded.cpp" /> 56 <ClCompile Include="embedded.cpp" />
56 <ClCompile Include="EngineForApplication.cpp" /> 57 <ClCompile Include="EngineForApplication.cpp" />
58 <ClCompile Include="EngineForExtension.cpp" />
57 <ClCompile Include="cabextract.cpp" /> 59 <ClCompile Include="cabextract.cpp" />
58 <ClCompile Include="cache.cpp" /> 60 <ClCompile Include="cache.cpp" />
59 <ClCompile Include="condition.cpp" /> 61 <ClCompile Include="condition.cpp" />
@@ -91,14 +93,10 @@
91 </ItemGroup> 93 </ItemGroup>
92 94
93 <ItemGroup> 95 <ItemGroup>
94 <ClInclude Include="..\inc\BootstrapperApplication.h" />
95 <ClInclude Include="..\inc\BootstrapperEngine.h" />
96 </ItemGroup>
97
98 <ItemGroup>
99 <ClInclude Include="apply.h" /> 96 <ClInclude Include="apply.h" />
100 <ClInclude Include="approvedexe.h" /> 97 <ClInclude Include="approvedexe.h" />
101 <ClInclude Include="bitsengine.h" /> 98 <ClInclude Include="bitsengine.h" />
99 <ClInclude Include="burnextension.h" />
102 <ClInclude Include="cabextract.h" /> 100 <ClInclude Include="cabextract.h" />
103 <ClInclude Include="cache.h" /> 101 <ClInclude Include="cache.h" />
104 <ClInclude Include="catalog.h" /> 102 <ClInclude Include="catalog.h" />
@@ -110,6 +108,7 @@
110 <ClInclude Include="elevation.h" /> 108 <ClInclude Include="elevation.h" />
111 <ClInclude Include="embedded.h" /> 109 <ClInclude Include="embedded.h" />
112 <ClInclude Include="EngineForApplication.h" /> 110 <ClInclude Include="EngineForApplication.h" />
111 <ClInclude Include="EngineForExtension.h" />
113 <ClInclude Include="exeengine.h" /> 112 <ClInclude Include="exeengine.h" />
114 <ClInclude Include="inc\engine.h" /> 113 <ClInclude Include="inc\engine.h" />
115 <ClInclude Include="logging.h" /> 114 <ClInclude Include="logging.h" />
@@ -167,7 +166,7 @@ rc.exe -fo "$(OutDir)engine.res" "$(IntDir)engine.messages.rc"</Command>
167 <PropertyGroup> 166 <PropertyGroup>
168 <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> 167 <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
169 </PropertyGroup> 168 </PropertyGroup>
170 <Error Condition="!Exists('..\..\packages\WixToolset.BootstrapperCore.Native.4.0.12\build\WixToolset.BootstrapperCore.Native.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\WixToolset.BootstrapperCore.Native.4.0.12\build\WixToolset.BootstrapperCore.Native.props'))" /> 169 <Error Condition="!Exists('..\..\packages\WixToolset.BootstrapperCore.Native.4.0.13\build\WixToolset.BootstrapperCore.Native.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\WixToolset.BootstrapperCore.Native.4.0.13\build\WixToolset.BootstrapperCore.Native.props'))" />
171 <Error Condition="!Exists('..\..\packages\WixToolset.DUtil.4.0.18\build\WixToolset.DUtil.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\WixToolset.DUtil.4.0.18\build\WixToolset.DUtil.props'))" /> 170 <Error Condition="!Exists('..\..\packages\WixToolset.DUtil.4.0.18\build\WixToolset.DUtil.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\WixToolset.DUtil.4.0.18\build\WixToolset.DUtil.props'))" />
172 <Error Condition="!Exists('..\..\packages\Nerdbank.GitVersioning.2.1.65\build\Nerdbank.GitVersioning.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Nerdbank.GitVersioning.2.1.65\build\Nerdbank.GitVersioning.targets'))" /> 171 <Error Condition="!Exists('..\..\packages\Nerdbank.GitVersioning.2.1.65\build\Nerdbank.GitVersioning.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Nerdbank.GitVersioning.2.1.65\build\Nerdbank.GitVersioning.targets'))" />
173 </Target> 172 </Target>
diff --git a/src/engine/manifest.cpp b/src/engine/manifest.cpp
index c2214a89..a20f1980 100644
--- a/src/engine/manifest.cpp
+++ b/src/engine/manifest.cpp
@@ -116,6 +116,10 @@ extern "C" HRESULT ManifestLoadXmlFromBuffer(
116 hr = ApprovedExesParseFromXml(&pEngineState->approvedExes, pixeBundle); 116 hr = ApprovedExesParseFromXml(&pEngineState->approvedExes, pixeBundle);
117 ExitOnFailure(hr, "Failed to parse approved exes."); 117 ExitOnFailure(hr, "Failed to parse approved exes.");
118 118
119 // parse extensions
120 hr = BurnExtensionParseFromXml(&pEngineState->extensions, &pEngineState->userExperience.payloads, pixeBundle);
121 ExitOnFailure(hr, "Failed to parse extensions.");
122
119LExit: 123LExit:
120 ReleaseObject(pixnChain); 124 ReleaseObject(pixnChain);
121 ReleaseObject(pixnLog); 125 ReleaseObject(pixnLog);
diff --git a/src/engine/packages.config b/src/engine/packages.config
index 01a9390c..75a6476b 100644
--- a/src/engine/packages.config
+++ b/src/engine/packages.config
@@ -1,6 +1,6 @@
1<?xml version="1.0" encoding="utf-8"?> 1<?xml version="1.0" encoding="utf-8"?>
2<packages> 2<packages>
3 <package id="Nerdbank.GitVersioning" version="2.1.65" targetFramework="native" developmentDependency="true" /> 3 <package id="Nerdbank.GitVersioning" version="2.1.65" targetFramework="native" developmentDependency="true" />
4 <package id="WixToolset.BootstrapperCore.Native" version="4.0.12" targetFramework="native" /> 4 <package id="WixToolset.BootstrapperCore.Native" version="4.0.13" targetFramework="native" />
5 <package id="WixToolset.DUtil" version="4.0.18" targetFramework="native" /> 5 <package id="WixToolset.DUtil" version="4.0.18" targetFramework="native" />
6</packages> \ No newline at end of file 6</packages> \ No newline at end of file
diff --git a/src/engine/precomp.h b/src/engine/precomp.h
index 477dc310..780822a1 100644
--- a/src/engine/precomp.h
+++ b/src/engine/precomp.h
@@ -61,6 +61,8 @@
61 61
62#include "BootstrapperEngine.h" 62#include "BootstrapperEngine.h"
63#include "BootstrapperApplication.h" 63#include "BootstrapperApplication.h"
64#include "BundleExtensionEngine.h"
65#include "BundleExtension.h"
64 66
65#include "platform.h" 67#include "platform.h"
66#include "variant.h" 68#include "variant.h"
@@ -73,6 +75,7 @@
73#include "catalog.h" 75#include "catalog.h"
74#include "payload.h" 76#include "payload.h"
75#include "cabextract.h" 77#include "cabextract.h"
78#include "burnextension.h"
76#include "userexperience.h" 79#include "userexperience.h"
77#include "package.h" 80#include "package.h"
78#include "update.h" 81#include "update.h"
@@ -100,4 +103,5 @@
100#include "netfxchainer.h" 103#include "netfxchainer.h"
101 104
102#include "EngineForApplication.h" 105#include "EngineForApplication.h"
106#include "EngineForExtension.h"
103#include "engine.messages.h" 107#include "engine.messages.h"
diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp
index 8d5271aa..26b20f39 100644
--- a/src/engine/userexperience.cpp
+++ b/src/engine/userexperience.cpp
@@ -103,7 +103,7 @@ extern "C" HRESULT UserExperienceLoad(
103 103
104 // Load BA DLL. 104 // Load BA DLL.
105 pUserExperience->hUXModule = ::LoadLibraryExW(pUserExperience->payloads.rgPayloads[0].sczLocalFilePath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); 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."); 106 ExitOnNullWithLastError(pUserExperience->hUXModule, hr, "Failed to load BA DLL.");
107 107
108 // Get BootstrapperApplicationCreate entry-point. 108 // Get BootstrapperApplicationCreate entry-point.
109 PFN_BOOTSTRAPPER_APPLICATION_CREATE pfnCreate = (PFN_BOOTSTRAPPER_APPLICATION_CREATE)::GetProcAddress(pUserExperience->hUXModule, "BootstrapperApplicationCreate"); 109 PFN_BOOTSTRAPPER_APPLICATION_CREATE pfnCreate = (PFN_BOOTSTRAPPER_APPLICATION_CREATE)::GetProcAddress(pUserExperience->hUXModule, "BootstrapperApplicationCreate");
diff --git a/src/engine/userexperience.h b/src/engine/userexperience.h
index 27a94115..bec6d292 100644
--- a/src/engine/userexperience.h
+++ b/src/engine/userexperience.h
@@ -15,7 +15,7 @@ const DWORD MB_RETRYTRYAGAIN = 0xF;
15 15
16// structs 16// structs
17 17
18struct BOOTSTRAPPER_ENGINE_CONTEXT; 18typedef struct _BOOTSTRAPPER_ENGINE_CONTEXT BOOTSTRAPPER_ENGINE_CONTEXT;
19 19
20typedef struct _BURN_USER_EXPERIENCE 20typedef struct _BURN_USER_EXPERIENCE
21{ 21{
diff --git a/src/engine/variable.cpp b/src/engine/variable.cpp
index 7377b116..8b1fd279 100644
--- a/src/engine/variable.cpp
+++ b/src/engine/variable.cpp
@@ -309,7 +309,7 @@ extern "C" HRESULT VariablesParseFromXml(
309 hr = pixnNodes->get_length((long*)&cNodes); 309 hr = pixnNodes->get_length((long*)&cNodes);
310 ExitOnFailure(hr, "Failed to get variable node count."); 310 ExitOnFailure(hr, "Failed to get variable node count.");
311 311
312 // parse package elements 312 // parse variable elements
313 for (DWORD i = 0; i < cNodes; ++i) 313 for (DWORD i = 0; i < cNodes; ++i)
314 { 314 {
315 hr = XmlNextElement(pixnNodes, &pixnNode, NULL); 315 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);