Skip to content

Commit

Permalink
Fix IsWindowsVersionOrGreater: Use Kernel32.dll product version rathe…
Browse files Browse the repository at this point in the history
…r than file version. It correctly ientifies Windows OS version number
  • Loading branch information
nirbar committed Nov 6, 2024
1 parent 8e2362e commit aa09c1b
Show file tree
Hide file tree
Showing 10 changed files with 110 additions and 42 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/github-actions-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ on:
psw_wix_version:
description: 'PanelSwWix4 version'
required: true
default: 5.0.0-psw-wix.0301-56
default: 5.0.0-psw-wix.0324-60
type: string

jobs:
Expand All @@ -37,7 +37,7 @@ jobs:
Add-Content -Path ${{ github.env }} -Value "PSW_WIX_VERSION=${{ env.DEFAULT_PSW_WIX_VERSION }}"
}
env:
DEFAULT_PSW_WIX_VERSION: '5.0.0-psw-wix.0301-56'
DEFAULT_PSW_WIX_VERSION: '5.0.0-psw-wix.0324-60'

- name: Prepare for build
run: |
Expand Down
2 changes: 1 addition & 1 deletion src/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<Wix4Version Condition=" '$(Wix4Version)' == '' ">4.0.5</Wix4Version>
<PanelSwWix4Version Condition=" '$(PanelSwWix4Version)' == '' ">5.0.0-psw-wix.0301-56</PanelSwWix4Version>
<PanelSwWix4Version Condition=" '$(PanelSwWix4Version)' == '' ">5.0.0-psw-wix.0324-60</PanelSwWix4Version>
<SevenZapVersion Condition=" '$(SevenZapVersion)' == '' ">24.8.41</SevenZapVersion>
</PropertyGroup>
<ItemGroup>
Expand Down
67 changes: 50 additions & 17 deletions src/PanelSwCustomActions/IsWindowsVersionOrGreater.cpp
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
#include "pch.h"

static HRESULT DAPI GetKernel32ProductVersion(ULARGE_INTEGER* pullKernelProductVersion);

extern "C" UINT __stdcall IsWindowsVersionOrGreater(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
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");
Expand All @@ -19,40 +20,42 @@ 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
while ((hr = WcaFetchRecord(hView, &hRecord)) != E_NOMOREITEMS)
{
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.");
Expand All @@ -64,3 +67,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;
}
38 changes: 28 additions & 10 deletions src/PanelSwWixExtension/PanelSwWixCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
using System.Xml;
using System.Xml.Linq;
using WixToolset.Data;
using WixToolset.Data.Burn;
using WixToolset.Data.Symbols;
using WixToolset.Data.WindowsInstaller;
using WixToolset.Extensibility;
Expand Down Expand Up @@ -1265,7 +1264,8 @@ private void ParseIsWindowsVersionOrGreater(IntermediateSection section, XElemen
{
SourceLineNumber sourceLineNumbers = ParseHelper.GetSourceLineNumbers(element);
string property = null;
string version = null;
string minVersion = null;
string maxVersion = null;

if (element.Parent.Name.LocalName != "Property")
{
Expand All @@ -1284,7 +1284,23 @@ private void ParseIsWindowsVersionOrGreater(IntermediateSection section, XElemen
switch (attrib.Name.LocalName)
{
case "Version":
version = ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
Messaging.Write(WarningMessages.DeprecatedAttribute(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName, "MinVersion"));
if (minVersion != null)
{
Messaging.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName, "MinVersion"));
break;
}
minVersion = ParseHelper.GetAttributeVersionValue(sourceLineNumbers, attrib);
break;
case "MinVersion":
if (minVersion != null)
{
Messaging.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName, "Version"));
}
minVersion = ParseHelper.GetAttributeVersionValue(sourceLineNumbers, attrib);
break;
case "MaxVersion":
maxVersion = ParseHelper.GetAttributeVersionValue(sourceLineNumbers, attrib);
break;

default:
Expand All @@ -1298,13 +1314,9 @@ private void ParseIsWindowsVersionOrGreater(IntermediateSection section, XElemen
{
Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Parent.Name.LocalName, "Id"));
}
if (string.IsNullOrEmpty(version))
{
Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Version"));
}
if (!Version.TryParse(version, out Version v))
if (string.IsNullOrEmpty(minVersion))
{
Messaging.Write(ErrorMessages.IllegalVersionValue(sourceLineNumbers, element.Name.LocalName, "Version", version));
Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "MinVersion"));
}

if (CheckNoCData(element) && !Messaging.EncounteredError)
Expand All @@ -1313,7 +1325,13 @@ private void ParseIsWindowsVersionOrGreater(IntermediateSection section, XElemen

PSW_IsWindowsVersionOrGreater row = section.AddSymbol(new PSW_IsWindowsVersionOrGreater(sourceLineNumbers));
row.Property_ = property;
row.Version = v;
row.MinVersion = Version.Parse(minVersion);
if (!string.IsNullOrEmpty(maxVersion) && Version.TryParse(maxVersion, out Version v))
{
int build = v.Build >= 0 ? v.Build : 0xFFFF;
int revision = v.Revision >= 0 ? v.Revision : 0xFFFF;
row.MaxVersion = new Version(v.Major, v.Minor, build, revision);
}
}
}

Expand Down
11 changes: 9 additions & 2 deletions src/PanelSwWixExtension/Symbols/PSW_IsWindowsVersionOrGreater.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ public static IEnumerable<ColumnDefinition> ColumnDefinitions
{
new ColumnDefinition(nameof(Id), ColumnType.String, 72, true, false, ColumnCategory.Identifier, modularizeType: ColumnModularizeType.Column),
new ColumnDefinition(nameof(Property_), ColumnType.String, 72, false, false, ColumnCategory.Identifier, modularizeType: ColumnModularizeType.Column),
new ColumnDefinition(nameof(Version), ColumnType.String, 0, false, false, ColumnCategory.Version, modularizeType: ColumnModularizeType.None),
new ColumnDefinition(nameof(MinVersion), ColumnType.String, 0, false, false, ColumnCategory.Version, modularizeType: ColumnModularizeType.None),
new ColumnDefinition(nameof(MaxVersion), ColumnType.String, 0, false, true, ColumnCategory.Version, modularizeType: ColumnModularizeType.None),
};
}
}
Expand All @@ -39,10 +40,16 @@ public string Property_
set => this.Set(0, value);
}

public Version Version
public Version MinVersion
{
get => Version.TryParse(Fields[1].AsString(), out Version v) ? v : new Version(0, 0, 0, 0);
set => this.Set(1, value.ToString());
}

public Version MaxVersion
{
get => Version.TryParse(Fields[2].AsString(), out Version v) ? v : new Version(0, 0, 0, 0);
set => this.Set(2, value.ToString());
}
}
}
16 changes: 13 additions & 3 deletions src/PanelSwWixExtension/Xsd/PanelSwWixExtension.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -326,15 +326,25 @@
<xse:parent namespace="http://wixtoolset.org/schemas/v4/wxs" ref="Property" />
</xs:appinfo>
<xs:documentation>
<![CDATA[Test whether Kernel32.dll's version is at least the given version. If yes, set the property to 1.]]>
<![CDATA[Test whether Kernel32.dll's product version is at least the given version. If yes, set the property to 1.]]>
</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="Version" type="xs:string" use="required">
<xs:attribute name="Version" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation>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.</xs:documentation>
<xs:documentation>Deperecated. Use MinVersion instead</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="MinVersion" type="xs:string" use="required">
<xs:annotation>
<xs:documentation>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.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="MaxVersion" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation>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.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:extension>
Expand Down
2 changes: 1 addition & 1 deletion src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,5 +215,5 @@ msbuild dirs.proj -restore "-p:SolutionDir=%CD%\"
Unit-test WiX are available in the solution folder 'UnitTests'.
To build a unit test project execute:
~~~~~~~~~~~~
msbuild dirs.proj -restore -p:UnitTest=ForceVersionUT
msbuild dirs.proj -restore -p:UnitTest=IsWindowsVersionOrGreaterUT
~~~~~~~~~~~~
2 changes: 1 addition & 1 deletion src/TidyBuild.custom.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<Project ToolsVersion="16.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildThisFileDirectory)TidyBuild.user.props" Condition="Exists('$(MSBuildThisFileDirectory)TidyBuild.user.props')"/>
<PropertyGroup>
<FullVersion>3.20.0</FullVersion>
<FullVersion>3.20.1</FullVersion>
<FullVersion Condition=" '$(GITHUB_RUN_NUMBER)'!='' ">$(FullVersion).$(GITHUB_RUN_NUMBER)</FullVersion>
<ProductName>PanelSwWixExtension</ProductName>
<Manufacturer>Panel::Software</Manufacturer>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@
<Property Id="MSIFASTINSTALL" Value="1"></Property>

<Property Id="TEST_5_0">
<PanelSW:IsWindowsVersionOrGreater Version="5.0" />
<PanelSW:IsWindowsVersionOrGreater MinVersion="5.0" />
</Property>
<Property Id="TEST_10">
<PanelSW:IsWindowsVersionOrGreater Version="6.3.18362" />
<PanelSW:IsWindowsVersionOrGreater MinVersion="10.0.18362" MaxVersion="10.0.21999" />
</Property>
<Property Id="TEST_11">
<PanelSW:IsWindowsVersionOrGreater Version="6.3.22000" />
<PanelSW:IsWindowsVersionOrGreater MinVersion="10.0.22000" />
</Property>
<Launch Condition="Installed Or TEST_5_0" Message="Surely we're no older than Windows 5.0"/>
<Launch Condition="Installed Or TEST_10" Message="Surely we're no older than Windows 10.0"/>
<Launch Condition="Installed Or TEST_10 Or TEST_11" Message="Surely we're no older than Windows 10.0"/>
<Launch Condition="Installed Or TEST_11" Message="Surely we're no older than Windows 11.0"/>

<CustomActionRef Id="TerminateSuccessfully_Immediate" />
Expand Down
2 changes: 1 addition & 1 deletion src/global.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"msbuild-sdks": {
"WixToolset.Sdk": "4.0.5",
"PanelSwWix4.Sdk": "5.0.0-psw-wix.0301-56",
"PanelSwWix4.Sdk": "5.0.0-psw-wix.0324-60",
"Microsoft.Build.Traversal": "4.0.0"
}
}

0 comments on commit aa09c1b

Please sign in to comment.