aboutsummaryrefslogtreecommitdiff
path: root/src/ext/NetFx/ca
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2021-05-03 16:05:08 -0700
committerRob Mensching <rob@firegiant.com>2021-05-03 16:05:08 -0700
commit7bdd5e9159b298e0411afa689a06c44e36e293cd (patch)
tree57d24ea7fdd8025baf6a822a99b588c9df74a60d /src/ext/NetFx/ca
parentca02e81316d700a3647414f355eab4d2115d6163 (diff)
downloadwix-7bdd5e9159b298e0411afa689a06c44e36e293cd.tar.gz
wix-7bdd5e9159b298e0411afa689a06c44e36e293cd.tar.bz2
wix-7bdd5e9159b298e0411afa689a06c44e36e293cd.zip
Move NetFx.wixext into ext
Diffstat (limited to 'src/ext/NetFx/ca')
-rw-r--r--src/ext/NetFx/ca/caDecor.h13
-rw-r--r--src/ext/NetFx/ca/cost.h7
-rw-r--r--src/ext/NetFx/ca/dllmain.cpp26
-rw-r--r--src/ext/NetFx/ca/netfxca.cpp823
-rw-r--r--src/ext/NetFx/ca/netfxca.def8
-rw-r--r--src/ext/NetFx/ca/netfxca.vcxproj73
-rw-r--r--src/ext/NetFx/ca/precomp.h14
7 files changed, 964 insertions, 0 deletions
diff --git a/src/ext/NetFx/ca/caDecor.h b/src/ext/NetFx/ca/caDecor.h
new file mode 100644
index 00000000..da274650
--- /dev/null
+++ b/src/ext/NetFx/ca/caDecor.h
@@ -0,0 +1,13 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#if defined(_M_ARM64)
6#define CUSTOM_ACTION_DECORATION(f) L"Wix4" f L"_A64"
7#elif defined(_M_AMD64)
8#define CUSTOM_ACTION_DECORATION(f) L"Wix4" f L"_X64"
9#elif defined(_M_ARM)
10#define CUSTOM_ACTION_DECORATION(f) L"Wix4" f L"_ARM"
11#else
12#define CUSTOM_ACTION_DECORATION(f) L"Wix4" f L"_X86"
13#endif
diff --git a/src/ext/NetFx/ca/cost.h b/src/ext/NetFx/ca/cost.h
new file mode 100644
index 00000000..95368eba
--- /dev/null
+++ b/src/ext/NetFx/ca/cost.h
@@ -0,0 +1,7 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5const UINT COST_NGEN_BLOCKING = 5000;
6const UINT COST_NGEN_NONBLOCKING = 500;
7
diff --git a/src/ext/NetFx/ca/dllmain.cpp b/src/ext/NetFx/ca/dllmain.cpp
new file mode 100644
index 00000000..df53f872
--- /dev/null
+++ b/src/ext/NetFx/ca/dllmain.cpp
@@ -0,0 +1,26 @@
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/********************************************************************
6DllMain - standard entry point for all WiX CustomActions
7
8********************************************************************/
9extern "C" BOOL WINAPI DllMain(
10 IN HINSTANCE hInst,
11 IN ULONG ulReason,
12 IN LPVOID)
13{
14 switch(ulReason)
15 {
16 case DLL_PROCESS_ATTACH:
17 WcaGlobalInitialize(hInst);
18 break;
19
20 case DLL_PROCESS_DETACH:
21 WcaGlobalFinalize();
22 break;
23 }
24
25 return TRUE;
26}
diff --git a/src/ext/NetFx/ca/netfxca.cpp b/src/ext/NetFx/ca/netfxca.cpp
new file mode 100644
index 00000000..3a71babf
--- /dev/null
+++ b/src/ext/NetFx/ca/netfxca.cpp
@@ -0,0 +1,823 @@
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
14LPCWSTR vpwzUnformattedQuotedFile = L"\"[#%s]\"";
15LPCWSTR vpwzUnformattedQuotedDirectory = L"\"[%s]\\\"";
16
17// These represent the length of the above strings in the case that the property resolves to an empty string
18const DWORD EMPTY_FORMATTED_LENGTH_QUOTED_FILE = 2;
19const DWORD EMPTY_FORMATTED_LENGTH_QUOTED_DIRECTORY = 3;
20
21LPCWSTR vcsFileId =
22 L"SELECT `File` FROM `File` WHERE `File`=?";
23enum eFileId { fiFile = 1 };
24
25LPCWSTR vcsNgenQuery =
26 L"SELECT `Wix4NetFxNativeImage`.`File_`, `Wix4NetFxNativeImage`.`Wix4NetFxNativeImage`, `Wix4NetFxNativeImage`.`Priority`, `Wix4NetFxNativeImage`.`Attributes`, `Wix4NetFxNativeImage`.`File_Application`, `Wix4NetFxNativeImage`.`Directory_ApplicationBase`, `File`.`Component_` "
27 L"FROM `Wix4NetFxNativeImage`, `File` WHERE `File`.`File`=`Wix4NetFxNativeImage`.`File_`";
28enum eNgenQuery { ngqFile = 1, ngqId, ngqPriority, ngqAttributes, ngqFileApp, ngqDirAppBase, ngqComponent };
29
30LPCWSTR vcsNgenGac =
31 L"SELECT `MsiAssembly`.`File_Application` "
32 L"FROM `File`, `MsiAssembly` WHERE `File`.`Component_`=`MsiAssembly`.`Component_` AND `File`.`File`=?";
33enum eNgenGac { nggApplication = 1 };
34
35LPCWSTR vcsNgenStrongName =
36 L"SELECT `Name`,`Value` FROM `MsiAssemblyName` WHERE `Component_`=?";
37enum eNgenStrongName { ngsnName = 1, ngsnValue };
38
39// Searches subdirectories of the given path for the highest version of ngen.exe available
40static HRESULT GetNgenVersion(
41 __in LPWSTR pwzParentPath,
42 __out LPWSTR* ppwzVersion
43 )
44{
45 Assert(pwzParentPath);
46
47 HRESULT hr = S_OK;
48 DWORD dwError = 0;
49 DWORD dwNgenFileFlags = 0;
50
51 LPWSTR pwzVersionSearch = NULL;
52 LPWSTR pwzNgen = NULL;
53 LPWSTR pwzTemp = NULL;
54 LPWSTR pwzTempVersion = NULL;
55 DWORD dwMaxMajorVersion = 0; // This stores the highest major version we've seen so far
56 DWORD dwMaxMinorVersion = 0; // This stores the minor version of the highest major version we've seen so far
57 DWORD dwMajorVersion = 0; // This stores the major version of the directory we're currently considering
58 DWORD dwMinorVersion = 0; // This stores the minor version of the directory we're currently considering
59 BOOL fFound = TRUE;
60 WIN32_FIND_DATAW wfdVersionDirectories;
61 HANDLE hFind = INVALID_HANDLE_VALUE;
62
63 hr = StrAllocFormatted(&pwzVersionSearch, L"%s*", pwzParentPath);
64 ExitOnFailure(hr, "failed to create outer directory search string from string %ls", pwzParentPath);
65 hFind = FindFirstFileW(pwzVersionSearch, &wfdVersionDirectories);
66 if (hFind == INVALID_HANDLE_VALUE)
67 {
68 ExitWithLastError(hr, "failed to call FindFirstFileW with string %ls", pwzVersionSearch);
69 }
70
71 while (fFound)
72 {
73 pwzTempVersion = (LPWSTR)&(wfdVersionDirectories.cFileName);
74
75 // Explicitly exclude v1.1.4322, which isn't backwards compatible and is not supported
76 if (wfdVersionDirectories.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
77 {
78 if (0 != lstrcmpW(L"v1.1.4322", pwzTempVersion))
79 {
80 // A potential candidate directory was found to run ngen from - let's make sure ngen actually exists here
81 hr = StrAllocFormatted(&pwzNgen, L"%s%s\\ngen.exe", pwzParentPath, pwzTempVersion);
82 ExitOnFailure(hr, "failed to create inner ngen search string with strings %ls and %ls", pwzParentPath, pwzTempVersion);
83
84 // If Ngen.exe does exist as a file here, then let's check the file version
85 if (FileExistsEx(pwzNgen, &dwNgenFileFlags) && (0 == (dwNgenFileFlags & FILE_ATTRIBUTE_DIRECTORY)))
86 {
87 hr = FileVersion(pwzNgen, &dwMajorVersion, &dwMinorVersion);
88
89 if (FAILED(hr))
90 {
91 WcaLog(LOGMSG_VERBOSE, "Failed to get version of %ls - continuing", pwzNgen);
92 }
93 else if (dwMajorVersion > dwMaxMajorVersion || (dwMajorVersion == dwMaxMajorVersion && dwMinorVersion > dwMaxMinorVersion))
94 {
95 // 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
96 hr = StrAllocString(ppwzVersion, pwzTempVersion, 0);
97 ExitOnFailure(hr, "failed to copy temp version string %ls to version string", pwzTempVersion);
98 // Add one for the backslash after the directory name
99 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));
100
101 dwMaxMajorVersion = dwMajorVersion;
102 dwMaxMinorVersion = dwMinorVersion;
103 }
104 }
105 else
106 {
107 WcaLog(LOGMSG_VERBOSE, "Ignoring %ls because it doesn't contain the file ngen.exe", pwzTempVersion);
108 }
109 }
110 else
111 {
112 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);
113 }
114 }
115 else
116 {
117 WcaLog(LOGMSG_VERBOSE, "Ignoring %ls because it isn't a directory", pwzTempVersion);
118 }
119
120 fFound = FindNextFileW(hFind, &wfdVersionDirectories);
121
122 if (!fFound)
123 {
124 dwError = ::GetLastError();
125 hr = (ERROR_NO_MORE_FILES == dwError) ? ERROR_SUCCESS : HRESULT_FROM_WIN32(dwError);
126 ExitOnFailure(hr, "Failed to call FindNextFileW() with query %ls", pwzVersionSearch);
127 }
128 }
129
130 if (NULL == *ppwzVersion)
131 {
132 hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
133 ExitOnRootFailure(hr, "Searched through all subdirectories of %ls, but failed to find any version of ngen.exe", pwzParentPath);
134 }
135 else
136 {
137 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));
138 }
139
140LExit:
141 if (hFind != INVALID_HANDLE_VALUE)
142 {
143 if (0 == FindClose(hFind))
144 {
145 dwError = ::GetLastError();
146 hr = HRESULT_FROM_WIN32(dwError);
147 WcaLog(LOGMSG_STANDARD, "Failed to close handle created by outer FindFirstFile with error %x - continuing", hr);
148 }
149 hFind = INVALID_HANDLE_VALUE;
150 }
151
152 ReleaseStr(pwzVersionSearch);
153 ReleaseStr(pwzNgen);
154 ReleaseStr(pwzTemp);
155 // Purposely don't release pwzTempVersion, because it wasn't allocated in this function, it's just a pointer to a string inside wfdVersionDirectories
156
157 return hr;
158}
159
160// Gets the path to ngen.exe
161static HRESULT GetNgenPath(
162 __out LPWSTR* ppwzNgenPath,
163 __in BOOL f64BitFramework
164 )
165{
166 Assert(ppwzNgenPath);
167 HRESULT hr = S_OK;
168
169 LPWSTR pwzVersion = NULL;
170 LPWSTR pwzWindowsFolder = NULL;
171
172 hr = WcaGetProperty(L"WindowsFolder", &pwzWindowsFolder);
173 ExitOnFailure(hr, "failed to get WindowsFolder property");
174
175 hr = StrAllocString(ppwzNgenPath, pwzWindowsFolder, 0);
176 ExitOnFailure(hr, "failed to copy to NgenPath windows folder: %ls", pwzWindowsFolder);
177
178 if (f64BitFramework)
179 {
180 WcaLog(LOGMSG_VERBOSE, "Searching for ngen under 64-bit framework path");
181
182 hr = StrAllocConcat(ppwzNgenPath, L"Microsoft.NET\\Framework64\\", 0);
183 ExitOnFailure(hr, "failed to copy platform portion of ngen path");
184 }
185 else
186 {
187 WcaLog(LOGMSG_VERBOSE, "Searching for ngen under 32-bit framework path");
188
189 hr = StrAllocConcat(ppwzNgenPath, L"Microsoft.NET\\Framework\\", 0);
190 ExitOnFailure(hr, "failed to copy platform portion of ngen path");
191 }
192
193 // 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
194 hr = GetNgenVersion(*ppwzNgenPath, &pwzVersion);
195 ExitOnFailure(hr, "failed to search for ngen under path %ls", *ppwzNgenPath);
196
197 hr = StrAllocConcat(ppwzNgenPath, pwzVersion, 0);
198 ExitOnFailure(hr, "failed to copy version portion of ngen path");
199
200 hr = StrAllocConcat(ppwzNgenPath, L"\\ngen.exe", 0);
201 ExitOnFailure(hr, "failed to copy \"\\ngen.exe\" portion of ngen path");
202
203LExit:
204 ReleaseStr(pwzVersion);
205 ReleaseStr(pwzWindowsFolder);
206
207 return hr;
208}
209
210
211static HRESULT GetStrongName(
212 __out LPWSTR* ppwzStrongName,
213 __in LPCWSTR pwzComponent
214 )
215{
216 Assert(ppwzStrongName);
217 HRESULT hr = S_OK;
218
219 PMSIHANDLE hView = NULL;
220 PMSIHANDLE hComponentRec = NULL;
221 PMSIHANDLE hRec = NULL;
222
223 LPWSTR pwzData = NULL;
224 LPWSTR pwzName = NULL;
225 LPWSTR pwzVersion = NULL;
226 LPWSTR pwzCulture = NULL;
227 LPWSTR pwzPublicKeyToken = NULL;
228
229 hComponentRec = ::MsiCreateRecord(1);
230 hr = WcaSetRecordString(hComponentRec, 1, pwzComponent);
231 ExitOnFailure(hr, "failed to set component value in record to: %ls", pwzComponent);
232
233 // get the name value records for this component
234 hr = WcaOpenView(vcsNgenStrongName, &hView);
235 ExitOnFailure(hr, "failed to open view on Wix4NetFxNativeImage table");
236
237 hr = WcaExecuteView(hView, hComponentRec);
238 ExitOnFailure(hr, "failed to execute strong name view");
239
240 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
241 {
242 hr = WcaGetRecordString(hRec, ngsnName, &pwzData);
243 ExitOnFailure(hr, "failed to get MsiAssemblyName.Name for component: %ls", pwzComponent);
244
245 if (0 == lstrcmpW(L"name", pwzData))
246 {
247 hr = WcaGetRecordString(hRec, ngsnValue, &pwzName);
248 ExitOnFailure(hr, "failed to get MsiAssemblyName.Value for component: %ls Name: %ls", pwzComponent, pwzData);
249 }
250 else if (0 == lstrcmpW(L"version", pwzData))
251 {
252 hr = WcaGetRecordString(hRec, ngsnValue, &pwzVersion);
253 ExitOnFailure(hr, "failed to get MsiAssemblyName.Value for component: %ls Name: %ls", pwzComponent, pwzData);
254 }
255 else if (0 == lstrcmpW(L"culture", pwzData))
256 {
257 hr = WcaGetRecordString(hRec, ngsnValue, &pwzCulture);
258 ExitOnFailure(hr, "failed to get MsiAssemblyName.Value for component: %ls Name: %ls", pwzComponent, pwzData);
259 }
260 else if (0 == lstrcmpW(L"publicKeyToken", pwzData))
261 {
262 hr = WcaGetRecordString(hRec, ngsnValue, &pwzPublicKeyToken);
263 ExitOnFailure(hr, "failed to get MsiAssemblyName.Value for component: %ls Name: %ls", pwzComponent, pwzData);
264 }
265 }
266 if (E_NOMOREITEMS == hr)
267 hr = S_OK;
268 ExitOnFailure(hr, "failed while looping through all names and values in MsiAssemblyName table for component: %ls", pwzComponent);
269
270 hr = StrAllocFormatted(ppwzStrongName, L"\"%s, Version=%s, Culture=%s, PublicKeyToken=%s\"", pwzName, pwzVersion, pwzCulture, pwzPublicKeyToken);
271 ExitOnFailure(hr, "failed to format strong name for component: %ls", pwzComponent);
272
273LExit:
274 ReleaseStr(pwzData);
275 ReleaseStr(pwzName);
276 ReleaseStr(pwzVersion);
277 ReleaseStr(pwzCulture);
278 ReleaseStr(pwzPublicKeyToken);
279
280 return hr;
281}
282
283static HRESULT CreateInstallCommand(
284 __out LPWSTR* ppwzCommandLine,
285 __in LPCWSTR pwzNgenPath,
286 __in LPCWSTR pwzFile,
287 __in int iPriority,
288 __in int iAttributes,
289 __in LPCWSTR pwzFileApp,
290 __in LPCWSTR pwzDirAppBase
291 )
292{
293 Assert(ppwzCommandLine && pwzNgenPath && *pwzNgenPath && pwzFile && *pwzFile&& pwzFileApp && pwzDirAppBase);
294 HRESULT hr = S_OK;
295
296 LPWSTR pwzQueueString = NULL;
297
298 hr = StrAllocFormatted(ppwzCommandLine, L"%s install %s", pwzNgenPath, pwzFile);
299 ExitOnFailure(hr, "failed to assemble install command line");
300
301 if (iPriority > 0)
302 {
303 hr = StrAllocFormatted(&pwzQueueString, L" /queue:%d", iPriority);
304 ExitOnFailure(hr, "failed to format queue string");
305
306 hr = StrAllocConcat(ppwzCommandLine, pwzQueueString, 0);
307 ExitOnFailure(hr, "failed to add queue string to NGEN command line");
308 }
309
310 if (NGEN_DEBUG & iAttributes)
311 {
312 hr = StrAllocConcat(ppwzCommandLine, L" /Debug", 0);
313 ExitOnFailure(hr, "failed to add debug to NGEN command line");
314 }
315
316 if (NGEN_PROFILE & iAttributes)
317 {
318 hr = StrAllocConcat(ppwzCommandLine, L" /Profile", 0);
319 ExitOnFailure(hr, "failed to add profile to NGEN command line");
320 }
321
322 if (NGEN_NODEP & iAttributes)
323 {
324 hr = StrAllocConcat(ppwzCommandLine, L" /NoDependencies", 0);
325 ExitOnFailure(hr, "failed to add no dependencies to NGEN command line");
326 }
327
328 // If it's more than just two quotes around an empty string
329 if (EMPTY_FORMATTED_LENGTH_QUOTED_FILE < lstrlenW(pwzFileApp))
330 {
331 hr = StrAllocConcat(ppwzCommandLine, L" /ExeConfig:", 0);
332 ExitOnFailure(hr, "failed to add exe config to NGEN command line");
333
334 hr = StrAllocConcat(ppwzCommandLine, pwzFileApp, 0);
335 ExitOnFailure(hr, "failed to add file app to NGEN command line");
336 }
337
338 // If it's more than just two quotes around a backslash
339 if (EMPTY_FORMATTED_LENGTH_QUOTED_DIRECTORY < lstrlenW(pwzDirAppBase))
340 {
341 hr = StrAllocConcat(ppwzCommandLine, L" /AppBase:", 0);
342 ExitOnFailure(hr, "failed to add app base to NGEN command line");
343
344 hr = StrAllocConcat(ppwzCommandLine, pwzDirAppBase, 0);
345 ExitOnFailure(hr, "failed to add dir app base to NGEN command line");
346 }
347
348LExit:
349 return hr;
350}
351
352/******************************************************************
353 FileIdExists - checks if the file ID is found in the File table
354
355 returns S_OK if the file exists; S_FALSE if not; otherwise, error
356********************************************************************/
357static HRESULT FileIdExists(
358 __in_opt LPCWSTR wzFile
359 )
360{
361 HRESULT hr = S_OK;
362 PMSIHANDLE hView = NULL;
363 PMSIHANDLE hRec = NULL;
364
365 if (!wzFile)
366 {
367 hr = S_FALSE;
368 ExitFunction();
369 }
370
371 hRec = ::MsiCreateRecord(1);
372 hr = WcaSetRecordString(hRec, fiFile, wzFile);
373 ExitOnFailure(hr, "failed to create a record with the file: %ls", wzFile);
374
375 hr = WcaTableExists(L"File");
376 if (S_OK == hr)
377 {
378 hr = WcaOpenView(vcsFileId, &hView);
379 ExitOnFailure(hr, "failed to open view on File table");
380
381 hr = WcaExecuteView(hView, hRec);
382 ExitOnFailure(hr, "failed to execute view on File table");
383
384 // Reuse the same record; the handle will be released.
385 hr = WcaFetchSingleRecord(hView, &hRec);
386 ExitOnFailure(hr, "failed to fetch File from File table");
387 }
388
389LExit:
390
391 return hr;
392}
393
394/******************************************************************
395 SchedNetFx - entry point for NetFx Custom Action
396
397********************************************************************/
398extern "C" UINT __stdcall SchedNetFx(
399 __in MSIHANDLE hInstall
400 )
401{
402 // AssertSz(FALSE, "debug SchedNetFx");
403
404 HRESULT hr = S_OK;
405 UINT er = ERROR_SUCCESS;
406
407 LPWSTR pwzInstallCustomActionData = NULL;
408 LPWSTR pwzUninstallCustomActionData = NULL;
409 UINT uiCost = 0;
410
411 PMSIHANDLE hView = NULL;
412 PMSIHANDLE hRec = NULL;
413 PMSIHANDLE hViewGac = NULL;
414 PMSIHANDLE hRecGac = NULL;
415
416 LPWSTR pwzId = NULL;
417 LPWSTR pwzData = NULL;
418 LPWSTR pwzTemp = NULL;
419 LPWSTR pwzFile = NULL;
420 int iPriority = 0;
421 int iAssemblyCost = 0;
422 int iAttributes = 0;
423 LPWSTR pwzFileApp = NULL;
424 LPWSTR pwzDirAppBase = NULL;
425 LPWSTR pwzComponent = NULL;
426
427 INSTALLSTATE isInstalled;
428 INSTALLSTATE isAction;
429
430 LPWSTR pwz32Ngen = NULL;
431 LPWSTR pwz64Ngen = NULL;
432
433 BOOL f32NgenExeExists = FALSE;
434 BOOL f64NgenExeExists = FALSE;
435
436 BOOL fNeedInstallUpdate32 = FALSE;
437 BOOL fNeedUninstallUpdate32 = FALSE;
438 BOOL fNeedInstallUpdate64 = FALSE;
439 BOOL fNeedUninstallUpdate64 = FALSE;
440
441 // initialize
442 hr = WcaInitialize(hInstall, "SchedNetFx");
443 ExitOnFailure(hr, "failed to initialize");
444
445 hr = GetNgenPath(&pwz32Ngen, FALSE);
446 f32NgenExeExists = SUCCEEDED(hr);
447 if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
448 {
449 hr = ERROR_SUCCESS;
450 WcaLog(LOGMSG_STANDARD, "Failed to find 32bit ngen. No actions will be scheduled to create native images for 32bit.");
451 }
452 ExitOnFailure(hr, "failed to get 32bit ngen.exe path");
453
454 hr = GetNgenPath(&pwz64Ngen, TRUE);
455 f64NgenExeExists = SUCCEEDED(hr);
456 if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
457 {
458 hr = ERROR_SUCCESS;
459 WcaLog(LOGMSG_STANDARD, "Failed to find 64bit ngen. No actions will be scheduled to create native images for 64bit.");
460 }
461 ExitOnFailure(hr, "failed to get 64bit ngen.exe path");
462
463 // loop through all the NetFx records
464 hr = WcaOpenExecuteView(vcsNgenQuery, &hView);
465 ExitOnFailure(hr, "failed to open view on Wix4NetFxNativeImage table");
466
467 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
468 {
469 // Get Id
470 hr = WcaGetRecordString(hRec, ngqId, &pwzId);
471 ExitOnFailure(hr, "failed to get Wix4NetFxNativeImage.Wix4NetFxNativeImage");
472
473 // Get File
474 hr = WcaGetRecordString(hRec, ngqFile, &pwzData);
475 ExitOnFailure(hr, "failed to get Wix4NetFxNativeImage.File_ for record: %ls", pwzId);
476 hr = StrAllocFormatted(&pwzTemp, vpwzUnformattedQuotedFile, pwzData);
477 ExitOnFailure(hr, "failed to format file string for file: %ls", pwzData);
478 hr = WcaGetFormattedString(pwzTemp, &pwzFile);
479 ExitOnFailure(hr, "failed to get formatted string for file: %ls", pwzData);
480
481 // Get Priority
482 hr = WcaGetRecordInteger(hRec, ngqPriority, &iPriority);
483 ExitOnFailure(hr, "failed to get Wix4NetFxNativeImage.Priority for record: %ls", pwzId);
484
485 if (0 == iPriority)
486 iAssemblyCost = COST_NGEN_BLOCKING;
487 else
488 iAssemblyCost = COST_NGEN_NONBLOCKING;
489
490 // Get Attributes
491 hr = WcaGetRecordInteger(hRec, ngqAttributes, &iAttributes);
492 ExitOnFailure(hr, "failed to get Wix4NetFxNativeImage.Attributes for record: %ls", pwzId);
493
494 // Get File_Application or leave pwzFileApp NULL.
495 hr = WcaGetRecordFormattedString(hRec, ngqFileApp, &pwzData);
496 ExitOnFailure(hr, "failed to get Wix4NetFxNativeImage.File_Application for record: %ls", pwzId);
497
498 // Check if the value resolves to a valid file ID.
499 if (S_OK == FileIdExists(pwzData))
500 {
501 // Resolve the file ID to a path.
502 hr = StrAllocFormatted(&pwzTemp, vpwzUnformattedQuotedFile, pwzData);
503 ExitOnFailure(hr, "failed to format file application string for file: %ls", pwzData);
504
505 hr = WcaGetFormattedString(pwzTemp, &pwzFileApp);
506 ExitOnFailure(hr, "failed to get formatted string for file application: %ls", pwzData);
507 }
508 else
509 {
510 // Assume record formatted to a path already.
511 hr = StrAllocString(&pwzFileApp, pwzData, 0);
512 ExitOnFailure(hr, "failed to allocate string for file path: %ls", pwzData);
513
514 hr = PathEnsureQuoted(&pwzFileApp, FALSE);
515 ExitOnFailure(hr, "failed to quote file path: %ls", pwzData);
516 }
517
518 // Get Directory_ApplicationBase or leave pwzDirAppBase NULL.
519 hr = WcaGetRecordFormattedString(hRec, ngqDirAppBase, &pwzData);
520 ExitOnFailure(hr, "failed to get Wix4NetFxNativeImage.Directory_ApplicationBase for record: %ls", pwzId);
521
522 if (WcaIsUnicodePropertySet(pwzData))
523 {
524 // Resolve the directory ID to a path.
525 hr = StrAllocFormatted(&pwzTemp, vpwzUnformattedQuotedDirectory, pwzData);
526 ExitOnFailure(hr, "failed to format directory application base string for property: %ls", pwzData);
527
528 hr = WcaGetFormattedString(pwzTemp, &pwzDirAppBase);
529 ExitOnFailure(hr, "failed to get formatted string for directory application base: %ls", pwzData);
530 }
531 else
532 {
533 // Assume record formatted to a path already.
534 hr = StrAllocString(&pwzDirAppBase, pwzData, 0);
535 ExitOnFailure(hr, "failed to allocate string for directory path: %ls", pwzData);
536
537 hr = PathEnsureQuoted(&pwzDirAppBase, TRUE);
538 ExitOnFailure(hr, "failed to quote and backslashify directory: %ls", pwzData);
539 }
540
541 // Get Component
542 hr = WcaGetRecordString(hRec, ngqComponent, &pwzComponent);
543 ExitOnFailure(hr, "failed to get Wix4NetFxNativeImage.Directory_ApplicationBase for record: %ls", pwzId);
544 er = ::MsiGetComponentStateW(hInstall, pwzComponent, &isInstalled, &isAction);
545 ExitOnWin32Error(er, hr, "failed to get install state for Component: %ls", pwzComponent);
546
547 //
548 // Figure out if it's going to be GAC'd. The possibility exists that no assemblies are going to be GAC'd
549 // so we have to check for the MsiAssembly table first.
550 //
551 if (S_OK == WcaTableExists(L"MsiAssembly"))
552 {
553 hr = WcaOpenView(vcsNgenGac, &hViewGac);
554 ExitOnFailure(hr, "failed to open view on File/MsiAssembly table");
555
556 hr = WcaExecuteView(hViewGac, hRec);
557 ExitOnFailure(hr, "failed to execute view on File/MsiAssembly table");
558
559 hr = WcaFetchSingleRecord(hViewGac, &hRecGac);
560 ExitOnFailure(hr, "failed to fetch File_Assembly from File/MsiAssembly table");
561
562 if (S_FALSE != hr)
563 {
564 hr = WcaGetRecordString(hRecGac, nggApplication, &pwzData);
565 ExitOnFailure(hr, "failed to get MsiAssembly.File_Application");
566
567 // If it's in the GAC replace the file name with the strong name
568 if (L'\0' == pwzData[0])
569 {
570 hr = GetStrongName(&pwzFile, pwzComponent);
571 ExitOnFailure(hr, "failed to get strong name for component: %ls", pwzData);
572 }
573 }
574 }
575
576 //
577 // Schedule the work
578 //
579 if (!(iAttributes & NGEN_32BIT) && !(iAttributes & NGEN_64BIT))
580 ExitOnFailure(hr = E_INVALIDARG, "Neither 32bit nor 64bit is specified for NGEN of file: %ls", pwzFile);
581
582 if (WcaIsInstalling(isInstalled, isAction) || WcaIsReInstalling(isInstalled, isAction))
583 {
584 if (iAttributes & NGEN_32BIT && f32NgenExeExists)
585 {
586 // Assemble the install command line
587 hr = CreateInstallCommand(&pwzData, pwz32Ngen, pwzFile, iPriority, iAttributes, pwzFileApp, pwzDirAppBase);
588 ExitOnFailure(hr, "failed to create install command line");
589
590 hr = WcaWriteStringToCaData(pwzData, &pwzInstallCustomActionData);
591 ExitOnFailure(hr, "failed to add install command to custom action data: %ls", pwzData);
592
593 hr = WcaWriteIntegerToCaData(iAssemblyCost, &pwzInstallCustomActionData);
594 ExitOnFailure(hr, "failed to add cost to custom action data: %ls", pwzData);
595
596 uiCost += iAssemblyCost;
597
598 fNeedInstallUpdate32 = TRUE;
599 }
600
601 if (iAttributes & NGEN_64BIT && f64NgenExeExists)
602 {
603 // Assemble the install command line
604 hr = CreateInstallCommand(&pwzData, pwz64Ngen, pwzFile, iPriority, iAttributes, pwzFileApp, pwzDirAppBase);
605 ExitOnFailure(hr, "failed to create install command line");
606
607 hr = WcaWriteStringToCaData(pwzData, &pwzInstallCustomActionData); // command
608 ExitOnFailure(hr, "failed to add install command to custom action data: %ls", pwzData);
609
610 hr = WcaWriteIntegerToCaData(iAssemblyCost, &pwzInstallCustomActionData); // cost
611 ExitOnFailure(hr, "failed to add cost to custom action data: %ls", pwzData);
612
613 uiCost += iAssemblyCost;
614
615 fNeedInstallUpdate64 = TRUE;
616 }
617 }
618 else if (WcaIsUninstalling(isInstalled, isAction))
619 {
620 if (iAttributes & NGEN_32BIT && f32NgenExeExists)
621 {
622 hr = StrAllocFormatted(&pwzData, L"%s uninstall %s", pwz32Ngen, pwzFile);
623 ExitOnFailure(hr, "failed to create update 32 command line");
624
625 hr = WcaWriteStringToCaData(pwzData, &pwzUninstallCustomActionData); // command
626 ExitOnFailure(hr, "failed to add install command to custom action data: %ls", pwzData);
627
628 hr = WcaWriteIntegerToCaData(COST_NGEN_NONBLOCKING, &pwzUninstallCustomActionData); // cost
629 ExitOnFailure(hr, "failed to add cost to custom action data: %ls", pwzData);
630
631 uiCost += COST_NGEN_NONBLOCKING;
632
633 fNeedUninstallUpdate32 = TRUE;
634 }
635
636 if (iAttributes & NGEN_64BIT && f64NgenExeExists)
637 {
638 hr = StrAllocFormatted(&pwzData, L"%s uninstall %s", pwz64Ngen, pwzFile);
639 ExitOnFailure(hr, "failed to create update 64 command line");
640
641 hr = WcaWriteStringToCaData(pwzData, &pwzUninstallCustomActionData); // command
642 ExitOnFailure(hr, "failed to add install command to custom action data: %ls", pwzData);
643
644 hr = WcaWriteIntegerToCaData(COST_NGEN_NONBLOCKING, &pwzUninstallCustomActionData); // cost
645 ExitOnFailure(hr, "failed to add cost to custom action data: %ls", pwzData);
646
647 uiCost += COST_NGEN_NONBLOCKING;
648
649 fNeedUninstallUpdate64 = TRUE;
650 }
651 }
652 }
653 if (E_NOMOREITEMS == hr)
654 hr = S_OK;
655 ExitOnFailure(hr, "failed while looping through all files to create native images for");
656
657 // If we need 32 bit install update
658 if (fNeedInstallUpdate32)
659 {
660 hr = StrAllocFormatted(&pwzData, L"%s update /queue", pwz32Ngen);
661 ExitOnFailure(hr, "failed to create install update 32 command line");
662
663 hr = WcaWriteStringToCaData(pwzData, &pwzInstallCustomActionData); // command
664 ExitOnFailure(hr, "failed to add install command to install custom action data: %ls", pwzData);
665
666 hr = WcaWriteIntegerToCaData(COST_NGEN_NONBLOCKING, &pwzInstallCustomActionData); // cost
667 ExitOnFailure(hr, "failed to add cost to install custom action data: %ls", pwzData);
668
669 uiCost += COST_NGEN_NONBLOCKING;
670 }
671
672 // If we need 32 bit uninstall update
673 if (fNeedUninstallUpdate32)
674 {
675 hr = StrAllocFormatted(&pwzData, L"%s update /queue", pwz32Ngen);
676 ExitOnFailure(hr, "failed to create uninstall update 32 command line");
677
678 hr = WcaWriteStringToCaData(pwzData, &pwzUninstallCustomActionData); // command
679 ExitOnFailure(hr, "failed to add install command to uninstall custom action data: %ls", pwzData);
680
681 hr = WcaWriteIntegerToCaData(COST_NGEN_NONBLOCKING, &pwzUninstallCustomActionData); // cost
682 ExitOnFailure(hr, "failed to add cost to uninstall custom action data: %ls", pwzData);
683
684 uiCost += COST_NGEN_NONBLOCKING;
685 }
686
687 // If we need 64 bit install update
688 if (fNeedInstallUpdate64)
689 {
690 hr = StrAllocFormatted(&pwzData, L"%s update /queue", pwz64Ngen);
691 ExitOnFailure(hr, "failed to create install update 64 command line");
692
693 hr = WcaWriteStringToCaData(pwzData, &pwzInstallCustomActionData); // command
694 ExitOnFailure(hr, "failed to add install command to install custom action data: %ls", pwzData);
695
696 hr = WcaWriteIntegerToCaData(COST_NGEN_NONBLOCKING, &pwzInstallCustomActionData); // cost
697 ExitOnFailure(hr, "failed to add cost to install custom action data: %ls", pwzData);
698
699 uiCost += COST_NGEN_NONBLOCKING;
700 }
701
702 // If we need 64 bit install update
703 if (fNeedUninstallUpdate64)
704 {
705 hr = StrAllocFormatted(&pwzData, L"%s update /queue", pwz64Ngen);
706 ExitOnFailure(hr, "failed to create uninstall update 64 command line");
707
708 hr = WcaWriteStringToCaData(pwzData, &pwzUninstallCustomActionData); // command
709 ExitOnFailure(hr, "failed to add install command to uninstall custom action data: %ls", pwzData);
710
711 hr = WcaWriteIntegerToCaData(COST_NGEN_NONBLOCKING, &pwzUninstallCustomActionData); // cost
712 ExitOnFailure(hr, "failed to add cost to uninstall custom action data: %ls", pwzData);
713
714 uiCost += COST_NGEN_NONBLOCKING;
715 }
716
717 // Add to progress bar
718 if ((pwzInstallCustomActionData && *pwzInstallCustomActionData) || (pwzUninstallCustomActionData && *pwzUninstallCustomActionData))
719 {
720 hr = WcaProgressMessage(uiCost, TRUE);
721 ExitOnFailure(hr, "failed to extend progress bar for NetFxExecuteNativeImage");
722 }
723
724 // Schedule the install custom action
725 if (pwzInstallCustomActionData && *pwzInstallCustomActionData)
726 {
727 hr = WcaSetProperty(L"NetFxExecuteNativeImageInstall", pwzInstallCustomActionData);
728 ExitOnFailure(hr, "failed to schedule NetFxExecuteNativeImageInstall action");
729
730 hr = WcaSetProperty(L"NetFxExecuteNativeImageCommitInstall", pwzInstallCustomActionData);
731 ExitOnFailure(hr, "failed to schedule NetFxExecuteNativeImageCommitInstall action");
732 }
733
734 // Schedule the uninstall custom action
735 if (pwzUninstallCustomActionData && *pwzUninstallCustomActionData)
736 {
737 hr = WcaSetProperty(L"NetFxExecuteNativeImageUninstall", pwzUninstallCustomActionData);
738 ExitOnFailure(hr, "failed to schedule NetFxExecuteNativeImageUninstall action");
739
740 hr = WcaSetProperty(L"NetFxExecuteNativeImageCommitUninstall", pwzUninstallCustomActionData);
741 ExitOnFailure(hr, "failed to schedule NetFxExecuteNativeImageCommitUninstall action");
742 }
743
744
745LExit:
746 ReleaseStr(pwzInstallCustomActionData);
747 ReleaseStr(pwzUninstallCustomActionData);
748 ReleaseStr(pwzId);
749 ReleaseStr(pwzData);
750 ReleaseStr(pwzTemp);
751 ReleaseStr(pwzFile);
752 ReleaseStr(pwzFileApp);
753 ReleaseStr(pwzDirAppBase);
754 ReleaseStr(pwzComponent);
755 ReleaseStr(pwz32Ngen);
756 ReleaseStr(pwz64Ngen);
757
758 if (FAILED(hr))
759 er = ERROR_INSTALL_FAILURE;
760 return WcaFinalize(er);
761}
762
763
764/******************************************************************
765 ExecNetFx - entry point for NetFx Custom Action
766
767*******************************************************************/
768extern "C" UINT __stdcall ExecNetFx(
769 __in MSIHANDLE hInstall
770 )
771{
772// AssertSz(FALSE, "debug ExecNetFx");
773
774 HRESULT hr = S_OK;
775 UINT er = ERROR_SUCCESS;
776
777 LPWSTR pwzCustomActionData = NULL;
778 LPWSTR pwzData = NULL;
779 LPWSTR pwz = NULL;
780 int iCost = 0;
781
782 // initialize
783 hr = WcaInitialize(hInstall, "ExecNetFx");
784 ExitOnFailure(hr, "failed to initialize");
785
786 hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData);
787 ExitOnFailure(hr, "failed to get CustomActionData");
788
789 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData);
790
791 pwz = pwzCustomActionData;
792
793 // loop through all the passed in data
794 while (pwz && *pwz)
795 {
796 hr = WcaReadStringFromCaData(&pwz, &pwzData);
797 ExitOnFailure(hr, "failed to read command line from custom action data");
798
799 hr = WcaReadIntegerFromCaData(&pwz, &iCost);
800 ExitOnFailure(hr, "failed to read cost from custom action data");
801
802 hr = QuietExec(pwzData, NGEN_TIMEOUT, TRUE, TRUE);
803 // If we fail here it isn't critical - keep looping through to try to act on the other assemblies on our list
804 if (FAILED(hr))
805 {
806 WcaLog(LOGMSG_STANDARD, "failed to execute Ngen command (with error 0x%x): %ls, continuing anyway", hr, pwzData);
807 hr = S_OK;
808 }
809
810 // Tick the progress bar along for this assembly
811 hr = WcaProgressMessage(iCost, FALSE);
812 ExitOnFailure(hr, "failed to tick progress bar for command line: %ls", pwzData);
813 }
814
815LExit:
816 ReleaseStr(pwzCustomActionData);
817 ReleaseStr(pwzData);
818
819 if (FAILED(hr))
820 er = ERROR_INSTALL_FAILURE;
821 return WcaFinalize(er);
822}
823
diff --git a/src/ext/NetFx/ca/netfxca.def b/src/ext/NetFx/ca/netfxca.def
new file mode 100644
index 00000000..c1d01f5f
--- /dev/null
+++ b/src/ext/NetFx/ca/netfxca.def
@@ -0,0 +1,8 @@
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
4LIBRARY "netfxca"
5
6EXPORTS
7 SchedNetFx
8 ExecNetFx
diff --git a/src/ext/NetFx/ca/netfxca.vcxproj b/src/ext/NetFx/ca/netfxca.vcxproj
new file mode 100644
index 00000000..5e25b683
--- /dev/null
+++ b/src/ext/NetFx/ca/netfxca.vcxproj
@@ -0,0 +1,73 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4<Project DefaultTargets="Build" ToolsVersion="16.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
5 <ItemGroup Label="ProjectConfigurations">
6 <ProjectConfiguration Include="Debug|Win32">
7 <Configuration>Debug</Configuration>
8 <Platform>Win32</Platform>
9 </ProjectConfiguration>
10 <ProjectConfiguration Include="Release|Win32">
11 <Configuration>Release</Configuration>
12 <Platform>Win32</Platform>
13 </ProjectConfiguration>
14 <ProjectConfiguration Include="Debug|x64">
15 <Configuration>Debug</Configuration>
16 <Platform>x64</Platform>
17 </ProjectConfiguration>
18 <ProjectConfiguration Include="Release|x64">
19 <Configuration>Release</Configuration>
20 <Platform>x64</Platform>
21 </ProjectConfiguration>
22 <ProjectConfiguration Include="Debug|ARM64">
23 <Configuration>Debug</Configuration>
24 <Platform>ARM64</Platform>
25 </ProjectConfiguration>
26 <ProjectConfiguration Include="Release|ARM64">
27 <Configuration>Release</Configuration>
28 <Platform>ARM64</Platform>
29 </ProjectConfiguration>
30 </ItemGroup>
31
32 <PropertyGroup Label="Globals">
33 <ProjectGuid>{F72D34CA-48DA-4DFD-91A9-A0C78BEF6981}</ProjectGuid>
34 <ConfigurationType>DynamicLibrary</ConfigurationType>
35 <TargetName>netfxca</TargetName>
36 <PlatformToolset>v142</PlatformToolset>
37 <CharacterSet>Unicode</CharacterSet>
38 <ProjectModuleDefinitionFile>netfxca.def</ProjectModuleDefinitionFile>
39 <Description>WiX Toolset .NET Framework CustomAction</Description>
40 </PropertyGroup>
41
42 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
43 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
44
45 <PropertyGroup>
46 <ProjectAdditionalLinkLibraries>msi.lib</ProjectAdditionalLinkLibraries>
47 </PropertyGroup>
48
49 <ItemGroup>
50 <ClCompile Include="dllmain.cpp">
51 <PrecompiledHeader>Create</PrecompiledHeader>
52 </ClCompile>
53 <ClCompile Include="netfxca.cpp" />
54 </ItemGroup>
55
56 <ItemGroup>
57 <ClInclude Include="cost.h" />
58 <ClInclude Include="precomp.h" />
59 </ItemGroup>
60
61 <ItemGroup>
62 <None Include="netfxca.def" />
63 </ItemGroup>
64
65 <ItemGroup>
66 <PackageReference Include="WixToolset.Dutil" Version="4.0.72" />
67 <PackageReference Include="WixToolset.WcaUtil" Version="4.0.19" />
68 <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" />
69 <PackageReference Include="Nerdbank.GitVersioning" Version="3.3.37" />
70 </ItemGroup>
71
72 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
73</Project>
diff --git a/src/ext/NetFx/ca/precomp.h b/src/ext/NetFx/ca/precomp.h
new file mode 100644
index 00000000..4a83c164
--- /dev/null
+++ b/src/ext/NetFx/ca/precomp.h
@@ -0,0 +1,14 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#include <windows.h>
6#include <msiquery.h>
7
8#include "wcautil.h"
9#include "fileutil.h"
10#include "strutil.h"
11#include "pathutil.h"
12
13#include "caDecor.h"
14#include "cost.h"