From 321c30138c82390ea5ad6b0a612dff294203a877 Mon Sep 17 00:00:00 2001 From: StefanStojanovic Date: Tue, 25 Oct 2022 22:40:36 +0200 Subject: Add NetFx .NET compatibility check for MSI (#262) Adds new custom element in NetFx extension for running NetCoreCheck.exe tool from within the MSI installer - ``. The checks are run before evaluating launch conditions, so their results can be used in those conditions. There is no limitation on the number of checks that can be run, so installer may query various runtimes on different platforms and versions and with different roll forward policies. Fixes https://github.com/wixtoolset/issues/issues/6264 --- src/ext/NetFx/ca/netfxca.cpp | 144 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) (limited to 'src/ext/NetFx/ca/netfxca.cpp') diff --git a/src/ext/NetFx/ca/netfxca.cpp b/src/ext/NetFx/ca/netfxca.cpp index f0704790..cd086c53 100644 --- a/src/ext/NetFx/ca/netfxca.cpp +++ b/src/ext/NetFx/ca/netfxca.cpp @@ -36,6 +36,10 @@ LPCWSTR vcsNgenStrongName = L"SELECT `Name`,`Value` FROM `MsiAssemblyName` WHERE `Component_`=?"; enum eNgenStrongName { ngsnName = 1, ngsnValue }; +LPCWSTR vscDotNetCompatibilityCheckQuery = + L"SELECT `Platform`, `RuntimeType`, `Version`, `RollForward`, `Property` FROM `Wix4NetFxDotNetCheck`"; +enum eDotNetCompatibilityCheckQuery { platform = 1, runtimeType, version, rollForward, property }; + // Searches subdirectories of the given path for the highest version of ngen.exe available static HRESULT GetNgenVersion( __in LPWSTR pwzParentPath, @@ -500,6 +504,15 @@ extern "C" UINT __stdcall SchedNetFx( hr = WcaInitialize(hInstall, "SchedNetFx"); ExitOnFailure(hr, "failed to initialize"); + // If Wix4NetFxNativeImage table doesn't exist skip the rest of this custom action + hr = WcaTableExists(L"Wix4NetFxNativeImage"); + if (S_FALSE == hr) + { + hr = S_OK; + ExitFunction(); + } + ExitOnFailure(hr, "failed to check if table Wix4NetFxNativeImage exists"); + hr = GetNgenPath(&pwz32Ngen, FALSE); f32NgenExeExists = SUCCEEDED(hr); if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr) @@ -879,3 +892,134 @@ LExit: return WcaFinalize(er); } +/****************************************************************** + DotNetCompatibilityCheck - entry point for NetFx Custom Action + +*******************************************************************/ +extern "C" UINT __stdcall DotNetCompatibilityCheck( + __in MSIHANDLE hInstall + ) +{ +// AssertSz(FALSE, "debug DotNetCompatibilityCheck"); + + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + PMSIHANDLE hView = NULL; + PMSIHANDLE hRec = NULL; + LPWSTR pwzPlatform = NULL; + LPWSTR pwzNetCoreCheckBinaryId = NULL; + LPWSTR pwzNetCoreCheckDirectoryName = NULL; + LPWSTR pwzNetCoreCheckDirectoryPath = NULL; + LPWSTR pwzNetCoreCheckFilePath = NULL; + LPWSTR pwzRuntimeType = NULL; + LPWSTR pwzVersion = NULL; + LPWSTR pwzRollForward = NULL; + LPWSTR pwzProperty = NULL; + LPWSTR pwzCommandLine = NULL; + HANDLE hProcess = NULL; + DWORD dwExitCode = 0; + + // Initialize + hr = WcaInitialize(hInstall, "DotNetCompatibilityCheck"); + ExitOnFailure(hr, "failed to initialize"); + + // If Wix4NetFxDotNetCheck table doesn't exist skip the rest of this custom action + hr = WcaTableExists(L"Wix4NetFxDotNetCheck"); + if (S_FALSE == hr) + { + hr = S_OK; + ExitFunction(); + } + ExitOnFailure(hr, "failed to check if table Wix4NetFxDotNetCheck exists"); + + // Open view on .NET compatibility check table + hr = WcaOpenExecuteView(vscDotNetCompatibilityCheckQuery, &hView); + ExitOnFailure(hr, "failed to open view on Wix4NetFxDotNetCheck table"); + + // Go through all records and run NetCorCheck.exe for each + while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) + { + // Extract NetCoreCheck.exe for platform to temp directory + hr = WcaGetRecordString(hRec, platform, &pwzPlatform); + ExitOnFailure(hr, "failed to get Wix4NetFxDotNetCheck.Platform"); + + hr = StrAllocFormatted(&pwzNetCoreCheckBinaryId, L"Wix4NetCheck_%ls", pwzPlatform); + ExitOnFailure(hr, "failed to get NetCoreCheck binary id for platform %ls", pwzPlatform); + + hr = GuidCreate(&pwzNetCoreCheckDirectoryName); + ExitOnFailure(hr, "failed to set NetCoreCheck directory name"); + + hr = PathCreateTempDirectory(NULL, pwzNetCoreCheckDirectoryName, 1, &pwzNetCoreCheckDirectoryPath); + ExitOnFailure(hr, "failed to make NetCoreCheck directory path for name %ls", pwzNetCoreCheckDirectoryName); + + hr = StrAllocFormatted(&pwzNetCoreCheckFilePath, L"%lsNetCoreCheck.exe", pwzNetCoreCheckDirectoryPath); + ExitOnFailure(hr, "failed to set NetCoreCheck file path for directory %ls", pwzNetCoreCheckDirectoryPath); + + hr = WcaExtractBinaryToFile(pwzNetCoreCheckBinaryId, pwzNetCoreCheckFilePath); + ExitOnFailure(hr, "failed to extract NetCoreCheck from binary '%ls' to file %ls", pwzNetCoreCheckBinaryId, pwzNetCoreCheckFilePath); + + // Read all NetCoreCheck.exe parameters and property + hr = WcaGetRecordString(hRec, runtimeType, &pwzRuntimeType); + ExitOnFailure(hr, "failed to get Wix4NetFxDotNetCheck.RuntimeType"); + + hr = WcaGetRecordString(hRec, version, &pwzVersion); + ExitOnFailure(hr, "failed to get Wix4NetFxDotNetCheck.Version"); + + hr = WcaGetRecordString(hRec, rollForward, &pwzRollForward); + ExitOnFailure(hr, "failed to get Wix4NetFxDotNetCheck.RollForward"); + + hr = WcaGetRecordString(hRec, property, &pwzProperty); + ExitOnFailure(hr, "failed to get Wix4NetFxDotNetCheck.Property"); + + // Run NetCoreCheck.exe and store its result in property + hr = StrAllocFormatted(&pwzCommandLine, L"-n %ls -v %ls -r %ls", pwzRuntimeType, pwzVersion, pwzRollForward); + ExitOnFailure(hr, "failed to set NetCoreCheck command line"); + WcaLog(LOGMSG_VERBOSE, "Command: %ls %ls", pwzNetCoreCheckFilePath, pwzCommandLine); + + hr = ProcExec(pwzNetCoreCheckFilePath, pwzCommandLine, SW_HIDE, &hProcess); + ExitOnFailure(hr, "failed to run NetCoreCheck from binary '%ls' with command line: %ls %ls", pwzNetCoreCheckBinaryId, pwzNetCoreCheckFilePath, pwzCommandLine); + + hr = ProcWaitForCompletion(hProcess, INFINITE, &dwExitCode); + ExitOnFailure(hr, "failed to finish NetCoreCheck from binary '%ls' with command line: %ls %ls", pwzNetCoreCheckBinaryId, pwzNetCoreCheckFilePath, pwzCommandLine); + WcaLog(LOGMSG_VERBOSE, "Exit code: %lu", dwExitCode); + ReleaseHandle(hProcess); + + hr = WcaSetIntProperty(pwzProperty, dwExitCode); + ExitOnFailure(hr, "failed to set NetCoreCheck result in %ls", pwzProperty); + + // Delete extracted NetCoreCheck.exe + DirEnsureDelete(pwzNetCoreCheckDirectoryPath, TRUE, TRUE); + } + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "failed while looping through all dot net compatibility checks"); + +LExit: + // Delete extracted NetCoreCheck.exe + if (NULL != pwzNetCoreCheckDirectoryPath) + { + DirEnsureDelete(pwzNetCoreCheckDirectoryPath, TRUE, TRUE); + } + + // Release allocated resources + ReleaseStr(pwzPlatform); + ReleaseStr(pwzNetCoreCheckBinaryId); + ReleaseStr(pwzNetCoreCheckDirectoryName); + ReleaseStr(pwzNetCoreCheckDirectoryPath); + ReleaseStr(pwzNetCoreCheckFilePath); + ReleaseStr(pwzRuntimeType); + ReleaseStr(pwzVersion); + ReleaseStr(pwzRollForward); + ReleaseStr(pwzProperty); + ReleaseStr(pwzCommandLine); + ReleaseHandle(hProcess); + + if (FAILED(hr)) + { + er = ERROR_INSTALL_FAILURE; + } + return WcaFinalize(er); +} -- cgit v1.2.3-55-g6feb