From 60d7baefb4e87bf0ddaae58a653faee0be0429f6 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Mon, 1 Jan 2018 10:30:42 -0800 Subject: Initial code commit --- src/Cpp.Build.props | 101 +++ src/Directory.Build.props | 22 + src/FindLocalWix.props | 10 + src/ca/cost.h | 7 + src/ca/dllmain.cpp | 26 + src/ca/netfxca.cpp | 823 +++++++++++++++++++++ src/ca/netfxca.def | 8 + src/ca/netfxca.vcxproj | 70 ++ src/ca/packages.config | 6 + src/ca/precomp.h | 13 + .../WixToolsetTest.Netfx/NetfxExtensionFixture.cs | 33 + .../TestData/UsingNativeImage/Package.en-us.wxl | 11 + .../TestData/UsingNativeImage/Package.wxs | 22 + .../UsingNativeImage/PackageComponents.wxs | 13 + .../TestData/UsingNativeImage/example.txt | 1 + .../WixToolsetTest.Netfx.csproj | 40 + src/wixext/NetFxCompiler.cs | 159 ++++ src/wixext/NetFxDecompiler.cs | 139 ++++ src/wixext/NetFxExtensionData.cs | 25 + src/wixext/NetfxExtensionFactory.cs | 18 + .../NetfxWindowsInstallerBackendExtension.cs | 27 + src/wixext/Tuples/NetFxNativeImageTuple.cs | 65 ++ src/wixext/Tuples/NetfxTupleDefinitions.cs | 27 + src/wixext/WixToolset.Netfx.wixext.csproj | 35 + src/wixext/WixToolset.Netfx.wixext.targets | 11 + src/wixext/netfx.xsd | 235 ++++++ src/wixlib/NetFx1.1.wxs | 192 +++++ src/wixlib/NetFx1.wxs | 31 + src/wixlib/NetFx2.wxs | 201 +++++ src/wixlib/NetFx3.5.wxs | 241 ++++++ src/wixlib/NetFx3.wxs | 195 +++++ src/wixlib/NetFx4.5.wxs | 274 +++++++ src/wixlib/NetFx4.wxs | 543 ++++++++++++++ src/wixlib/NetFx451.wxs | 96 +++ src/wixlib/NetFx452.wxs | 95 +++ src/wixlib/NetFx46.wxs | 96 +++ src/wixlib/NetFx461.wxs | 95 +++ src/wixlib/NetFx462.wxs | 95 +++ src/wixlib/NetFxExtension.wxs | 39 + src/wixlib/NetFxExtension_Platform.wxi | 28 + src/wixlib/NetFxExtension_x86.wxs | 8 + src/wixlib/caSuffix.wxi | 28 + src/wixlib/netfx.wixproj | 53 ++ src/wixlib/packages.config | 6 + 44 files changed, 4263 insertions(+) create mode 100644 src/Cpp.Build.props create mode 100644 src/Directory.Build.props create mode 100644 src/FindLocalWix.props create mode 100644 src/ca/cost.h create mode 100644 src/ca/dllmain.cpp create mode 100644 src/ca/netfxca.cpp create mode 100644 src/ca/netfxca.def create mode 100644 src/ca/netfxca.vcxproj create mode 100644 src/ca/packages.config create mode 100644 src/ca/precomp.h create mode 100644 src/test/WixToolsetTest.Netfx/NetfxExtensionFixture.cs create mode 100644 src/test/WixToolsetTest.Netfx/TestData/UsingNativeImage/Package.en-us.wxl create mode 100644 src/test/WixToolsetTest.Netfx/TestData/UsingNativeImage/Package.wxs create mode 100644 src/test/WixToolsetTest.Netfx/TestData/UsingNativeImage/PackageComponents.wxs create mode 100644 src/test/WixToolsetTest.Netfx/TestData/UsingNativeImage/example.txt create mode 100644 src/test/WixToolsetTest.Netfx/WixToolsetTest.Netfx.csproj create mode 100644 src/wixext/NetFxCompiler.cs create mode 100644 src/wixext/NetFxDecompiler.cs create mode 100644 src/wixext/NetFxExtensionData.cs create mode 100644 src/wixext/NetfxExtensionFactory.cs create mode 100644 src/wixext/NetfxWindowsInstallerBackendExtension.cs create mode 100644 src/wixext/Tuples/NetFxNativeImageTuple.cs create mode 100644 src/wixext/Tuples/NetfxTupleDefinitions.cs create mode 100644 src/wixext/WixToolset.Netfx.wixext.csproj create mode 100644 src/wixext/WixToolset.Netfx.wixext.targets create mode 100644 src/wixext/netfx.xsd create mode 100644 src/wixlib/NetFx1.1.wxs create mode 100644 src/wixlib/NetFx1.wxs create mode 100644 src/wixlib/NetFx2.wxs create mode 100644 src/wixlib/NetFx3.5.wxs create mode 100644 src/wixlib/NetFx3.wxs create mode 100644 src/wixlib/NetFx4.5.wxs create mode 100644 src/wixlib/NetFx4.wxs create mode 100644 src/wixlib/NetFx451.wxs create mode 100644 src/wixlib/NetFx452.wxs create mode 100644 src/wixlib/NetFx46.wxs create mode 100644 src/wixlib/NetFx461.wxs create mode 100644 src/wixlib/NetFx462.wxs create mode 100644 src/wixlib/NetFxExtension.wxs create mode 100644 src/wixlib/NetFxExtension_Platform.wxi create mode 100644 src/wixlib/NetFxExtension_x86.wxs create mode 100644 src/wixlib/caSuffix.wxi create mode 100644 src/wixlib/netfx.wixproj create mode 100644 src/wixlib/packages.config (limited to 'src') diff --git a/src/Cpp.Build.props b/src/Cpp.Build.props new file mode 100644 index 00000000..453aa442 --- /dev/null +++ b/src/Cpp.Build.props @@ -0,0 +1,101 @@ + + + + + + Win32 + $(OutputPath) + $(BaseIntermediateOutputPath)$(Platform)\ + $(OutputPath)$(Platform)\ + + + + + $(DisableSpecificCompilerWarnings) + Level4 + $(ProjectDir)inc;$(MSBuildProjectDirectory);$(IntDir);$(SqlCESdkIncludePath);$(ProjectAdditionalIncludeDirectories);%(AdditionalIncludeDirectories) + WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0501;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) + Use + precomp.h + StdCall + true + false + -YlprecompDefine + /Zc:threadSafeInit- %(AdditionalOptions) + true + + + $(ArmPreprocessorDefinitions);%(PreprocessorDefinitions) + $(ProjectAdditionalResourceIncludeDirectories);%(AdditionalIncludeDirectories) + + + $(OutDir);$(AdditionalMultiTargetLibraryPath);$(ProjectAdditionalLibraryDirectories);%(AdditionalLibraryDirectories) + + + $(ProjectSubSystem) + $(ProjectModuleDefinitionFile) + $(ResourceOnlyDll) + true + $(ProjectAdditionalLinkLibraries);advapi32.lib;comdlg32.lib;user32.lib;oleaut32.lib;gdi32.lib;shell32.lib;ole32.lib;version.lib;%(AdditionalDependencies) + $(OutDir);$(AdditionalMultiTargetLibraryPath);$(ArmLibraryDirectories);$(ProjectAdditionalLinkLibraryDirectories);%(AdditionalLibraryDirectories) + /IGNORE:4099 %(AdditionalOptions) + + + + + + NoExtensions + + + + + CDecl + + + + + OldStyle + true + true + + + + + Disabled + EnableFastChecks + _DEBUG;DEBUG;%(PreprocessorDefinitions) + MultiThreadedDebug + + + + + + MultiThreadedDebugDll + + + + + MinSpace + NDEBUG;%(PreprocessorDefinitions) + true + true + MultiThreaded + + + true + true + + + + + + MultiThreadedDll + + + + + $(LinkKeyFile) + $(LinkDelaySign) + + + diff --git a/src/Directory.Build.props b/src/Directory.Build.props new file mode 100644 index 00000000..63ad5d6e --- /dev/null +++ b/src/Directory.Build.props @@ -0,0 +1,22 @@ + + + + + + Debug + AnyCPU + $(MSBuildThisFileDirectory)..\build\obj\$(MSBuildProjectName)\ + $(MSBuildThisFileDirectory)..\build\$(Configuration)\ + + WiX Toolset Team + WiX Toolset + Copyright (c) .NET Foundation and contributors. All rights reserved. + + + + $(MSBuildThisFileDirectory)..\..\ + + + + + diff --git a/src/FindLocalWix.props b/src/FindLocalWix.props new file mode 100644 index 00000000..c33939ea --- /dev/null +++ b/src/FindLocalWix.props @@ -0,0 +1,10 @@ + + + + + + $(MSBuildThisFileDirectory)..\..\Core\build\Debug\net461\wix.targets + + $(MSBuildThisFileDirectory)..\..\Util.wixext\build\Debug\netstandard2.0\WixToolset.Util.wixext.dll + + 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 @@ +#pragma once +// 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. + + +const UINT COST_NGEN_BLOCKING = 5000; +const UINT COST_NGEN_NONBLOCKING = 500; + 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 @@ +// 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. + +#include "precomp.h" + +/******************************************************************** +DllMain - standard entry point for all WiX CustomActions + +********************************************************************/ +extern "C" BOOL WINAPI DllMain( + IN HINSTANCE hInst, + IN ULONG ulReason, + IN LPVOID) +{ + switch(ulReason) + { + case DLL_PROCESS_ATTACH: + WcaGlobalInitialize(hInst); + break; + + case DLL_PROCESS_DETACH: + WcaGlobalFinalize(); + break; + } + + return TRUE; +} 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 @@ +// 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. + +#include "precomp.h" + +#define NGEN_DEBUG 0x0001 +#define NGEN_NODEP 0x0002 +#define NGEN_PROFILE 0x0004 +#define NGEN_32BIT 0x0008 +#define NGEN_64BIT 0x0010 + +#define NGEN_TIMEOUT 60000 // 60 seconds + +// If you change one of these strings, be sure to change the appropriate EmptyFormattedLength variable right below +LPCWSTR vpwzUnformattedQuotedFile = L"\"[#%s]\""; +LPCWSTR vpwzUnformattedQuotedDirectory = L"\"[%s]\\\""; + +// These represent the length of the above strings in the case that the property resolves to an empty string +const DWORD EMPTY_FORMATTED_LENGTH_QUOTED_FILE = 2; +const DWORD EMPTY_FORMATTED_LENGTH_QUOTED_DIRECTORY = 3; + +LPCWSTR vcsFileId = + L"SELECT `File` FROM `File` WHERE `File`=?"; +enum eFileId { fiFile = 1 }; + +LPCWSTR vcsNgenQuery = + L"SELECT `NetFxNativeImage`.`File_`, `NetFxNativeImage`.`NetFxNativeImage`, `NetFxNativeImage`.`Priority`, `NetFxNativeImage`.`Attributes`, `NetFxNativeImage`.`File_Application`, `NetFxNativeImage`.`Directory_ApplicationBase`, `File`.`Component_` " + L"FROM `NetFxNativeImage`, `File` WHERE `File`.`File`=`NetFxNativeImage`.`File_`"; +enum eNgenQuery { ngqFile = 1, ngqId, ngqPriority, ngqAttributes, ngqFileApp, ngqDirAppBase, ngqComponent }; + +LPCWSTR vcsNgenGac = + L"SELECT `MsiAssembly`.`File_Application` " + L"FROM `File`, `MsiAssembly` WHERE `File`.`Component_`=`MsiAssembly`.`Component_` AND `File`.`File`=?"; +enum eNgenGac { nggApplication = 1 }; + +LPCWSTR vcsNgenStrongName = + L"SELECT `Name`,`Value` FROM `MsiAssemblyName` WHERE `Component_`=?"; +enum eNgenStrongName { ngsnName = 1, ngsnValue }; + +// Searches subdirectories of the given path for the highest version of ngen.exe available +static HRESULT GetNgenVersion( + __in LPWSTR pwzParentPath, + __out LPWSTR* ppwzVersion + ) +{ + Assert(pwzParentPath); + + HRESULT hr = S_OK; + DWORD dwError = 0; + DWORD dwNgenFileFlags = 0; + + LPWSTR pwzVersionSearch = NULL; + LPWSTR pwzNgen = NULL; + LPWSTR pwzTemp = NULL; + LPWSTR pwzTempVersion = NULL; + DWORD dwMaxMajorVersion = 0; // This stores the highest major version we've seen so far + DWORD dwMaxMinorVersion = 0; // This stores the minor version of the highest major version we've seen so far + DWORD dwMajorVersion = 0; // This stores the major version of the directory we're currently considering + DWORD dwMinorVersion = 0; // This stores the minor version of the directory we're currently considering + BOOL fFound = TRUE; + WIN32_FIND_DATAW wfdVersionDirectories; + HANDLE hFind = INVALID_HANDLE_VALUE; + + hr = StrAllocFormatted(&pwzVersionSearch, L"%s*", pwzParentPath); + ExitOnFailure(hr, "failed to create outer directory search string from string %ls", pwzParentPath); + hFind = FindFirstFileW(pwzVersionSearch, &wfdVersionDirectories); + if (hFind == INVALID_HANDLE_VALUE) + { + ExitWithLastError(hr, "failed to call FindFirstFileW with string %ls", pwzVersionSearch); + } + + while (fFound) + { + pwzTempVersion = (LPWSTR)&(wfdVersionDirectories.cFileName); + + // Explicitly exclude v1.1.4322, which isn't backwards compatible and is not supported + if (wfdVersionDirectories.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + if (0 != lstrcmpW(L"v1.1.4322", pwzTempVersion)) + { + // A potential candidate directory was found to run ngen from - let's make sure ngen actually exists here + hr = StrAllocFormatted(&pwzNgen, L"%s%s\\ngen.exe", pwzParentPath, pwzTempVersion); + ExitOnFailure(hr, "failed to create inner ngen search string with strings %ls and %ls", pwzParentPath, pwzTempVersion); + + // If Ngen.exe does exist as a file here, then let's check the file version + if (FileExistsEx(pwzNgen, &dwNgenFileFlags) && (0 == (dwNgenFileFlags & FILE_ATTRIBUTE_DIRECTORY))) + { + hr = FileVersion(pwzNgen, &dwMajorVersion, &dwMinorVersion); + + if (FAILED(hr)) + { + WcaLog(LOGMSG_VERBOSE, "Failed to get version of %ls - continuing", pwzNgen); + } + else if (dwMajorVersion > dwMaxMajorVersion || (dwMajorVersion == dwMaxMajorVersion && dwMinorVersion > dwMaxMinorVersion)) + { + // 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 + hr = StrAllocString(ppwzVersion, pwzTempVersion, 0); + ExitOnFailure(hr, "failed to copy temp version string %ls to version string", pwzTempVersion); + // Add one for the backslash after the directory name + 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)); + + dwMaxMajorVersion = dwMajorVersion; + dwMaxMinorVersion = dwMinorVersion; + } + } + else + { + WcaLog(LOGMSG_VERBOSE, "Ignoring %ls because it doesn't contain the file ngen.exe", pwzTempVersion); + } + } + else + { + 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); + } + } + else + { + WcaLog(LOGMSG_VERBOSE, "Ignoring %ls because it isn't a directory", pwzTempVersion); + } + + fFound = FindNextFileW(hFind, &wfdVersionDirectories); + + if (!fFound) + { + dwError = ::GetLastError(); + hr = (ERROR_NO_MORE_FILES == dwError) ? ERROR_SUCCESS : HRESULT_FROM_WIN32(dwError); + ExitOnFailure(hr, "Failed to call FindNextFileW() with query %ls", pwzVersionSearch); + } + } + + if (NULL == *ppwzVersion) + { + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + ExitOnRootFailure(hr, "Searched through all subdirectories of %ls, but failed to find any version of ngen.exe", pwzParentPath); + } + else + { + 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)); + } + +LExit: + if (hFind != INVALID_HANDLE_VALUE) + { + if (0 == FindClose(hFind)) + { + dwError = ::GetLastError(); + hr = HRESULT_FROM_WIN32(dwError); + WcaLog(LOGMSG_STANDARD, "Failed to close handle created by outer FindFirstFile with error %x - continuing", hr); + } + hFind = INVALID_HANDLE_VALUE; + } + + ReleaseStr(pwzVersionSearch); + ReleaseStr(pwzNgen); + ReleaseStr(pwzTemp); + // Purposely don't release pwzTempVersion, because it wasn't allocated in this function, it's just a pointer to a string inside wfdVersionDirectories + + return hr; +} + +// Gets the path to ngen.exe +static HRESULT GetNgenPath( + __out LPWSTR* ppwzNgenPath, + __in BOOL f64BitFramework + ) +{ + Assert(ppwzNgenPath); + HRESULT hr = S_OK; + + LPWSTR pwzVersion = NULL; + LPWSTR pwzWindowsFolder = NULL; + + hr = WcaGetProperty(L"WindowsFolder", &pwzWindowsFolder); + ExitOnFailure(hr, "failed to get WindowsFolder property"); + + hr = StrAllocString(ppwzNgenPath, pwzWindowsFolder, 0); + ExitOnFailure(hr, "failed to copy to NgenPath windows folder: %ls", pwzWindowsFolder); + + if (f64BitFramework) + { + WcaLog(LOGMSG_VERBOSE, "Searching for ngen under 64-bit framework path"); + + hr = StrAllocConcat(ppwzNgenPath, L"Microsoft.NET\\Framework64\\", 0); + ExitOnFailure(hr, "failed to copy platform portion of ngen path"); + } + else + { + WcaLog(LOGMSG_VERBOSE, "Searching for ngen under 32-bit framework path"); + + hr = StrAllocConcat(ppwzNgenPath, L"Microsoft.NET\\Framework\\", 0); + ExitOnFailure(hr, "failed to copy platform portion of ngen path"); + } + + // 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 + hr = GetNgenVersion(*ppwzNgenPath, &pwzVersion); + ExitOnFailure(hr, "failed to search for ngen under path %ls", *ppwzNgenPath); + + hr = StrAllocConcat(ppwzNgenPath, pwzVersion, 0); + ExitOnFailure(hr, "failed to copy version portion of ngen path"); + + hr = StrAllocConcat(ppwzNgenPath, L"\\ngen.exe", 0); + ExitOnFailure(hr, "failed to copy \"\\ngen.exe\" portion of ngen path"); + +LExit: + ReleaseStr(pwzVersion); + ReleaseStr(pwzWindowsFolder); + + return hr; +} + + +static HRESULT GetStrongName( + __out LPWSTR* ppwzStrongName, + __in LPCWSTR pwzComponent + ) +{ + Assert(ppwzStrongName); + HRESULT hr = S_OK; + + PMSIHANDLE hView = NULL; + PMSIHANDLE hComponentRec = NULL; + PMSIHANDLE hRec = NULL; + + LPWSTR pwzData = NULL; + LPWSTR pwzName = NULL; + LPWSTR pwzVersion = NULL; + LPWSTR pwzCulture = NULL; + LPWSTR pwzPublicKeyToken = NULL; + + hComponentRec = ::MsiCreateRecord(1); + hr = WcaSetRecordString(hComponentRec, 1, pwzComponent); + ExitOnFailure(hr, "failed to set component value in record to: %ls", pwzComponent); + + // get the name value records for this component + hr = WcaOpenView(vcsNgenStrongName, &hView); + ExitOnFailure(hr, "failed to open view on NetFxNativeImage table"); + + hr = WcaExecuteView(hView, hComponentRec); + ExitOnFailure(hr, "failed to execute strong name view"); + + while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) + { + hr = WcaGetRecordString(hRec, ngsnName, &pwzData); + ExitOnFailure(hr, "failed to get MsiAssemblyName.Name for component: %ls", pwzComponent); + + if (0 == lstrcmpW(L"name", pwzData)) + { + hr = WcaGetRecordString(hRec, ngsnValue, &pwzName); + ExitOnFailure(hr, "failed to get MsiAssemblyName.Value for component: %ls Name: %ls", pwzComponent, pwzData); + } + else if (0 == lstrcmpW(L"version", pwzData)) + { + hr = WcaGetRecordString(hRec, ngsnValue, &pwzVersion); + ExitOnFailure(hr, "failed to get MsiAssemblyName.Value for component: %ls Name: %ls", pwzComponent, pwzData); + } + else if (0 == lstrcmpW(L"culture", pwzData)) + { + hr = WcaGetRecordString(hRec, ngsnValue, &pwzCulture); + ExitOnFailure(hr, "failed to get MsiAssemblyName.Value for component: %ls Name: %ls", pwzComponent, pwzData); + } + else if (0 == lstrcmpW(L"publicKeyToken", pwzData)) + { + hr = WcaGetRecordString(hRec, ngsnValue, &pwzPublicKeyToken); + ExitOnFailure(hr, "failed to get MsiAssemblyName.Value for component: %ls Name: %ls", pwzComponent, pwzData); + } + } + if (E_NOMOREITEMS == hr) + hr = S_OK; + ExitOnFailure(hr, "failed while looping through all names and values in MsiAssemblyName table for component: %ls", pwzComponent); + + hr = StrAllocFormatted(ppwzStrongName, L"\"%s, Version=%s, Culture=%s, PublicKeyToken=%s\"", pwzName, pwzVersion, pwzCulture, pwzPublicKeyToken); + ExitOnFailure(hr, "failed to format strong name for component: %ls", pwzComponent); + +LExit: + ReleaseStr(pwzData); + ReleaseStr(pwzName); + ReleaseStr(pwzVersion); + ReleaseStr(pwzCulture); + ReleaseStr(pwzPublicKeyToken); + + return hr; +} + +static HRESULT CreateInstallCommand( + __out LPWSTR* ppwzCommandLine, + __in LPCWSTR pwzNgenPath, + __in LPCWSTR pwzFile, + __in int iPriority, + __in int iAttributes, + __in LPCWSTR pwzFileApp, + __in LPCWSTR pwzDirAppBase + ) +{ + Assert(ppwzCommandLine && pwzNgenPath && *pwzNgenPath && pwzFile && *pwzFile&& pwzFileApp && pwzDirAppBase); + HRESULT hr = S_OK; + + LPWSTR pwzQueueString = NULL; + + hr = StrAllocFormatted(ppwzCommandLine, L"%s install %s", pwzNgenPath, pwzFile); + ExitOnFailure(hr, "failed to assemble install command line"); + + if (iPriority > 0) + { + hr = StrAllocFormatted(&pwzQueueString, L" /queue:%d", iPriority); + ExitOnFailure(hr, "failed to format queue string"); + + hr = StrAllocConcat(ppwzCommandLine, pwzQueueString, 0); + ExitOnFailure(hr, "failed to add queue string to NGEN command line"); + } + + if (NGEN_DEBUG & iAttributes) + { + hr = StrAllocConcat(ppwzCommandLine, L" /Debug", 0); + ExitOnFailure(hr, "failed to add debug to NGEN command line"); + } + + if (NGEN_PROFILE & iAttributes) + { + hr = StrAllocConcat(ppwzCommandLine, L" /Profile", 0); + ExitOnFailure(hr, "failed to add profile to NGEN command line"); + } + + if (NGEN_NODEP & iAttributes) + { + hr = StrAllocConcat(ppwzCommandLine, L" /NoDependencies", 0); + ExitOnFailure(hr, "failed to add no dependencies to NGEN command line"); + } + + // If it's more than just two quotes around an empty string + if (EMPTY_FORMATTED_LENGTH_QUOTED_FILE < lstrlenW(pwzFileApp)) + { + hr = StrAllocConcat(ppwzCommandLine, L" /ExeConfig:", 0); + ExitOnFailure(hr, "failed to add exe config to NGEN command line"); + + hr = StrAllocConcat(ppwzCommandLine, pwzFileApp, 0); + ExitOnFailure(hr, "failed to add file app to NGEN command line"); + } + + // If it's more than just two quotes around a backslash + if (EMPTY_FORMATTED_LENGTH_QUOTED_DIRECTORY < lstrlenW(pwzDirAppBase)) + { + hr = StrAllocConcat(ppwzCommandLine, L" /AppBase:", 0); + ExitOnFailure(hr, "failed to add app base to NGEN command line"); + + hr = StrAllocConcat(ppwzCommandLine, pwzDirAppBase, 0); + ExitOnFailure(hr, "failed to add dir app base to NGEN command line"); + } + +LExit: + return hr; +} + +/****************************************************************** + FileIdExists - checks if the file ID is found in the File table + + returns S_OK if the file exists; S_FALSE if not; otherwise, error +********************************************************************/ +static HRESULT FileIdExists( + __in_opt LPCWSTR wzFile + ) +{ + HRESULT hr = S_OK; + PMSIHANDLE hView = NULL; + PMSIHANDLE hRec = NULL; + + if (!wzFile) + { + hr = S_FALSE; + ExitFunction(); + } + + hRec = ::MsiCreateRecord(1); + hr = WcaSetRecordString(hRec, fiFile, wzFile); + ExitOnFailure(hr, "failed to create a record with the file: %ls", wzFile); + + hr = WcaTableExists(L"File"); + if (S_OK == hr) + { + hr = WcaOpenView(vcsFileId, &hView); + ExitOnFailure(hr, "failed to open view on File table"); + + hr = WcaExecuteView(hView, hRec); + ExitOnFailure(hr, "failed to execute view on File table"); + + // Reuse the same record; the handle will be released. + hr = WcaFetchSingleRecord(hView, &hRec); + ExitOnFailure(hr, "failed to fetch File from File table"); + } + +LExit: + + return hr; +} + +/****************************************************************** + SchedNetFx - entry point for NetFx Custom Action + +********************************************************************/ +extern "C" UINT __stdcall SchedNetFx( + __in MSIHANDLE hInstall + ) +{ + // AssertSz(FALSE, "debug SchedNetFx"); + + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + LPWSTR pwzInstallCustomActionData = NULL; + LPWSTR pwzUninstallCustomActionData = NULL; + UINT uiCost = 0; + + PMSIHANDLE hView = NULL; + PMSIHANDLE hRec = NULL; + PMSIHANDLE hViewGac = NULL; + PMSIHANDLE hRecGac = NULL; + + LPWSTR pwzId = NULL; + LPWSTR pwzData = NULL; + LPWSTR pwzTemp = NULL; + LPWSTR pwzFile = NULL; + int iPriority = 0; + int iAssemblyCost = 0; + int iAttributes = 0; + LPWSTR pwzFileApp = NULL; + LPWSTR pwzDirAppBase = NULL; + LPWSTR pwzComponent = NULL; + + INSTALLSTATE isInstalled; + INSTALLSTATE isAction; + + LPWSTR pwz32Ngen = NULL; + LPWSTR pwz64Ngen = NULL; + + BOOL f32NgenExeExists = FALSE; + BOOL f64NgenExeExists = FALSE; + + BOOL fNeedInstallUpdate32 = FALSE; + BOOL fNeedUninstallUpdate32 = FALSE; + BOOL fNeedInstallUpdate64 = FALSE; + BOOL fNeedUninstallUpdate64 = FALSE; + + // initialize + hr = WcaInitialize(hInstall, "SchedNetFx"); + ExitOnFailure(hr, "failed to initialize"); + + hr = GetNgenPath(&pwz32Ngen, FALSE); + f32NgenExeExists = SUCCEEDED(hr); + if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr) + { + hr = ERROR_SUCCESS; + WcaLog(LOGMSG_STANDARD, "Failed to find 32bit ngen. No actions will be scheduled to create native images for 32bit."); + } + ExitOnFailure(hr, "failed to get 32bit ngen.exe path"); + + hr = GetNgenPath(&pwz64Ngen, TRUE); + f64NgenExeExists = SUCCEEDED(hr); + if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr) + { + hr = ERROR_SUCCESS; + WcaLog(LOGMSG_STANDARD, "Failed to find 64bit ngen. No actions will be scheduled to create native images for 64bit."); + } + ExitOnFailure(hr, "failed to get 64bit ngen.exe path"); + + // loop through all the NetFx records + hr = WcaOpenExecuteView(vcsNgenQuery, &hView); + ExitOnFailure(hr, "failed to open view on NetFxNativeImage table"); + + while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) + { + // Get Id + hr = WcaGetRecordString(hRec, ngqId, &pwzId); + ExitOnFailure(hr, "failed to get NetFxNativeImage.NetFxNativeImage"); + + // Get File + hr = WcaGetRecordString(hRec, ngqFile, &pwzData); + ExitOnFailure(hr, "failed to get NetFxNativeImage.File_ for record: %ls", pwzId); + hr = StrAllocFormatted(&pwzTemp, vpwzUnformattedQuotedFile, pwzData); + ExitOnFailure(hr, "failed to format file string for file: %ls", pwzData); + hr = WcaGetFormattedString(pwzTemp, &pwzFile); + ExitOnFailure(hr, "failed to get formatted string for file: %ls", pwzData); + + // Get Priority + hr = WcaGetRecordInteger(hRec, ngqPriority, &iPriority); + ExitOnFailure(hr, "failed to get NetFxNativeImage.Priority for record: %ls", pwzId); + + if (0 == iPriority) + iAssemblyCost = COST_NGEN_BLOCKING; + else + iAssemblyCost = COST_NGEN_NONBLOCKING; + + // Get Attributes + hr = WcaGetRecordInteger(hRec, ngqAttributes, &iAttributes); + ExitOnFailure(hr, "failed to get NetFxNativeImage.Attributes for record: %ls", pwzId); + + // Get File_Application or leave pwzFileApp NULL. + hr = WcaGetRecordFormattedString(hRec, ngqFileApp, &pwzData); + ExitOnFailure(hr, "failed to get NetFxNativeImage.File_Application for record: %ls", pwzId); + + // Check if the value resolves to a valid file ID. + if (S_OK == FileIdExists(pwzData)) + { + // Resolve the file ID to a path. + hr = StrAllocFormatted(&pwzTemp, vpwzUnformattedQuotedFile, pwzData); + ExitOnFailure(hr, "failed to format file application string for file: %ls", pwzData); + + hr = WcaGetFormattedString(pwzTemp, &pwzFileApp); + ExitOnFailure(hr, "failed to get formatted string for file application: %ls", pwzData); + } + else + { + // Assume record formatted to a path already. + hr = StrAllocString(&pwzFileApp, pwzData, 0); + ExitOnFailure(hr, "failed to allocate string for file path: %ls", pwzData); + + hr = PathEnsureQuoted(&pwzFileApp, FALSE); + ExitOnFailure(hr, "failed to quote file path: %ls", pwzData); + } + + // Get Directory_ApplicationBase or leave pwzDirAppBase NULL. + hr = WcaGetRecordFormattedString(hRec, ngqDirAppBase, &pwzData); + ExitOnFailure(hr, "failed to get NetFxNativeImage.Directory_ApplicationBase for record: %ls", pwzId); + + if (WcaIsUnicodePropertySet(pwzData)) + { + // Resolve the directory ID to a path. + hr = StrAllocFormatted(&pwzTemp, vpwzUnformattedQuotedDirectory, pwzData); + ExitOnFailure(hr, "failed to format directory application base string for property: %ls", pwzData); + + hr = WcaGetFormattedString(pwzTemp, &pwzDirAppBase); + ExitOnFailure(hr, "failed to get formatted string for directory application base: %ls", pwzData); + } + else + { + // Assume record formatted to a path already. + hr = StrAllocString(&pwzDirAppBase, pwzData, 0); + ExitOnFailure(hr, "failed to allocate string for directory path: %ls", pwzData); + + hr = PathEnsureQuoted(&pwzDirAppBase, TRUE); + ExitOnFailure(hr, "failed to quote and backslashify directory: %ls", pwzData); + } + + // Get Component + hr = WcaGetRecordString(hRec, ngqComponent, &pwzComponent); + ExitOnFailure(hr, "failed to get NetFxNativeImage.Directory_ApplicationBase for record: %ls", pwzId); + er = ::MsiGetComponentStateW(hInstall, pwzComponent, &isInstalled, &isAction); + ExitOnWin32Error(er, hr, "failed to get install state for Component: %ls", pwzComponent); + + // + // Figure out if it's going to be GAC'd. The possibility exists that no assemblies are going to be GAC'd + // so we have to check for the MsiAssembly table first. + // + if (S_OK == WcaTableExists(L"MsiAssembly")) + { + hr = WcaOpenView(vcsNgenGac, &hViewGac); + ExitOnFailure(hr, "failed to open view on File/MsiAssembly table"); + + hr = WcaExecuteView(hViewGac, hRec); + ExitOnFailure(hr, "failed to execute view on File/MsiAssembly table"); + + hr = WcaFetchSingleRecord(hViewGac, &hRecGac); + ExitOnFailure(hr, "failed to fetch File_Assembly from File/MsiAssembly table"); + + if (S_FALSE != hr) + { + hr = WcaGetRecordString(hRecGac, nggApplication, &pwzData); + ExitOnFailure(hr, "failed to get MsiAssembly.File_Application"); + + // If it's in the GAC replace the file name with the strong name + if (L'\0' == pwzData[0]) + { + hr = GetStrongName(&pwzFile, pwzComponent); + ExitOnFailure(hr, "failed to get strong name for component: %ls", pwzData); + } + } + } + + // + // Schedule the work + // + if (!(iAttributes & NGEN_32BIT) && !(iAttributes & NGEN_64BIT)) + ExitOnFailure(hr = E_INVALIDARG, "Neither 32bit nor 64bit is specified for NGEN of file: %ls", pwzFile); + + if (WcaIsInstalling(isInstalled, isAction) || WcaIsReInstalling(isInstalled, isAction)) + { + if (iAttributes & NGEN_32BIT && f32NgenExeExists) + { + // Assemble the install command line + hr = CreateInstallCommand(&pwzData, pwz32Ngen, pwzFile, iPriority, iAttributes, pwzFileApp, pwzDirAppBase); + ExitOnFailure(hr, "failed to create install command line"); + + hr = WcaWriteStringToCaData(pwzData, &pwzInstallCustomActionData); + ExitOnFailure(hr, "failed to add install command to custom action data: %ls", pwzData); + + hr = WcaWriteIntegerToCaData(iAssemblyCost, &pwzInstallCustomActionData); + ExitOnFailure(hr, "failed to add cost to custom action data: %ls", pwzData); + + uiCost += iAssemblyCost; + + fNeedInstallUpdate32 = TRUE; + } + + if (iAttributes & NGEN_64BIT && f64NgenExeExists) + { + // Assemble the install command line + hr = CreateInstallCommand(&pwzData, pwz64Ngen, pwzFile, iPriority, iAttributes, pwzFileApp, pwzDirAppBase); + ExitOnFailure(hr, "failed to create install command line"); + + hr = WcaWriteStringToCaData(pwzData, &pwzInstallCustomActionData); // command + ExitOnFailure(hr, "failed to add install command to custom action data: %ls", pwzData); + + hr = WcaWriteIntegerToCaData(iAssemblyCost, &pwzInstallCustomActionData); // cost + ExitOnFailure(hr, "failed to add cost to custom action data: %ls", pwzData); + + uiCost += iAssemblyCost; + + fNeedInstallUpdate64 = TRUE; + } + } + else if (WcaIsUninstalling(isInstalled, isAction)) + { + if (iAttributes & NGEN_32BIT && f32NgenExeExists) + { + hr = StrAllocFormatted(&pwzData, L"%s uninstall %s", pwz32Ngen, pwzFile); + ExitOnFailure(hr, "failed to create update 32 command line"); + + hr = WcaWriteStringToCaData(pwzData, &pwzUninstallCustomActionData); // command + ExitOnFailure(hr, "failed to add install command to custom action data: %ls", pwzData); + + hr = WcaWriteIntegerToCaData(COST_NGEN_NONBLOCKING, &pwzUninstallCustomActionData); // cost + ExitOnFailure(hr, "failed to add cost to custom action data: %ls", pwzData); + + uiCost += COST_NGEN_NONBLOCKING; + + fNeedUninstallUpdate32 = TRUE; + } + + if (iAttributes & NGEN_64BIT && f64NgenExeExists) + { + hr = StrAllocFormatted(&pwzData, L"%s uninstall %s", pwz64Ngen, pwzFile); + ExitOnFailure(hr, "failed to create update 64 command line"); + + hr = WcaWriteStringToCaData(pwzData, &pwzUninstallCustomActionData); // command + ExitOnFailure(hr, "failed to add install command to custom action data: %ls", pwzData); + + hr = WcaWriteIntegerToCaData(COST_NGEN_NONBLOCKING, &pwzUninstallCustomActionData); // cost + ExitOnFailure(hr, "failed to add cost to custom action data: %ls", pwzData); + + uiCost += COST_NGEN_NONBLOCKING; + + fNeedUninstallUpdate64 = TRUE; + } + } + } + if (E_NOMOREITEMS == hr) + hr = S_OK; + ExitOnFailure(hr, "failed while looping through all files to create native images for"); + + // If we need 32 bit install update + if (fNeedInstallUpdate32) + { + hr = StrAllocFormatted(&pwzData, L"%s update /queue", pwz32Ngen); + ExitOnFailure(hr, "failed to create install update 32 command line"); + + hr = WcaWriteStringToCaData(pwzData, &pwzInstallCustomActionData); // command + ExitOnFailure(hr, "failed to add install command to install custom action data: %ls", pwzData); + + hr = WcaWriteIntegerToCaData(COST_NGEN_NONBLOCKING, &pwzInstallCustomActionData); // cost + ExitOnFailure(hr, "failed to add cost to install custom action data: %ls", pwzData); + + uiCost += COST_NGEN_NONBLOCKING; + } + + // If we need 32 bit uninstall update + if (fNeedUninstallUpdate32) + { + hr = StrAllocFormatted(&pwzData, L"%s update /queue", pwz32Ngen); + ExitOnFailure(hr, "failed to create uninstall update 32 command line"); + + hr = WcaWriteStringToCaData(pwzData, &pwzUninstallCustomActionData); // command + ExitOnFailure(hr, "failed to add install command to uninstall custom action data: %ls", pwzData); + + hr = WcaWriteIntegerToCaData(COST_NGEN_NONBLOCKING, &pwzUninstallCustomActionData); // cost + ExitOnFailure(hr, "failed to add cost to uninstall custom action data: %ls", pwzData); + + uiCost += COST_NGEN_NONBLOCKING; + } + + // If we need 64 bit install update + if (fNeedInstallUpdate64) + { + hr = StrAllocFormatted(&pwzData, L"%s update /queue", pwz64Ngen); + ExitOnFailure(hr, "failed to create install update 64 command line"); + + hr = WcaWriteStringToCaData(pwzData, &pwzInstallCustomActionData); // command + ExitOnFailure(hr, "failed to add install command to install custom action data: %ls", pwzData); + + hr = WcaWriteIntegerToCaData(COST_NGEN_NONBLOCKING, &pwzInstallCustomActionData); // cost + ExitOnFailure(hr, "failed to add cost to install custom action data: %ls", pwzData); + + uiCost += COST_NGEN_NONBLOCKING; + } + + // If we need 64 bit install update + if (fNeedUninstallUpdate64) + { + hr = StrAllocFormatted(&pwzData, L"%s update /queue", pwz64Ngen); + ExitOnFailure(hr, "failed to create uninstall update 64 command line"); + + hr = WcaWriteStringToCaData(pwzData, &pwzUninstallCustomActionData); // command + ExitOnFailure(hr, "failed to add install command to uninstall custom action data: %ls", pwzData); + + hr = WcaWriteIntegerToCaData(COST_NGEN_NONBLOCKING, &pwzUninstallCustomActionData); // cost + ExitOnFailure(hr, "failed to add cost to uninstall custom action data: %ls", pwzData); + + uiCost += COST_NGEN_NONBLOCKING; + } + + // Add to progress bar + if ((pwzInstallCustomActionData && *pwzInstallCustomActionData) || (pwzUninstallCustomActionData && *pwzUninstallCustomActionData)) + { + hr = WcaProgressMessage(uiCost, TRUE); + ExitOnFailure(hr, "failed to extend progress bar for NetFxExecuteNativeImage"); + } + + // Schedule the install custom action + if (pwzInstallCustomActionData && *pwzInstallCustomActionData) + { + hr = WcaSetProperty(L"NetFxExecuteNativeImageInstall", pwzInstallCustomActionData); + ExitOnFailure(hr, "failed to schedule NetFxExecuteNativeImageInstall action"); + + hr = WcaSetProperty(L"NetFxExecuteNativeImageCommitInstall", pwzInstallCustomActionData); + ExitOnFailure(hr, "failed to schedule NetFxExecuteNativeImageCommitInstall action"); + } + + // Schedule the uninstall custom action + if (pwzUninstallCustomActionData && *pwzUninstallCustomActionData) + { + hr = WcaSetProperty(L"NetFxExecuteNativeImageUninstall", pwzUninstallCustomActionData); + ExitOnFailure(hr, "failed to schedule NetFxExecuteNativeImageUninstall action"); + + hr = WcaSetProperty(L"NetFxExecuteNativeImageCommitUninstall", pwzUninstallCustomActionData); + ExitOnFailure(hr, "failed to schedule NetFxExecuteNativeImageCommitUninstall action"); + } + + +LExit: + ReleaseStr(pwzInstallCustomActionData); + ReleaseStr(pwzUninstallCustomActionData); + ReleaseStr(pwzId); + ReleaseStr(pwzData); + ReleaseStr(pwzTemp); + ReleaseStr(pwzFile); + ReleaseStr(pwzFileApp); + ReleaseStr(pwzDirAppBase); + ReleaseStr(pwzComponent); + ReleaseStr(pwz32Ngen); + ReleaseStr(pwz64Ngen); + + if (FAILED(hr)) + er = ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + + +/****************************************************************** + ExecNetFx - entry point for NetFx Custom Action + +*******************************************************************/ +extern "C" UINT __stdcall ExecNetFx( + __in MSIHANDLE hInstall + ) +{ +// AssertSz(FALSE, "debug ExecNetFx"); + + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + LPWSTR pwzCustomActionData = NULL; + LPWSTR pwzData = NULL; + LPWSTR pwz = NULL; + int iCost = 0; + + // initialize + hr = WcaInitialize(hInstall, "ExecNetFx"); + ExitOnFailure(hr, "failed to initialize"); + + hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData); + ExitOnFailure(hr, "failed to get CustomActionData"); + + WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData); + + pwz = pwzCustomActionData; + + // loop through all the passed in data + while (pwz && *pwz) + { + hr = WcaReadStringFromCaData(&pwz, &pwzData); + ExitOnFailure(hr, "failed to read command line from custom action data"); + + hr = WcaReadIntegerFromCaData(&pwz, &iCost); + ExitOnFailure(hr, "failed to read cost from custom action data"); + + hr = QuietExec(pwzData, NGEN_TIMEOUT, TRUE, TRUE); + // If we fail here it isn't critical - keep looping through to try to act on the other assemblies on our list + if (FAILED(hr)) + { + WcaLog(LOGMSG_STANDARD, "failed to execute Ngen command (with error 0x%x): %ls, continuing anyway", hr, pwzData); + hr = S_OK; + } + + // Tick the progress bar along for this assembly + hr = WcaProgressMessage(iCost, FALSE); + ExitOnFailure(hr, "failed to tick progress bar for command line: %ls", pwzData); + } + +LExit: + ReleaseStr(pwzCustomActionData); + ReleaseStr(pwzData); + + if (FAILED(hr)) + er = ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + 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 @@ +; 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. + + +LIBRARY "netfxca" + +EXPORTS + SchedNetFx + 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 @@ + + + + + + + + + + Debug + Win32 + + + Release + Win32 + + + + + {F72D34CA-48DA-4DFD-91A9-A0C78BEF6981} + DynamicLibrary + netfxca + v141 + Unicode + netfxca.def + WiX Toolset .NET Framework CustomAction + + + + + + + + + + + + + + msi.lib + + + + + Create + + + + + + + + + + + + + + + + + + + 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}. + + + + + + \ 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 @@ + + + + + + \ 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 @@ +#pragma once +// 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. + + +#include +#include + +#include "wcautil.h" +#include "fileutil.h" +#include "strutil.h" +#include "pathutil.h" + +#include "cost.h" diff --git a/src/test/WixToolsetTest.Netfx/NetfxExtensionFixture.cs b/src/test/WixToolsetTest.Netfx/NetfxExtensionFixture.cs new file mode 100644 index 00000000..a9428d7c --- /dev/null +++ b/src/test/WixToolsetTest.Netfx/NetfxExtensionFixture.cs @@ -0,0 +1,33 @@ +// 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. + +namespace WixToolsetTest.Netfx +{ + using System.Linq; + using WixBuildTools.TestSupport; + using WixToolset.Core.TestPackage; + using WixToolset.Netfx; + using Xunit; + + public class NetfxExtensionFixture + { + [Fact] + public void CanBuildUsingNativeImage() + { + var folder = TestData.Get(@"TestData\UsingNativeImage"); + var build = new Builder(folder, typeof(NetfxExtensionFactory), new[] { folder }); + + var results = build.BuildAndQuery(Build, "NetFxNativeImage"); + Assert.Equal(new[] + { + "NetFxNativeImage:ExampleNgen\tfil6349_KNDJhqShNzVdHX3ihhvA6Y\t3\t8\t\t", + }, results.OrderBy(s => s).ToArray()); + } + + private static void Build(string[] args) + { + var result = WixRunner.Execute(args, out var messages); + Assert.Equal(0, result); + Assert.Empty(messages); + } + } +} diff --git a/src/test/WixToolsetTest.Netfx/TestData/UsingNativeImage/Package.en-us.wxl b/src/test/WixToolsetTest.Netfx/TestData/UsingNativeImage/Package.en-us.wxl new file mode 100644 index 00000000..38c12ac1 --- /dev/null +++ b/src/test/WixToolsetTest.Netfx/TestData/UsingNativeImage/Package.en-us.wxl @@ -0,0 +1,11 @@ + + + + + + A newer version of [ProductName] is already installed. + MsiPackage + + diff --git a/src/test/WixToolsetTest.Netfx/TestData/UsingNativeImage/Package.wxs b/src/test/WixToolsetTest.Netfx/TestData/UsingNativeImage/Package.wxs new file mode 100644 index 00000000..68ff98fd --- /dev/null +++ b/src/test/WixToolsetTest.Netfx/TestData/UsingNativeImage/Package.wxs @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/WixToolsetTest.Netfx/TestData/UsingNativeImage/PackageComponents.wxs b/src/test/WixToolsetTest.Netfx/TestData/UsingNativeImage/PackageComponents.wxs new file mode 100644 index 00000000..21c92f26 --- /dev/null +++ b/src/test/WixToolsetTest.Netfx/TestData/UsingNativeImage/PackageComponents.wxs @@ -0,0 +1,13 @@ + + + + + + + + + + + + diff --git a/src/test/WixToolsetTest.Netfx/TestData/UsingNativeImage/example.txt b/src/test/WixToolsetTest.Netfx/TestData/UsingNativeImage/example.txt new file mode 100644 index 00000000..1b4ffe8a --- /dev/null +++ b/src/test/WixToolsetTest.Netfx/TestData/UsingNativeImage/example.txt @@ -0,0 +1 @@ +This is example.txt. \ No newline at end of file diff --git a/src/test/WixToolsetTest.Netfx/WixToolsetTest.Netfx.csproj b/src/test/WixToolsetTest.Netfx/WixToolsetTest.Netfx.csproj new file mode 100644 index 00000000..45429574 --- /dev/null +++ b/src/test/WixToolsetTest.Netfx/WixToolsetTest.Netfx.csproj @@ -0,0 +1,40 @@ + + + + + + netcoreapp2.0 + false + + + + NU1701 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/wixext/NetFxCompiler.cs b/src/wixext/NetFxCompiler.cs new file mode 100644 index 00000000..55db4297 --- /dev/null +++ b/src/wixext/NetFxCompiler.cs @@ -0,0 +1,159 @@ +// 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. + +namespace WixToolset.Netfx +{ + using System; + using System.Collections.Generic; + using System.Xml.Linq; + using WixToolset.Data; + using WixToolset.Extensibility; + + /// + /// The compiler for the WiX Toolset .NET Framework Extension. + /// + public sealed class NetfxCompiler : BaseCompilerExtension + { + public override XNamespace Namespace => "http://wixtoolset.org/schemas/v4/wxs/netfx"; + + /// + /// Processes an element for the Compiler. + /// + /// Parent element of element to process. + /// Element to process. + /// Extra information about the context in which this element is being parsed. + public override void ParseElement(Intermediate intermediate, IntermediateSection section, XElement parentElement, XElement element, IDictionary context) + { + switch (parentElement.Name.LocalName) + { + case "File": + string fileId = context["FileId"]; + + switch (element.Name.LocalName) + { + case "NativeImage": + this.ParseNativeImageElement(intermediate, section, element, fileId); + break; + default: + this.ParseHelper.UnexpectedElement(parentElement, element); + break; + } + break; + default: + this.ParseHelper.UnexpectedElement(parentElement, element); + break; + } + } + + /// + /// Parses a NativeImage element. + /// + /// The element to parse. + /// The file identifier of the parent element. + private void ParseNativeImageElement(Intermediate intermediate, IntermediateSection section, XElement element, string fileId) + { + SourceLineNumber sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + Identifier id = null; + string appBaseDirectory = null; + string assemblyApplication = null; + int attributes = 0x8; // 32bit is on by default + int priority = 3; + + foreach (XAttribute attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "AppBaseDirectory": + appBaseDirectory = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + + // See if a formatted value is specified. + if (-1 == appBaseDirectory.IndexOf("[", StringComparison.Ordinal)) + { + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, "Directory", appBaseDirectory); + } + break; + case "AssemblyApplication": + assemblyApplication = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + + // See if a formatted value is specified. + if (-1 == assemblyApplication.IndexOf("[", StringComparison.Ordinal)) + { + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, "File", assemblyApplication); + } + break; + case "Debug": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + attributes |= 0x1; + } + break; + case "Dependencies": + if (YesNoType.No == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + attributes |= 0x2; + } + break; + case "Platform": + string platformValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + if (0 < platformValue.Length) + { + switch (platformValue) + { + case "32bit": + // 0x8 is already on by default + break; + case "64bit": + attributes &= ~0x8; + attributes |= 0x10; + break; + case "all": + attributes |= 0x10; + break; + } + } + break; + case "Priority": + priority = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, 3); + break; + case "Profile": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + attributes |= 0x4; + } + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + if (null == id) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Id")); + } + + this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); + + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, "CustomAction", "NetFxScheduleNativeImage"); + + if (!this.Messaging.EncounteredError) + { + var row = this.ParseHelper.CreateRow(section, sourceLineNumbers, "NetFxNativeImage", id); + row.Set(1, fileId); + row.Set(2, priority); + row.Set(3, attributes); + row.Set(4, assemblyApplication); + row.Set(5, appBaseDirectory); + } + } + } +} diff --git a/src/wixext/NetFxDecompiler.cs b/src/wixext/NetFxDecompiler.cs new file mode 100644 index 00000000..e30905d1 --- /dev/null +++ b/src/wixext/NetFxDecompiler.cs @@ -0,0 +1,139 @@ +// 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. + +namespace WixToolset.Extensions +{ +#if TODO_CONSIDER_DECOMPILER + using System; + using System.Collections; + using System.Diagnostics; + using System.Globalization; + using WixToolset.Data; + using WixToolset.Extensibility; + using NetFx = WixToolset.Extensions.Serialize.NetFx; + using Wix = WixToolset.Data.Serialize; + + /// + /// The decompiler for the WiX Toolset .NET Framework Extension. + /// + public sealed class NetFxDecompiler : DecompilerExtension + { + /// + /// Creates a decompiler for NetFx Extension. + /// + public NetFxDecompiler() + { + this.TableDefinitions = NetFxExtensionData.GetExtensionTableDefinitions(); + } + + /// + /// Get the extensions library to be removed. + /// + /// Table definitions for library. + /// Library to remove from decompiled output. + public override Library GetLibraryToRemove(TableDefinitionCollection tableDefinitions) + { + return NetFxExtensionData.GetExtensionLibrary(tableDefinitions); + } + + /// + /// Decompiles an extension table. + /// + /// The table to decompile. + public override void DecompileTable(Table table) + { + switch (table.Name) + { + case "NetFxNativeImage": + this.DecompileNetFxNativeImageTable(table); + break; + default: + base.DecompileTable(table); + break; + } + } + + /// + /// Decompile the NetFxNativeImage table. + /// + /// The table to decompile. + private void DecompileNetFxNativeImageTable(Table table) + { + foreach (Row row in table.Rows) + { + NetFx.NativeImage nativeImage = new NetFx.NativeImage(); + + nativeImage.Id = (string)row[0]; + + switch ((int)row[2]) + { + case 0: + nativeImage.Priority = NetFx.NativeImage.PriorityType.Item0; + break; + case 1: + nativeImage.Priority = NetFx.NativeImage.PriorityType.Item1; + break; + case 2: + nativeImage.Priority = NetFx.NativeImage.PriorityType.Item2; + break; + case 3: + nativeImage.Priority = NetFx.NativeImage.PriorityType.Item3; + break; + } + + if (null != row[3]) + { + int attributes = (int)row[3]; + + if (0x1 == (attributes & 0x1)) + { + nativeImage.Debug = NetFx.YesNoType.yes; + } + + if (0x2 == (attributes & 0x2)) + { + nativeImage.Dependencies = NetFx.YesNoType.no; + } + + if (0x4 == (attributes & 0x4)) + { + nativeImage.Profile = NetFx.YesNoType.yes; + } + + if (0x8 == (attributes & 0x8) && 0x10 == (attributes & 0x10)) + { + nativeImage.Platform = NetFx.NativeImage.PlatformType.all; + } + else if (0x8 == (attributes & 0x8)) + { + nativeImage.Platform = NetFx.NativeImage.PlatformType.Item32bit; + } + else if (0x10 == (attributes & 0x10)) + { + nativeImage.Platform = NetFx.NativeImage.PlatformType.Item64bit; + } + } + + if (null != row[4]) + { + nativeImage.AssemblyApplication = (string)row[4]; + } + + if (null != row[5]) + { + nativeImage.AppBaseDirectory = (string)row[5]; + } + + Wix.File file = (Wix.File)this.Core.GetIndexedElement("File", (string)row[1]); + if (null != file) + { + file.AddChild(nativeImage); + } + else + { + this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "File_", (string)row[1], "File")); + } + } + } + } +#endif +} diff --git a/src/wixext/NetFxExtensionData.cs b/src/wixext/NetFxExtensionData.cs new file mode 100644 index 00000000..b68741fd --- /dev/null +++ b/src/wixext/NetFxExtensionData.cs @@ -0,0 +1,25 @@ +// 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. + +namespace WixToolset.Netfx +{ + using WixToolset.Data; + using WixToolset.Extensibility; + using WixToolset.Netfx.Tuples; + + /// + /// The WiX Toolset .NET Framework Extension. + /// + public sealed class NetfxExtensionData : BaseExtensionData + { + public override bool TryGetTupleDefinitionByName(string name, out IntermediateTupleDefinition tupleDefinition) + { + tupleDefinition = (name == NetfxTupleDefinitionNames.NetFxNativeImage) ? NetfxTupleDefinitions.NetFxNativeImage : null; + return tupleDefinition != null; + } + + public override Intermediate GetLibrary(ITupleDefinitionCreator tupleDefinitions) + { + return Intermediate.Load(typeof(NetfxExtensionData).Assembly, "WixToolset.Netfx.netfx.wixlib", tupleDefinitions); + } + } +} diff --git a/src/wixext/NetfxExtensionFactory.cs b/src/wixext/NetfxExtensionFactory.cs new file mode 100644 index 00000000..756d1b2a --- /dev/null +++ b/src/wixext/NetfxExtensionFactory.cs @@ -0,0 +1,18 @@ +// 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. + +namespace WixToolset.Netfx +{ + using System; + using System.Collections.Generic; + using WixToolset.Extensibility; + + public class NetfxExtensionFactory : BaseExtensionFactory + { + protected override IEnumerable ExtensionTypes => new[] + { + typeof(NetfxCompiler), + typeof(NetfxExtensionData), + typeof(NetfxWindowsInstallerBackendExtension), + }; + } +} diff --git a/src/wixext/NetfxWindowsInstallerBackendExtension.cs b/src/wixext/NetfxWindowsInstallerBackendExtension.cs new file mode 100644 index 00000000..50266ef4 --- /dev/null +++ b/src/wixext/NetfxWindowsInstallerBackendExtension.cs @@ -0,0 +1,27 @@ +// 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. + +namespace WixToolset.Netfx +{ + using WixToolset.Data.WindowsInstaller; + using WixToolset.Extensibility; + + public class NetfxWindowsInstallerBackendExtension : BaseWindowsInstallerBackendExtension + { + private static readonly TableDefinition[] Tables = new[] { + new TableDefinition( + "NetFxNativeImage", + new[] + { + new ColumnDefinition("NetFxNativeImage", ColumnType.String, 72, true, false, ColumnCategory.Identifier, description: "The primary key, a non-localized token."), + new ColumnDefinition("File_", ColumnType.String, 0, false, false, ColumnCategory.Identifier, keyTable:"File", keyColumn: 1, description: "The assembly for which a native image will be generated."), + new ColumnDefinition("Priority", ColumnType.Number, 2, false, false, ColumnCategory.Integer, maxValue: 3, description: "The priority for generating this native image: 0 is syncronous, 1-3 represent various levels of queued generation."), + new ColumnDefinition("Attributes", ColumnType.Number, 4, false, false, ColumnCategory.Integer, maxValue: 2147483647, description: "Integer containing bit flags representing native image attributes."), + new ColumnDefinition("File_Application", ColumnType.String, 72, false, true, ColumnCategory.Formatted, description: "The application which loads this assembly."), + new ColumnDefinition("Directory_ApplicationBase", ColumnType.String, 72, false, true, ColumnCategory.Formatted, description: "The directory containing the application which loads this assembly."), + } + ), + }; + + protected override TableDefinition[] TableDefinitionsForTuples => Tables; + } +} diff --git a/src/wixext/Tuples/NetFxNativeImageTuple.cs b/src/wixext/Tuples/NetFxNativeImageTuple.cs new file mode 100644 index 00000000..af507137 --- /dev/null +++ b/src/wixext/Tuples/NetFxNativeImageTuple.cs @@ -0,0 +1,65 @@ +// 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. + +namespace WixToolset.Netfx.Tuples +{ + using WixToolset.Data; + + public enum NetFxNativeImageTupleFields + { + NetFxNativeImage, + File_, + Priority, + Attributes, + File_Application, + Directory_ApplicationBase, + } + + public class NetFxNativeImageTuple : IntermediateTuple + { + public NetFxNativeImageTuple() : base(NetfxTupleDefinitions.NetFxNativeImage, null, null) + { + } + + public NetFxNativeImageTuple(SourceLineNumber sourceLineNumber, Identifier id = null) : base(NetfxTupleDefinitions.NetFxNativeImage, sourceLineNumber, id) + { + } + + public IntermediateField this[NetFxNativeImageTupleFields index] => this.Fields[(int)index]; + + public string NetFxNativeImage + { + get => this.Fields[(int)NetFxNativeImageTupleFields.NetFxNativeImage].AsString(); + set => this.Set((int)NetFxNativeImageTupleFields.NetFxNativeImage, value); + } + + public string File_ + { + get => this.Fields[(int)NetFxNativeImageTupleFields.File_].AsString(); + set => this.Set((int)NetFxNativeImageTupleFields.File_, value); + } + + public int Priority + { + get => this.Fields[(int)NetFxNativeImageTupleFields.Priority].AsNumber(); + set => this.Set((int)NetFxNativeImageTupleFields.Priority, value); + } + + public int Attributes + { + get => this.Fields[(int)NetFxNativeImageTupleFields.Attributes].AsNumber(); + set => this.Set((int)NetFxNativeImageTupleFields.Attributes, value); + } + + public string File_Application + { + get => this.Fields[(int)NetFxNativeImageTupleFields.File_Application].AsString(); + set => this.Set((int)NetFxNativeImageTupleFields.File_Application, value); + } + + public string Directory_ApplicationBase + { + get => this.Fields[(int)NetFxNativeImageTupleFields.Directory_ApplicationBase].AsString(); + set => this.Set((int)NetFxNativeImageTupleFields.Directory_ApplicationBase, value); + } + } +} \ No newline at end of file diff --git a/src/wixext/Tuples/NetfxTupleDefinitions.cs b/src/wixext/Tuples/NetfxTupleDefinitions.cs new file mode 100644 index 00000000..aa584b38 --- /dev/null +++ b/src/wixext/Tuples/NetfxTupleDefinitions.cs @@ -0,0 +1,27 @@ +// 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. + +namespace WixToolset.Netfx.Tuples +{ + using WixToolset.Data; + + public static class NetfxTupleDefinitionNames + { + public static string NetFxNativeImage { get; } = "NetFxNativeImage"; + } + + public static class NetfxTupleDefinitions + { + public static readonly IntermediateTupleDefinition NetFxNativeImage = new IntermediateTupleDefinition( + NetfxTupleDefinitionNames.NetFxNativeImage, + new[] + { + new IntermediateFieldDefinition(nameof(NetFxNativeImageTupleFields.NetFxNativeImage), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(NetFxNativeImageTupleFields.File_), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(NetFxNativeImageTupleFields.Priority), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(NetFxNativeImageTupleFields.Attributes), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(NetFxNativeImageTupleFields.File_Application), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(NetFxNativeImageTupleFields.Directory_ApplicationBase), IntermediateFieldType.String), + }, + typeof(NetFxNativeImageTuple)); + } +} diff --git a/src/wixext/WixToolset.Netfx.wixext.csproj b/src/wixext/WixToolset.Netfx.wixext.csproj new file mode 100644 index 00000000..d925e348 --- /dev/null +++ b/src/wixext/WixToolset.Netfx.wixext.csproj @@ -0,0 +1,35 @@ + + + + + + netstandard2.0 + WixToolset.Netfx + WiX Toolset .NET Framework Extension + WiX Toolset .NET Framework Extension + true + build + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/wixext/WixToolset.Netfx.wixext.targets b/src/wixext/WixToolset.Netfx.wixext.targets new file mode 100644 index 00000000..17312abf --- /dev/null +++ b/src/wixext/WixToolset.Netfx.wixext.targets @@ -0,0 +1,11 @@ + + + + + + $(MSBuildThisFileDirectory)..\tools\WixToolset.Netfx.wixext.dll" + + + + + diff --git a/src/wixext/netfx.xsd b/src/wixext/netfx.xsd new file mode 100644 index 00000000..6ef4e9b6 --- /dev/null +++ b/src/wixext/netfx.xsd @@ -0,0 +1,235 @@ + + + + + + + + The source code schema for the WiX Toolset .NET Framework Extension. + + + + + + + Improves the performance of managed applications by creating native images. + Requires the .NET Framework 2.0 or newer to be installed on the target machine since + it runs NGen. + + + + + + Native images are files containing compiled processor-specific machine code, which + are installed into the native image cache on the local computer. The runtime + can use native images from the cache instead using the just-in-time (JIT) + compiler to compile the original assembly. + + + The native image custom actions are configured to ignore failures so that failing + to generate or remove a native image will not cause setup to fail and roll back. + + + Note for patches: if you built your target, or baseline, MSI with + previous versions 3.0 or 3.5 of this extension and want to upgrade to formattable + values for @AssemblyApplication or @AppBaseDirectory you must also include a + BinaryRef to "NetFxCA" to pull in necessary changes. If you do use formattable + values and do not include the binary changes ngen.exe will not optimize your + native images for the specified application. + + + This should be a rare occurrence, however. Because you cannot remove components + in a patch - and pyro does validate you do not - it is not practical to switch + from using identifiers to formattable values in a patch. One practical possibility + is if you wanted to use a different application to optimize your native images + and that application is not already installed with the MSI to be updated. + + + + + + + + + The identifier for this NativeImage. + + + + + + + + The directory to use for locating dependent assemblies. + For DLL assemblies and assemblies installed to the Global Assembly Cache (GAC), + this attribute should be set to the directory of the application which loads this + assembly. For EXE assemblies, this attribute does not need to be set because NGen + will use the directory of the assembly file by default. + + + The value can be in the form of a directory identifier, or a formatted string + that resolves to either a directory identifier or a full path to a directory. + + + + + + + + + The application which will load this assembly. + For DLL assemblies which are loaded via reflection, this attribute should + be set to indicate the application which will load this assembly. + The configuration of the application (usually specified via an exe.config file) will be used + to determine how to resolve dependencies for this assembly. + + + The value can be in the form of a file identifier, or a formatted string + that resolves to either a file identifier or a full path to a file. + + + When a shared component is loaded at run time, using the Load method, the + application's configuration file determines the dependencies that are loaded + for the shared component — for example, the version of a dependency that is loaded. + This attribute gives guidance on which dependencies would be loaded at run time in order + to figure out which dependency assemblies will also need to have native images generated + (assuming the Dependency attribute is not set to "no"). + + + This attribute cannot be set if the AssemblyApplication attribute is set on the parent + File element (please note that these attributes both refer to the same application + assembly but do very different things: specifiying File/@AssemblyApplication will force + an assembly to install to a private location next to the indicated application, whereas + this AssemblyApplication attribute will be used to help resolve dependent assemblies + while generating native images for this assembly). + + + + + + + + Set to "yes" to generate native images that can be used under a debugger. + The default value is "no". + + + + + + + Set to "no" to generate the minimum number of native images. + The default value is "yes". + + + + + + + Sets the platform(s) for which native images will be generated. + + + + + + + + Attempt to generate native images only for the 32-bit version of the .NET Framework + on the target machine. If the 32-bit version of the .NET Framework 2.0 or newer is not + present on the target machine, native image custom actions will not be scheduled. + This is the default value. + + + + + + + Attempt to generate native images only for the 64-bit version of the .NET Framework + on the target machine. If a 64-bit version of the .NET Framework 2.0 or newer is not + present on the target machine, native image custom actions will not be scheduled. + + + + + + + Attempt to generate native images for the 32-bit and 64-bit versions of the .NET Framework + on the target machine. If a version of the .NET Framework 2.0 or newer is not present on the + target machine for a processor architecture, native image custom actions will not be + scheduled for that processor architecture. + + + + + + + + + + Sets the priority of generating the native images for this assembly. + + + + + + + + This is the highest priority, it means that image generation occurs syncronously + during the setup process. This option will slow down setup performance. + + + + + + + This will queue image generation to the NGen service to occur immediately. + This option will slow down setup performance. + + + + + + + This will queue image generation to the NGen service to occur after all priority 1 + assemblies have completed. + This option will slow down setup performance. + + + + + + + This is the lowest priority, it will queue image generation to occur when the + machine is idle. + This option should not slow down setup performance. + This is the default value. + + + + + + + + + + Set to "yes" to generate native images that can be used under a profiler. + The default value is "no". + + + + + + + + + Values of this type will either be "yes" or "no". + + + + + + + + diff --git a/src/wixlib/NetFx1.1.wxs b/src/wixlib/NetFx1.1.wxs new file mode 100644 index 00000000..406aef3f --- /dev/null +++ b/src/wixlib/NetFx1.1.wxs @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/wixlib/NetFx1.wxs b/src/wixlib/NetFx1.wxs new file mode 100644 index 00000000..c1b266e8 --- /dev/null +++ b/src/wixlib/NetFx1.wxs @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/wixlib/NetFx2.wxs b/src/wixlib/NetFx2.wxs new file mode 100644 index 00000000..92386b02 --- /dev/null +++ b/src/wixlib/NetFx2.wxs @@ -0,0 +1,201 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/wixlib/NetFx3.5.wxs b/src/wixlib/NetFx3.5.wxs new file mode 100644 index 00000000..422608da --- /dev/null +++ b/src/wixlib/NetFx3.5.wxs @@ -0,0 +1,241 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/wixlib/NetFx3.wxs b/src/wixlib/NetFx3.wxs new file mode 100644 index 00000000..12d51750 --- /dev/null +++ b/src/wixlib/NetFx3.wxs @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/wixlib/NetFx4.5.wxs b/src/wixlib/NetFx4.5.wxs new file mode 100644 index 00000000..f2ddd051 --- /dev/null +++ b/src/wixlib/NetFx4.5.wxs @@ -0,0 +1,274 @@ + + + + + + + + + + + + + + + + + WIXNETFX4RELEASEINSTALLED >= "#$(var.NetFx45MinRelease)" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/wixlib/NetFx4.wxs b/src/wixlib/NetFx4.wxs new file mode 100644 index 00000000..fa26435f --- /dev/null +++ b/src/wixlib/NetFx4.wxs @@ -0,0 +1,543 @@ + + + + + + + + + + + + + + + + + + NETFRAMEWORK40FULL + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/wixlib/NetFx451.wxs b/src/wixlib/NetFx451.wxs new file mode 100644 index 00000000..7feff124 --- /dev/null +++ b/src/wixlib/NetFx451.wxs @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + WIXNETFX4RELEASEINSTALLED >= "#$(var.NetFx451MinRelease)" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/wixlib/NetFx452.wxs b/src/wixlib/NetFx452.wxs new file mode 100644 index 00000000..16c55aac --- /dev/null +++ b/src/wixlib/NetFx452.wxs @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + WIXNETFX4RELEASEINSTALLED >= "#$(var.NetFx452MinRelease)" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/wixlib/NetFx46.wxs b/src/wixlib/NetFx46.wxs new file mode 100644 index 00000000..c4120e90 --- /dev/null +++ b/src/wixlib/NetFx46.wxs @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + WIXNETFX4RELEASEINSTALLED >= "#$(var.NetFx46MinRelease)" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/wixlib/NetFx461.wxs b/src/wixlib/NetFx461.wxs new file mode 100644 index 00000000..907c2a35 --- /dev/null +++ b/src/wixlib/NetFx461.wxs @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + WIXNETFX4RELEASEINSTALLED >= "#$(var.NetFx461MinRelease)" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/wixlib/NetFx462.wxs b/src/wixlib/NetFx462.wxs new file mode 100644 index 00000000..08f5bd53 --- /dev/null +++ b/src/wixlib/NetFx462.wxs @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + WIXNETFX4RELEASEINSTALLED >= "#$(var.NetFx462MinRelease)" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/wixlib/NetFxExtension.wxs b/src/wixlib/NetFxExtension.wxs new file mode 100644 index 00000000..b64dc004 --- /dev/null +++ b/src/wixlib/NetFxExtension.wxs @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/wixlib/NetFxExtension_Platform.wxi b/src/wixlib/NetFxExtension_Platform.wxi new file mode 100644 index 00000000..9236f316 --- /dev/null +++ b/src/wixlib/NetFxExtension_Platform.wxi @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + RollbackDisabled <> 1 + RollbackDisabled = 1 + RollbackDisabled <> 1 + RollbackDisabled = 1 + + + + + + + + diff --git a/src/wixlib/NetFxExtension_x86.wxs b/src/wixlib/NetFxExtension_x86.wxs new file mode 100644 index 00000000..7245a4dd --- /dev/null +++ b/src/wixlib/NetFxExtension_x86.wxs @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/wixlib/caSuffix.wxi b/src/wixlib/caSuffix.wxi new file mode 100644 index 00000000..a56a2393 --- /dev/null +++ b/src/wixlib/caSuffix.wxi @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/wixlib/netfx.wixproj b/src/wixlib/netfx.wixproj new file mode 100644 index 00000000..8a734c84 --- /dev/null +++ b/src/wixlib/netfx.wixproj @@ -0,0 +1,53 @@ + + + + + + + {45e4a6ac-3190-4e17-83f0-9935ffa5dc2b} + netfx + Library + true + true + + + + + + + + + + + + + + + + + + + + + + + netfxca + {F72D34CA-48DA-4DFD-91A9-A0C78BEF6981} + + + + + + + + + + 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}. + + + + + + + + \ No newline at end of file diff --git a/src/wixlib/packages.config b/src/wixlib/packages.config new file mode 100644 index 00000000..ba130574 --- /dev/null +++ b/src/wixlib/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file -- cgit v1.2.3-55-g6feb