diff options
Diffstat (limited to 'src/ca')
| -rw-r--r-- | src/ca/cost.h | 7 | ||||
| -rw-r--r-- | src/ca/dllmain.cpp | 26 | ||||
| -rw-r--r-- | src/ca/netfxca.cpp | 823 | ||||
| -rw-r--r-- | src/ca/netfxca.def | 8 | ||||
| -rw-r--r-- | src/ca/netfxca.vcxproj | 70 | ||||
| -rw-r--r-- | src/ca/packages.config | 6 | ||||
| -rw-r--r-- | src/ca/precomp.h | 13 |
7 files changed, 953 insertions, 0 deletions
diff --git a/src/ca/cost.h b/src/ca/cost.h new file mode 100644 index 00000000..95368eba --- /dev/null +++ b/src/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 | |||
| 5 | const UINT COST_NGEN_BLOCKING = 5000; | ||
| 6 | const UINT COST_NGEN_NONBLOCKING = 500; | ||
| 7 | |||
diff --git a/src/ca/dllmain.cpp b/src/ca/dllmain.cpp new file mode 100644 index 00000000..df53f872 --- /dev/null +++ b/src/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 | /******************************************************************** | ||
| 6 | DllMain - standard entry point for all WiX CustomActions | ||
| 7 | |||
| 8 | ********************************************************************/ | ||
| 9 | extern "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/ca/netfxca.cpp b/src/ca/netfxca.cpp new file mode 100644 index 00000000..15802f2b --- /dev/null +++ b/src/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 `NetFxNativeImage`.`File_`, `NetFxNativeImage`.`NetFxNativeImage`, `NetFxNativeImage`.`Priority`, `NetFxNativeImage`.`Attributes`, `NetFxNativeImage`.`File_Application`, `NetFxNativeImage`.`Directory_ApplicationBase`, `File`.`Component_` " | ||
| 27 | L"FROM `NetFxNativeImage`, `File` WHERE `File`.`File`=`NetFxNativeImage`.`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 NetFxNativeImage 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 NetFxNativeImage 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 NetFxNativeImage.NetFxNativeImage"); | ||
| 472 | |||
| 473 | // Get File | ||
| 474 | hr = WcaGetRecordString(hRec, ngqFile, &pwzData); | ||
| 475 | ExitOnFailure(hr, "failed to get NetFxNativeImage.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 NetFxNativeImage.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 NetFxNativeImage.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 NetFxNativeImage.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 NetFxNativeImage.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 NetFxNativeImage.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 | |||
diff --git a/src/ca/netfxca.def b/src/ca/netfxca.def new file mode 100644 index 00000000..c1d01f5f --- /dev/null +++ b/src/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 | |||
| 4 | LIBRARY "netfxca" | ||
| 5 | |||
| 6 | EXPORTS | ||
| 7 | SchedNetFx | ||
| 8 | ExecNetFx | ||
diff --git a/src/ca/netfxca.vcxproj b/src/ca/netfxca.vcxproj new file mode 100644 index 00000000..98b7352b --- /dev/null +++ b/src/ca/netfxca.vcxproj | |||
| @@ -0,0 +1,70 @@ | |||
| 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="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||
| 5 | <Import Project="..\..\packages\WixToolset.DUtil.4.0.6\build\WixToolset.DUtil.props" Condition="Exists('..\..\packages\WixToolset.DUtil.4.0.6\build\WixToolset.DUtil.props')" /> | ||
| 6 | <Import Project="..\..\packages\WixToolset.WcaUtil.4.0.2\build\WixToolset.WcaUtil.props" Condition="Exists('..\..\packages\WixToolset.WcaUtil.4.0.2\build\WixToolset.WcaUtil.props')" /> | ||
| 7 | |||
| 8 | <ItemGroup Label="ProjectConfigurations"> | ||
| 9 | <ProjectConfiguration Include="Debug|Win32"> | ||
| 10 | <Configuration>Debug</Configuration> | ||
| 11 | <Platform>Win32</Platform> | ||
| 12 | </ProjectConfiguration> | ||
| 13 | <ProjectConfiguration Include="Release|Win32"> | ||
| 14 | <Configuration>Release</Configuration> | ||
| 15 | <Platform>Win32</Platform> | ||
| 16 | </ProjectConfiguration> | ||
| 17 | </ItemGroup> | ||
| 18 | |||
| 19 | <PropertyGroup Label="Globals"> | ||
| 20 | <ProjectGuid>{F72D34CA-48DA-4DFD-91A9-A0C78BEF6981}</ProjectGuid> | ||
| 21 | <ConfigurationType>DynamicLibrary</ConfigurationType> | ||
| 22 | <TargetName>netfxca</TargetName> | ||
| 23 | <PlatformToolset>v141</PlatformToolset> | ||
| 24 | <CharacterSet>Unicode</CharacterSet> | ||
| 25 | <ProjectModuleDefinitionFile>netfxca.def</ProjectModuleDefinitionFile> | ||
| 26 | <Description>WiX Toolset .NET Framework CustomAction</Description> | ||
| 27 | </PropertyGroup> | ||
| 28 | |||
| 29 | <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> | ||
| 30 | <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> | ||
| 31 | |||
| 32 | <ImportGroup Label="ExtensionSettings"> | ||
| 33 | </ImportGroup> | ||
| 34 | |||
| 35 | <ImportGroup Label="Shared"> | ||
| 36 | <Import Project="..\..\packages\Microsoft.VisualStudio.Setup.Configuration.Native.1.14.114\build\native\Microsoft.VisualStudio.Setup.Configuration.Native.targets" Condition="Exists('..\..\packages\Microsoft.VisualStudio.Setup.Configuration.Native.1.14.114\build\native\Microsoft.VisualStudio.Setup.Configuration.Native.targets')" /> | ||
| 37 | </ImportGroup> | ||
| 38 | |||
| 39 | <PropertyGroup> | ||
| 40 | <ProjectAdditionalLinkLibraries>msi.lib</ProjectAdditionalLinkLibraries> | ||
| 41 | </PropertyGroup> | ||
| 42 | |||
| 43 | <ItemGroup> | ||
| 44 | <ClCompile Include="dllmain.cpp"> | ||
| 45 | <PrecompiledHeader>Create</PrecompiledHeader> | ||
| 46 | </ClCompile> | ||
| 47 | <ClCompile Include="netfxca.cpp" /> | ||
| 48 | </ItemGroup> | ||
| 49 | |||
| 50 | <ItemGroup> | ||
| 51 | <ClInclude Include="cost.h" /> | ||
| 52 | <ClInclude Include="precomp.h" /> | ||
| 53 | </ItemGroup> | ||
| 54 | |||
| 55 | <ItemGroup> | ||
| 56 | <None Include="packages.config" /> | ||
| 57 | <None Include="netfxca.def" /> | ||
| 58 | </ItemGroup> | ||
| 59 | |||
| 60 | <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> | ||
| 61 | |||
| 62 | <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> | ||
| 63 | <PropertyGroup> | ||
| 64 | <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> | ||
| 65 | </PropertyGroup> | ||
| 66 | <Error Condition="!Exists('..\..\packages\Microsoft.VisualStudio.Setup.Configuration.Native.1.14.114\build\native\Microsoft.VisualStudio.Setup.Configuration.Native.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.VisualStudio.Setup.Configuration.Native.1.14.114\build\native\Microsoft.VisualStudio.Setup.Configuration.Native.targets'))" /> | ||
| 67 | <Error Condition="!Exists('..\..\packages\WixToolset.DUtil.4.0.6\build\WixToolset.DUtil.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\WixToolset.DUtil.4.0.6\build\WixToolset.DUtil.props'))" /> | ||
| 68 | <Error Condition="!Exists('..\..\packages\WixToolset.WcaUtil.4.0.2\build\WixToolset.WcaUtil.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\WixToolset.WcaUtil.4.0.2\build\WixToolset.WcaUtil.props'))" /> | ||
| 69 | </Target> | ||
| 70 | </Project> \ No newline at end of file | ||
diff --git a/src/ca/packages.config b/src/ca/packages.config new file mode 100644 index 00000000..b74ff5d0 --- /dev/null +++ b/src/ca/packages.config | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | <packages> | ||
| 3 | <package id="Microsoft.VisualStudio.Setup.Configuration.Native" version="1.14.114" targetFramework="native" developmentDependency="true" /> | ||
| 4 | <package id="WixToolset.DUtil" version="4.0.6" targetFramework="native" /> | ||
| 5 | <package id="WixToolset.WcaUtil" version="4.0.2" targetFramework="native" /> | ||
| 6 | </packages> \ No newline at end of file | ||
diff --git a/src/ca/precomp.h b/src/ca/precomp.h new file mode 100644 index 00000000..5caedb16 --- /dev/null +++ b/src/ca/precomp.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 | #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 "cost.h" | ||
