@joebigelow / wix / commits / 1b6a4f9b

Add CompareVersions engine method and expose verutil in Mba.Core.

Sean Hall committed Oct 18, 2020 at 22:38 UTC 1b6a4f9b4600079e829d5884f768563ed7dea5e5
15 files changed +392 -2
src/WixToolset.Mba.Core/Engine.cs
+6
@@ -49,6 +49,12 @@ namespace WixToolset.Mba.Core
49 this.engine.CloseSplashScreen();
50 }
51
52 + public int CompareVersions(string version1, string version2)
53 + {
54 + this.engine.CompareVersions(version1, version2, out var result);
55 + return result;
56 + }
57 +
58 public bool ContainsVariable(string name)
59 {
60 int capacity = 0;
src/WixToolset.Mba.Core/IBootstrapperEngine.cs
+6
@@ -145,6 +145,12 @@ namespace WixToolset.Mba.Core
145 [MarshalAs(UnmanagedType.LPWStr)] string wzArguments,
146 [MarshalAs(UnmanagedType.U4)] int dwWaitForInputIdleTimeout
147 );
148 +
149 + void CompareVersions(
150 + [MarshalAs(UnmanagedType.LPWStr)] string wzVersion1,
151 + [MarshalAs(UnmanagedType.LPWStr)] string wzVersion2,
152 + [MarshalAs(UnmanagedType.I4)] out int pnResult
153 + );
154 }
155
156 /// <summary>
src/WixToolset.Mba.Core/IEngine.cs
+3
@@ -25,6 +25,9 @@ namespace WixToolset.Mba.Core
25 /// </summary>
26 void CloseSplashScreen();
27
28 + /// <returns>0 if equal, 1 if version1 &gt; version2, -1 if version1 &lt; version2</returns>
29 + int CompareVersions(string version1, string version2);
30 +
31 /// <summary>
32 /// Checks if a variable exists in the engine.
33 /// </summary>
src/WixToolset.Mba.Core/VerUtil.cs new
+126
@@ -0,0 +1,126 @@
1 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 +
3 +namespace WixToolset.Mba.Core
4 +{
5 + using System;
6 + using System.Runtime.InteropServices;
7 + using System.Text;
8 +
9 + public static class VerUtil
10 + {
11 + [DllImport("mbanative.dll", ExactSpelling = true, PreserveSig = false)]
12 + internal static extern int VerCompareParsedVersions(
13 + VersionHandle pVersion1,
14 + VersionHandle pVersion2
15 + );
16 +
17 + [DllImport("mbanative.dll", ExactSpelling = true, PreserveSig = false)]
18 + internal static extern int VerCompareStringVersions(
19 + [MarshalAs(UnmanagedType.LPWStr)] string wzVersion1,
20 + [MarshalAs(UnmanagedType.LPWStr)] string wzVersion2,
21 + [MarshalAs(UnmanagedType.Bool)] bool fStrict
22 + );
23 +
24 + [DllImport("mbanative.dll", ExactSpelling = true, PreserveSig = false)]
25 + internal static extern VersionHandle VerCopyVersion(
26 + VersionHandle pSource
27 + );
28 +
29 + [DllImport("mbanative.dll", ExactSpelling = true)]
30 + internal static extern void VerFreeVersion(
31 + IntPtr pVersion
32 + );
33 +
34 + [DllImport("mbanative.dll", ExactSpelling = true, PreserveSig = false)]
35 + internal static extern VersionHandle VerParseVersion(
36 + [MarshalAs(UnmanagedType.LPWStr)] string wzVersion,
37 + [MarshalAs(UnmanagedType.U4)] uint cchValue,
38 + [MarshalAs(UnmanagedType.Bool)] bool fStrict
39 + );
40 +
41 + [DllImport("mbanative.dll", ExactSpelling = true, PreserveSig = false)]
42 + internal static extern VersionHandle VerVersionFromQword(
43 + [MarshalAs(UnmanagedType.I8)] long qwVersion
44 + );
45 +
46 + [StructLayout(LayoutKind.Sequential)]
47 + internal struct VersionReleaseLabelStruct
48 + {
49 + public bool fNumeric;
50 + public uint dwValue;
51 + public IntPtr cchLabelOffset;
52 + public int cchLabel;
53 + }
54 +
55 + [StructLayout(LayoutKind.Sequential)]
56 + internal struct VersionStruct
57 + {
58 + public IntPtr sczVersion;
59 + public uint dwMajor;
60 + public uint dwMinor;
61 + public uint dwPatch;
62 + public uint dwRevision;
63 + public int cReleaseLabels;
64 + public IntPtr rgReleaseLabels;
65 + public IntPtr cchMetadataOffset;
66 + public bool fInvalid;
67 + }
68 +
69 + internal static string VersionStringFromOffset(IntPtr wzVersion, IntPtr cchOffset, int? cchLength = null)
70 + {
71 + var offset = cchOffset.ToInt64() * UnicodeEncoding.CharSize;
72 + var wz = new IntPtr(wzVersion.ToInt64() + offset);
73 + if (cchLength.HasValue)
74 + {
75 + return Marshal.PtrToStringUni(wz, (int)cchLength);
76 + }
77 + else
78 + {
79 + return Marshal.PtrToStringUni(wz);
80 + }
81 + }
82 +
83 + internal sealed class VersionHandle : SafeHandle
84 + {
85 + public VersionHandle() : base(IntPtr.Zero, true) { }
86 +
87 + public override bool IsInvalid => false;
88 +
89 + protected override bool ReleaseHandle()
90 + {
91 + VerFreeVersion(this.handle);
92 + return true;
93 + }
94 + }
95 +
96 + /// <returns>0 if equal, 1 if version1 &gt; version2, -1 if version1 &lt; version2</returns>
97 + public static int CompareParsedVersions(VerUtilVersion version1, VerUtilVersion version2)
98 + {
99 + return VerCompareParsedVersions(version1.GetHandle(), version2.GetHandle());
100 + }
101 +
102 + /// <returns>0 if equal, 1 if version1 &gt; version2, -1 if version1 &lt; version2</returns>
103 + public static int CompareStringVersions(string version1, string version2, bool strict)
104 + {
105 + return VerCompareStringVersions(version1, version2, strict);
106 + }
107 +
108 + public static VerUtilVersion CopyVersion(VerUtilVersion version)
109 + {
110 + var handle = VerCopyVersion(version.GetHandle());
111 + return new VerUtilVersion(handle);
112 + }
113 +
114 + public static VerUtilVersion ParseVersion(string version, bool strict)
115 + {
116 + var handle = VerParseVersion(version, 0, strict);
117 + return new VerUtilVersion(handle);
118 + }
119 +
120 + public static VerUtilVersion VersionFromQword(long version)
121 + {
122 + var handle = VerVersionFromQword(version);
123 + return new VerUtilVersion(handle);
124 + }
125 + }
126 +}
src/WixToolset.Mba.Core/VerUtilVersion.cs new
+63
@@ -0,0 +1,63 @@
1 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 +
3 +namespace WixToolset.Mba.Core
4 +{
5 + using System;
6 + using System.Runtime.InteropServices;
7 +
8 + public sealed class VerUtilVersion : IDisposable
9 + {
10 + internal VerUtilVersion(VerUtil.VersionHandle handle)
11 + {
12 + this.Handle = handle;
13 +
14 + var pVersion = handle.DangerousGetHandle();
15 + var version = (VerUtil.VersionStruct)Marshal.PtrToStructure(pVersion, typeof(VerUtil.VersionStruct));
16 + this.Version = Marshal.PtrToStringUni(version.sczVersion);
17 + this.Major = version.dwMajor;
18 + this.Minor = version.dwMinor;
19 + this.Patch = version.dwPatch;
20 + this.Revision = version.dwRevision;
21 + this.ReleaseLabels = new VerUtilVersionReleaseLabel[version.cReleaseLabels];
22 + this.Metadata = VerUtil.VersionStringFromOffset(version.sczVersion, version.cchMetadataOffset);
23 + this.IsInvalid = version.fInvalid;
24 +
25 + for (var i = 0; i < version.cReleaseLabels; ++i)
26 + {
27 + var offset = i * Marshal.SizeOf(typeof(VerUtil.VersionReleaseLabelStruct));
28 + var pReleaseLabel = new IntPtr(version.rgReleaseLabels.ToInt64() + offset);
29 + this.ReleaseLabels[i] = new VerUtilVersionReleaseLabel(pReleaseLabel, version.sczVersion);
30 + }
31 + }
32 +
33 + public string Version { get; private set; }
34 + public uint Major { get; private set; }
35 + public uint Minor { get; private set; }
36 + public uint Patch { get; private set; }
37 + public uint Revision { get; private set; }
38 + public VerUtilVersionReleaseLabel[] ReleaseLabels { get; private set; }
39 + public string Metadata { get; private set; }
40 + public bool IsInvalid { get; private set; }
41 +
42 + public void Dispose()
43 + {
44 + if (this.Handle != null)
45 + {
46 + this.Handle.Dispose();
47 + this.Handle = null;
48 + }
49 + }
50 +
51 + private VerUtil.VersionHandle Handle { get; set; }
52 +
53 + internal VerUtil.VersionHandle GetHandle()
54 + {
55 + if (this.Handle == null)
56 + {
57 + throw new ObjectDisposedException(this.Version);
58 + }
59 +
60 + return this.Handle;
61 + }
62 + }
63 +}
src/WixToolset.Mba.Core/VerUtilVersionReleaseLabel.cs new
+22
@@ -0,0 +1,22 @@
1 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 +
3 +namespace WixToolset.Mba.Core
4 +{
5 + using System;
6 + using System.Runtime.InteropServices;
7 +
8 + public sealed class VerUtilVersionReleaseLabel
9 + {
10 + internal VerUtilVersionReleaseLabel(IntPtr pReleaseLabel, IntPtr wzVersion)
11 + {
12 + var releaseLabel = (VerUtil.VersionReleaseLabelStruct)Marshal.PtrToStructure(pReleaseLabel, typeof(VerUtil.VersionReleaseLabelStruct));
13 + this.IsNumeric = releaseLabel.fNumeric;
14 + this.Value = releaseLabel.dwValue;
15 + this.Label = VerUtil.VersionStringFromOffset(wzVersion, releaseLabel.cchLabelOffset, releaseLabel.cchLabel);
16 + }
17 +
18 + public bool IsNumeric { get; private set; }
19 + public uint Value { get; private set; }
20 + public string Label { get; private set; }
21 + }
22 +}
src/balutil/BalBootstrapperEngine.cpp
+26
@@ -535,6 +535,32 @@ public: // IBootstrapperEngine
535 return m_pfnBAEngineProc(BOOTSTRAPPER_ENGINE_MESSAGE_LAUNCHAPPROVEDEXE, &args, &results, m_pvBAEngineProcContext);
536 }
537
538 + virtual STDMETHODIMP CompareVersions(
539 + __in_z LPCWSTR wzVersion1,
540 + __in_z LPCWSTR wzVersion2,
541 + __out int* pnResult
542 + )
543 + {
544 + HRESULT hr = S_OK;
545 + BAENGINE_COMPAREVERSIONS_ARGS args = { };
546 + BAENGINE_COMPAREVERSIONS_RESULTS results = { };
547 +
548 + ExitOnNull(pnResult, hr, E_INVALIDARG, "pnResult is required");
549 +
550 + args.cbSize = sizeof(args);
551 + args.wzVersion1 = wzVersion1;
552 + args.wzVersion2 = wzVersion2;
553 +
554 + results.cbSize = sizeof(results);
555 +
556 + hr = m_pfnBAEngineProc(BOOTSTRAPPER_ENGINE_MESSAGE_COMPAREVERSIONS, &args, &results, m_pvBAEngineProcContext);
557 +
558 + *pnResult = results.nResult;
559 +
560 + LExit:
561 + return hr;
562 + }
563 +
564 public: // IMarshal
565 virtual STDMETHODIMP GetUnmarshalClass(
566 __in REFIID /*riid*/,
src/balutil/inc/IBootstrapperApplication.h
+1 -1
@@ -396,7 +396,7 @@ DECLARE_INTERFACE_IID_(IBootstrapperApplication, IUnknown, "53C31D56-49C0-426B-A
396 __inout BOOL* pfCancel
397 ) = 0;
398
399 - // OnExecuteBegin - called when the engine begins executing a package.
399 + // OnExecutePackageBegin - called when the engine begins executing a package.
400 //
401 STDMETHOD(OnExecutePackageBegin)(
402 __in_z LPCWSTR wzPackageId,
src/balutil/inc/IBootstrapperEngine.h
+6
@@ -127,4 +127,10 @@ DECLARE_INTERFACE_IID_(IBootstrapperEngine, IUnknown, "6480D616-27A0-44D7-905B-8
127 __in_z_opt LPCWSTR wzArguments,
128 __in DWORD dwWaitForInputIdleTimeout
129 ) = 0;
130 +
131 + STDMETHOD(CompareVersions)(
132 + __in_z LPCWSTR wzVersion1,
133 + __in_z LPCWSTR wzVersion2,
134 + __out int* pnResult
135 + ) = 0;
136 };
src/bextutil/BextBundleExtensionEngine.cpp
+26
@@ -280,6 +280,32 @@ public: // IBundleExtensionEngine
280 return m_pfnBundleExtensionEngineProc(BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLEVERSION, &args, &results, m_pvBundleExtensionEngineProcContext);
281 }
282
283 + virtual STDMETHODIMP CompareVersions(
284 + __in_z LPCWSTR wzVersion1,
285 + __in_z LPCWSTR wzVersion2,
286 + __out int* pnResult
287 + )
288 + {
289 + HRESULT hr = S_OK;
290 + BUNDLE_EXTENSION_ENGINE_COMPAREVERSIONS_ARGS args = { };
291 + BUNDLE_EXTENSION_ENGINE_COMPAREVERSIONS_RESULTS results = { };
292 +
293 + ExitOnNull(pnResult, hr, E_INVALIDARG, "pnResult is required");
294 +
295 + args.cbSize = sizeof(args);
296 + args.wzVersion1 = wzVersion1;
297 + args.wzVersion2 = wzVersion2;
298 +
299 + results.cbSize = sizeof(results);
300 +
301 + hr = m_pfnBundleExtensionEngineProc(BUNDLE_EXTENSION_ENGINE_MESSAGE_COMPAREVERSIONS, &args, &results, m_pvBundleExtensionEngineProcContext);
302 +
303 + *pnResult = results.nResult;
304 +
305 + LExit:
306 + return hr;
307 + }
308 +
309 public:
310 CBextBundleExtensionEngine(
311 __in PFN_BUNDLE_EXTENSION_ENGINE_PROC pfnBundleExtensionEngineProc,
src/bextutil/inc/IBundleExtensionEngine.h
+6
@@ -58,4 +58,10 @@ DECLARE_INTERFACE_IID_(IBundleExtensionEngine, IUnknown, "9D027A39-F6B6-42CC-973
58 __in_z LPCWSTR wzVariable,
59 __in_z_opt LPCWSTR wzValue
60 ) = 0;
61 +
62 + STDMETHOD(CompareVersions)(
63 + __in_z LPCWSTR wzVersion1,
64 + __in_z LPCWSTR wzVersion2,
65 + __out int* pnResult
66 + ) = 0;
67 };
src/mbanative/mbanative.def
+6
@@ -4,3 +4,9 @@
4 EXPORTS
5 InitializeFromCreateArgs
6 StoreBAInCreateResults
7 + VerCompareParsedVersions
8 + VerCompareStringVersions
9 + VerCopyVersion
10 + VerFreeVersion
11 + VerParseVersion
12 + VerVersionFromQword
src/mbanative/precomp.h
+1
@@ -6,6 +6,7 @@
6 #include <msiquery.h>
7
8 #include <dutil.h>
9 +#include <verutil.h>
10
11 #include <BootstrapperEngine.h>
12 #include <BootstrapperApplication.h>
src/test/WixToolsetTest.Mba.Core/BaseBootstrapperApplicationFactoryFixture.cs
+1 -1
@@ -1,6 +1,6 @@
1 // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 -namespace WixToolsetTest.Util
3 +namespace WixToolsetTest.Mba.Core
4 {
5 using System;
6 using System.Runtime.InteropServices;
src/test/WixToolsetTest.Mba.Core/VerUtilFixture.cs new
+93
@@ -0,0 +1,93 @@
1 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 +
3 +namespace WixToolsetTest.Mba.Core
4 +{
5 + using System;
6 + using WixToolset.Mba.Core;
7 + using Xunit;
8 +
9 + public class VerUtilFixture
10 + {
11 + [Fact]
12 + public void CanCompareStringVersions()
13 + {
14 + var version1 = "1.2.3.4+abcd";
15 + var version2 = "1.2.3.4+zyxw";
16 +
17 + Assert.Equal(0, VerUtil.CompareStringVersions(version1, version2, strict: false));
18 + }
19 +
20 + [Fact]
21 + public void CanCopyVersion()
22 + {
23 + var version = "1.2.3.4-5.6.7.8.9.0";
24 +
25 + VerUtilVersion copiedVersion = null;
26 + try
27 + {
28 + using (var parsedVersion = VerUtil.ParseVersion(version, strict: true))
29 + {
30 + copiedVersion = VerUtil.CopyVersion(parsedVersion);
31 + }
32 +
33 + using (var secondVersion = VerUtil.ParseVersion(version, strict: true))
34 + {
35 + Assert.Equal(0, VerUtil.CompareParsedVersions(copiedVersion, secondVersion));
36 + }
37 + }
38 + finally
39 + {
40 + copiedVersion?.Dispose();
41 + }
42 + }
43 +
44 + [Fact]
45 + public void CanCreateFromQword()
46 + {
47 + var version = new Version(100, 200, 300, 400);
48 + var qwVersion = Engine.VersionToLong(version);
49 +
50 + using var parsedVersion = VerUtil.VersionFromQword(qwVersion);
51 + Assert.Equal("100.200.300.400", parsedVersion.Version);
52 + Assert.Equal(100u, parsedVersion.Major);
53 + Assert.Equal(200u, parsedVersion.Minor);
54 + Assert.Equal(300u, parsedVersion.Patch);
55 + Assert.Equal(400u, parsedVersion.Revision);
56 + Assert.Empty(parsedVersion.ReleaseLabels);
57 + Assert.Equal("", parsedVersion.Metadata);
58 + Assert.False(parsedVersion.IsInvalid);
59 + }
60 +
61 + [Fact]
62 + public void CanParseVersion()
63 + {
64 + var version = "1.2.3.4-a.b.c.d.5.+abc123";
65 +
66 + using var parsedVersion = VerUtil.ParseVersion(version, strict: false);
67 + Assert.Equal(version, parsedVersion.Version);
68 + Assert.Equal(1u, parsedVersion.Major);
69 + Assert.Equal(2u, parsedVersion.Minor);
70 + Assert.Equal(3u, parsedVersion.Patch);
71 + Assert.Equal(4u, parsedVersion.Revision);
72 + Assert.Equal(5, parsedVersion.ReleaseLabels.Length);
73 + Assert.Equal("+abc123", parsedVersion.Metadata);
74 + Assert.True(parsedVersion.IsInvalid);
75 +
76 + Assert.Equal("a", parsedVersion.ReleaseLabels[0].Label);
77 + Assert.False(parsedVersion.ReleaseLabels[0].IsNumeric);
78 +
79 + Assert.Equal("b", parsedVersion.ReleaseLabels[1].Label);
80 + Assert.False(parsedVersion.ReleaseLabels[1].IsNumeric);
81 +
82 + Assert.Equal("c", parsedVersion.ReleaseLabels[2].Label);
83 + Assert.False(parsedVersion.ReleaseLabels[2].IsNumeric);
84 +
85 + Assert.Equal("d", parsedVersion.ReleaseLabels[3].Label);
86 + Assert.False(parsedVersion.ReleaseLabels[3].IsNumeric);
87 +
88 + Assert.Equal("5", parsedVersion.ReleaseLabels[4].Label);
89 + Assert.True(parsedVersion.ReleaseLabels[4].IsNumeric);
90 + Assert.Equal(5u, parsedVersion.ReleaseLabels[4].Value);
91 + }
92 + }
93 +}