From ac6efc8061f4c01c4f6f6a01ba9d88962201eea0 Mon Sep 17 00:00:00 2001 From: Nir Bar Date: Wed, 6 Nov 2024 09:39:31 +0200 Subject: [PATCH] Fix IsWindowsVersionOrGreater: Use Kernel32.dll product version rather than file version. It correctly ientifies Windows OS version number --- .../IsWindowsVersionOrGreater.cpp | 68 ++++++++++++++----- PanelSwWixExtension/Data/tables.xml | 3 +- PanelSwWixExtension/PanelSwWixCompiler.cs | 34 ++++++++-- .../Xsd/PanelSwWixExtension.xsd | 16 ++++- README.md | 2 +- TidyBuild.custom.props | 2 +- .../IsWindowsVersionOrGreaterUT.wxs | 8 +-- 7 files changed, 99 insertions(+), 34 deletions(-) diff --git a/PanelSwCustomActions/IsWindowsVersionOrGreater.cpp b/PanelSwCustomActions/IsWindowsVersionOrGreater.cpp index e74cf8ea..b27b47d6 100644 --- a/PanelSwCustomActions/IsWindowsVersionOrGreater.cpp +++ b/PanelSwCustomActions/IsWindowsVersionOrGreater.cpp @@ -2,6 +2,9 @@ #include #include #include +#include + +static HRESULT DAPI GetKernel32ProductVersion(ULARGE_INTEGER* pullKernelProductVersion); extern "C" UINT __stdcall IsWindowsVersionOrGreater(MSIHANDLE hInstall) { @@ -10,9 +13,8 @@ extern "C" UINT __stdcall IsWindowsVersionOrGreater(MSIHANDLE hInstall) BOOL bRes = TRUE; PMSIHANDLE hView; PMSIHANDLE hRecord; - HMODULE hKernel32 = NULL; - CWixString szKernel32Path, szKernel32Version; - ULARGE_INTEGER ullKernelVersion; + CWixString szKernel32Version; + ULARGE_INTEGER ullKernelVersion = {}; hr = WcaInitialize(hInstall, __FUNCTION__); ExitOnFailure(hr, "Failed to initialize"); @@ -22,21 +24,15 @@ extern "C" UINT __stdcall IsWindowsVersionOrGreater(MSIHANDLE hInstall) ExitOnFailure(hr, "Failed to check if table exists 'PSW_IsWindowsVersionOrGreater'"); ExitOnNull((hr == S_OK), hr, E_FAIL, "Table does not exist 'PSW_IsWindowsVersionOrGreater'. Have you authored 'PanelSw:IsWindowsVersionOrGreater' entries in WiX code?"); - hKernel32 = ::GetModuleHandleW(L"kernel32"); - ExitOnNullWithLastError(hKernel32, hr, "Failed to get module handle for kernel32."); - - hr = PathForCurrentProcess((LPWSTR*)szKernel32Path, hKernel32); - ExitOnFailure(hr, "Failed to get full path of Kernel32.dll"); - - hr = FileVersion((LPCWSTR)szKernel32Path, &ullKernelVersion.HighPart, &ullKernelVersion.LowPart); - ExitOnFailure(hr, "Failed to get version of Kernel32.dll"); + hr = GetKernel32ProductVersion(&ullKernelVersion); + ExitOnFailure(hr, "Failed to get product version of Kernel32.dll"); hr = FileVersionToStringEx(ullKernelVersion.QuadPart, (LPWSTR*)szKernel32Version); - ExitOnFailure(hr, "Failed to parse version of Kernel32.dll"); - WcaLog(LOGMSG_STANDARD, "Detected version '%ls' of '%ls'", (LPCWSTR)szKernel32Version, (LPCWSTR)szKernel32Path); + ExitOnFailure(hr, "Failed to parse product version of Kernel32.dll"); + WcaLog(LOGMSG_STANDARD, "Detected product version '%ls' of Kernel32.dll", (LPCWSTR)szKernel32Version); // Execute view - hr = WcaOpenExecuteView(L"SELECT `Property_`, `Version` FROM `PSW_IsWindowsVersionOrGreater`", &hView); + hr = WcaOpenExecuteView(L"SELECT `Property_`, `MinVersion`, `MaxVersion` FROM `PSW_IsWindowsVersionOrGreater`", &hView); ExitOnFailure(hr, "Failed to execute MSI SQL query"); // Loop @@ -44,18 +40,26 @@ extern "C" UINT __stdcall IsWindowsVersionOrGreater(MSIHANDLE hInstall) { ExitOnFailure(hr, "Failed to fetch record."); - CWixString szProperty, szMinVersion; - ULARGE_INTEGER ullMinVersion; + CWixString szProperty, szMinVersion, szMaxVersion; + ULARGE_INTEGER ullMinVersion = {}, ullMaxVersion = {}; hr = WcaGetRecordString(hRecord, 1, (LPWSTR*)szProperty); ExitOnFailure(hr, "Failed to get Property_."); hr = WcaGetRecordString(hRecord, 2, (LPWSTR*)szMinVersion); ExitOnFailure(hr, "Failed to get Version."); + hr = WcaGetRecordString(hRecord, 3, (LPWSTR*)szMaxVersion); + ExitOnFailure(hr, "Failed to get Version."); hr = FileVersionFromString((LPCWSTR)szMinVersion, &ullMinVersion.HighPart, &ullMinVersion.LowPart); ExitOnFailure(hr, "Failed to parse minimal version '%ls' for '%ls'.", (LPCWSTR)szMinVersion, (LPCWSTR)szProperty); - if (ullKernelVersion.QuadPart >= ullMinVersion.QuadPart) + if (!szMaxVersion.IsNullOrEmpty()) + { + hr = FileVersionFromString((LPCWSTR)szMaxVersion, &ullMaxVersion.HighPart, &ullMaxVersion.LowPart); + ExitOnFailure(hr, "Failed to parse maximal version '%ls' for '%ls'.", (LPCWSTR)szMinVersion, (LPCWSTR)szProperty); + } + + if ((ullKernelVersion.QuadPart >= ullMinVersion.QuadPart) && (szMaxVersion.IsNullOrEmpty() || (ullKernelVersion.QuadPart <= ullMaxVersion.QuadPart))) { hr = WcaSetIntProperty((LPCWSTR)szProperty, 1); ExitOnFailure(hr, "Failed to set property."); @@ -67,3 +71,33 @@ extern "C" UINT __stdcall IsWindowsVersionOrGreater(MSIHANDLE hInstall) er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; return WcaFinalize(er); } + +static HRESULT DAPI GetKernel32ProductVersion(ULARGE_INTEGER* pullKernelProductVersion) +{ + HRESULT hr = S_OK; + UINT cbVerBuffer = 0; + LPVOID pVerBuffer = nullptr; + VS_FIXEDFILEINFO* pvsFileInfo = nullptr; + UINT cbFileInfo = 0; + BOOL bRes = TRUE; + + cbVerBuffer = ::GetFileVersionInfoSize(L"Kernel32", nullptr); + ExitOnNullWithLastError(cbVerBuffer, hr, "Failed to get Kernel32.dll version info size"); + + pVerBuffer = ::MemAlloc(cbVerBuffer, TRUE); + ExitOnNullWithLastError(pVerBuffer, hr, "Failed to allocate memory"); + + bRes = ::GetFileVersionInfo(L"Kernel32", 0, cbVerBuffer, pVerBuffer); + ExitOnNullWithLastError(bRes, hr, "Failed to get Kernel32.dll version info"); + + bRes = ::VerQueryValue(pVerBuffer, L"\\", (void**)&pvsFileInfo, &cbFileInfo); + ExitOnNullWithLastError(bRes, hr, "Failed to get Kernel32.dll version"); + + pullKernelProductVersion->HighPart = pvsFileInfo->dwProductVersionMS; + pullKernelProductVersion->LowPart = pvsFileInfo->dwProductVersionLS; + +LExit: + ReleaseMem(pVerBuffer); + + return hr; +} diff --git a/PanelSwWixExtension/Data/tables.xml b/PanelSwWixExtension/Data/tables.xml index 928b591f..181839ff 100644 --- a/PanelSwWixExtension/Data/tables.xml +++ b/PanelSwWixExtension/Data/tables.xml @@ -417,7 +417,8 @@ - + + diff --git a/PanelSwWixExtension/PanelSwWixCompiler.cs b/PanelSwWixExtension/PanelSwWixCompiler.cs index fb5374c1..0cf69e89 100644 --- a/PanelSwWixExtension/PanelSwWixCompiler.cs +++ b/PanelSwWixExtension/PanelSwWixCompiler.cs @@ -1399,7 +1399,8 @@ private void ParseIsWindowsVersionOrGreater(XmlNode node) { SourceLineNumberCollection sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); string property = null; - string version = null; + string minVersion = null; + string maxVersion = null; if (node.ParentNode.LocalName != "Property") { @@ -1418,7 +1419,23 @@ private void ParseIsWindowsVersionOrGreater(XmlNode node) switch (attrib.LocalName) { case "Version": - version = Core.GetAttributeValue(sourceLineNumbers, attrib); + Core.OnMessage(WixWarnings.DeprecatedAttribute(sourceLineNumbers, node.LocalName, attrib.LocalName, "MinVersion")); + if (minVersion != null) + { + Core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.LocalName, attrib.LocalName, "MinVersion")); + break; + } + minVersion = Core.GetAttributeVersionValue(sourceLineNumbers, attrib, true); + break; + case "MinVersion": + if (minVersion != null) + { + Core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.LocalName, attrib.LocalName, "Version")); + } + minVersion = Core.GetAttributeVersionValue(sourceLineNumbers, attrib, true); + break; + case "MaxVersion": + maxVersion = Core.GetAttributeVersionValue(sourceLineNumbers, attrib, true); break; default: @@ -1432,13 +1449,15 @@ private void ParseIsWindowsVersionOrGreater(XmlNode node) { Core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.ParentNode.LocalName, "Id")); } - if (string.IsNullOrEmpty(version)) + if (string.IsNullOrEmpty(minVersion)) { - Core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.LocalName, "Version")); + Core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.LocalName, "MinVersion")); } - if (!Version.TryParse(version, out Version v)) + if (!string.IsNullOrEmpty(maxVersion) && Version.TryParse(maxVersion, out Version v)) { - Core.OnMessage(WixErrors.IllegalVersionValue(sourceLineNumbers, node.LocalName, "Version", version)); + int build = v.Build >= 0 ? v.Build : 0xFFFF; + int revision = v.Revision >= 0 ? v.Revision : 0xFFFF; + maxVersion = $"{v.Major}.{v.Minor}.{build}.{revision}"; } // find unexpected child elements @@ -1457,7 +1476,8 @@ private void ParseIsWindowsVersionOrGreater(XmlNode node) Row row = Core.CreateRow(sourceLineNumbers, "PSW_IsWindowsVersionOrGreater"); row[0] = "wmv" + Guid.NewGuid().ToString("N"); row[1] = property; - row[2] = version; + row[2] = minVersion; + row[3] = maxVersion; } } diff --git a/PanelSwWixExtension/Xsd/PanelSwWixExtension.xsd b/PanelSwWixExtension/Xsd/PanelSwWixExtension.xsd index db94d292..aa2e6ab9 100644 --- a/PanelSwWixExtension/Xsd/PanelSwWixExtension.xsd +++ b/PanelSwWixExtension/Xsd/PanelSwWixExtension.xsd @@ -236,15 +236,25 @@ - + - + - Minimal version to test. The version is compared against Kernel32.dll's version. The property is set to 1 if Kernel32.dll's is at least the minimal version. Otherwise, the property is unmodified. + Deperecated. Use MinVersion instead + + + + + Minimal version to test. The version is compared against Kernel32.dll's product version. The property is set to 1 if Kernel32.dll's is at least the minimal version. Otherwise, the property is unmodified. + + + + + Maximal version to test. The version is compared against Kernel32.dll's product version. The property is set to 1 if Kernel32.dll's is not greater than the maximal version. Otherwise, the property is unmodified. diff --git a/README.md b/README.md index 9c551c91..d4aea84d 100644 --- a/README.md +++ b/README.md @@ -209,5 +209,5 @@ After building a unit test project, you'll need to shutdown Visual Studio before This is due to the unfortunate habit of Visual Studio to hold the extension file in use. You may find it convenient to build unit test projects from a command prompt to workaround this limitation ~~~~~~~~~~~~ -MSBuild UnitTests\WebsiteConfigUT\WebsiteConfigUT.wixproj /p:Configuration=Release /p:Platform=x86 /t:Rebuild "/p:SolutionDir=%CD%\\" +MSBuild UnitTests\IsWindowsVersionOrGreaterUT\IsWindowsVersionOrGreaterUT.wixproj /p:Configuration=Release /p:Platform=x86 /t:Rebuild "/p:SolutionDir=%CD%\\" ~~~~~~~~~~~~ diff --git a/TidyBuild.custom.props b/TidyBuild.custom.props index abe9280c..16968761 100644 --- a/TidyBuild.custom.props +++ b/TidyBuild.custom.props @@ -2,7 +2,7 @@ - 3.12.0 + 3.12.1 $(FullVersion).$(GITHUB_RUN_NUMBER) PanelSwWixExtension Panel::Software diff --git a/UnitTests/IsWindowsVersionOrGreaterUT/IsWindowsVersionOrGreaterUT.wxs b/UnitTests/IsWindowsVersionOrGreaterUT/IsWindowsVersionOrGreaterUT.wxs index 84e83e46..73329e27 100644 --- a/UnitTests/IsWindowsVersionOrGreaterUT/IsWindowsVersionOrGreaterUT.wxs +++ b/UnitTests/IsWindowsVersionOrGreaterUT/IsWindowsVersionOrGreaterUT.wxs @@ -22,17 +22,17 @@ - + - + - + - +