diff options
author | Rob Mensching <rob@firegiant.com> | 2021-05-03 16:05:08 -0700 |
---|---|---|
committer | Rob Mensching <rob@firegiant.com> | 2021-05-03 16:05:08 -0700 |
commit | 7bdd5e9159b298e0411afa689a06c44e36e293cd (patch) | |
tree | 57d24ea7fdd8025baf6a822a99b588c9df74a60d /src/ext/NetFx/ca/netfxca.cpp | |
parent | ca02e81316d700a3647414f355eab4d2115d6163 (diff) | |
download | wix-7bdd5e9159b298e0411afa689a06c44e36e293cd.tar.gz wix-7bdd5e9159b298e0411afa689a06c44e36e293cd.tar.bz2 wix-7bdd5e9159b298e0411afa689a06c44e36e293cd.zip |
Move NetFx.wixext into ext
Diffstat (limited to 'src/ext/NetFx/ca/netfxca.cpp')
-rw-r--r-- | src/ext/NetFx/ca/netfxca.cpp | 823 |
1 files changed, 823 insertions, 0 deletions
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 | ||
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`.`Wix4NetFxNativeImage`, `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 | // Searches subdirectories of the given path for the highest version of ngen.exe available | ||
40 | static 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 | |||
140 | LExit: | ||
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 | ||
161 | static 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 | |||
203 | LExit: | ||
204 | ReleaseStr(pwzVersion); | ||
205 | ReleaseStr(pwzWindowsFolder); | ||
206 | |||
207 | return hr; | ||
208 | } | ||
209 | |||
210 | |||
211 | static 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 | |||
273 | LExit: | ||
274 | ReleaseStr(pwzData); | ||
275 | ReleaseStr(pwzName); | ||
276 | ReleaseStr(pwzVersion); | ||
277 | ReleaseStr(pwzCulture); | ||
278 | ReleaseStr(pwzPublicKeyToken); | ||
279 | |||
280 | return hr; | ||
281 | } | ||
282 | |||
283 | static 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 | |||
348 | LExit: | ||
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 | ********************************************************************/ | ||
357 | static 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 | |||
389 | LExit: | ||
390 | |||
391 | return hr; | ||
392 | } | ||
393 | |||
394 | /****************************************************************** | ||
395 | SchedNetFx - entry point for NetFx Custom Action | ||
396 | |||
397 | ********************************************************************/ | ||
398 | extern "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 | |||
745 | LExit: | ||
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 | *******************************************************************/ | ||
768 | extern "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 | |||
815 | LExit: | ||
816 | ReleaseStr(pwzCustomActionData); | ||
817 | ReleaseStr(pwzData); | ||
818 | |||
819 | if (FAILED(hr)) | ||
820 | er = ERROR_INSTALL_FAILURE; | ||
821 | return WcaFinalize(er); | ||
822 | } | ||
823 | |||