main
cpp 1,032 lines 40 KB
Raw
1 // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 #include "precomp.h"
4
5 #define NGEN_DEBUG 0x0001
6 #define NGEN_NODEP 0x0002
7 #define NGEN_PROFILE 0x0004
8 #define NGEN_32BIT 0x0008
9 #define NGEN_64BIT 0x0010
10
11 #define NGEN_TIMEOUT 60000 // 60 seconds
12
13 // If you change one of these strings, be sure to change the appropriate EmptyFormattedLength variable right below
14 LPCWSTR vpwzUnformattedQuotedFile = L"\"[#%s]\"";
15 LPCWSTR vpwzUnformattedQuotedDirectory = L"\"[%s]\\\"";
16
17 // These represent the length of the above strings in the case that the property resolves to an empty string
18 const DWORD EMPTY_FORMATTED_LENGTH_QUOTED_FILE = 2;
19 const DWORD EMPTY_FORMATTED_LENGTH_QUOTED_DIRECTORY = 3;
20
21 LPCWSTR vcsFileId =
22 L"SELECT `File` FROM `File` WHERE `File`=?";
23 enum eFileId { fiFile = 1 };
24
25 LPCWSTR vcsNgenQuery =
26 L"SELECT `Wix4NetFxNativeImage`.`File_`, `Wix4NetFxNativeImage`.`NetFxNativeImage`, `Wix4NetFxNativeImage`.`Priority`, `Wix4NetFxNativeImage`.`Attributes`, `Wix4NetFxNativeImage`.`File_Application`, `Wix4NetFxNativeImage`.`Directory_ApplicationBase`, `File`.`Component_` "
27 L"FROM `Wix4NetFxNativeImage`, `File` WHERE `File`.`File`=`Wix4NetFxNativeImage`.`File_`";
28 enum eNgenQuery { ngqFile = 1, ngqId, ngqPriority, ngqAttributes, ngqFileApp, ngqDirAppBase, ngqComponent };
29
30 LPCWSTR vcsNgenGac =
31 L"SELECT `MsiAssembly`.`File_Application` "
32 L"FROM `File`, `MsiAssembly` WHERE `File`.`Component_`=`MsiAssembly`.`Component_` AND `File`.`File`=?";
33 enum eNgenGac { nggApplication = 1 };
34
35 LPCWSTR vcsNgenStrongName =
36 L"SELECT `Name`,`Value` FROM `MsiAssemblyName` WHERE `Component_`=?";
37 enum eNgenStrongName { ngsnName = 1, ngsnValue };
38
39 LPCWSTR vscDotNetCompatibilityCheckQuery =
40 L"SELECT `Platform`, `RuntimeType`, `Version`, `RollForward`, `Property` FROM `Wix4NetFxDotNetCheck`";
41 enum eDotNetCompatibilityCheckQuery { platform = 1, runtimeType, version, rollForward, property };
42
43 // Searches subdirectories of the given path for the highest version of ngen.exe available
44 static HRESULT GetNgenVersion(
45 __in LPWSTR pwzParentPath,
46 __out LPWSTR* ppwzVersion
47 )
48 {
49 Assert(pwzParentPath);
50
51 HRESULT hr = S_OK;
52 DWORD dwError = 0;
53 DWORD dwNgenFileFlags = 0;
54
55 LPWSTR pwzVersionSearch = NULL;
56 LPWSTR pwzNgen = NULL;
57 LPWSTR pwzTemp = NULL;
58 LPWSTR pwzTempVersion = NULL;
59 DWORD dwMaxMajorVersion = 0; // This stores the highest major version we've seen so far
60 DWORD dwMaxMinorVersion = 0; // This stores the minor version of the highest major version we've seen so far
61 DWORD dwMajorVersion = 0; // This stores the major version of the directory we're currently considering
62 DWORD dwMinorVersion = 0; // This stores the minor version of the directory we're currently considering
63 BOOL fFound = TRUE;
64 WIN32_FIND_DATAW wfdVersionDirectories;
65 HANDLE hFind = INVALID_HANDLE_VALUE;
66
67 hr = StrAllocFormatted(&pwzVersionSearch, L"%s*", pwzParentPath);
68 ExitOnFailure(hr, "failed to create outer directory search string from string %ls", pwzParentPath);
69 hFind = FindFirstFileW(pwzVersionSearch, &wfdVersionDirectories);
70 if (hFind == INVALID_HANDLE_VALUE)
71 {
72 ExitWithLastError(hr, "failed to call FindFirstFileW with string %ls", pwzVersionSearch);
73 }
74
75 while (fFound)
76 {
77 pwzTempVersion = (LPWSTR)&(wfdVersionDirectories.cFileName);
78
79 // Explicitly exclude v1.1.4322, which isn't backwards compatible and is not supported
80 if (wfdVersionDirectories.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
81 {
82 if (0 != lstrcmpW(L"v1.1.4322", pwzTempVersion))
83 {
84 // A potential candidate directory was found to run ngen from - let's make sure ngen actually exists here
85 hr = StrAllocFormatted(&pwzNgen, L"%s%s\\ngen.exe", pwzParentPath, pwzTempVersion);
86 ExitOnFailure(hr, "failed to create inner ngen search string with strings %ls and %ls", pwzParentPath, pwzTempVersion);
87
88 // If Ngen.exe does exist as a file here, then let's check the file version
89 if (FileExistsEx(pwzNgen, &dwNgenFileFlags) && (0 == (dwNgenFileFlags & FILE_ATTRIBUTE_DIRECTORY)))
90 {
91 hr = FileVersion(pwzNgen, &dwMajorVersion, &dwMinorVersion);
92
93 if (FAILED(hr))
94 {
95 WcaLog(LOGMSG_VERBOSE, "Failed to get version of %ls - continuing", pwzNgen);
96 }
97 else if (dwMajorVersion > dwMaxMajorVersion || (dwMajorVersion == dwMaxMajorVersion && dwMinorVersion > dwMaxMinorVersion))
98 {
99 // If the version we found is the highest we've seen so far in this search, it will be our new best-so-far candidate
100 hr = StrAllocString(ppwzVersion, pwzTempVersion, 0);
101 ExitOnFailure(hr, "failed to copy temp version string %ls to version string", pwzTempVersion);
102 // Add one for the backslash after the directory name
103 WcaLog(LOGMSG_VERBOSE, "Found highest-so-far version of ngen.exe (in directory %ls, version %u.%u.%u.%u)", *ppwzVersion, (DWORD)HIWORD(dwMajorVersion), (DWORD)LOWORD(dwMajorVersion), (DWORD)HIWORD(dwMinorVersion), (DWORD)LOWORD(dwMinorVersion));
104
105 dwMaxMajorVersion = dwMajorVersion;
106 dwMaxMinorVersion = dwMinorVersion;
107 }
108 }
109 else
110 {
111 WcaLog(LOGMSG_VERBOSE, "Ignoring %ls because it doesn't contain the file ngen.exe", pwzTempVersion);
112 }
113 }
114 else
115 {
116 WcaLog(LOGMSG_VERBOSE, "Ignoring %ls because it is from .NET Framework v1.1, which is not backwards compatible with other versions of the Framework and thus is not supported by this custom action.", pwzTempVersion);
117 }
118 }
119 else
120 {
121 WcaLog(LOGMSG_VERBOSE, "Ignoring %ls because it isn't a directory", pwzTempVersion);
122 }
123
124 fFound = FindNextFileW(hFind, &wfdVersionDirectories);
125
126 if (!fFound)
127 {
128 dwError = ::GetLastError();
129 hr = (ERROR_NO_MORE_FILES == dwError) ? ERROR_SUCCESS : HRESULT_FROM_WIN32(dwError);
130 ExitOnFailure(hr, "Failed to call FindNextFileW() with query %ls", pwzVersionSearch);
131 }
132 }
133
134 if (NULL == *ppwzVersion)
135 {
136 hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
137 ExitOnRootFailure(hr, "Searched through all subdirectories of %ls, but failed to find any version of ngen.exe", pwzParentPath);
138 }
139 else
140 {
141 WcaLog(LOGMSG_VERBOSE, "Using highest version of ngen found, located in this subdirectory: %ls, version %u.%u.%u.%u", *ppwzVersion, (DWORD)HIWORD(dwMajorVersion), (DWORD)LOWORD(dwMajorVersion), (DWORD)HIWORD(dwMinorVersion), (DWORD)LOWORD(dwMinorVersion));
142 }
143
144 LExit:
145 if (hFind != INVALID_HANDLE_VALUE)
146 {
147 if (0 == FindClose(hFind))
148 {
149 dwError = ::GetLastError();
150 hr = HRESULT_FROM_WIN32(dwError);
151 WcaLog(LOGMSG_STANDARD, "Failed to close handle created by outer FindFirstFile with error %x - continuing", hr);
152 }
153 hFind = INVALID_HANDLE_VALUE;
154 }
155
156 ReleaseStr(pwzVersionSearch);
157 ReleaseStr(pwzNgen);
158 ReleaseStr(pwzTemp);
159 // Purposely don't release pwzTempVersion, because it wasn't allocated in this function, it's just a pointer to a string inside wfdVersionDirectories
160
161 return hr;
162 }
163
164 // Gets the path to ngen.exe
165 static HRESULT GetNgenPath(
166 __out LPWSTR* ppwzNgenPath,
167 __in BOOL f64BitFramework
168 )
169 {
170 Assert(ppwzNgenPath);
171 HRESULT hr = S_OK;
172
173 LPWSTR pwzVersion = NULL;
174 LPWSTR pwzWindowsFolder = NULL;
175
176 hr = WcaGetProperty(L"WindowsFolder", &pwzWindowsFolder);
177 ExitOnFailure(hr, "failed to get WindowsFolder property");
178
179 hr = StrAllocString(ppwzNgenPath, pwzWindowsFolder, 0);
180 ExitOnFailure(hr, "failed to copy to NgenPath windows folder: %ls", pwzWindowsFolder);
181
182 if (f64BitFramework)
183 {
184 WcaLog(LOGMSG_VERBOSE, "Searching for ngen under 64-bit framework path");
185
186 hr = StrAllocConcat(ppwzNgenPath, L"Microsoft.NET\\Framework64\\", 0);
187 ExitOnFailure(hr, "failed to copy platform portion of ngen path");
188 }
189 else
190 {
191 WcaLog(LOGMSG_VERBOSE, "Searching for ngen under 32-bit framework path");
192
193 hr = StrAllocConcat(ppwzNgenPath, L"Microsoft.NET\\Framework\\", 0);
194 ExitOnFailure(hr, "failed to copy platform portion of ngen path");
195 }
196
197 // We want to run the highest version of ngen possible, because they should be backwards compatible - so let's find the most appropriate directory now
198 hr = GetNgenVersion(*ppwzNgenPath, &pwzVersion);
199 ExitOnFailure(hr, "failed to search for ngen under path %ls", *ppwzNgenPath);
200
201 hr = StrAllocConcat(ppwzNgenPath, pwzVersion, 0);
202 ExitOnFailure(hr, "failed to copy version portion of ngen path");
203
204 hr = StrAllocConcat(ppwzNgenPath, L"\\ngen.exe", 0);
205 ExitOnFailure(hr, "failed to copy \"\\ngen.exe\" portion of ngen path");
206
207 LExit:
208 ReleaseStr(pwzVersion);
209 ReleaseStr(pwzWindowsFolder);
210
211 return hr;
212 }
213
214
215 static HRESULT GetStrongName(
216 __out LPWSTR* ppwzStrongName,
217 __in LPCWSTR pwzComponent
218 )
219 {
220 Assert(ppwzStrongName);
221 HRESULT hr = S_OK;
222
223 PMSIHANDLE hView = NULL;
224 PMSIHANDLE hComponentRec = NULL;
225 PMSIHANDLE hRec = NULL;
226
227 LPWSTR pwzData = NULL;
228 LPWSTR pwzName = NULL;
229 LPWSTR pwzVersion = NULL;
230 LPWSTR pwzCulture = NULL;
231 LPWSTR pwzPublicKeyToken = NULL;
232
233 hComponentRec = ::MsiCreateRecord(1);
234 hr = WcaSetRecordString(hComponentRec, 1, pwzComponent);
235 ExitOnFailure(hr, "failed to set component value in record to: %ls", pwzComponent);
236
237 // get the name value records for this component
238 hr = WcaOpenView(vcsNgenStrongName, &hView);
239 ExitOnFailure(hr, "failed to open view on Wix4NetFxNativeImage table");
240
241 hr = WcaExecuteView(hView, hComponentRec);
242 ExitOnFailure(hr, "failed to execute strong name view");
243
244 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
245 {
246 hr = WcaGetRecordString(hRec, ngsnName, &pwzData);
247 ExitOnFailure(hr, "failed to get MsiAssemblyName.Name for component: %ls", pwzComponent);
248
249 if (0 == lstrcmpW(L"name", pwzData))
250 {
251 hr = WcaGetRecordString(hRec, ngsnValue, &pwzName);
252 ExitOnFailure(hr, "failed to get MsiAssemblyName.Value for component: %ls Name: %ls", pwzComponent, pwzData);
253 }
254 else if (0 == lstrcmpW(L"version", pwzData))
255 {
256 hr = WcaGetRecordString(hRec, ngsnValue, &pwzVersion);
257 ExitOnFailure(hr, "failed to get MsiAssemblyName.Value for component: %ls Name: %ls", pwzComponent, pwzData);
258 }
259 else if (0 == lstrcmpW(L"culture", pwzData))
260 {
261 hr = WcaGetRecordString(hRec, ngsnValue, &pwzCulture);
262 ExitOnFailure(hr, "failed to get MsiAssemblyName.Value for component: %ls Name: %ls", pwzComponent, pwzData);
263 }
264 else if (0 == lstrcmpW(L"publicKeyToken", pwzData))
265 {
266 hr = WcaGetRecordString(hRec, ngsnValue, &pwzPublicKeyToken);
267 ExitOnFailure(hr, "failed to get MsiAssemblyName.Value for component: %ls Name: %ls", pwzComponent, pwzData);
268 }
269 }
270 if (E_NOMOREITEMS == hr)
271 hr = S_OK;
272 ExitOnFailure(hr, "failed while looping through all names and values in MsiAssemblyName table for component: %ls", pwzComponent);
273
274 hr = StrAllocFormatted(ppwzStrongName, L"\"%s, Version=%s, Culture=%s, PublicKeyToken=%s\"", pwzName, pwzVersion, pwzCulture, pwzPublicKeyToken);
275 ExitOnFailure(hr, "failed to format strong name for component: %ls", pwzComponent);
276
277 LExit:
278 ReleaseStr(pwzData);
279 ReleaseStr(pwzName);
280 ReleaseStr(pwzVersion);
281 ReleaseStr(pwzCulture);
282 ReleaseStr(pwzPublicKeyToken);
283
284 return hr;
285 }
286
287 // This has netfxca specific functionality, like turning " into "" and leaving an unescaped \ at the end of a directory.
288 static HRESULT PathEnsureQuoted(
289 __inout LPWSTR* ppszPath,
290 __in BOOL fDirectory
291 )
292 {
293 Assert(ppszPath && *ppszPath);
294
295 HRESULT hr = S_OK;
296 size_t cchPath = 0;
297
298 hr = ::StringCchLengthW(*ppszPath, STRSAFE_MAX_CCH, &cchPath);
299 ExitOnFailure(hr, "Failed to get the length of the path.");
300
301 // Handle simple special cases.
302 if (0 == cchPath || (1 == cchPath && L'"' == (*ppszPath)[0]))
303 {
304 hr = StrAllocString(ppszPath, L"\"\"", 2);
305 ExitOnFailure(hr, "Failed to allocate a quoted empty string.");
306
307 ExitFunction();
308 }
309
310 if (L'"' != (*ppszPath)[0])
311 {
312 hr = StrAllocPrefix(ppszPath, L"\"", 1);
313 ExitOnFailure(hr, "Failed to allocate an opening quote.");
314
315 // Add a char for the opening quote.
316 ++cchPath;
317 }
318
319 if (L'"' != (*ppszPath)[cchPath - 1])
320 {
321 hr = StrAllocConcat(ppszPath, L"\"", 1);
322 ExitOnFailure(hr, "Failed to allocate a closing quote.");
323
324 // Add a char for the closing quote.
325 ++cchPath;
326 }
327
328 if (fDirectory)
329 {
330 if (L'\\' != (*ppszPath)[cchPath - 2])
331 {
332 // Change the last char to a backslash and re-append the closing quote.
333 (*ppszPath)[cchPath - 1] = L'\\';
334
335 hr = StrAllocConcat(ppszPath, L"\"", 1);
336 ExitOnFailure(hr, "Failed to allocate another closing quote after the backslash.");
337 }
338 }
339
340 LExit:
341
342 return hr;
343 }
344
345 static HRESULT CreateInstallCommand(
346 __out LPWSTR* ppwzCommandLine,
347 __in LPCWSTR pwzNgenPath,
348 __in LPCWSTR pwzFile,
349 __in int iPriority,
350 __in int iAttributes,
351 __in LPCWSTR pwzFileApp,
352 __in LPCWSTR pwzDirAppBase
353 )
354 {
355 Assert(ppwzCommandLine && pwzNgenPath && *pwzNgenPath && pwzFile && *pwzFile&& pwzFileApp && pwzDirAppBase);
356 HRESULT hr = S_OK;
357
358 LPWSTR pwzQueueString = NULL;
359
360 hr = StrAllocFormatted(ppwzCommandLine, L"%s install %s", pwzNgenPath, pwzFile);
361 ExitOnFailure(hr, "failed to assemble install command line");
362
363 if (iPriority > 0)
364 {
365 hr = StrAllocFormatted(&pwzQueueString, L" /queue:%d", iPriority);
366 ExitOnFailure(hr, "failed to format queue string");
367
368 hr = StrAllocConcat(ppwzCommandLine, pwzQueueString, 0);
369 ExitOnFailure(hr, "failed to add queue string to NGEN command line");
370 }
371
372 if (NGEN_DEBUG & iAttributes)
373 {
374 hr = StrAllocConcat(ppwzCommandLine, L" /Debug", 0);
375 ExitOnFailure(hr, "failed to add debug to NGEN command line");
376 }
377
378 if (NGEN_PROFILE & iAttributes)
379 {
380 hr = StrAllocConcat(ppwzCommandLine, L" /Profile", 0);
381 ExitOnFailure(hr, "failed to add profile to NGEN command line");
382 }
383
384 if (NGEN_NODEP & iAttributes)
385 {
386 hr = StrAllocConcat(ppwzCommandLine, L" /NoDependencies", 0);
387 ExitOnFailure(hr, "failed to add no dependencies to NGEN command line");
388 }
389
390 // If it's more than just two quotes around an empty string
391 if (EMPTY_FORMATTED_LENGTH_QUOTED_FILE < lstrlenW(pwzFileApp))
392 {
393 hr = StrAllocConcat(ppwzCommandLine, L" /ExeConfig:", 0);
394 ExitOnFailure(hr, "failed to add exe config to NGEN command line");
395
396 hr = StrAllocConcat(ppwzCommandLine, pwzFileApp, 0);
397 ExitOnFailure(hr, "failed to add file app to NGEN command line");
398 }
399
400 // If it's more than just two quotes around a backslash
401 if (EMPTY_FORMATTED_LENGTH_QUOTED_DIRECTORY < lstrlenW(pwzDirAppBase))
402 {
403 hr = StrAllocConcat(ppwzCommandLine, L" /AppBase:", 0);
404 ExitOnFailure(hr, "failed to add app base to NGEN command line");
405
406 hr = StrAllocConcat(ppwzCommandLine, pwzDirAppBase, 0);
407 ExitOnFailure(hr, "failed to add dir app base to NGEN command line");
408 }
409
410 LExit:
411 return hr;
412 }
413
414 /******************************************************************
415 FileIdExists - checks if the file ID is found in the File table
416
417 returns S_OK if the file exists; S_FALSE if not; otherwise, error
418 ********************************************************************/
419 static HRESULT FileIdExists(
420 __in_opt LPCWSTR wzFile
421 )
422 {
423 HRESULT hr = S_OK;
424 PMSIHANDLE hView = NULL;
425 PMSIHANDLE hRec = NULL;
426
427 if (!wzFile)
428 {
429 hr = S_FALSE;
430 ExitFunction();
431 }
432
433 hRec = ::MsiCreateRecord(1);
434 hr = WcaSetRecordString(hRec, fiFile, wzFile);
435 ExitOnFailure(hr, "failed to create a record with the file: %ls", wzFile);
436
437 hr = WcaTableExists(L"File");
438 if (S_OK == hr)
439 {
440 hr = WcaOpenView(vcsFileId, &hView);
441 ExitOnFailure(hr, "failed to open view on File table");
442
443 hr = WcaExecuteView(hView, hRec);
444 ExitOnFailure(hr, "failed to execute view on File table");
445
446 // Reuse the same record; the handle will be released.
447 hr = WcaFetchSingleRecord(hView, &hRec);
448 ExitOnFailure(hr, "failed to fetch File from File table");
449 }
450
451 LExit:
452
453 return hr;
454 }
455
456 /******************************************************************
457 SchedNetFx - entry point for NetFx Custom Action
458
459 ********************************************************************/
460 extern "C" UINT __stdcall SchedNetFx(
461 __in MSIHANDLE hInstall
462 )
463 {
464 // AssertSz(FALSE, "debug SchedNetFx");
465
466 HRESULT hr = S_OK;
467 UINT er = ERROR_SUCCESS;
468
469 LPWSTR pwzInstallCustomActionData = NULL;
470 LPWSTR pwzUninstallCustomActionData = NULL;
471 UINT uiCost = 0;
472
473 PMSIHANDLE hView = NULL;
474 PMSIHANDLE hRec = NULL;
475 PMSIHANDLE hViewGac = NULL;
476 PMSIHANDLE hRecGac = NULL;
477
478 LPWSTR pwzId = NULL;
479 LPWSTR pwzData = NULL;
480 LPWSTR pwzTemp = NULL;
481 LPWSTR pwzFile = NULL;
482 int iPriority = 0;
483 int iAssemblyCost = 0;
484 int iAttributes = 0;
485 LPWSTR pwzFileApp = NULL;
486 LPWSTR pwzDirAppBase = NULL;
487 LPWSTR pwzComponent = NULL;
488
489 INSTALLSTATE isInstalled;
490 INSTALLSTATE isAction;
491
492 LPWSTR pwz32Ngen = NULL;
493 LPWSTR pwz64Ngen = NULL;
494
495 BOOL f32NgenExeExists = FALSE;
496 BOOL f64NgenExeExists = FALSE;
497
498 BOOL fNeedInstallUpdate32 = FALSE;
499 BOOL fNeedUninstallUpdate32 = FALSE;
500 BOOL fNeedInstallUpdate64 = FALSE;
501 BOOL fNeedUninstallUpdate64 = FALSE;
502
503 // initialize
504 hr = WcaInitialize(hInstall, "SchedNetFx");
505 ExitOnFailure(hr, "failed to initialize");
506
507 // If Wix4NetFxNativeImage table doesn't exist skip the rest of this custom action
508 hr = WcaTableExists(L"Wix4NetFxNativeImage");
509 if (S_FALSE == hr)
510 {
511 hr = S_OK;
512 ExitFunction();
513 }
514 ExitOnFailure(hr, "failed to check if table Wix4NetFxNativeImage exists");
515
516 hr = GetNgenPath(&pwz32Ngen, FALSE);
517 f32NgenExeExists = SUCCEEDED(hr);
518 if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
519 {
520 hr = ERROR_SUCCESS;
521 WcaLog(LOGMSG_STANDARD, "Failed to find 32bit ngen. No actions will be scheduled to create native images for 32bit.");
522 }
523 ExitOnFailure(hr, "failed to get 32bit ngen.exe path");
524
525 hr = GetNgenPath(&pwz64Ngen, TRUE);
526 f64NgenExeExists = SUCCEEDED(hr);
527 if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
528 {
529 hr = ERROR_SUCCESS;
530 WcaLog(LOGMSG_STANDARD, "Failed to find 64bit ngen. No actions will be scheduled to create native images for 64bit.");
531 }
532 ExitOnFailure(hr, "failed to get 64bit ngen.exe path");
533
534 // loop through all the NetFx records
535 hr = WcaOpenExecuteView(vcsNgenQuery, &hView);
536 ExitOnFailure(hr, "failed to open view on Wix4NetFxNativeImage table");
537
538 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
539 {
540 // Get Id
541 hr = WcaGetRecordString(hRec, ngqId, &pwzId);
542 ExitOnFailure(hr, "failed to get Wix4NetFxNativeImage.Wix4NetFxNativeImage");
543
544 // Get File
545 hr = WcaGetRecordString(hRec, ngqFile, &pwzData);
546 ExitOnFailure(hr, "failed to get Wix4NetFxNativeImage.File_ for record: %ls", pwzId);
547 hr = StrAllocFormatted(&pwzTemp, vpwzUnformattedQuotedFile, pwzData);
548 ExitOnFailure(hr, "failed to format file string for file: %ls", pwzData);
549 hr = WcaGetFormattedString(pwzTemp, &pwzFile);
550 ExitOnFailure(hr, "failed to get formatted string for file: %ls", pwzData);
551
552 // Get Priority
553 hr = WcaGetRecordInteger(hRec, ngqPriority, &iPriority);
554 ExitOnFailure(hr, "failed to get Wix4NetFxNativeImage.Priority for record: %ls", pwzId);
555
556 if (0 == iPriority)
557 iAssemblyCost = COST_NGEN_BLOCKING;
558 else
559 iAssemblyCost = COST_NGEN_NONBLOCKING;
560
561 // Get Attributes
562 hr = WcaGetRecordInteger(hRec, ngqAttributes, &iAttributes);
563 ExitOnFailure(hr, "failed to get Wix4NetFxNativeImage.Attributes for record: %ls", pwzId);
564
565 // Get File_Application or leave pwzFileApp NULL.
566 hr = WcaGetRecordFormattedString(hRec, ngqFileApp, &pwzData);
567 ExitOnFailure(hr, "failed to get Wix4NetFxNativeImage.File_Application for record: %ls", pwzId);
568
569 // Check if the value resolves to a valid file ID.
570 if (S_OK == FileIdExists(pwzData))
571 {
572 // Resolve the file ID to a path.
573 hr = StrAllocFormatted(&pwzTemp, vpwzUnformattedQuotedFile, pwzData);
574 ExitOnFailure(hr, "failed to format file application string for file: %ls", pwzData);
575
576 hr = WcaGetFormattedString(pwzTemp, &pwzFileApp);
577 ExitOnFailure(hr, "failed to get formatted string for file application: %ls", pwzData);
578 }
579 else
580 {
581 // Assume record formatted to a path already.
582 hr = StrAllocString(&pwzFileApp, pwzData, 0);
583 ExitOnFailure(hr, "failed to allocate string for file path: %ls", pwzData);
584
585 hr = PathEnsureQuoted(&pwzFileApp, FALSE);
586 ExitOnFailure(hr, "failed to quote file path: %ls", pwzData);
587 }
588
589 // Get Directory_ApplicationBase or leave pwzDirAppBase NULL.
590 hr = WcaGetRecordFormattedString(hRec, ngqDirAppBase, &pwzData);
591 ExitOnFailure(hr, "failed to get Wix4NetFxNativeImage.Directory_ApplicationBase for record: %ls", pwzId);
592
593 if (WcaIsUnicodePropertySet(pwzData))
594 {
595 // Resolve the directory ID to a path.
596 hr = StrAllocFormatted(&pwzTemp, vpwzUnformattedQuotedDirectory, pwzData);
597 ExitOnFailure(hr, "failed to format directory application base string for property: %ls", pwzData);
598
599 hr = WcaGetFormattedString(pwzTemp, &pwzDirAppBase);
600 ExitOnFailure(hr, "failed to get formatted string for directory application base: %ls", pwzData);
601 }
602 else
603 {
604 // Assume record formatted to a path already.
605 hr = StrAllocString(&pwzDirAppBase, pwzData, 0);
606 ExitOnFailure(hr, "failed to allocate string for directory path: %ls", pwzData);
607
608 hr = PathEnsureQuoted(&pwzDirAppBase, TRUE);
609 ExitOnFailure(hr, "failed to quote and backslashify directory: %ls", pwzData);
610 }
611
612 // Get Component
613 hr = WcaGetRecordString(hRec, ngqComponent, &pwzComponent);
614 ExitOnFailure(hr, "failed to get Wix4NetFxNativeImage.Directory_ApplicationBase for record: %ls", pwzId);
615 er = ::MsiGetComponentStateW(hInstall, pwzComponent, &isInstalled, &isAction);
616 ExitOnWin32Error(er, hr, "failed to get install state for Component: %ls", pwzComponent);
617
618 //
619 // Figure out if it's going to be GAC'd. The possibility exists that no assemblies are going to be GAC'd
620 // so we have to check for the MsiAssembly table first.
621 //
622 if (S_OK == WcaTableExists(L"MsiAssembly"))
623 {
624 hr = WcaOpenView(vcsNgenGac, &hViewGac);
625 ExitOnFailure(hr, "failed to open view on File/MsiAssembly table");
626
627 hr = WcaExecuteView(hViewGac, hRec);
628 ExitOnFailure(hr, "failed to execute view on File/MsiAssembly table");
629
630 hr = WcaFetchSingleRecord(hViewGac, &hRecGac);
631 ExitOnFailure(hr, "failed to fetch File_Assembly from File/MsiAssembly table");
632
633 if (S_FALSE != hr)
634 {
635 hr = WcaGetRecordString(hRecGac, nggApplication, &pwzData);
636 ExitOnFailure(hr, "failed to get MsiAssembly.File_Application");
637
638 // If it's in the GAC replace the file name with the strong name
639 if (L'\0' == pwzData[0])
640 {
641 hr = GetStrongName(&pwzFile, pwzComponent);
642 ExitOnFailure(hr, "failed to get strong name for component: %ls", pwzData);
643 }
644 }
645 }
646
647 //
648 // Schedule the work
649 //
650 if (!(iAttributes & NGEN_32BIT) && !(iAttributes & NGEN_64BIT))
651 ExitOnFailure(hr = E_INVALIDARG, "Neither 32bit nor 64bit is specified for NGEN of file: %ls", pwzFile);
652
653 if (WcaIsInstalling(isInstalled, isAction) || WcaIsReInstalling(isInstalled, isAction))
654 {
655 if (iAttributes & NGEN_32BIT && f32NgenExeExists)
656 {
657 // Assemble the install command line
658 hr = CreateInstallCommand(&pwzData, pwz32Ngen, pwzFile, iPriority, iAttributes, pwzFileApp, pwzDirAppBase);
659 ExitOnFailure(hr, "failed to create install command line");
660
661 hr = WcaWriteStringToCaData(pwzData, &pwzInstallCustomActionData);
662 ExitOnFailure(hr, "failed to add install command to custom action data: %ls", pwzData);
663
664 hr = WcaWriteIntegerToCaData(iAssemblyCost, &pwzInstallCustomActionData);
665 ExitOnFailure(hr, "failed to add cost to custom action data: %ls", pwzData);
666
667 uiCost += iAssemblyCost;
668
669 fNeedInstallUpdate32 = TRUE;
670 }
671
672 if (iAttributes & NGEN_64BIT && f64NgenExeExists)
673 {
674 // Assemble the install command line
675 hr = CreateInstallCommand(&pwzData, pwz64Ngen, pwzFile, iPriority, iAttributes, pwzFileApp, pwzDirAppBase);
676 ExitOnFailure(hr, "failed to create install command line");
677
678 hr = WcaWriteStringToCaData(pwzData, &pwzInstallCustomActionData); // command
679 ExitOnFailure(hr, "failed to add install command to custom action data: %ls", pwzData);
680
681 hr = WcaWriteIntegerToCaData(iAssemblyCost, &pwzInstallCustomActionData); // cost
682 ExitOnFailure(hr, "failed to add cost to custom action data: %ls", pwzData);
683
684 uiCost += iAssemblyCost;
685
686 fNeedInstallUpdate64 = TRUE;
687 }
688 }
689 else if (WcaIsUninstalling(isInstalled, isAction))
690 {
691 if (iAttributes & NGEN_32BIT && f32NgenExeExists)
692 {
693 hr = StrAllocFormatted(&pwzData, L"%s uninstall %s", pwz32Ngen, pwzFile);
694 ExitOnFailure(hr, "failed to create update 32 command line");
695
696 hr = WcaWriteStringToCaData(pwzData, &pwzUninstallCustomActionData); // command
697 ExitOnFailure(hr, "failed to add install command to custom action data: %ls", pwzData);
698
699 hr = WcaWriteIntegerToCaData(COST_NGEN_NONBLOCKING, &pwzUninstallCustomActionData); // cost
700 ExitOnFailure(hr, "failed to add cost to custom action data: %ls", pwzData);
701
702 uiCost += COST_NGEN_NONBLOCKING;
703
704 fNeedUninstallUpdate32 = TRUE;
705 }
706
707 if (iAttributes & NGEN_64BIT && f64NgenExeExists)
708 {
709 hr = StrAllocFormatted(&pwzData, L"%s uninstall %s", pwz64Ngen, pwzFile);
710 ExitOnFailure(hr, "failed to create update 64 command line");
711
712 hr = WcaWriteStringToCaData(pwzData, &pwzUninstallCustomActionData); // command
713 ExitOnFailure(hr, "failed to add install command to custom action data: %ls", pwzData);
714
715 hr = WcaWriteIntegerToCaData(COST_NGEN_NONBLOCKING, &pwzUninstallCustomActionData); // cost
716 ExitOnFailure(hr, "failed to add cost to custom action data: %ls", pwzData);
717
718 uiCost += COST_NGEN_NONBLOCKING;
719
720 fNeedUninstallUpdate64 = TRUE;
721 }
722 }
723 }
724 if (E_NOMOREITEMS == hr)
725 hr = S_OK;
726 ExitOnFailure(hr, "failed while looping through all files to create native images for");
727
728 // If we need 32 bit install update
729 if (fNeedInstallUpdate32)
730 {
731 hr = StrAllocFormatted(&pwzData, L"%s update /queue", pwz32Ngen);
732 ExitOnFailure(hr, "failed to create install update 32 command line");
733
734 hr = WcaWriteStringToCaData(pwzData, &pwzInstallCustomActionData); // command
735 ExitOnFailure(hr, "failed to add install command to install custom action data: %ls", pwzData);
736
737 hr = WcaWriteIntegerToCaData(COST_NGEN_NONBLOCKING, &pwzInstallCustomActionData); // cost
738 ExitOnFailure(hr, "failed to add cost to install custom action data: %ls", pwzData);
739
740 uiCost += COST_NGEN_NONBLOCKING;
741 }
742
743 // If we need 32 bit uninstall update
744 if (fNeedUninstallUpdate32)
745 {
746 hr = StrAllocFormatted(&pwzData, L"%s update /queue", pwz32Ngen);
747 ExitOnFailure(hr, "failed to create uninstall update 32 command line");
748
749 hr = WcaWriteStringToCaData(pwzData, &pwzUninstallCustomActionData); // command
750 ExitOnFailure(hr, "failed to add install command to uninstall custom action data: %ls", pwzData);
751
752 hr = WcaWriteIntegerToCaData(COST_NGEN_NONBLOCKING, &pwzUninstallCustomActionData); // cost
753 ExitOnFailure(hr, "failed to add cost to uninstall custom action data: %ls", pwzData);
754
755 uiCost += COST_NGEN_NONBLOCKING;
756 }
757
758 // If we need 64 bit install update
759 if (fNeedInstallUpdate64)
760 {
761 hr = StrAllocFormatted(&pwzData, L"%s update /queue", pwz64Ngen);
762 ExitOnFailure(hr, "failed to create install update 64 command line");
763
764 hr = WcaWriteStringToCaData(pwzData, &pwzInstallCustomActionData); // command
765 ExitOnFailure(hr, "failed to add install command to install custom action data: %ls", pwzData);
766
767 hr = WcaWriteIntegerToCaData(COST_NGEN_NONBLOCKING, &pwzInstallCustomActionData); // cost
768 ExitOnFailure(hr, "failed to add cost to install custom action data: %ls", pwzData);
769
770 uiCost += COST_NGEN_NONBLOCKING;
771 }
772
773 // If we need 64 bit install update
774 if (fNeedUninstallUpdate64)
775 {
776 hr = StrAllocFormatted(&pwzData, L"%s update /queue", pwz64Ngen);
777 ExitOnFailure(hr, "failed to create uninstall update 64 command line");
778
779 hr = WcaWriteStringToCaData(pwzData, &pwzUninstallCustomActionData); // command
780 ExitOnFailure(hr, "failed to add install command to uninstall custom action data: %ls", pwzData);
781
782 hr = WcaWriteIntegerToCaData(COST_NGEN_NONBLOCKING, &pwzUninstallCustomActionData); // cost
783 ExitOnFailure(hr, "failed to add cost to uninstall custom action data: %ls", pwzData);
784
785 uiCost += COST_NGEN_NONBLOCKING;
786 }
787
788 // Add to progress bar
789 if ((pwzInstallCustomActionData && *pwzInstallCustomActionData) || (pwzUninstallCustomActionData && *pwzUninstallCustomActionData))
790 {
791 hr = WcaProgressMessage(uiCost, TRUE);
792 ExitOnFailure(hr, "failed to extend progress bar for NetFxExecuteNativeImage");
793 }
794
795 // Schedule the install custom action
796 if (pwzInstallCustomActionData && *pwzInstallCustomActionData)
797 {
798 hr = WcaSetProperty(CUSTOM_ACTION_DECORATION(L"NetFxExecuteNativeImageInstall"), pwzInstallCustomActionData);
799 ExitOnFailure(hr, "failed to schedule NetFxExecuteNativeImageInstall action");
800
801 hr = WcaSetProperty(CUSTOM_ACTION_DECORATION(L"NetFxExecuteNativeImageCommitInstall"), pwzInstallCustomActionData);
802 ExitOnFailure(hr, "failed to schedule NetFxExecuteNativeImageCommitInstall action");
803 }
804
805 // Schedule the uninstall custom action
806 if (pwzUninstallCustomActionData && *pwzUninstallCustomActionData)
807 {
808 hr = WcaSetProperty(CUSTOM_ACTION_DECORATION(L"NetFxExecuteNativeImageUninstall"), pwzUninstallCustomActionData);
809 ExitOnFailure(hr, "failed to schedule NetFxExecuteNativeImageUninstall action");
810
811 hr = WcaSetProperty(CUSTOM_ACTION_DECORATION(L"NetFxExecuteNativeImageCommitUninstall"), pwzUninstallCustomActionData);
812 ExitOnFailure(hr, "failed to schedule NetFxExecuteNativeImageCommitUninstall action");
813 }
814
815 LExit:
816 ReleaseStr(pwzInstallCustomActionData);
817 ReleaseStr(pwzUninstallCustomActionData);
818 ReleaseStr(pwzId);
819 ReleaseStr(pwzData);
820 ReleaseStr(pwzTemp);
821 ReleaseStr(pwzFile);
822 ReleaseStr(pwzFileApp);
823 ReleaseStr(pwzDirAppBase);
824 ReleaseStr(pwzComponent);
825 ReleaseStr(pwz32Ngen);
826 ReleaseStr(pwz64Ngen);
827
828 if (FAILED(hr))
829 er = ERROR_INSTALL_FAILURE;
830 return WcaFinalize(er);
831 }
832
833
834 /******************************************************************
835 ExecNetFx - entry point for NetFx Custom Action
836
837 *******************************************************************/
838 extern "C" UINT __stdcall ExecNetFx(
839 __in MSIHANDLE hInstall
840 )
841 {
842 // AssertSz(FALSE, "debug ExecNetFx");
843
844 HRESULT hr = S_OK;
845 UINT er = ERROR_SUCCESS;
846
847 LPWSTR pwzCustomActionData = NULL;
848 LPWSTR pwzData = NULL;
849 LPWSTR pwz = NULL;
850 int iCost = 0;
851
852 // initialize
853 hr = WcaInitialize(hInstall, "ExecNetFx");
854 ExitOnFailure(hr, "failed to initialize");
855
856 hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData);
857 ExitOnFailure(hr, "failed to get CustomActionData");
858
859 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData);
860
861 pwz = pwzCustomActionData;
862
863 // loop through all the passed in data
864 while (pwz && *pwz)
865 {
866 hr = WcaReadStringFromCaData(&pwz, &pwzData);
867 ExitOnFailure(hr, "failed to read command line from custom action data");
868
869 hr = WcaReadIntegerFromCaData(&pwz, &iCost);
870 ExitOnFailure(hr, "failed to read cost from custom action data");
871
872 hr = QuietExec(pwzData, NGEN_TIMEOUT, TRUE, TRUE);
873 // If we fail here it isn't critical - keep looping through to try to act on the other assemblies on our list
874 if (FAILED(hr))
875 {
876 WcaLog(LOGMSG_STANDARD, "failed to execute Ngen command (with error 0x%x): %ls, continuing anyway", hr, pwzData);
877 hr = S_OK;
878 }
879
880 // Tick the progress bar along for this assembly
881 hr = WcaProgressMessage(iCost, FALSE);
882 ExitOnFailure(hr, "failed to tick progress bar for command line: %ls", pwzData);
883 }
884
885 LExit:
886 ReleaseStr(pwzCustomActionData);
887 ReleaseStr(pwzData);
888
889 if (FAILED(hr))
890 er = ERROR_INSTALL_FAILURE;
891 return WcaFinalize(er);
892 }
893
894 /******************************************************************
895 DotNetCompatibilityCheck - entry point for NetFx Custom Action
896
897 *******************************************************************/
898 extern "C" UINT __stdcall DotNetCompatibilityCheck(
899 __in MSIHANDLE hInstall
900 )
901 {
902 // AssertSz(FALSE, "debug DotNetCompatibilityCheck");
903
904 HRESULT hr = S_OK;
905 UINT er = ERROR_SUCCESS;
906
907 PMSIHANDLE hView = NULL;
908 PMSIHANDLE hRec = NULL;
909 LPWSTR pwzPlatform = NULL;
910 LPWSTR pwzNetCoreCheckBinaryId = NULL;
911 LPWSTR pwzNetCoreCheckDirectoryName = NULL;
912 LPWSTR pwzNetCoreCheckDirectoryPath = NULL;
913 LPWSTR pwzNetCoreCheckFilePath = NULL;
914 LPWSTR pwzRuntimeType = NULL;
915 LPWSTR pwzVersion = NULL;
916 LPWSTR pwzRollForward = NULL;
917 LPWSTR pwzProperty = NULL;
918 LPWSTR pwzCommandLine = NULL;
919 HANDLE hProcess = NULL;
920 DWORD dwExitCode = 0;
921
922 // Initialize
923 hr = WcaInitialize(hInstall, "DotNetCompatibilityCheck");
924 ExitOnFailure(hr, "failed to initialize");
925
926 // If Wix4NetFxDotNetCheck table doesn't exist skip the rest of this custom action
927 hr = WcaTableExists(L"Wix4NetFxDotNetCheck");
928 if (S_FALSE == hr)
929 {
930 hr = S_OK;
931 ExitFunction();
932 }
933 ExitOnFailure(hr, "failed to check if table Wix4NetFxDotNetCheck exists");
934
935 // Open view on .NET compatibility check table
936 hr = WcaOpenExecuteView(vscDotNetCompatibilityCheckQuery, &hView);
937 ExitOnFailure(hr, "failed to open view on Wix4NetFxDotNetCheck table");
938
939 // Go through all records and run NetCorCheck.exe for each
940 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
941 {
942 // Extract NetCoreCheck.exe for platform to temp directory
943 hr = WcaGetRecordString(hRec, platform, &pwzPlatform);
944 ExitOnFailure(hr, "failed to get Wix4NetFxDotNetCheck.Platform");
945
946 hr = StrAllocFormatted(&pwzNetCoreCheckBinaryId, L"Wix4NetCheck_%ls", pwzPlatform);
947 ExitOnFailure(hr, "failed to get NetCoreCheck binary id for platform %ls", pwzPlatform);
948
949 hr = GuidCreate(&pwzNetCoreCheckDirectoryName);
950 ExitOnFailure(hr, "failed to set NetCoreCheck directory name");
951
952 hr = PathCreateTempDirectory(NULL, pwzNetCoreCheckDirectoryName, 1, &pwzNetCoreCheckDirectoryPath);
953 ExitOnFailure(hr, "failed to make NetCoreCheck directory path for name %ls", pwzNetCoreCheckDirectoryName);
954
955 hr = StrAllocFormatted(&pwzNetCoreCheckFilePath, L"%lsNetCoreCheck.exe", pwzNetCoreCheckDirectoryPath);
956 ExitOnFailure(hr, "failed to set NetCoreCheck file path for directory %ls", pwzNetCoreCheckDirectoryPath);
957
958 hr = WcaExtractBinaryToFile(pwzNetCoreCheckBinaryId, pwzNetCoreCheckFilePath);
959 ExitOnFailure(hr, "failed to extract NetCoreCheck from binary '%ls' to file %ls", pwzNetCoreCheckBinaryId, pwzNetCoreCheckFilePath);
960
961 // Read all NetCoreCheck.exe parameters and property
962 hr = WcaGetRecordString(hRec, runtimeType, &pwzRuntimeType);
963 ExitOnFailure(hr, "failed to get Wix4NetFxDotNetCheck.RuntimeType");
964
965 hr = WcaGetRecordString(hRec, version, &pwzVersion);
966 ExitOnFailure(hr, "failed to get Wix4NetFxDotNetCheck.Version");
967
968 hr = WcaGetRecordString(hRec, rollForward, &pwzRollForward);
969 ExitOnFailure(hr, "failed to get Wix4NetFxDotNetCheck.RollForward");
970
971 hr = WcaGetRecordString(hRec, property, &pwzProperty);
972 ExitOnFailure(hr, "failed to get Wix4NetFxDotNetCheck.Property");
973
974 // Run NetCoreCheck.exe and store its result in property
975 hr = StrAllocFormatted(&pwzCommandLine, L"-n %ls -v %ls -r %ls", pwzRuntimeType, pwzVersion, pwzRollForward);
976 ExitOnFailure(hr, "failed to set NetCoreCheck command line");
977 WcaLog(LOGMSG_VERBOSE, "Command: %ls %ls", pwzNetCoreCheckFilePath, pwzCommandLine);
978
979 hr = ProcExec(pwzNetCoreCheckFilePath, pwzCommandLine, SW_HIDE, &hProcess);
980 if (hr == HRESULT_FROM_WIN32(ERROR_EXE_MACHINE_TYPE_MISMATCH) || hr == HRESULT_FROM_WIN32(ERROR_BAD_EXE_FORMAT))
981 {
982 dwExitCode = 13;
983 WcaLog(LOGMSG_VERBOSE, "NetCoreCheck executable for platform %ls is not compatible with current OS", pwzPlatform);
984 }
985 else
986 {
987 ExitOnFailure(hr, "failed to run NetCoreCheck from binary '%ls' with command line: %ls %ls", pwzNetCoreCheckBinaryId, pwzNetCoreCheckFilePath, pwzCommandLine);
988
989 hr = ProcWaitForCompletion(hProcess, INFINITE, &dwExitCode);
990 ExitOnFailure(hr, "failed to finish NetCoreCheck from binary '%ls' with command line: %ls %ls", pwzNetCoreCheckBinaryId, pwzNetCoreCheckFilePath, pwzCommandLine);
991 WcaLog(LOGMSG_VERBOSE, "Exit code: %lu", dwExitCode);
992 ReleaseHandle(hProcess);
993 }
994
995 hr = WcaSetIntProperty(pwzProperty, dwExitCode);
996 ExitOnFailure(hr, "failed to set NetCoreCheck result in %ls", pwzProperty);
997
998 // Delete extracted NetCoreCheck.exe
999 DirEnsureDelete(pwzNetCoreCheckDirectoryPath, TRUE, TRUE);
1000 }
1001 if (E_NOMOREITEMS == hr)
1002 {
1003 hr = S_OK;
1004 }
1005 ExitOnFailure(hr, "failed while looping through all dot net compatibility checks");
1006
1007 LExit:
1008 // Delete extracted NetCoreCheck.exe
1009 if (NULL != pwzNetCoreCheckDirectoryPath)
1010 {
1011 DirEnsureDelete(pwzNetCoreCheckDirectoryPath, TRUE, TRUE);
1012 }
1013
1014 // Release allocated resources
1015 ReleaseStr(pwzPlatform);
1016 ReleaseStr(pwzNetCoreCheckBinaryId);
1017 ReleaseStr(pwzNetCoreCheckDirectoryName);
1018 ReleaseStr(pwzNetCoreCheckDirectoryPath);
1019 ReleaseStr(pwzNetCoreCheckFilePath);
1020 ReleaseStr(pwzRuntimeType);
1021 ReleaseStr(pwzVersion);
1022 ReleaseStr(pwzRollForward);
1023 ReleaseStr(pwzProperty);
1024 ReleaseStr(pwzCommandLine);
1025 ReleaseHandle(hProcess);
1026
1027 if (FAILED(hr))
1028 {
1029 er = ERROR_INSTALL_FAILURE;
1030 }
1031 return WcaFinalize(er);
1032 }