aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2021-04-16 10:20:41 -0500
committerSean Hall <r.sean.hall@gmail.com>2021-04-19 23:12:55 -0500
commit5d6046bee5021052da4a666c1e2ceeb0f16af349 (patch)
tree589ddcbe270e2e4df8cffdbd26792bcee4c29e1c
parentb941c2754748251520dc5032d11396c9844fad8e (diff)
downloadwix-5d6046bee5021052da4a666c1e2ceeb0f16af349.tar.gz
wix-5d6046bee5021052da4a666c1e2ceeb0f16af349.tar.bz2
wix-5d6046bee5021052da4a666c1e2ceeb0f16af349.zip
Replace OnResolveSource with OnCacheAcquireResolving
Inactivate the engine during OnCacheAcquireBegin and Complete to allow setting the source from there. Fixes #3640 Contributes to #5253
-rw-r--r--src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h63
-rw-r--r--src/engine/apply.cpp242
-rw-r--r--src/engine/cache.cpp203
-rw-r--r--src/engine/cache.h9
-rw-r--r--src/engine/engine.mc32
-rw-r--r--src/engine/externalengine.cpp43
-rw-r--r--src/engine/userexperience.cpp126
-rw-r--r--src/engine/userexperience.h26
8 files changed, 413 insertions, 331 deletions
diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h
index 2d086f38..cf330c29 100644
--- a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h
+++ b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h
@@ -54,8 +54,13 @@ enum BOOTSTRAPPER_RELATED_OPERATION
54 54
55enum BOOTSTRAPPER_CACHE_OPERATION 55enum BOOTSTRAPPER_CACHE_OPERATION
56{ 56{
57 // There is no source available.
58 BOOTSTRAPPER_CACHE_OPERATION_NONE,
59 // Copy the payload or container from the chosen local source.
57 BOOTSTRAPPER_CACHE_OPERATION_COPY, 60 BOOTSTRAPPER_CACHE_OPERATION_COPY,
61 // Download the payload or container using the download URL.
58 BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD, 62 BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD,
63 // Extract the payload from the container.
59 BOOTSTRAPPER_CACHE_OPERATION_EXTRACT, 64 BOOTSTRAPPER_CACHE_OPERATION_EXTRACT,
60}; 65};
61 66
@@ -112,7 +117,7 @@ enum BOOTSTRAPPER_APPLICATION_MESSAGE
112 BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPACKAGEBEGIN, 117 BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPACKAGEBEGIN,
113 BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIREBEGIN, 118 BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIREBEGIN,
114 BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIREPROGRESS, 119 BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIREPROGRESS,
115 BOOTSTRAPPER_APPLICATION_MESSAGE_ONRESOLVESOURCE, 120 BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIRERESOLVING,
116 BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIRECOMPLETE, 121 BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIRECOMPLETE,
117 BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEVERIFYBEGIN, 122 BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEVERIFYBEGIN,
118 BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEVERIFYCOMPLETE, 123 BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEVERIFYCOMPLETE,
@@ -158,7 +163,7 @@ enum BOOTSTRAPPER_APPLYCOMPLETE_ACTION
158enum BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION 163enum BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION
159{ 164{
160 BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION_NONE, 165 BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION_NONE,
161 // Instructs the engine to try the acquisition of the package again. 166 // Instructs the engine to try the acquisition of the payload again.
162 // Ignored if hrStatus is a success. 167 // Ignored if hrStatus is a success.
163 BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION_RETRY, 168 BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION_RETRY,
164}; 169};
@@ -202,16 +207,6 @@ enum BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION
202 BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_SUSPEND, 207 BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_SUSPEND,
203}; 208};
204 209
205enum BOOTSTRAPPER_RESOLVESOURCE_ACTION
206{
207 // Instructs the engine that the source can't be found.
208 BOOTSTRAPPER_RESOLVESOURCE_ACTION_NONE,
209 // Instructs the engine to try the local source again.
210 BOOTSTRAPPER_RESOLVESOURCE_ACTION_RETRY,
211 // Instructs the engine to try the download source.
212 BOOTSTRAPPER_RESOLVESOURCE_ACTION_DOWNLOAD,
213};
214
215enum BOOTSTRAPPER_SHUTDOWN_ACTION 210enum BOOTSTRAPPER_SHUTDOWN_ACTION
216{ 211{
217 BOOTSTRAPPER_SHUTDOWN_ACTION_NONE, 212 BOOTSTRAPPER_SHUTDOWN_ACTION_NONE,
@@ -315,14 +310,17 @@ struct BA_ONCACHEACQUIREBEGIN_ARGS
315 DWORD cbSize; 310 DWORD cbSize;
316 LPCWSTR wzPackageOrContainerId; 311 LPCWSTR wzPackageOrContainerId;
317 LPCWSTR wzPayloadId; 312 LPCWSTR wzPayloadId;
318 BOOTSTRAPPER_CACHE_OPERATION operation;
319 LPCWSTR wzSource; 313 LPCWSTR wzSource;
314 LPCWSTR wzDownloadUrl;
315 LPCWSTR wzPayloadContainerId;
316 BOOTSTRAPPER_CACHE_OPERATION recommendation;
320}; 317};
321 318
322struct BA_ONCACHEACQUIREBEGIN_RESULTS 319struct BA_ONCACHEACQUIREBEGIN_RESULTS
323{ 320{
324 DWORD cbSize; 321 DWORD cbSize;
325 BOOL fCancel; 322 BOOL fCancel;
323 BOOTSTRAPPER_CACHE_OPERATION action;
326}; 324};
327 325
328struct BA_ONCACHEACQUIRECOMPLETE_ARGS 326struct BA_ONCACHEACQUIRECOMPLETE_ARGS
@@ -356,6 +354,28 @@ struct BA_ONCACHEACQUIREPROGRESS_RESULTS
356 BOOL fCancel; 354 BOOL fCancel;
357}; 355};
358 356
357struct BA_ONCACHEACQUIRERESOLVING_ARGS
358{
359 DWORD cbSize;
360 LPCWSTR wzPackageOrContainerId;
361 LPCWSTR wzPayloadId;
362 LPCWSTR* rgSearchPaths;
363 DWORD cSearchPaths;
364 BOOL fFoundLocal;
365 DWORD dwRecommendedSearchPath;
366 LPCWSTR wzDownloadUrl;
367 LPCWSTR wzPayloadContainerId;
368 BOOTSTRAPPER_CACHE_OPERATION recommendation;
369};
370
371struct BA_ONCACHEACQUIRERESOLVING_RESULTS
372{
373 DWORD cbSize;
374 DWORD dwChosenSearchPath;
375 BOOTSTRAPPER_CACHE_OPERATION action;
376 BOOL fCancel;
377};
378
359struct BA_ONCACHEBEGIN_ARGS 379struct BA_ONCACHEBEGIN_ARGS
360{ 380{
361 DWORD cbSize; 381 DWORD cbSize;
@@ -1013,23 +1033,6 @@ struct BA_ONREGISTERCOMPLETE_RESULTS
1013 DWORD cbSize; 1033 DWORD cbSize;
1014}; 1034};
1015 1035
1016struct BA_ONRESOLVESOURCE_ARGS
1017{
1018 DWORD cbSize;
1019 LPCWSTR wzPackageOrContainerId;
1020 LPCWSTR wzPayloadId;
1021 LPCWSTR wzLocalSource;
1022 LPCWSTR wzDownloadSource;
1023 BOOTSTRAPPER_RESOLVESOURCE_ACTION recommendation;
1024};
1025
1026struct BA_ONRESOLVESOURCE_RESULTS
1027{
1028 DWORD cbSize;
1029 BOOTSTRAPPER_RESOLVESOURCE_ACTION action;
1030 BOOL fCancel;
1031};
1032
1033struct BA_ONROLLBACKMSITRANSACTIONBEGIN_ARGS 1036struct BA_ONROLLBACKMSITRANSACTIONBEGIN_ARGS
1034{ 1037{
1035 DWORD cbSize; 1038 DWORD cbSize;
diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp
index dffbc6d6..dc9f905b 100644
--- a/src/engine/apply.cpp
+++ b/src/engine/apply.cpp
@@ -23,6 +23,9 @@ typedef struct _BURN_CACHE_CONTEXT
23 DWORD64 qwTotalCacheSize; 23 DWORD64 qwTotalCacheSize;
24 DWORD64 qwSuccessfulCacheProgress; 24 DWORD64 qwSuccessfulCacheProgress;
25 LPCWSTR wzLayoutDirectory; 25 LPCWSTR wzLayoutDirectory;
26 LPWSTR* rgSearchPaths;
27 DWORD cSearchPaths;
28 DWORD cSearchPathsMax;
26} BURN_CACHE_CONTEXT; 29} BURN_CACHE_CONTEXT;
27 30
28typedef struct _BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT 31typedef struct _BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT
@@ -113,15 +116,6 @@ static HRESULT LayoutOrCacheContainerOrPayload(
113 __in DWORD cTryAgainAttempts, 116 __in DWORD cTryAgainAttempts,
114 __out BOOL* pfRetry 117 __out BOOL* pfRetry
115 ); 118 );
116static HRESULT PromptForSource(
117 __in BURN_USER_EXPERIENCE* pUX,
118 __in_z LPCWSTR wzPackageOrContainerId,
119 __in_z_opt LPCWSTR wzPayloadId,
120 __in_z LPCWSTR wzLocalSource,
121 __in_z_opt LPCWSTR wzDownloadSource,
122 __out BOOL* pfRetry,
123 __out BOOL* pfDownload
124 );
125static HRESULT CopyPayload( 119static HRESULT CopyPayload(
126 __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress, 120 __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress,
127 __in HANDLE hSourceFile, 121 __in HANDLE hSourceFile,
@@ -485,6 +479,9 @@ extern "C" HRESULT ApplyCache(
485 cacheContext.qwTotalCacheSize = pPlan->qwCacheSizeTotal; 479 cacheContext.qwTotalCacheSize = pPlan->qwCacheSizeTotal;
486 cacheContext.wzLayoutDirectory = pPlan->sczLayoutDirectory; 480 cacheContext.wzLayoutDirectory = pPlan->sczLayoutDirectory;
487 481
482 hr = MemAllocArray(reinterpret_cast<LPVOID*>(&cacheContext.rgSearchPaths), sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS);
483 ExitOnNull(cacheContext.rgSearchPaths, hr, E_OUTOFMEMORY, "Failed to allocate cache search paths array.");
484
488 for (DWORD i = 0; i < pPlan->cCacheActions; ++i) 485 for (DWORD i = 0; i < pPlan->cCacheActions; ++i)
489 { 486 {
490 BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + i; 487 BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + i;
@@ -561,6 +558,12 @@ LExit:
561 558
562 CacheCleanup(FALSE, pPlan->wzBundleId); 559 CacheCleanup(FALSE, pPlan->wzBundleId);
563 560
561 for (DWORD i = 0; i < cacheContext.cSearchPathsMax; ++i)
562 {
563 ReleaseNullStr(cacheContext.rgSearchPaths[i]);
564 }
565 ReleaseMem(cacheContext.rgSearchPaths);
566
564 UserExperienceOnCacheComplete(pUX, hr); 567 UserExperienceOnCacheComplete(pUX, hr);
565 return hr; 568 return hr;
566} 569}
@@ -949,17 +952,8 @@ static HRESULT ApplyProcessPayload(
949 { 952 {
950 fRetry = FALSE; 953 fRetry = FALSE;
951 954
952 if (pPayload->pContainer) 955 hr = AcquireContainerOrPayload(pContext, NULL, pPackage, pPayload);
953 { 956 LogExitOnFailure(hr, MSG_FAILED_ACQUIRE_PAYLOAD, "Failed to acquire payload: %ls to working path: %ls", pPayload->sczKey, pPayload->sczUnverifiedPath);
954 // TODO: only extract container if payload isn't already extracted
955 hr = ApplyExtractContainer(pContext, pPayload->pContainer);
956 ExitOnFailure(hr, "Failed to extract container for payload: %ls", pPayload->sczKey);
957 }
958 else
959 {
960 hr = AcquireContainerOrPayload(pContext, NULL, pPackage, pPayload);
961 LogExitOnFailure(hr, MSG_FAILED_ACQUIRE_PAYLOAD, "Failed to acquire payload: %ls to working path: %ls", pPayload->sczKey, pPayload->sczUnverifiedPath);
962 }
963 957
964 pContext->qwSuccessfulCacheProgress += pPayload->qwFileSize; 958 pContext->qwSuccessfulCacheProgress += pPayload->qwFileSize;
965 959
@@ -1054,8 +1048,10 @@ static HRESULT LayoutBundle(
1054{ 1048{
1055 HRESULT hr = S_OK; 1049 HRESULT hr = S_OK;
1056 LPWSTR sczBundlePath = NULL; 1050 LPWSTR sczBundlePath = NULL;
1051 LPWSTR sczBundleDownloadUrl = NULL;
1057 LPWSTR sczDestinationPath = NULL; 1052 LPWSTR sczDestinationPath = NULL;
1058 int nEquivalentPaths = 0; 1053 int nEquivalentPaths = 0;
1054 BOOTSTRAPPER_CACHE_OPERATION cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_NONE;
1059 BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT progress = { }; 1055 BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT progress = { };
1060 BOOL fRetry = FALSE; 1056 BOOL fRetry = FALSE;
1061 BOOL fRetryAcquire = FALSE; 1057 BOOL fRetryAcquire = FALSE;
@@ -1096,7 +1092,7 @@ static HRESULT LayoutBundle(
1096 fRetryAcquire = FALSE; 1092 fRetryAcquire = FALSE;
1097 progress.fCancel = FALSE; 1093 progress.fCancel = FALSE;
1098 1094
1099 hr = UserExperienceOnCacheAcquireBegin(pContext->pUX, NULL, NULL, BOOTSTRAPPER_CACHE_OPERATION_COPY, sczBundlePath); 1095 hr = UserExperienceOnCacheAcquireBegin(pContext->pUX, NULL, NULL, &sczBundlePath, &sczBundleDownloadUrl, NULL, &cacheOperation);
1100 ExitOnRootFailure(hr, "BA aborted cache acquire begin."); 1096 ExitOnRootFailure(hr, "BA aborted cache acquire begin.");
1101 1097
1102 hr = CopyPayload(&progress, pContext->hSourceEngineFile, sczBundlePath, wzUnverifiedPath); 1098 hr = CopyPayload(&progress, pContext->hSourceEngineFile, sczBundlePath, wzUnverifiedPath);
@@ -1142,6 +1138,7 @@ static HRESULT LayoutBundle(
1142 1138
1143LExit: 1139LExit:
1144 ReleaseStr(sczDestinationPath); 1140 ReleaseStr(sczDestinationPath);
1141 ReleaseStr(sczBundleDownloadUrl);
1145 ReleaseStr(sczBundlePath); 1142 ReleaseStr(sczBundlePath);
1146 1143
1147 return hr; 1144 return hr;
@@ -1160,11 +1157,16 @@ static HRESULT AcquireContainerOrPayload(
1160 int nEquivalentPaths = 0; 1157 int nEquivalentPaths = 0;
1161 LPCWSTR wzPackageOrContainerId = pContainer ? pContainer->sczId : pPackage ? pPackage->sczId : NULL; 1158 LPCWSTR wzPackageOrContainerId = pContainer ? pContainer->sczId : pPackage ? pPackage->sczId : NULL;
1162 LPCWSTR wzPayloadId = pPayload ? pPayload->sczKey : NULL; 1159 LPCWSTR wzPayloadId = pPayload ? pPayload->sczKey : NULL;
1160 LPCWSTR wzPayloadContainerId = pPayload && pPayload->pContainer ? pPayload->pContainer->sczId : NULL;
1163 LPCWSTR wzDestinationPath = pContainer ? pContainer->sczUnverifiedPath: pPayload->sczUnverifiedPath; 1161 LPCWSTR wzDestinationPath = pContainer ? pContainer->sczUnverifiedPath: pPayload->sczUnverifiedPath;
1164 LPCWSTR wzRelativePath = pContainer ? pContainer->sczFilePath : pPayload->sczFilePath; 1162 LPCWSTR wzRelativePath = pContainer ? pContainer->sczFilePath : pPayload->sczFilePath;
1165 LPWSTR sczSourceFullPath = NULL;
1166 BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT progress = { }; 1163 BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT progress = { };
1164 BOOL fBeginCalled = FALSE;
1167 BOOL fRetry = FALSE; 1165 BOOL fRetry = FALSE;
1166 DWORD dwChosenSearchPath = 0;
1167 BOOTSTRAPPER_CACHE_OPERATION cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_NONE;
1168 LPWSTR* pwzDownloadUrl = pContainer ? &pContainer->downloadSource.sczUrl : &pPayload->downloadSource.sczUrl;
1169 LPWSTR* pwzSourcePath = pContainer ? &pContainer->sczSourcePath : &pPayload->sczSourcePath;
1168 1170
1169 progress.pCacheContext = pContext; 1171 progress.pCacheContext = pContext;
1170 progress.pContainer = pContainer; 1172 progress.pContainer = pContainer;
@@ -1173,86 +1175,148 @@ static HRESULT AcquireContainerOrPayload(
1173 1175
1174 do 1176 do
1175 { 1177 {
1176 LPCWSTR wzDownloadUrl = pContainer ? pContainer->downloadSource.sczUrl : pPayload->downloadSource.sczUrl;
1177 LPCWSTR wzSourcePath = pContainer ? pContainer->sczSourcePath : pPayload->sczSourcePath;
1178
1179 BOOL fFoundLocal = FALSE; 1178 BOOL fFoundLocal = FALSE;
1180 BOOL fCopy = FALSE;
1181 BOOL fDownload = FALSE;
1182 1179
1180 pContext->cSearchPaths = 0;
1181 dwChosenSearchPath = 0;
1183 fRetry = FALSE; 1182 fRetry = FALSE;
1184 progress.fCancel = FALSE; 1183 progress.fCancel = FALSE;
1184 fBeginCalled = TRUE;
1185 1185
1186 hr = CacheFindLocalSource(wzSourcePath, wzDestinationPath, pContext->pVariables, &fFoundLocal, &sczSourceFullPath); 1186 hr = UserExperienceOnCacheAcquireBegin(pContext->pUX, wzPackageOrContainerId, wzPayloadId, pwzSourcePath, pwzDownloadUrl, wzPayloadContainerId, &cacheOperation);
1187 ExitOnFailure(hr, "Failed to search local source."); 1187 ExitOnRootFailure(hr, "BA aborted cache acquire begin.");
1188
1189 if (fFoundLocal) // the file exists locally, so copy it.
1190 {
1191 // If the source path and destination path are different, do the copy (otherwise there's no point).
1192 hr = PathCompare(sczSourceFullPath, wzDestinationPath, &nEquivalentPaths);
1193 ExitOnFailure(hr, "Failed to determine if payload source path was equivalent to the destination path.");
1194 1188
1195 fCopy = (CSTR_EQUAL != nEquivalentPaths); 1189 // Skip the Resolving event and probing local paths if the BA already knew it wanted to download or extract.
1196 } 1190 if (BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD != cacheOperation &&
1197 else // can't find the file locally, so prompt for source. 1191 BOOTSTRAPPER_CACHE_OPERATION_EXTRACT != cacheOperation)
1198 { 1192 {
1199 DWORD dwLogId = pContainer ? (wzPayloadId ? MSG_PROMPT_CONTAINER_PAYLOAD_SOURCE : MSG_PROMPT_CONTAINER_SOURCE) : pPackage ? MSG_PROMPT_PACKAGE_PAYLOAD_SOURCE : MSG_PROMPT_BUNDLE_PAYLOAD_SOURCE; 1193 hr = CacheGetLocalSourcePaths(wzRelativePath, *pwzSourcePath, wzDestinationPath, pContext->wzLayoutDirectory, pContext->pVariables, &pContext->rgSearchPaths, &pContext->cSearchPaths);
1200 LogId(REPORT_STANDARD, dwLogId, wzPackageOrContainerId ? wzPackageOrContainerId : L"", wzPayloadId ? wzPayloadId : L"", sczSourceFullPath); 1194 ExitOnFailure(hr, "Failed to search local source.");
1201 1195
1202 hr = PromptForSource(pContext->pUX, wzPackageOrContainerId, wzPayloadId, sczSourceFullPath, wzDownloadUrl, &fRetry, &fDownload); 1196 for (DWORD i = 0; i < pContext->cSearchPaths; ++i)
1197 {
1198 // If the file exists locally, choose it.
1199 if (FileExistsEx(pContext->rgSearchPaths[i], NULL))
1200 {
1201 dwChosenSearchPath = i;
1202
1203 fFoundLocal = TRUE;
1204 break;
1205 }
1206 }
1203 1207
1204 // If the BA requested download then ensure a download url is available (it may have been set 1208 if (BOOTSTRAPPER_CACHE_OPERATION_COPY == cacheOperation)
1205 // during PromptForSource so we need to check again). 1209 {
1206 if (fDownload) 1210 if (!fFoundLocal)
1211 {
1212 cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_NONE;
1213 }
1214 }
1215 else
1207 { 1216 {
1208 wzDownloadUrl = pContainer ? pContainer->downloadSource.sczUrl : pPayload->downloadSource.sczUrl; 1217 if (fFoundLocal) // the file exists locally, so copy it.
1209 if (!wzDownloadUrl || !*wzDownloadUrl) 1218 {
1219 cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_COPY;
1220 }
1221 else if (wzPayloadContainerId)
1222 {
1223 cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_EXTRACT;
1224 }
1225 else if (*pwzDownloadUrl && **pwzDownloadUrl)
1210 { 1226 {
1211 hr = E_INVALIDARG; 1227 cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD;
1212 } 1228 }
1213 } 1229 }
1214 1230
1215 // Log the error 1231 // Let the BA have a chance to override the action, but their chance to change the source is during begin or complete.
1216 LogExitOnFailure(hr, MSG_PAYLOAD_FILE_NOT_PRESENT, "Failed while prompting for source (original path '%ls').", sczSourceFullPath); 1232 hr = UserExperienceOnCacheAcquireResolving(pContext->pUX, wzPackageOrContainerId, wzPayloadId, pContext->rgSearchPaths, pContext->cSearchPaths, fFoundLocal, &dwChosenSearchPath, *pwzDownloadUrl, wzPayloadContainerId, &cacheOperation);
1233 ExitOnRootFailure(hr, "BA aborted cache acquire resolving.");
1217 } 1234 }
1218 1235
1219 if (fCopy) 1236 switch (cacheOperation)
1220 { 1237 {
1221 hr = UserExperienceOnCacheAcquireBegin(pContext->pUX, wzPackageOrContainerId, wzPayloadId, BOOTSTRAPPER_CACHE_OPERATION_COPY, sczSourceFullPath); 1238 case BOOTSTRAPPER_CACHE_OPERATION_COPY:
1222 ExitOnRootFailure(hr, "BA aborted cache acquire begin."); 1239 // If the source path and destination path are different, do the copy (otherwise there's no point).
1240 hr = PathCompare(pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath, &nEquivalentPaths);
1241 ExitOnFailure(hr, "Failed to determine if payload paths were equivalent, source: %ls, destination: %ls.", pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath);
1223 1242
1224 hr = CopyPayload(&progress, INVALID_HANDLE_VALUE, sczSourceFullPath, wzDestinationPath); 1243 if (CSTR_EQUAL != nEquivalentPaths)
1225 // Error handling happens after sending complete message to BA. 1244 {
1245 hr = CopyPayload(&progress, INVALID_HANDLE_VALUE, pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath);
1246 // Error handling happens after sending complete message to BA.
1247
1248 // TODO: wait for verification?
1249 // We successfully copied from a source location, set that as the last used source.
1250 if (SUCCEEDED(hr))
1251 {
1252 CacheSetLastUsedSource(pContext->pVariables, pContext->rgSearchPaths[dwChosenSearchPath], wzRelativePath);
1253 }
1254 }
1226 1255
1227 // We successfully copied from a source location, set that as the last used source. 1256 fBeginCalled = FALSE;
1228 if (SUCCEEDED(hr)) 1257 UserExperienceOnCacheAcquireComplete(pContext->pUX, wzPackageOrContainerId, wzPayloadId, hr, &fRetry);
1258 if (fRetry)
1229 { 1259 {
1230 CacheSetLastUsedSource(pContext->pVariables, sczSourceFullPath, wzRelativePath); 1260 hr = S_OK;
1231 } 1261 }
1232 }
1233 else if (fDownload)
1234 {
1235 hr = UserExperienceOnCacheAcquireBegin(pContext->pUX, wzPackageOrContainerId, wzPayloadId, BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD, wzDownloadUrl);
1236 ExitOnRootFailure(hr, "BA aborted cache download payload begin.");
1237 1262
1263 ExitOnFailure(hr, "Failed to acquire payload from: '%ls' to working path: '%ls'", pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath);
1264
1265 break;
1266 case BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD:
1238 hr = DownloadPayload(&progress, wzDestinationPath); 1267 hr = DownloadPayload(&progress, wzDestinationPath);
1239 // Error handling happens after sending complete message to BA. 1268 // Error handling happens after sending complete message to BA.
1240 }
1241 1269
1242 if (fCopy || fDownload) 1270 fBeginCalled = FALSE;
1243 { 1271 UserExperienceOnCacheAcquireComplete(pContext->pUX, wzPackageOrContainerId, wzPayloadId, hr, &fRetry);
1272 if (fRetry)
1273 {
1274 hr = S_OK;
1275 }
1276
1277 ExitOnFailure(hr, "Failed to acquire payload from: '%ls' to working path: '%ls'", *pwzDownloadUrl, wzDestinationPath);
1278
1279 break;
1280 case BOOTSTRAPPER_CACHE_OPERATION_EXTRACT:
1281 Assert(pPayload->pContainer);
1282
1283 hr = ApplyExtractContainer(pContext, pPayload->pContainer);
1284 // Error handling happens after sending complete message to BA.
1285
1286 fBeginCalled = FALSE;
1287 UserExperienceOnCacheAcquireComplete(pContext->pUX, wzPackageOrContainerId, wzPayloadId, hr, &fRetry);
1288 if (fRetry)
1289 {
1290 hr = S_OK;
1291 }
1292
1293 ExitOnFailure(hr, "Failed to extract container for payload: %ls", wzPayloadId);
1294
1295 break;
1296 case BOOTSTRAPPER_CACHE_OPERATION_NONE:
1297 hr = E_FILENOTFOUND;
1298 LogErrorId(hr, MSG_RESOLVE_SOURCE_FAILED, wzPayloadId, pPackage ? pPackage->sczId : NULL, pContainer ? pContainer->sczId : NULL);
1299
1300 fBeginCalled = FALSE;
1244 UserExperienceOnCacheAcquireComplete(pContext->pUX, wzPackageOrContainerId, wzPayloadId, hr, &fRetry); 1301 UserExperienceOnCacheAcquireComplete(pContext->pUX, wzPackageOrContainerId, wzPayloadId, hr, &fRetry);
1245 if (fRetry) 1302 if (fRetry)
1246 { 1303 {
1247 hr = S_OK; 1304 hr = S_OK;
1248 } 1305 }
1306
1307 ExitOnFailure(hr, "Failed to resolve source, payload: %ls, package: %ls, container: %ls", wzPayloadId, pPackage ? pPackage->sczId : NULL, pContainer ? pContainer->sczId : NULL);
1308
1309 break;
1249 } 1310 }
1250 ExitOnFailure(hr, "Failed to acquire payload from: '%ls' to working path: '%ls'", fCopy ? sczSourceFullPath : wzDownloadUrl, wzDestinationPath);
1251 } while (fRetry); 1311 } while (fRetry);
1252 ExitOnFailure(hr, "Failed to find external payload to cache.");
1253 1312
1254LExit: 1313LExit:
1255 ReleaseStr(sczSourceFullPath); 1314 if (fBeginCalled)
1315 {
1316 UserExperienceOnCacheAcquireComplete(pContext->pUX, wzPackageOrContainerId, wzPayloadId, hr, &fRetry);
1317 }
1318
1319 pContext->cSearchPathsMax = max(pContext->cSearchPaths, pContext->cSearchPathsMax);
1256 1320
1257 return hr; 1321 return hr;
1258} 1322}
@@ -1355,48 +1419,6 @@ LExit:
1355 return hr; 1419 return hr;
1356} 1420}
1357 1421
1358static HRESULT PromptForSource(
1359 __in BURN_USER_EXPERIENCE* pUX,
1360 __in_z LPCWSTR wzPackageOrContainerId,
1361 __in_z_opt LPCWSTR wzPayloadId,
1362 __in_z LPCWSTR wzLocalSource,
1363 __in_z_opt LPCWSTR wzDownloadSource,
1364 __inout BOOL* pfRetry,
1365 __inout BOOL* pfDownload
1366 )
1367{
1368 HRESULT hr = S_OK;
1369 BOOTSTRAPPER_RESOLVESOURCE_ACTION action = BOOTSTRAPPER_RESOLVESOURCE_ACTION_NONE;
1370
1371 hr = UserExperienceOnResolveSource(pUX, wzPackageOrContainerId, wzPayloadId, wzLocalSource, wzDownloadSource, &action);
1372 if (FAILED(hr))
1373 {
1374 ExitFunction();
1375 }
1376
1377 switch (action)
1378 {
1379 case BOOTSTRAPPER_RESOLVESOURCE_ACTION_NONE:
1380 hr = E_FILENOTFOUND;
1381 break;
1382
1383 case BOOTSTRAPPER_RESOLVESOURCE_ACTION_RETRY:
1384 *pfRetry = TRUE;
1385 break;
1386
1387 case BOOTSTRAPPER_RESOLVESOURCE_ACTION_DOWNLOAD:
1388 *pfDownload = TRUE;
1389 break;
1390
1391 default:
1392 hr = E_INVALIDARG;
1393 break;
1394 }
1395
1396LExit:
1397 return hr;
1398}
1399
1400static HRESULT CopyPayload( 1422static HRESULT CopyPayload(
1401 __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress, 1423 __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress,
1402 __in HANDLE hSourceFile, 1424 __in HANDLE hSourceFile,
diff --git a/src/engine/cache.cpp b/src/engine/cache.cpp
index 6ac313e0..84b7f131 100644
--- a/src/engine/cache.cpp
+++ b/src/engine/cache.cpp
@@ -11,7 +11,7 @@ static const DWORD FILE_OPERATION_RETRY_WAIT = 2000;
11 11
12static BOOL vfInitializedCache = FALSE; 12static BOOL vfInitializedCache = FALSE;
13static BOOL vfRunningFromCache = FALSE; 13static BOOL vfRunningFromCache = FALSE;
14static LPWSTR vsczSourceProcessPath = NULL; 14static LPWSTR vsczSourceProcessFolder = NULL;
15static LPWSTR vsczWorkingFolder = NULL; 15static LPWSTR vsczWorkingFolder = NULL;
16static LPWSTR vsczDefaultUserPackageCache = NULL; 16static LPWSTR vsczDefaultUserPackageCache = NULL;
17static LPWSTR vsczDefaultMachinePackageCache = NULL; 17static LPWSTR vsczDefaultMachinePackageCache = NULL;
@@ -140,8 +140,8 @@ extern "C" HRESULT CacheInitialize(
140 wzSourceProcessPath = sczCurrentPath; 140 wzSourceProcessPath = sczCurrentPath;
141 } 141 }
142 142
143 hr = StrAllocString(&vsczSourceProcessPath, wzSourceProcessPath, 0); 143 hr = PathGetDirectory(wzSourceProcessPath, &vsczSourceProcessFolder);
144 ExitOnFailure(hr, "Failed to initialize cache source path."); 144 ExitOnFailure(hr, "Failed to initialize cache source folder.");
145 145
146 // If we're not running from the cache, ensure the original source is set. 146 // If we're not running from the cache, ensure the original source is set.
147 if (!vfRunningFromCache) 147 if (!vfRunningFromCache)
@@ -393,112 +393,166 @@ LExit:
393 return hr; 393 return hr;
394} 394}
395 395
396extern "C" HRESULT CacheFindLocalSource( 396extern "C" HRESULT CacheGetLocalSourcePaths(
397 __in_z LPCWSTR wzRelativePath,
397 __in_z LPCWSTR wzSourcePath, 398 __in_z LPCWSTR wzSourcePath,
398 __in_z LPCWSTR wzDestinationPath, 399 __in_z LPCWSTR wzDestinationPath,
400 __in_z_opt LPCWSTR wzLayoutDirectory,
399 __in BURN_VARIABLES* pVariables, 401 __in BURN_VARIABLES* pVariables,
400 __out BOOL* pfFound, 402 __inout LPWSTR** prgSearchPaths,
401 __out_z LPWSTR* psczSourceFullPath 403 __out DWORD* pcSearchPaths
402 ) 404 )
403{ 405{
404 HRESULT hr = S_OK; 406 HRESULT hr = S_OK;
405 LPWSTR sczSourceProcessFolder = NULL;
406 LPWSTR sczCurrentPath = NULL; 407 LPWSTR sczCurrentPath = NULL;
407 LPWSTR sczLastSourcePath = NULL;
408 LPWSTR sczLastSourceFolder = NULL; 408 LPWSTR sczLastSourceFolder = NULL;
409 LPWSTR sczLayoutPath = NULL; 409 LPWSTR* psczPath = NULL;
410 LPWSTR sczLayoutFolder = NULL; 410 BOOL fPreferSourcePathLocation = FALSE;
411 LPCWSTR rgwzSearchPaths[4] = { }; 411 BOOL fTryLastFolder = FALSE;
412 BOOL fTryRelativePath = FALSE;
413 BOOL fSourceIsAbsolute = FALSE;
412 DWORD cSearchPaths = 0; 414 DWORD cSearchPaths = 0;
413 415
414 // If the source path provided is a full path, obviously that is where we should be looking. 416 AssertSz(vfInitializedCache, "Cache wasn't initialized");
415 if (PathIsAbsolute(wzSourcePath)) 417
418 hr = GetLastUsedSourceFolder(pVariables, &sczLastSourceFolder);
419 fPreferSourcePathLocation = !vfRunningFromCache || FAILED(hr);
420 fTryLastFolder = SUCCEEDED(hr) && sczLastSourceFolder && *sczLastSourceFolder && CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, vsczSourceProcessFolder, -1, sczLastSourceFolder, -1);
421 fTryRelativePath = CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, wzSourcePath, -1, wzRelativePath, -1);
422 fSourceIsAbsolute = PathIsAbsolute(wzSourcePath);
423
424 // If the source path provided is a full path, try that first.
425 if (fSourceIsAbsolute)
416 { 426 {
417 rgwzSearchPaths[0] = wzSourcePath; 427 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS);
418 cSearchPaths = 1; 428 ExitOnFailure(hr, "Failed to ensure size for search paths array.");
429
430 psczPath = *prgSearchPaths + cSearchPaths;
431 ++cSearchPaths;
432
433 hr = StrAllocString(psczPath, wzSourcePath, 0);
434 ExitOnFailure(hr, "Failed to copy absolute source path.");
419 } 435 }
420 else 436
437 // Try the destination path next.
438 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS);
439 ExitOnFailure(hr, "Failed to ensure size for search paths array.");
440
441 psczPath = *prgSearchPaths + cSearchPaths;
442 ++cSearchPaths;
443
444 hr = StrAllocString(psczPath, wzDestinationPath, 0);
445 ExitOnFailure(hr, "Failed to copy absolute source path.");
446
447 if (!fSourceIsAbsolute)
421 { 448 {
422 // Use the destination path first. 449 // Calculate the source path location.
423 rgwzSearchPaths[0] = wzDestinationPath; 450 // In the case where we are in the bundle's package cache and
424 cSearchPaths = 1; 451 // couldn't find a last used source that will be the package cache path
425 452 // which isn't likely to have what we are looking for.
426 // If we're not running from cache or we couldn't get the last source, use 453 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS);
427 // the source path location. In the case where we are in the bundle's 454 ExitOnFailure(hr, "Failed to ensure size for search paths array.");
428 // package cache and couldn't find a last used source we unfortunately will 455
429 // be picking the package cache path which isn't likely to have what we are 456 hr = PathConcat(vsczSourceProcessFolder, wzSourcePath, &sczCurrentPath);
430 // looking for. 457 ExitOnFailure(hr, "Failed to combine source process folder with source.");
431 hr = GetLastUsedSourceFolder(pVariables, &sczLastSourceFolder); 458
432 if (!vfRunningFromCache || FAILED(hr)) 459 // If we're not running from cache or we couldn't get the last source,
460 // try the source path location next.
461 if (fPreferSourcePathLocation)
433 { 462 {
434 hr = PathGetDirectory(vsczSourceProcessPath, &sczSourceProcessFolder); 463 (*prgSearchPaths)[cSearchPaths] = sczCurrentPath;
435 ExitOnFailure(hr, "Failed to get current process directory."); 464 ++cSearchPaths;
465 sczCurrentPath = NULL;
466 }
436 467
437 hr = PathConcat(sczSourceProcessFolder, wzSourcePath, &sczCurrentPath); 468 // If we have a last used source and it is not the source path location,
469 // add the last used source to the search path next.
470 if (fTryLastFolder)
471 {
472 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS);
473 ExitOnFailure(hr, "Failed to ensure size for search paths array.");
474
475 psczPath = *prgSearchPaths + cSearchPaths;
476 ++cSearchPaths;
477
478 hr = PathConcat(sczLastSourceFolder, wzSourcePath, psczPath);
438 ExitOnFailure(hr, "Failed to combine last source with source."); 479 ExitOnFailure(hr, "Failed to combine last source with source.");
480 }
439 481
440 rgwzSearchPaths[cSearchPaths] = sczCurrentPath; 482 if (!fPreferSourcePathLocation)
483 {
484 (*prgSearchPaths)[cSearchPaths] = sczCurrentPath;
441 ++cSearchPaths; 485 ++cSearchPaths;
486 sczCurrentPath = NULL;
442 } 487 }
443 488
444 // If we have a last used source and it does not duplicate the existing search path, 489 // Also consider the layout directory if doing Layout.
445 // add the last used source to the search path second. 490 if (wzLayoutDirectory)
446 if (sczLastSourceFolder && *sczLastSourceFolder)
447 { 491 {
448 hr = PathConcat(sczLastSourceFolder, wzSourcePath, &sczLastSourcePath); 492 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS);
449 ExitOnFailure(hr, "Failed to combine last source with source."); 493 ExitOnFailure(hr, "Failed to ensure size for search paths array.");
450 494
451 if (1 == cSearchPaths || CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, rgwzSearchPaths[1], -1, sczLastSourcePath, -1)) 495 psczPath = *prgSearchPaths + cSearchPaths;
452 { 496 ++cSearchPaths;
453 rgwzSearchPaths[cSearchPaths] = sczLastSourcePath; 497
454 ++cSearchPaths; 498 hr = PathConcat(wzLayoutDirectory, wzSourcePath, psczPath);
455 } 499 ExitOnFailure(hr, "Failed to combine layout source with source.");
456 } 500 }
501 }
457 502
458 // Also consider the layout directory if set on the command line or by the BA. 503 if (fTryRelativePath)
459 hr = VariableGetString(pVariables, BURN_BUNDLE_LAYOUT_DIRECTORY, &sczLayoutFolder); 504 {
460 if (E_NOTFOUND != hr) 505 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS);
506 ExitOnFailure(hr, "Failed to ensure size for search paths array.");
507
508 hr = PathConcat(vsczSourceProcessFolder, wzRelativePath, &sczCurrentPath);
509 ExitOnFailure(hr, "Failed to combine source process folder with relative.");
510
511 if (fPreferSourcePathLocation)
461 { 512 {
462 ExitOnFailure(hr, "Failed to get bundle layout directory property."); 513 (*prgSearchPaths)[cSearchPaths] = sczCurrentPath;
514 ++cSearchPaths;
515 sczCurrentPath = NULL;
516 }
463 517
464 hr = PathConcat(sczLayoutFolder, wzSourcePath, &sczLayoutPath); 518 if (fTryLastFolder)
465 ExitOnFailure(hr, "Failed to combine layout source with source."); 519 {
520 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS);
521 ExitOnFailure(hr, "Failed to ensure size for search paths array.");
466 522
467 rgwzSearchPaths[cSearchPaths] = sczLayoutPath; 523 psczPath = *prgSearchPaths + cSearchPaths;
468 ++cSearchPaths; 524 ++cSearchPaths;
525
526 hr = PathConcat(sczLastSourceFolder, wzRelativePath, psczPath);
527 ExitOnFailure(hr, "Failed to combine last source with relative.");
469 } 528 }
470 }
471 529
472 *pfFound = FALSE; // assume we won't find the file locally. 530 if (!fPreferSourcePathLocation)
531 {
532 (*prgSearchPaths)[cSearchPaths] = sczCurrentPath;
533 ++cSearchPaths;
534 sczCurrentPath = NULL;
535 }
473 536
474 for (DWORD i = 0; i < cSearchPaths; ++i) 537 if (wzLayoutDirectory)
475 {
476 // If the file exists locally, copy its path.
477 if (FileExistsEx(rgwzSearchPaths[i], NULL))
478 { 538 {
479 hr = StrAllocString(psczSourceFullPath, rgwzSearchPaths[i], 0); 539 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS);
480 ExitOnFailure(hr, "Failed to copy source path."); 540 ExitOnFailure(hr, "Failed to ensure size for search paths array.");
481 541
482 *pfFound = TRUE; 542 psczPath = *prgSearchPaths + cSearchPaths;
483 break; 543 ++cSearchPaths;
484 }
485 }
486 544
487 // If nothing was found, return the first thing in our search path as the 545 hr = PathConcat(wzLayoutDirectory, wzSourcePath, psczPath);
488 // best path where we thought we should have found the file. 546 ExitOnFailure(hr, "Failed to combine layout source with relative.");
489 if (!*pfFound) 547 }
490 {
491 hr = StrAllocString(psczSourceFullPath, rgwzSearchPaths[0], 0);
492 ExitOnFailure(hr, "Failed to copy source path.");
493 } 548 }
494 549
495LExit: 550LExit:
496 ReleaseStr(sczCurrentPath); 551 ReleaseStr(sczCurrentPath);
497 ReleaseStr(sczSourceProcessFolder);
498 ReleaseStr(sczLastSourceFolder); 552 ReleaseStr(sczLastSourceFolder);
499 ReleaseStr(sczLastSourcePath); 553
500 ReleaseStr(sczLayoutFolder); 554 AssertSz(cSearchPaths <= BURN_CACHE_MAX_SEARCH_PATHS, "Got more than BURN_CACHE_MAX_SEARCH_PATHS search paths");
501 ReleaseStr(sczLayoutPath); 555 *pcSearchPaths = cSearchPaths;
502 556
503 return hr; 557 return hr;
504} 558}
@@ -1079,7 +1133,7 @@ extern "C" void CacheUninitialize()
1079 ReleaseNullStr(vsczDefaultMachinePackageCache); 1133 ReleaseNullStr(vsczDefaultMachinePackageCache);
1080 ReleaseNullStr(vsczDefaultUserPackageCache); 1134 ReleaseNullStr(vsczDefaultUserPackageCache);
1081 ReleaseNullStr(vsczWorkingFolder); 1135 ReleaseNullStr(vsczWorkingFolder);
1082 ReleaseNullStr(vsczSourceProcessPath); 1136 ReleaseNullStr(vsczSourceProcessFolder);
1083 1137
1084 vfRunningFromCache = FALSE; 1138 vfRunningFromCache = FALSE;
1085 vfInitializedCache = FALSE; 1139 vfInitializedCache = FALSE;
@@ -1224,17 +1278,12 @@ static HRESULT GetLastUsedSourceFolder(
1224 ) 1278 )
1225{ 1279{
1226 HRESULT hr = S_OK; 1280 HRESULT hr = S_OK;
1227 LPWSTR sczOriginalSource = NULL;
1228 1281
1229 hr = VariableGetString(pVariables, BURN_BUNDLE_LAST_USED_SOURCE, psczLastSource); 1282 hr = VariableGetString(pVariables, BURN_BUNDLE_LAST_USED_SOURCE, psczLastSource);
1230 if (E_NOTFOUND == hr) 1283 if (E_NOTFOUND == hr)
1231 { 1284 {
1232 // Try the original source folder. 1285 // Try the original source folder.
1233 hr = VariableGetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE, &sczOriginalSource); 1286 hr = VariableGetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER, psczLastSource);
1234 if (SUCCEEDED(hr))
1235 {
1236 hr = PathGetDirectory(sczOriginalSource, psczLastSource);
1237 }
1238 } 1287 }
1239 1288
1240 return hr; 1289 return hr;
diff --git a/src/engine/cache.h b/src/engine/cache.h
index acc7acb7..eb964f58 100644
--- a/src/engine/cache.h
+++ b/src/engine/cache.h
@@ -1,6 +1,7 @@
1#pragma once 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. 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#define BURN_CACHE_MAX_SEARCH_PATHS 7
4 5
5#ifdef __cplusplus 6#ifdef __cplusplus
6extern "C" { 7extern "C" {
@@ -52,12 +53,14 @@ HRESULT CacheGetResumePath(
52 __in_z LPCWSTR wzPayloadWorkingPath, 53 __in_z LPCWSTR wzPayloadWorkingPath,
53 __deref_out_z LPWSTR* psczResumePath 54 __deref_out_z LPWSTR* psczResumePath
54 ); 55 );
55HRESULT CacheFindLocalSource( 56HRESULT CacheGetLocalSourcePaths(
57 __in_z LPCWSTR wzRelativePath,
56 __in_z LPCWSTR wzSourcePath, 58 __in_z LPCWSTR wzSourcePath,
57 __in_z LPCWSTR wzDestinationPath, 59 __in_z LPCWSTR wzDestinationPath,
60 __in_z_opt LPCWSTR wzLayoutDirectory,
58 __in BURN_VARIABLES* pVariables, 61 __in BURN_VARIABLES* pVariables,
59 __out BOOL* pfFound, 62 __inout LPWSTR** prgSearchPaths,
60 __out_z LPWSTR* psczSourceFullPath 63 __out DWORD* pcSearchPaths
61 ); 64 );
62HRESULT CacheSetLastUsedSource( 65HRESULT CacheSetLastUsedSource(
63 __in BURN_VARIABLES* pVariables, 66 __in BURN_VARIABLES* pVariables,
diff --git a/src/engine/engine.mc b/src/engine/engine.mc
index c8b46806..f03bc1ea 100644
--- a/src/engine/engine.mc
+++ b/src/engine/engine.mc
@@ -151,9 +151,9 @@ Bundle global condition check didn't succeed - aborting without loading applicat
151 151
152MessageId=54 152MessageId=54
153Severity=Error 153Severity=Error
154SymbolicName=MSG_PAYLOAD_FILE_NOT_PRESENT 154SymbolicName=MSG_RESOLVE_SOURCE_FAILED
155Language=English 155Language=English
156Failed to resolve source for file: %2!ls!, error: %1!ls!. 156Failed to resolve source for payload: %2!ls!, package: %3!ls!, container: %4!ls!, error: %1!ls!.
157. 157.
158 158
159MessageId=55 159MessageId=55
@@ -724,34 +724,6 @@ Language=English
724Acquiring package: %1!ls!, payload: %2!ls!, %3!hs! from: %4!ls! 724Acquiring package: %1!ls!, payload: %2!ls!, %3!hs! from: %4!ls!
725. 725.
726 726
727MessageId=340
728Severity=Warning
729SymbolicName=MSG_PROMPT_BUNDLE_PAYLOAD_SOURCE
730Language=English
731Prompt for source of bundle payload: %2!ls!, path: %3!ls!
732.
733
734MessageId=341
735Severity=Warning
736SymbolicName=MSG_PROMPT_CONTAINER_SOURCE
737Language=English
738Prompt for source of container: %1!ls!, path: %3!ls!
739.
740
741MessageId=342
742Severity=Warning
743SymbolicName=MSG_PROMPT_CONTAINER_PAYLOAD_SOURCE
744Language=English
745Prompt for source of container: %1!ls!, payload: %2!ls!, path: %3!ls!
746.
747
748MessageId=343
749Severity=Warning
750SymbolicName=MSG_PROMPT_PACKAGE_PAYLOAD_SOURCE
751Language=English
752Prompt for source of package: %1!ls!, payload: %2!ls!, path: %3!ls!
753.
754
755MessageId=348 727MessageId=348
756Severity=Warning 728Severity=Warning
757SymbolicName=MSG_APPLY_RETRYING_PACKAGE 729SymbolicName=MSG_APPLY_RETRYING_PACKAGE
diff --git a/src/engine/externalengine.cpp b/src/engine/externalengine.cpp
index 7e9bb25c..d6c44736 100644
--- a/src/engine/externalengine.cpp
+++ b/src/engine/externalengine.cpp
@@ -269,8 +269,7 @@ HRESULT ExternalEngineSetUpdate(
269 ) 269 )
270{ 270{
271 HRESULT hr = S_OK; 271 HRESULT hr = S_OK;
272 LPCWSTR sczId = NULL; 272 LPWSTR sczFilePath = NULL;
273 LPWSTR sczLocalSource = NULL;
274 LPWSTR sczCommandline = NULL; 273 LPWSTR sczCommandline = NULL;
275 UUID guid = { }; 274 UUID guid = { };
276 WCHAR wzGuid[39]; 275 WCHAR wzGuid[39];
@@ -296,38 +295,30 @@ HRESULT ExternalEngineSetUpdate(
296 { 295 {
297 UpdateUninitialize(&pEngineState->update); 296 UpdateUninitialize(&pEngineState->update);
298 297
299 if (!wzLocalSource || !*wzLocalSource)
300 {
301 hr = StrAllocFormatted(&sczLocalSource, L"update\\%ls", pEngineState->registration.sczExecutableName);
302 ExitOnFailure(hr, "Failed to default local update source");
303 }
304
305 hr = CoreRecreateCommandLine(&sczCommandline, BOOTSTRAPPER_ACTION_INSTALL, pEngineState->command.display, pEngineState->command.restart, BOOTSTRAPPER_RELATION_NONE, FALSE, pEngineState->registration.sczActiveParent, pEngineState->registration.sczAncestors, NULL, pEngineState->command.wzCommandLine); 298 hr = CoreRecreateCommandLine(&sczCommandline, BOOTSTRAPPER_ACTION_INSTALL, pEngineState->command.display, pEngineState->command.restart, BOOTSTRAPPER_RELATION_NONE, FALSE, pEngineState->registration.sczActiveParent, pEngineState->registration.sczAncestors, NULL, pEngineState->command.wzCommandLine);
306 ExitOnFailure(hr, "Failed to recreate command-line for update bundle."); 299 ExitOnFailure(hr, "Failed to recreate command-line for update bundle.");
307 300
308 // Per-user bundles would fail to use the downloaded update bundle, as the existing install would already be cached 301 // Bundles would fail to use the downloaded update bundle, as the running bundle would be one of the search paths.
309 // at the registration id's location. Here I am generating a random guid, but in the future it would be nice if the 302 // Here I am generating a random guid, but in the future it would be nice if the feed would provide the ID of the update.
310 // feed would provide the ID of the update. 303 rs = ::UuidCreate(&guid);
311 if (!pEngineState->registration.fPerMachine) 304 hr = HRESULT_FROM_RPC(rs);
305 ExitOnFailure(hr, "Failed to create bundle update guid.");
306
307 if (!::StringFromGUID2(guid, wzGuid, countof(wzGuid)))
312 { 308 {
313 rs = ::UuidCreate(&guid); 309 hr = E_OUTOFMEMORY;
314 hr = HRESULT_FROM_RPC(rs); 310 ExitOnRootFailure(hr, "Failed to convert bundle update guid into string.");
315 ExitOnFailure(hr, "Failed to create bundle update guid."); 311 }
316 312
317 if (!::StringFromGUID2(guid, wzGuid, countof(wzGuid))) 313 hr = StrAllocFormatted(&sczFilePath, L"%ls\\%ls", wzGuid, pEngineState->registration.sczExecutableName);
318 { 314 ExitOnFailure(hr, "Failed to build bundle update file path.");
319 hr = E_OUTOFMEMORY;
320 ExitOnRootFailure(hr, "Failed to convert bundle update guid into string.");
321 }
322 315
323 sczId = wzGuid; 316 if (!wzLocalSource || !*wzLocalSource)
324 }
325 else
326 { 317 {
327 sczId = pEngineState->registration.sczId; 318 wzLocalSource = sczFilePath;
328 } 319 }
329 320
330 hr = PseudoBundleInitialize(FILEMAKEVERSION(rmj, rmm, rup, rpr), &pEngineState->update.package, FALSE, sczId, BOOTSTRAPPER_RELATION_UPDATE, BOOTSTRAPPER_PACKAGE_STATE_ABSENT, FALSE, pEngineState->registration.sczExecutableName, sczLocalSource ? sczLocalSource : wzLocalSource, wzDownloadSource, qwSize, TRUE, sczCommandline, NULL, NULL, NULL, rgbHash, cbHash); 321 hr = PseudoBundleInitialize(FILEMAKEVERSION(rmj, rmm, rup, rpr), &pEngineState->update.package, FALSE, pEngineState->registration.sczId, BOOTSTRAPPER_RELATION_UPDATE, BOOTSTRAPPER_PACKAGE_STATE_ABSENT, FALSE, sczFilePath, wzLocalSource, wzDownloadSource, qwSize, TRUE, sczCommandline, NULL, NULL, NULL, rgbHash, cbHash);
331 ExitOnFailure(hr, "Failed to set update bundle."); 322 ExitOnFailure(hr, "Failed to set update bundle.");
332 323
333 pEngineState->update.fUpdateAvailable = TRUE; 324 pEngineState->update.fUpdateAvailable = TRUE;
@@ -337,7 +328,7 @@ LExit:
337 ::LeaveCriticalSection(&pEngineState->userExperience.csEngineActive); 328 ::LeaveCriticalSection(&pEngineState->userExperience.csEngineActive);
338 329
339 ReleaseStr(sczCommandline); 330 ReleaseStr(sczCommandline);
340 ReleaseStr(sczLocalSource); 331 ReleaseStr(sczFilePath);
341 332
342 return hr; 333 return hr;
343} 334}
diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp
index b6bd65dc..f6ae1491 100644
--- a/src/engine/userexperience.cpp
+++ b/src/engine/userexperience.cpp
@@ -395,29 +395,46 @@ EXTERN_C BAAPI UserExperienceOnCacheAcquireBegin(
395 __in BURN_USER_EXPERIENCE* pUserExperience, 395 __in BURN_USER_EXPERIENCE* pUserExperience,
396 __in_z_opt LPCWSTR wzPackageOrContainerId, 396 __in_z_opt LPCWSTR wzPackageOrContainerId,
397 __in_z_opt LPCWSTR wzPayloadId, 397 __in_z_opt LPCWSTR wzPayloadId,
398 __in BOOTSTRAPPER_CACHE_OPERATION operation, 398 __in_z LPWSTR* pwzSource,
399 __in_z LPCWSTR wzSource 399 __in_z LPWSTR* pwzDownloadUrl,
400 __in_z_opt LPCWSTR wzPayloadContainerId,
401 __out BOOTSTRAPPER_CACHE_OPERATION* pCacheOperation
400 ) 402 )
401{ 403{
402 HRESULT hr = S_OK; 404 HRESULT hr = S_OK;
403 BA_ONCACHEACQUIREBEGIN_ARGS args = { }; 405 BA_ONCACHEACQUIREBEGIN_ARGS args = { };
404 BA_ONCACHEACQUIREBEGIN_RESULTS results = { }; 406 BA_ONCACHEACQUIREBEGIN_RESULTS results = { };
407 *pCacheOperation = BOOTSTRAPPER_CACHE_OPERATION_NONE;
405 408
406 args.cbSize = sizeof(args); 409 args.cbSize = sizeof(args);
407 args.wzPackageOrContainerId = wzPackageOrContainerId; 410 args.wzPackageOrContainerId = wzPackageOrContainerId;
408 args.wzPayloadId = wzPayloadId; 411 args.wzPayloadId = wzPayloadId;
409 args.operation = operation; 412 args.wzSource = *pwzSource;
410 args.wzSource = wzSource; 413 args.wzDownloadUrl = *pwzDownloadUrl;
414 args.wzPayloadContainerId = wzPayloadContainerId;
415 args.recommendation = *pCacheOperation;
411 416
412 results.cbSize = sizeof(results); 417 results.cbSize = sizeof(results);
418 results.action = *pCacheOperation;
413 419
414 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIREBEGIN, &args, &results); 420 hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIREBEGIN, &args, &results);
415 ExitOnFailure(hr, "BA OnCacheAcquireBegin failed."); 421 ExitOnFailure(hr, "BA OnCacheAcquireBegin failed.");
416 422
417 if (results.fCancel) 423 if (results.fCancel)
418 { 424 {
419 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); 425 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
420 } 426 }
427 else
428 {
429 // Verify the BA requested an action that is possible.
430 if (BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD == results.action && *pwzDownloadUrl && **pwzDownloadUrl ||
431 BOOTSTRAPPER_CACHE_OPERATION_EXTRACT == results.action && wzPayloadContainerId ||
432 BOOTSTRAPPER_CACHE_OPERATION_COPY == results.action ||
433 BOOTSTRAPPER_CACHE_OPERATION_NONE == results.action)
434 {
435 *pCacheOperation = results.action;
436 }
437 }
421 438
422LExit: 439LExit:
423 return hr; 440 return hr;
@@ -444,7 +461,7 @@ EXTERN_C BAAPI UserExperienceOnCacheAcquireComplete(
444 results.cbSize = sizeof(results); 461 results.cbSize = sizeof(results);
445 results.action = args.recommendation; 462 results.action = args.recommendation;
446 463
447 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIRECOMPLETE, &args, &results); 464 hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIRECOMPLETE, &args, &results);
448 ExitOnFailure(hr, "BA OnCacheAcquireComplete failed."); 465 ExitOnFailure(hr, "BA OnCacheAcquireComplete failed.");
449 466
450 if (FAILED(hrStatus)) 467 if (FAILED(hrStatus))
@@ -490,6 +507,64 @@ LExit:
490 return hr; 507 return hr;
491} 508}
492 509
510EXTERN_C BAAPI UserExperienceOnCacheAcquireResolving(
511 __in BURN_USER_EXPERIENCE* pUserExperience,
512 __in_z_opt LPCWSTR wzPackageOrContainerId,
513 __in_z_opt LPCWSTR wzPayloadId,
514 __in_z LPWSTR* rgSearchPaths,
515 __in DWORD cSearchPaths,
516 __in BOOL fFoundLocal,
517 __in DWORD* pdwChosenSearchPath,
518 __in_z_opt LPCWSTR wzDownloadUrl,
519 __in_z_opt LPCWSTR wzPayloadContainerId,
520 __inout BOOTSTRAPPER_CACHE_OPERATION* pCacheOperation
521 )
522{
523 HRESULT hr = S_OK;
524 BA_ONCACHEACQUIRERESOLVING_ARGS args = { };
525 BA_ONCACHEACQUIRERESOLVING_RESULTS results = { };
526
527 args.cbSize = sizeof(args);
528 args.wzPackageOrContainerId = wzPackageOrContainerId;
529 args.wzPayloadId = wzPayloadId;
530 args.rgSearchPaths = const_cast<LPCWSTR*>(rgSearchPaths);
531 args.cSearchPaths = cSearchPaths;
532 args.fFoundLocal = fFoundLocal;
533 args.dwRecommendedSearchPath = *pdwChosenSearchPath;
534 args.wzDownloadUrl = wzDownloadUrl;
535 args.recommendation = *pCacheOperation;
536
537 results.cbSize = sizeof(results);
538 results.dwChosenSearchPath = *pdwChosenSearchPath;
539 results.action = *pCacheOperation;
540
541 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIRERESOLVING, &args, &results);
542 ExitOnFailure(hr, "BA OnCacheAcquireResolving failed.");
543
544 if (results.fCancel)
545 {
546 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
547 }
548 else
549 {
550 // Verify the BA requested an action that is possible.
551 if (BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD == results.action && wzDownloadUrl && *wzDownloadUrl ||
552 BOOTSTRAPPER_CACHE_OPERATION_EXTRACT == results.action && wzPayloadContainerId ||
553 BOOTSTRAPPER_CACHE_OPERATION_NONE == results.action)
554 {
555 *pCacheOperation = results.action;
556 }
557 else if (BOOTSTRAPPER_CACHE_OPERATION_COPY == results.action && results.dwChosenSearchPath < cSearchPaths)
558 {
559 *pdwChosenSearchPath = results.dwChosenSearchPath;
560 *pCacheOperation = results.action;
561 }
562 }
563
564LExit:
565 return hr;
566}
567
493EXTERN_C BAAPI UserExperienceOnCacheBegin( 568EXTERN_C BAAPI UserExperienceOnCacheBegin(
494 __in BURN_USER_EXPERIENCE* pUserExperience 569 __in BURN_USER_EXPERIENCE* pUserExperience
495 ) 570 )
@@ -1861,45 +1936,6 @@ LExit:
1861 return hr; 1936 return hr;
1862} 1937}
1863 1938
1864EXTERN_C BAAPI UserExperienceOnResolveSource(
1865 __in BURN_USER_EXPERIENCE* pUserExperience,
1866 __in_z LPCWSTR wzPackageOrContainerId,
1867 __in_z_opt LPCWSTR wzPayloadId,
1868 __in_z LPCWSTR wzLocalSource,
1869 __in_z_opt LPCWSTR wzDownloadSource,
1870 __inout BOOTSTRAPPER_RESOLVESOURCE_ACTION* pAction
1871 )
1872{
1873 HRESULT hr = S_OK;
1874 BA_ONRESOLVESOURCE_ARGS args = { };
1875 BA_ONRESOLVESOURCE_RESULTS results = { };
1876
1877 args.cbSize = sizeof(args);
1878 args.wzPackageOrContainerId = wzPackageOrContainerId;
1879 args.wzPayloadId = wzPayloadId;
1880 args.wzLocalSource = wzLocalSource;
1881 args.wzDownloadSource = wzDownloadSource;
1882 args.recommendation = *pAction;
1883
1884 results.cbSize = sizeof(results);
1885 results.action = *pAction;
1886
1887 hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONRESOLVESOURCE, &args, &results);
1888 ExitOnFailure(hr, "BA OnResolveSource failed.");
1889
1890 if (results.fCancel)
1891 {
1892 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1893 }
1894 else
1895 {
1896 *pAction = results.action;
1897 }
1898
1899LExit:
1900 return hr;
1901}
1902
1903EXTERN_C BAAPI UserExperienceOnRollbackMsiTransactionBegin( 1939EXTERN_C BAAPI UserExperienceOnRollbackMsiTransactionBegin(
1904 __in BURN_USER_EXPERIENCE* pUserExperience, 1940 __in BURN_USER_EXPERIENCE* pUserExperience,
1905 __in LPCWSTR wzTransactionId 1941 __in LPCWSTR wzTransactionId
diff --git a/src/engine/userexperience.h b/src/engine/userexperience.h
index a1fb67a0..dd1fb086 100644
--- a/src/engine/userexperience.h
+++ b/src/engine/userexperience.h
@@ -126,8 +126,10 @@ BAAPI UserExperienceOnCacheAcquireBegin(
126 __in BURN_USER_EXPERIENCE* pUserExperience, 126 __in BURN_USER_EXPERIENCE* pUserExperience,
127 __in_z_opt LPCWSTR wzPackageOrContainerId, 127 __in_z_opt LPCWSTR wzPackageOrContainerId,
128 __in_z_opt LPCWSTR wzPayloadId, 128 __in_z_opt LPCWSTR wzPayloadId,
129 __in BOOTSTRAPPER_CACHE_OPERATION operation, 129 __in_z LPWSTR* pwzSource,
130 __in_z LPCWSTR wzSource 130 __in_z LPWSTR* pwzDownloadUrl,
131 __in_z_opt LPCWSTR wzPayloadContainerId,
132 __out BOOTSTRAPPER_CACHE_OPERATION* pCacheOperation
131 ); 133 );
132BAAPI UserExperienceOnCacheAcquireComplete( 134BAAPI UserExperienceOnCacheAcquireComplete(
133 __in BURN_USER_EXPERIENCE* pUserExperience, 135 __in BURN_USER_EXPERIENCE* pUserExperience,
@@ -144,6 +146,18 @@ BAAPI UserExperienceOnCacheAcquireProgress(
144 __in DWORD64 dw64Total, 146 __in DWORD64 dw64Total,
145 __in DWORD dwOverallPercentage 147 __in DWORD dwOverallPercentage
146 ); 148 );
149BAAPI UserExperienceOnCacheAcquireResolving(
150 __in BURN_USER_EXPERIENCE* pUserExperience,
151 __in_z_opt LPCWSTR wzPackageOrContainerId,
152 __in_z_opt LPCWSTR wzPayloadId,
153 __in_z LPWSTR* rgSearchPaths,
154 __in DWORD cSearchPaths,
155 __in BOOL fFoundLocal,
156 __in DWORD* pdwChosenSearchPath,
157 __in_z_opt LPCWSTR wzDownloadUrl,
158 __in_z_opt LPCWSTR wzPayloadContainerId,
159 __inout BOOTSTRAPPER_CACHE_OPERATION* pCacheOperation
160 );
147BAAPI UserExperienceOnCacheBegin( 161BAAPI UserExperienceOnCacheBegin(
148 __in BURN_USER_EXPERIENCE* pUserExperience 162 __in BURN_USER_EXPERIENCE* pUserExperience
149 ); 163 );
@@ -426,14 +440,6 @@ BAAPI UserExperienceOnRegisterComplete(
426 __in BURN_USER_EXPERIENCE* pUserExperience, 440 __in BURN_USER_EXPERIENCE* pUserExperience,
427 __in HRESULT hrStatus 441 __in HRESULT hrStatus
428 ); 442 );
429BAAPI UserExperienceOnResolveSource(
430 __in BURN_USER_EXPERIENCE* pUserExperience,
431 __in_z LPCWSTR wzPackageOrContainerId,
432 __in_z_opt LPCWSTR wzPayloadId,
433 __in_z LPCWSTR wzLocalSource,
434 __in_z_opt LPCWSTR wzDownloadSource,
435 __inout BOOTSTRAPPER_RESOLVESOURCE_ACTION* pAction
436 );
437BAAPI UserExperienceOnRollbackMsiTransactionBegin( 443BAAPI UserExperienceOnRollbackMsiTransactionBegin(
438 __in BURN_USER_EXPERIENCE* pUserExperience, 444 __in BURN_USER_EXPERIENCE* pUserExperience,
439 __in LPCWSTR wzTransactionId 445 __in LPCWSTR wzTransactionId