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 > version2, -1 if version1 < 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 > version2, -1 if version1 < 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 > version2, -1 if version1 < 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
+}