WIXFEAT:6210 Parse and compare bundle versions kind of like SemVer.
Sean Hall committed
Aug 29, 2020 at 21:29 UTC
d1d31466bb9f2e887a277807d60378afef9cc57d
10 files changed
+1630
src/dutil/dutil.vcxproj
+2
@@ -111,6 +111,7 @@
111
<ClCompile Include="uncutil.cpp" />
112
<ClCompile Include="uriutil.cpp" />
113
<ClCompile Include="userutil.cpp" />
114
+ <ClCompile Include="verutil.cpp" />
115
<ClCompile Include="wiutil.cpp" />
116
<ClCompile Include="wuautil.cpp" />
117
<ClCompile Include="xmlutil.cpp" />
@@ -167,6 +168,7 @@
168
<ClInclude Include="inc\timeutil.h" />
169
<ClInclude Include="inc\uriutil.h" />
170
<ClInclude Include="inc\userutil.h" />
171
+ <ClInclude Include="inc\verutil.h" />
172
<ClInclude Include="inc\wiutil.h" />
173
<ClInclude Include="inc\wuautil.h" />
174
<ClInclude Include="inc\xmlutil.h" />
src/dutil/dutil.vcxproj.filters
+6
@@ -159,6 +159,9 @@
159
<ClCompile Include="userutil.cpp">
160
<Filter>Source Files</Filter>
161
</ClCompile>
162
+ <ClCompile Include="verutil.cpp">
163
+ <Filter>Source Files</Filter>
164
+ </ClCompile>
165
<ClCompile Include="wiutil.cpp">
166
<Filter>Source Files</Filter>
167
</ClCompile>
@@ -329,6 +332,9 @@
332
<ClInclude Include="inc\userutil.h">
333
<Filter>Header Files</Filter>
334
</ClInclude>
335
+ <ClInclude Include="inc\verutil.h">
336
+ <Filter>Header Files</Filter>
337
+ </ClInclude>
338
<ClInclude Include="inc\wiutil.h">
339
<Filter>Header Files</Filter>
340
</ClInclude>
src/dutil/inc/dutilsources.h
+1
@@ -60,6 +60,7 @@ typedef enum DUTIL_SOURCE
60
DUTIL_SOURCE_WIUTIL,
61
DUTIL_SOURCE_WUAUTIL,
62
DUTIL_SOURCE_XMLUTIL,
63
+ DUTIL_SOURCE_VERUTIL,
64
65
DUTIL_SOURCE_EXTERNAL = 256,
66
} DUTIL_SOURCE;
src/dutil/inc/verutil.h
new
+93
@@ -0,0 +1,93 @@
1
+#pragma once
2
+// 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.
3
+
4
+
5
+#ifdef __cplusplus
6
+extern "C" {
7
+#endif
8
+
9
+#define ReleaseVerutilVersion(p) if (p) { VerFreeVersion(p); p = NULL; }
10
+
11
+typedef struct _VERUTIL_VERSION_RELEASE_LABEL
12
+{
13
+ BOOL fNumeric;
14
+ DWORD dwValue;
15
+ DWORD_PTR cchLabelOffset;
16
+ int cchLabel;
17
+} VERUTIL_VERSION_RELEASE_LABEL;
18
+
19
+typedef struct _VERUTIL_VERSION
20
+{
21
+ LPWSTR sczVersion;
22
+ DWORD dwMajor;
23
+ DWORD dwMinor;
24
+ DWORD dwPatch;
25
+ DWORD dwRevision;
26
+ DWORD cReleaseLabels;
27
+ VERUTIL_VERSION_RELEASE_LABEL* rgReleaseLabels;
28
+ DWORD_PTR cchMetadataOffset;
29
+ BOOL fInvalid;
30
+} VERUTIL_VERSION;
31
+
32
+/*******************************************************************
33
+ VerCompareParsedVersions - compares the Verutil versions.
34
+
35
+*******************************************************************/
36
+HRESULT DAPI VerCompareParsedVersions(
37
+ __in VERUTIL_VERSION* pVersion1,
38
+ __in VERUTIL_VERSION* pVersion2,
39
+ __out int* pnResult
40
+ );
41
+
42
+/*******************************************************************
43
+ VerCompareStringVersions - parses the strings with VerParseVersion and then
44
+ compares the Verutil versions with VerCompareParsedVersions.
45
+
46
+*******************************************************************/
47
+HRESULT DAPI VerCompareStringVersions(
48
+ __in_z LPCWSTR wzVersion1,
49
+ __in_z LPCWSTR wzVersion2,
50
+ __in BOOL fStrict,
51
+ __out int* pnResult
52
+ );
53
+
54
+/********************************************************************
55
+ VerCopyVersion - copies the given Verutil version.
56
+
57
+*******************************************************************/
58
+HRESULT DAPI VerCopyVersion(
59
+ __in VERUTIL_VERSION* pSource,
60
+ __out VERUTIL_VERSION** ppVersion
61
+ );
62
+
63
+/********************************************************************
64
+ VerFreeVersion - frees any memory associated with a Verutil version.
65
+
66
+*******************************************************************/
67
+void DAPI VerFreeVersion(
68
+ __in VERUTIL_VERSION* pVersion
69
+ );
70
+
71
+/*******************************************************************
72
+ VerParseVersion - parses the string into a Verutil version.
73
+
74
+*******************************************************************/
75
+HRESULT DAPI VerParseVersion(
76
+ __in_z LPCWSTR wzVersion,
77
+ __in DWORD cchVersion,
78
+ __in BOOL fStrict,
79
+ __out VERUTIL_VERSION** ppVersion
80
+ );
81
+
82
+/*******************************************************************
83
+ VerParseVersion - parses the QWORD into a Verutil version.
84
+
85
+*******************************************************************/
86
+HRESULT DAPI VerVersionFromQword(
87
+ __in DWORD64 qwVersion,
88
+ __out VERUTIL_VERSION** ppVersion
89
+ );
90
+
91
+#ifdef __cplusplus
92
+}
93
+#endif
src/dutil/precomp.h
+1
@@ -89,6 +89,7 @@
89
#include "uncutil.h"
90
#include "uriutil.h"
91
#include "userutil.h"
92
+#include "verutil.h"
93
#include "wiutil.h"
94
#include "wuautil.h"
95
#include <comutil.h> // This header is needed for msxml2.h to compile correctly
src/dutil/verutil.cpp
new
+631
@@ -0,0 +1,631 @@
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
+#include "precomp.h"
4
+
5
+// Exit macros
6
+#define VerExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__)
7
+#define VerExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__)
8
+#define VerExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__)
9
+#define VerExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__)
10
+#define VerExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__)
11
+#define VerExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__)
12
+#define VerExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_VERUTIL, p, x, e, s, __VA_ARGS__)
13
+#define VerExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_VERUTIL, p, x, s, __VA_ARGS__)
14
+#define VerExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_VERUTIL, p, x, e, s, __VA_ARGS__)
15
+#define VerExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_VERUTIL, p, x, s, __VA_ARGS__)
16
+#define VerExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_VERUTIL, e, x, s, __VA_ARGS__)
17
+
18
+// constants
19
+const DWORD GROW_RELEASE_LABELS = 3;
20
+
21
+// Forward declarations.
22
+static int CompareDword(
23
+ __in const DWORD& dw1,
24
+ __in const DWORD& dw2
25
+ );
26
+static HRESULT CompareReleaseLabel(
27
+ __in const VERUTIL_VERSION_RELEASE_LABEL* p1,
28
+ __in LPCWSTR wzVersion1,
29
+ __in const VERUTIL_VERSION_RELEASE_LABEL* p2,
30
+ __in LPCWSTR wzVersion2,
31
+ __out int* pnResult
32
+ );
33
+static HRESULT CompareVersionSubstring(
34
+ __in LPCWSTR wzString1,
35
+ __in int cchCount1,
36
+ __in LPCWSTR wzString2,
37
+ __in int cchCount2,
38
+ __out int* pnResult
39
+ );
40
+
41
+
42
+DAPI_(HRESULT) VerCompareParsedVersions(
43
+ __in VERUTIL_VERSION* pVersion1,
44
+ __in VERUTIL_VERSION* pVersion2,
45
+ __out int* pnResult
46
+ )
47
+{
48
+ HRESULT hr = S_OK;
49
+ int nResult = 0;
50
+ DWORD cMaxReleaseLabels = 0;
51
+ BOOL fCompareMetadata = FALSE;
52
+
53
+ if (!pVersion1 || !pVersion1->sczVersion ||
54
+ !pVersion2 || !pVersion2->sczVersion)
55
+ {
56
+ ExitFunction1(hr = E_INVALIDARG);
57
+ }
58
+
59
+ if (pVersion1 == pVersion2)
60
+ {
61
+ ExitFunction1(nResult = 0);
62
+ }
63
+
64
+ nResult = CompareDword(pVersion1->dwMajor, pVersion2->dwMajor);
65
+ if (0 != nResult)
66
+ {
67
+ ExitFunction();
68
+ }
69
+
70
+ nResult = CompareDword(pVersion1->dwMinor, pVersion2->dwMinor);
71
+ if (0 != nResult)
72
+ {
73
+ ExitFunction();
74
+ }
75
+
76
+ nResult = CompareDword(pVersion1->dwPatch, pVersion2->dwPatch);
77
+ if (0 != nResult)
78
+ {
79
+ ExitFunction();
80
+ }
81
+
82
+ nResult = CompareDword(pVersion1->dwRevision, pVersion2->dwRevision);
83
+ if (0 != nResult)
84
+ {
85
+ ExitFunction();
86
+ }
87
+
88
+ if (pVersion1->fInvalid)
89
+ {
90
+ if (!pVersion2->fInvalid)
91
+ {
92
+ ExitFunction1(nResult = -1);
93
+ }
94
+ else
95
+ {
96
+ fCompareMetadata = TRUE;
97
+ }
98
+ }
99
+ else if (pVersion2->fInvalid)
100
+ {
101
+ ExitFunction1(nResult = 1);
102
+ }
103
+
104
+ if (pVersion1->cReleaseLabels)
105
+ {
106
+ if (pVersion2->cReleaseLabels)
107
+ {
108
+ cMaxReleaseLabels = max(pVersion1->cReleaseLabels, pVersion2->cReleaseLabels);
109
+ }
110
+ else
111
+ {
112
+ ExitFunction1(nResult = -1);
113
+ }
114
+ }
115
+ else if (pVersion2->cReleaseLabels)
116
+ {
117
+ ExitFunction1(nResult = 1);
118
+ }
119
+
120
+ if (cMaxReleaseLabels)
121
+ {
122
+ for (DWORD i = 0; i < cMaxReleaseLabels; ++i)
123
+ {
124
+ VERUTIL_VERSION_RELEASE_LABEL* pReleaseLabel1 = pVersion1->cReleaseLabels > i ? pVersion1->rgReleaseLabels + i : NULL;
125
+ VERUTIL_VERSION_RELEASE_LABEL* pReleaseLabel2 = pVersion2->cReleaseLabels > i ? pVersion2->rgReleaseLabels + i : NULL;
126
+
127
+ hr = CompareReleaseLabel(pReleaseLabel1, pVersion1->sczVersion, pReleaseLabel2, pVersion2->sczVersion, &nResult);
128
+ if (FAILED(hr) || 0 != nResult)
129
+ {
130
+ ExitFunction();
131
+ }
132
+ }
133
+ }
134
+
135
+ if (fCompareMetadata)
136
+ {
137
+ hr = CompareVersionSubstring(pVersion1->sczVersion + pVersion1->cchMetadataOffset, -1, pVersion2->sczVersion + pVersion2->cchMetadataOffset, -1, &nResult);
138
+ }
139
+
140
+LExit:
141
+ *pnResult = nResult;
142
+ return hr;
143
+}
144
+
145
+DAPI_(HRESULT) VerCompareStringVersions(
146
+ __in_z LPCWSTR wzVersion1,
147
+ __in_z LPCWSTR wzVersion2,
148
+ __in BOOL fStrict,
149
+ __out int* pnResult
150
+ )
151
+{
152
+ HRESULT hr = S_OK;
153
+ VERUTIL_VERSION* pVersion1 = NULL;
154
+ VERUTIL_VERSION* pVersion2 = NULL;
155
+ int nResult = 0;
156
+
157
+ hr = VerParseVersion(wzVersion1, 0, fStrict, &pVersion1);
158
+ VerExitOnFailure(hr, "Failed to parse Verutil version '%ls'", wzVersion1);
159
+
160
+ hr = VerParseVersion(wzVersion2, 0, fStrict, &pVersion2);
161
+ VerExitOnFailure(hr, "Failed to parse Verutil version '%ls'", wzVersion2);
162
+
163
+ hr = VerCompareParsedVersions(pVersion1, pVersion2, &nResult);
164
+ VerExitOnFailure(hr, "Failed to compare parsed Verutil versions '%ls' and '%ls'.", wzVersion1, wzVersion2);
165
+
166
+LExit:
167
+ *pnResult = nResult;
168
+
169
+ ReleaseVerutilVersion(pVersion1);
170
+ ReleaseVerutilVersion(pVersion2);
171
+
172
+ return hr;
173
+}
174
+
175
+DAPI_(HRESULT) VerCopyVersion(
176
+ __in VERUTIL_VERSION* pSource,
177
+ __out VERUTIL_VERSION** ppVersion
178
+ )
179
+{
180
+ HRESULT hr = S_OK;
181
+ VERUTIL_VERSION* pCopy = NULL;
182
+
183
+ pCopy = reinterpret_cast<VERUTIL_VERSION*>(MemAlloc(sizeof(VERUTIL_VERSION), TRUE));
184
+ VerExitOnNull(pCopy, hr, E_OUTOFMEMORY, "Failed to allocate memory for Verutil version copy.");
185
+
186
+ hr = StrAllocString(&pCopy->sczVersion, pSource->sczVersion, 0);
187
+ VerExitOnFailure(hr, "Failed to copy Verutil version string '%ls'.", pSource->sczVersion);
188
+
189
+ pCopy->dwMajor = pSource->dwMajor;
190
+ pCopy->dwMinor = pSource->dwMinor;
191
+ pCopy->dwPatch = pSource->dwPatch;
192
+ pCopy->dwRevision = pSource->dwRevision;
193
+
194
+ hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pCopy->rgReleaseLabels), 0, sizeof(VERUTIL_VERSION_RELEASE_LABEL), pSource->cReleaseLabels);
195
+ VerExitOnFailure(hr, "Failed to allocate memory for Verutil version release labels copies.");
196
+
197
+ pCopy->cReleaseLabels = pSource->cReleaseLabels;
198
+
199
+ for (DWORD i = 0; i < pCopy->cReleaseLabels; ++i)
200
+ {
201
+ VERUTIL_VERSION_RELEASE_LABEL* pSourceLabel = pSource->rgReleaseLabels + i;
202
+ VERUTIL_VERSION_RELEASE_LABEL* pCopyLabel = pCopy->rgReleaseLabels + i;
203
+
204
+ pCopyLabel->cchLabelOffset = pSourceLabel->cchLabelOffset;
205
+ pCopyLabel->cchLabel = pSourceLabel->cchLabel;
206
+ pCopyLabel->fNumeric = pSourceLabel->fNumeric;
207
+ pCopyLabel->dwValue = pSourceLabel->dwValue;
208
+ }
209
+
210
+ pCopy->cchMetadataOffset = pSource->cchMetadataOffset;
211
+ pCopy->fInvalid = pSource->fInvalid;
212
+
213
+ *ppVersion = pCopy;
214
+ pCopy = NULL;
215
+
216
+LExit:
217
+ ReleaseVerutilVersion(pCopy);
218
+
219
+ return hr;
220
+}
221
+
222
+DAPI_(void) VerFreeVersion(
223
+ __in VERUTIL_VERSION* pVersion
224
+ )
225
+{
226
+ if (pVersion)
227
+ {
228
+ ReleaseStr(pVersion->sczVersion);
229
+ ReleaseMem(pVersion->rgReleaseLabels);
230
+ ReleaseMem(pVersion);
231
+ }
232
+}
233
+
234
+DAPI_(HRESULT) VerParseVersion(
235
+ __in_z LPCWSTR wzVersion,
236
+ __in DWORD cchVersion,
237
+ __in BOOL fStrict,
238
+ __out VERUTIL_VERSION** ppVersion
239
+ )
240
+{
241
+ HRESULT hr = S_OK;
242
+ VERUTIL_VERSION* pVersion = NULL;
243
+ LPCWSTR wzEnd = NULL;
244
+ LPCWSTR wzPartBegin = NULL;
245
+ LPCWSTR wzPartEnd = NULL;
246
+ BOOL fInvalid = FALSE;
247
+ BOOL fLastPart = FALSE;
248
+ BOOL fTrailingDot = FALSE;
249
+ BOOL fParsedVersionNumber = FALSE;
250
+ BOOL fExpectedReleaseLabels = FALSE;
251
+ DWORD iPart = 0;
252
+
253
+ if (!wzVersion || !ppVersion)
254
+ {
255
+ ExitFunction1(hr = E_INVALIDARG);
256
+ }
257
+
258
+ // Get string length if not provided.
259
+ if (0 == cchVersion)
260
+ {
261
+ cchVersion = lstrlenW(wzVersion);
262
+ }
263
+
264
+ if (L'v' == *wzVersion || L'V' == *wzVersion)
265
+ {
266
+ ++wzVersion;
267
+ --cchVersion;
268
+ }
269
+
270
+ pVersion = reinterpret_cast<VERUTIL_VERSION*>(MemAlloc(sizeof(VERUTIL_VERSION), TRUE));
271
+ VerExitOnNull(pVersion, hr, E_OUTOFMEMORY, "Failed to allocate memory for Verutil version '%ls'.", wzVersion);
272
+
273
+ hr = StrAllocString(&pVersion->sczVersion, wzVersion, cchVersion);
274
+ VerExitOnFailure(hr, "Failed to copy Verutil version string '%ls'.", wzVersion);
275
+
276
+ wzVersion = wzPartBegin = wzPartEnd = pVersion->sczVersion;
277
+
278
+ // Save end pointer.
279
+ wzEnd = wzVersion + cchVersion;
280
+
281
+ // Parse version number
282
+ while (wzPartBegin < wzEnd)
283
+ {
284
+ fTrailingDot = FALSE;
285
+
286
+ // Find end of part.
287
+ for (;;)
288
+ {
289
+ if (wzPartEnd >= wzEnd)
290
+ {
291
+ fLastPart = TRUE;
292
+ break;
293
+ }
294
+
295
+ switch (*wzPartEnd)
296
+ {
297
+ case L'0':
298
+ case L'1':
299
+ case L'2':
300
+ case L'3':
301
+ case L'4':
302
+ case L'5':
303
+ case L'6':
304
+ case L'7':
305
+ case L'8':
306
+ case L'9':
307
+ ++wzPartEnd;
308
+ continue;
309
+ case L'.':
310
+ fTrailingDot = TRUE;
311
+ break;
312
+ case L'-':
313
+ case L'+':
314
+ fLastPart = TRUE;
315
+ break;
316
+ default:
317
+ fInvalid = TRUE;
318
+ break;
319
+ }
320
+
321
+ break;
322
+ }
323
+
324
+ if (wzPartBegin == wzPartEnd)
325
+ {
326
+ fInvalid = TRUE;
327
+ }
328
+
329
+ if (fInvalid)
330
+ {
331
+ break;
332
+ }
333
+
334
+ DWORD cchPart = 0;
335
+ hr = ::PtrdiffTToDWord(wzPartEnd - wzPartBegin, &cchPart);
336
+ if (FAILED(hr))
337
+ {
338
+ fInvalid = TRUE;
339
+ break;
340
+ }
341
+
342
+ // Parse version part.
343
+ UINT uPart = 0;
344
+ hr = StrStringToUInt32(wzPartBegin, cchPart, &uPart);
345
+ if (FAILED(hr))
346
+ {
347
+ fInvalid = TRUE;
348
+ break;
349
+ }
350
+
351
+ switch (iPart)
352
+ {
353
+ case 0:
354
+ pVersion->dwMajor = uPart;
355
+ break;
356
+ case 1:
357
+ pVersion->dwMinor = uPart;
358
+ break;
359
+ case 2:
360
+ pVersion->dwPatch = uPart;
361
+ break;
362
+ case 3:
363
+ pVersion->dwRevision = uPart;
364
+ break;
365
+ }
366
+
367
+ if (fTrailingDot)
368
+ {
369
+ ++wzPartEnd;
370
+ }
371
+ wzPartBegin = wzPartEnd;
372
+ ++iPart;
373
+
374
+ if (4 <= iPart || fLastPart)
375
+ {
376
+ fParsedVersionNumber = TRUE;
377
+ break;
378
+ }
379
+ }
380
+
381
+ fInvalid |= !fParsedVersionNumber || fTrailingDot;
382
+
383
+ if (!fInvalid && wzPartBegin < wzEnd && *wzPartBegin == L'-')
384
+ {
385
+ wzPartBegin = wzPartEnd = wzPartBegin + 1;
386
+ fExpectedReleaseLabels = TRUE;
387
+ fLastPart = FALSE;
388
+ }
389
+
390
+ while (fExpectedReleaseLabels && wzPartBegin < wzEnd)
391
+ {
392
+ fTrailingDot = FALSE;
393
+
394
+ // Find end of part.
395
+ for (;;)
396
+ {
397
+ if (wzPartEnd >= wzEnd)
398
+ {
399
+ fLastPart = TRUE;
400
+ break;
401
+ }
402
+
403
+ if (*wzPartEnd >= L'0' && *wzPartEnd <= L'9' ||
404
+ *wzPartEnd >= L'A' && *wzPartEnd <= L'Z' ||
405
+ *wzPartEnd >= L'a' && *wzPartEnd <= L'z' ||
406
+ *wzPartEnd == L'-')
407
+ {
408
+ ++wzPartEnd;
409
+ continue;
410
+ }
411
+ else if (*wzPartEnd == L'+')
412
+ {
413
+ fLastPart = TRUE;
414
+ }
415
+ else if (*wzPartEnd == L'.')
416
+ {
417
+ fTrailingDot = TRUE;
418
+ }
419
+ else
420
+ {
421
+ fInvalid = TRUE;
422
+ }
423
+
424
+ break;
425
+ }
426
+
427
+ if (wzPartBegin == wzPartEnd)
428
+ {
429
+ fInvalid = TRUE;
430
+ }
431
+
432
+ if (fInvalid)
433
+ {
434
+ break;
435
+ }
436
+
437
+ int cchLabel = 0;
438
+ hr = ::PtrdiffTToInt32(wzPartEnd - wzPartBegin, &cchLabel);
439
+ if (FAILED(hr) || 0 > cchLabel)
440
+ {
441
+ fInvalid = TRUE;
442
+ break;
443
+ }
444
+
445
+ hr = MemReAllocArray(reinterpret_cast<LPVOID*>(&pVersion->rgReleaseLabels), pVersion->cReleaseLabels, sizeof(VERUTIL_VERSION_RELEASE_LABEL), GROW_RELEASE_LABELS - (pVersion->cReleaseLabels % GROW_RELEASE_LABELS));
446
+ VerExitOnFailure(hr, "Failed to allocate memory for Verutil version release labels '%ls'", wzVersion);
447
+
448
+ VERUTIL_VERSION_RELEASE_LABEL* pReleaseLabel = pVersion->rgReleaseLabels + pVersion->cReleaseLabels;
449
+ ++pVersion->cReleaseLabels;
450
+
451
+ // Try to parse as number.
452
+ UINT uLabel = 0;
453
+ hr = StrStringToUInt32(wzPartBegin, cchLabel, &uLabel);
454
+ if (SUCCEEDED(hr))
455
+ {
456
+ pReleaseLabel->fNumeric = TRUE;
457
+ pReleaseLabel->dwValue = uLabel;
458
+ }
459
+
460
+ pReleaseLabel->cchLabelOffset = wzPartBegin - pVersion->sczVersion;
461
+ pReleaseLabel->cchLabel = cchLabel;
462
+
463
+ if (fTrailingDot)
464
+ {
465
+ ++wzPartEnd;
466
+ }
467
+ wzPartBegin = wzPartEnd;
468
+
469
+ if (fLastPart)
470
+ {
471
+ break;
472
+ }
473
+ }
474
+
475
+ fInvalid |= fExpectedReleaseLabels && (!pVersion->cReleaseLabels || fTrailingDot);
476
+
477
+ if (!fInvalid && wzPartBegin < wzEnd)
478
+ {
479
+ if (*wzPartBegin == L'+')
480
+ {
481
+ wzPartBegin = wzPartEnd = wzPartBegin + 1;
482
+ }
483
+ else
484
+ {
485
+ fInvalid = TRUE;
486
+ }
487
+ }
488
+
489
+ if (fInvalid && fStrict)
490
+ {
491
+ ExitFunction1(hr = E_INVALIDARG);
492
+ }
493
+
494
+ pVersion->cchMetadataOffset = min(wzPartBegin, wzEnd) - pVersion->sczVersion;
495
+ pVersion->fInvalid = fInvalid;
496
+
497
+ *ppVersion = pVersion;
498
+ pVersion = NULL;
499
+ hr = S_OK;
500
+
501
+LExit:
502
+ ReleaseVerutilVersion(pVersion);
503
+
504
+ return hr;
505
+}
506
+
507
+DAPI_(HRESULT) VerVersionFromQword(
508
+ __in DWORD64 qwVersion,
509
+ __out VERUTIL_VERSION** ppVersion
510
+ )
511
+{
512
+ HRESULT hr = S_OK;
513
+ VERUTIL_VERSION* pVersion = NULL;
514
+
515
+ pVersion = reinterpret_cast<VERUTIL_VERSION*>(MemAlloc(sizeof(VERUTIL_VERSION), TRUE));
516
+ VerExitOnNull(pVersion, hr, E_OUTOFMEMORY, "Failed to allocate memory for Verutil version from QWORD.");
517
+
518
+ pVersion->dwMajor = (WORD)(qwVersion >> 48 & 0xffff);
519
+ pVersion->dwMinor = (WORD)(qwVersion >> 32 & 0xffff);
520
+ pVersion->dwPatch = (WORD)(qwVersion >> 16 & 0xffff);
521
+ pVersion->dwRevision = (WORD)(qwVersion & 0xffff);
522
+
523
+ hr = StrAllocFormatted(&pVersion->sczVersion, L"%lu.%lu.%lu.%lu", pVersion->dwMajor, pVersion->dwMinor, pVersion->dwPatch, pVersion->dwRevision);
524
+ ExitOnFailure(hr, "Failed to allocate and format the version string.");
525
+
526
+ pVersion->cchMetadataOffset = lstrlenW(pVersion->sczVersion);
527
+
528
+ *ppVersion = pVersion;
529
+ pVersion = NULL;
530
+
531
+LExit:
532
+ ReleaseVerutilVersion(pVersion);
533
+
534
+ return hr;
535
+}
536
+
537
+
538
+static int CompareDword(
539
+ __in const DWORD& dw1,
540
+ __in const DWORD& dw2
541
+ )
542
+{
543
+ int nResult = 0;
544
+
545
+ if (dw1 > dw2)
546
+ {
547
+ nResult = 1;
548
+ }
549
+ else if (dw1 < dw2)
550
+ {
551
+ nResult = -1;
552
+ }
553
+
554
+ return nResult;
555
+}
556
+
557
+static HRESULT CompareReleaseLabel(
558
+ __in const VERUTIL_VERSION_RELEASE_LABEL* p1,
559
+ __in LPCWSTR wzVersion1,
560
+ __in const VERUTIL_VERSION_RELEASE_LABEL* p2,
561
+ __in LPCWSTR wzVersion2,
562
+ __out int* pnResult
563
+ )
564
+{
565
+ HRESULT hr = S_OK;
566
+ int nResult = 0;
567
+
568
+ if (p1 == p2)
569
+ {
570
+ ExitFunction();
571
+ }
572
+ else if (p1 && !p2)
573
+ {
574
+ ExitFunction1(nResult = 1);
575
+ }
576
+ else if (!p1 && p2)
577
+ {
578
+ ExitFunction1(nResult = -1);
579
+ }
580
+
581
+ if (p1->fNumeric)
582
+ {
583
+ if (p2->fNumeric)
584
+ {
585
+ nResult = CompareDword(p1->dwValue, p2->dwValue);
586
+ }
587
+ else
588
+ {
589
+ nResult = -1;
590
+ }
591
+ }
592
+ else
593
+ {
594
+ if (p2->fNumeric)
595
+ {
596
+ nResult = 1;
597
+ }
598
+ else
599
+ {
600
+ hr = CompareVersionSubstring(wzVersion1 + p1->cchLabelOffset, p1->cchLabel, wzVersion2 + p2->cchLabelOffset, p2->cchLabel, &nResult);
601
+ }
602
+ }
603
+
604
+LExit:
605
+ *pnResult = nResult;
606
+
607
+ return hr;
608
+}
609
+
610
+static HRESULT CompareVersionSubstring(
611
+ __in LPCWSTR wzString1,
612
+ __in int cchCount1,
613
+ __in LPCWSTR wzString2,
614
+ __in int cchCount2,
615
+ __out int* pnResult
616
+ )
617
+{
618
+ HRESULT hr = S_OK;
619
+ int nResult = 0;
620
+
621
+ nResult = ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzString1, cchCount1, wzString2, cchCount2);
622
+ if (!nResult)
623
+ {
624
+ VerExitOnLastError(hr, "Failed to compare version substrings");
625
+ }
626
+
627
+LExit:
628
+ *pnResult = nResult - 2;
629
+
630
+ return hr;
631
+}
src/test/DUtilUnitTest/DUtilUnitTest.vcxproj
+1
@@ -49,6 +49,7 @@
49
<ClCompile Include="SceUtilTest.cpp" Condition=" Exists('$(SqlCESdkIncludePath)') " />
50
<ClCompile Include="StrUtilTest.cpp" />
51
<ClCompile Include="UriUtilTest.cpp" />
52
+ <ClCompile Include="VerUtilTests.cpp" />
53
</ItemGroup>
54
<ItemGroup>
55
<ClInclude Include="precomp.h" />
src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters
+3
@@ -57,6 +57,9 @@
57
<ClCompile Include="UriUtilTest.cpp">
58
<Filter>Source Files</Filter>
59
</ClCompile>
60
+ <ClCompile Include="VerUtilTests.cpp">
61
+ <Filter>Source Files</Filter>
62
+ </ClCompile>
63
</ItemGroup>
64
<ItemGroup>
65
<ResourceCompile Include="UnitTest.rc">
src/test/DUtilUnitTest/VerUtilTests.cpp
new
+891
@@ -0,0 +1,891 @@
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
+#include "precomp.h"
4
+
5
+using namespace System;
6
+using namespace Xunit;
7
+using namespace WixBuildTools::TestSupport;
8
+
9
+namespace DutilTests
10
+{
11
+ public ref class VerUtil
12
+ {
13
+ public:
14
+ [Fact]
15
+ void VerCompareVersionsTreatsMissingRevisionAsZero()
16
+ {
17
+ HRESULT hr = S_OK;
18
+ VERUTIL_VERSION* pVersion1 = NULL;
19
+ VERUTIL_VERSION* pVersion2 = NULL;
20
+ VERUTIL_VERSION* pVersion3 = NULL;
21
+ LPCWSTR wzVersion1 = L"1.2.3.4";
22
+ LPCWSTR wzVersion2 = L"1.2.3";
23
+ LPCWSTR wzVersion3 = L"1.2.3.0";
24
+
25
+ try
26
+ {
27
+ hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1);
28
+ NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1);
29
+
30
+ hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2);
31
+ NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2);
32
+
33
+ hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3);
34
+ NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3);
35
+
36
+ NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion);
37
+ Assert::Equal<DWORD>(1, pVersion1->dwMajor);
38
+ Assert::Equal<DWORD>(2, pVersion1->dwMinor);
39
+ Assert::Equal<DWORD>(3, pVersion1->dwPatch);
40
+ Assert::Equal<DWORD>(4, pVersion1->dwRevision);
41
+ Assert::Equal<DWORD>(0, pVersion1->cReleaseLabels);
42
+ Assert::Equal<DWORD>(7, pVersion1->cchMetadataOffset);
43
+ Assert::Equal<BOOL>(FALSE, pVersion1->fInvalid);
44
+
45
+ NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion);
46
+ Assert::Equal<DWORD>(1, pVersion2->dwMajor);
47
+ Assert::Equal<DWORD>(2, pVersion2->dwMinor);
48
+ Assert::Equal<DWORD>(3, pVersion2->dwPatch);
49
+ Assert::Equal<DWORD>(0, pVersion2->dwRevision);
50
+ Assert::Equal<DWORD>(0, pVersion2->cReleaseLabels);
51
+ Assert::Equal<DWORD>(5, pVersion2->cchMetadataOffset);
52
+ Assert::Equal<BOOL>(FALSE, pVersion2->fInvalid);
53
+
54
+ NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion);
55
+ Assert::Equal<DWORD>(1, pVersion3->dwMajor);
56
+ Assert::Equal<DWORD>(2, pVersion3->dwMinor);
57
+ Assert::Equal<DWORD>(3, pVersion3->dwPatch);
58
+ Assert::Equal<DWORD>(0, pVersion3->dwRevision);
59
+ Assert::Equal<DWORD>(0, pVersion3->cReleaseLabels);
60
+ Assert::Equal<DWORD>(7, pVersion3->cchMetadataOffset);
61
+ Assert::Equal<BOOL>(FALSE, pVersion3->fInvalid);
62
+
63
+ TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1);
64
+ TestVerutilCompareParsedVersions(pVersion3, pVersion2, 0);
65
+ }
66
+ finally
67
+ {
68
+ ReleaseVerutilVersion(pVersion1);
69
+ ReleaseVerutilVersion(pVersion2);
70
+ ReleaseVerutilVersion(pVersion3);
71
+ }
72
+ }
73
+
74
+ [Fact]
75
+ void VerCompareVersionsTreatsNumericReleaseLabelsAsNumbers()
76
+ {
77
+ HRESULT hr = S_OK;
78
+ VERUTIL_VERSION* pVersion1 = NULL;
79
+ VERUTIL_VERSION* pVersion2 = NULL;
80
+ LPCWSTR wzVersion1 = L"1.0-2.0";
81
+ LPCWSTR wzVersion2 = L"1.0-19";
82
+
83
+ try
84
+ {
85
+ hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1);
86
+ NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1);
87
+
88
+ hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2);
89
+ NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2);
90
+
91
+ NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion);
92
+ Assert::Equal<DWORD>(1, pVersion1->dwMajor);
93
+ Assert::Equal<DWORD>(0, pVersion1->dwMinor);
94
+ Assert::Equal<DWORD>(0, pVersion1->dwPatch);
95
+ Assert::Equal<DWORD>(0, pVersion1->dwRevision);
96
+ Assert::Equal<DWORD>(2, pVersion1->cReleaseLabels);
97
+
98
+ Assert::Equal<BOOL>(TRUE, pVersion1->rgReleaseLabels[0].fNumeric);
99
+ Assert::Equal<DWORD>(2, pVersion1->rgReleaseLabels[0].dwValue);
100
+ Assert::Equal<DWORD>(1, pVersion1->rgReleaseLabels[0].cchLabel);
101
+ Assert::Equal<DWORD>(4, pVersion1->rgReleaseLabels[0].cchLabelOffset);
102
+
103
+ Assert::Equal<BOOL>(TRUE, pVersion1->rgReleaseLabels[1].fNumeric);
104
+ Assert::Equal<DWORD>(0, pVersion1->rgReleaseLabels[1].dwValue);
105
+ Assert::Equal<DWORD>(1, pVersion1->rgReleaseLabels[1].cchLabel);
106
+ Assert::Equal<DWORD>(6, pVersion1->rgReleaseLabels[1].cchLabelOffset);
107
+
108
+ Assert::Equal<DWORD>(7, pVersion1->cchMetadataOffset);
109
+ Assert::Equal<BOOL>(FALSE, pVersion1->fInvalid);
110
+
111
+ NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion);
112
+ Assert::Equal<DWORD>(1, pVersion2->dwMajor);
113
+ Assert::Equal<DWORD>(0, pVersion2->dwMinor);
114
+ Assert::Equal<DWORD>(0, pVersion2->dwPatch);
115
+ Assert::Equal<DWORD>(0, pVersion2->dwRevision);
116
+ Assert::Equal<DWORD>(1, pVersion2->cReleaseLabels);
117
+
118
+ Assert::Equal<BOOL>(TRUE, pVersion2->rgReleaseLabels[0].fNumeric);
119
+ Assert::Equal<DWORD>(19, pVersion2->rgReleaseLabels[0].dwValue);
120
+ Assert::Equal<DWORD>(2, pVersion2->rgReleaseLabels[0].cchLabel);
121
+ Assert::Equal<DWORD>(4, pVersion2->rgReleaseLabels[0].cchLabelOffset);
122
+
123
+ Assert::Equal<DWORD>(6, pVersion2->cchMetadataOffset);
124
+ Assert::Equal<BOOL>(FALSE, pVersion2->fInvalid);
125
+
126
+ TestVerutilCompareParsedVersions(pVersion1, pVersion2, -1);
127
+ }
128
+ finally
129
+ {
130
+ ReleaseVerutilVersion(pVersion1);
131
+ ReleaseVerutilVersion(pVersion2);
132
+ }
133
+ }
134
+
135
+ [Fact]
136
+ void VerCompareVersionsHandlesNormallyInvalidVersions()
137
+ {
138
+ HRESULT hr = S_OK;
139
+ VERUTIL_VERSION* pVersion1 = NULL;
140
+ VERUTIL_VERSION* pVersion2 = NULL;
141
+ VERUTIL_VERSION* pVersion3 = NULL;
142
+ VERUTIL_VERSION* pVersion4 = NULL;
143
+ VERUTIL_VERSION* pVersion5 = NULL;
144
+ VERUTIL_VERSION* pVersion6 = NULL;
145
+ LPCWSTR wzVersion1 = L"10.-4.0";
146
+ LPCWSTR wzVersion2 = L"10.-2.0";
147
+ LPCWSTR wzVersion3 = L"0";
148
+ LPCWSTR wzVersion4 = L"";
149
+ LPCWSTR wzVersion5 = L"10-2";
150
+ LPCWSTR wzVersion6 = L"10-4.@";
151
+
152
+ try
153
+ {
154
+ hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1);
155
+ NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1);
156
+
157
+ hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2);
158
+ NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2);
159
+
160
+ hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3);
161
+ NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3);
162
+
163
+ hr = VerParseVersion(wzVersion4, 0, FALSE, &pVersion4);
164
+ NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion4);
165
+
166
+ hr = VerParseVersion(wzVersion5, 0, FALSE, &pVersion5);
167
+ NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion5);
168
+
169
+ hr = VerParseVersion(wzVersion6, 0, FALSE, &pVersion6);
170
+ NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion6);
171
+
172
+ NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion);
173
+ Assert::Equal<DWORD>(10, pVersion1->dwMajor);
174
+ Assert::Equal<DWORD>(0, pVersion1->dwMinor);
175
+ Assert::Equal<DWORD>(0, pVersion1->dwPatch);
176
+ Assert::Equal<DWORD>(0, pVersion1->dwRevision);
177
+ Assert::Equal<DWORD>(0, pVersion1->cReleaseLabels);
178
+ Assert::Equal<DWORD>(3, pVersion1->cchMetadataOffset);
179
+ Assert::Equal<BOOL>(TRUE, pVersion1->fInvalid);
180
+
181
+ NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion);
182
+ Assert::Equal<DWORD>(10, pVersion2->dwMajor);
183
+ Assert::Equal<DWORD>(0, pVersion2->dwMinor);
184
+ Assert::Equal<DWORD>(0, pVersion2->dwPatch);
185
+ Assert::Equal<DWORD>(0, pVersion2->dwRevision);
186
+ Assert::Equal<DWORD>(0, pVersion2->cReleaseLabels);
187
+ Assert::Equal<DWORD>(3, pVersion2->cchMetadataOffset);
188
+ Assert::Equal<BOOL>(TRUE, pVersion2->fInvalid);
189
+
190
+ NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion);
191
+ Assert::Equal<DWORD>(0, pVersion3->dwMajor);
192
+ Assert::Equal<DWORD>(0, pVersion3->dwMinor);
193
+ Assert::Equal<DWORD>(0, pVersion3->dwPatch);
194
+ Assert::Equal<DWORD>(0, pVersion3->dwRevision);
195
+ Assert::Equal<DWORD>(0, pVersion3->cReleaseLabels);
196
+ Assert::Equal<DWORD>(1, pVersion3->cchMetadataOffset);
197
+ Assert::Equal<BOOL>(FALSE, pVersion3->fInvalid);
198
+
199
+ NativeAssert::StringEqual(wzVersion4, pVersion4->sczVersion);
200
+ Assert::Equal<DWORD>(0, pVersion4->dwMajor);
201
+ Assert::Equal<DWORD>(0, pVersion4->dwMinor);
202
+ Assert::Equal<DWORD>(0, pVersion4->dwPatch);
203
+ Assert::Equal<DWORD>(0, pVersion4->dwRevision);
204
+ Assert::Equal<DWORD>(0, pVersion4->cReleaseLabels);
205
+ Assert::Equal<DWORD>(0, pVersion4->cchMetadataOffset);
206
+ Assert::Equal<BOOL>(TRUE, pVersion4->fInvalid);
207
+
208
+ NativeAssert::StringEqual(wzVersion5, pVersion5->sczVersion);
209
+ Assert::Equal<DWORD>(10, pVersion5->dwMajor);
210
+ Assert::Equal<DWORD>(0, pVersion5->dwMinor);
211
+ Assert::Equal<DWORD>(0, pVersion5->dwPatch);
212
+ Assert::Equal<DWORD>(0, pVersion5->dwRevision);
213
+ Assert::Equal<DWORD>(1, pVersion5->cReleaseLabels);
214
+
215
+ Assert::Equal<BOOL>(TRUE, pVersion5->rgReleaseLabels[0].fNumeric);
216
+ Assert::Equal<DWORD>(2, pVersion5->rgReleaseLabels[0].dwValue);
217
+ Assert::Equal<DWORD>(1, pVersion5->rgReleaseLabels[0].cchLabel);
218
+ Assert::Equal<DWORD>(3, pVersion5->rgReleaseLabels[0].cchLabelOffset);
219
+
220
+ Assert::Equal<DWORD>(4, pVersion5->cchMetadataOffset);
221
+ Assert::Equal<BOOL>(FALSE, pVersion5->fInvalid);
222
+
223
+ NativeAssert::StringEqual(wzVersion6, pVersion6->sczVersion);
224
+ Assert::Equal<DWORD>(10, pVersion6->dwMajor);
225
+ Assert::Equal<DWORD>(0, pVersion6->dwMinor);
226
+ Assert::Equal<DWORD>(0, pVersion6->dwPatch);
227
+ Assert::Equal<DWORD>(0, pVersion6->dwRevision);
228
+ Assert::Equal<DWORD>(1, pVersion6->cReleaseLabels);
229
+
230
+ Assert::Equal<BOOL>(TRUE, pVersion6->rgReleaseLabels[0].fNumeric);
231
+ Assert::Equal<DWORD>(4, pVersion6->rgReleaseLabels[0].dwValue);
232
+ Assert::Equal<DWORD>(1, pVersion6->rgReleaseLabels[0].cchLabel);
233
+ Assert::Equal<DWORD>(3, pVersion6->rgReleaseLabels[0].cchLabelOffset);
234
+
235
+ Assert::Equal<DWORD>(5, pVersion6->cchMetadataOffset);
236
+ Assert::Equal<BOOL>(TRUE, pVersion6->fInvalid);
237
+
238
+ TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1);
239
+ TestVerutilCompareParsedVersions(pVersion3, pVersion4, 1);
240
+ TestVerutilCompareParsedVersions(pVersion5, pVersion6, 1);
241
+ }
242
+ finally
243
+ {
244
+ ReleaseVerutilVersion(pVersion1);
245
+ ReleaseVerutilVersion(pVersion2);
246
+ ReleaseVerutilVersion(pVersion3);
247
+ ReleaseVerutilVersion(pVersion4);
248
+ ReleaseVerutilVersion(pVersion5);
249
+ ReleaseVerutilVersion(pVersion6);
250
+ }
251
+ }
252
+
253
+ [Fact]
254
+ void VerCompareVersionsTreatsHyphenAsVersionSeparator()
255
+ {
256
+ HRESULT hr = S_OK;
257
+ VERUTIL_VERSION* pVersion1 = NULL;
258
+ VERUTIL_VERSION* pVersion2 = NULL;
259
+ VERUTIL_VERSION* pVersion3 = NULL;
260
+ LPCWSTR wzVersion1 = L"0.0.1-a";
261
+ LPCWSTR wzVersion2 = L"0-2";
262
+ LPCWSTR wzVersion3 = L"1-2";
263
+
264
+ try
265
+ {
266
+ hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1);
267
+ NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1);
268
+
269
+ hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2);
270
+ NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2);
271
+
272
+ hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3);
273
+ NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3);
274
+
275
+ NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion);
276
+ Assert::Equal<DWORD>(0, pVersion1->dwMajor);
277
+ Assert::Equal<DWORD>(0, pVersion1->dwMinor);
278
+ Assert::Equal<DWORD>(1, pVersion1->dwPatch);
279
+ Assert::Equal<DWORD>(0, pVersion1->dwRevision);
280
+ Assert::Equal<DWORD>(1, pVersion1->cReleaseLabels);
281
+
282
+ Assert::Equal<BOOL>(FALSE, pVersion1->rgReleaseLabels[0].fNumeric);
283
+ Assert::Equal<DWORD>(1, pVersion1->rgReleaseLabels[0].cchLabel);
284
+ Assert::Equal<DWORD>(6, pVersion1->rgReleaseLabels[0].cchLabelOffset);
285
+
286
+ Assert::Equal<DWORD>(7, pVersion1->cchMetadataOffset);
287
+ Assert::Equal<BOOL>(FALSE, pVersion1->fInvalid);
288
+
289
+ NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion);
290
+ Assert::Equal<DWORD>(0, pVersion2->dwMajor);
291
+ Assert::Equal<DWORD>(0, pVersion2->dwMinor);
292
+ Assert::Equal<DWORD>(0, pVersion2->dwPatch);
293
+ Assert::Equal<DWORD>(0, pVersion2->dwRevision);
294
+ Assert::Equal<DWORD>(1, pVersion2->cReleaseLabels);
295
+
296
+ Assert::Equal<BOOL>(TRUE, pVersion2->rgReleaseLabels[0].fNumeric);
297
+ Assert::Equal<DWORD>(2, pVersion2->rgReleaseLabels[0].dwValue);
298
+ Assert::Equal<DWORD>(1, pVersion2->rgReleaseLabels[0].cchLabel);
299
+ Assert::Equal<DWORD>(2, pVersion2->rgReleaseLabels[0].cchLabelOffset);
300
+
301
+ Assert::Equal<DWORD>(3, pVersion2->cchMetadataOffset);
302
+ Assert::Equal<BOOL>(FALSE, pVersion2->fInvalid);
303
+
304
+ NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion);
305
+ Assert::Equal<DWORD>(1, pVersion3->dwMajor);
306
+ Assert::Equal<DWORD>(0, pVersion3->dwMinor);
307
+ Assert::Equal<DWORD>(0, pVersion3->dwPatch);
308
+ Assert::Equal<DWORD>(0, pVersion3->dwRevision);
309
+ Assert::Equal<DWORD>(1, pVersion3->cReleaseLabels);
310
+
311
+ Assert::Equal<BOOL>(TRUE, pVersion3->rgReleaseLabels[0].fNumeric);
312
+ Assert::Equal<DWORD>(2, pVersion3->rgReleaseLabels[0].dwValue);
313
+ Assert::Equal<DWORD>(1, pVersion3->rgReleaseLabels[0].cchLabel);
314
+ Assert::Equal<DWORD>(2, pVersion3->rgReleaseLabels[0].cchLabelOffset);
315
+
316
+ Assert::Equal<DWORD>(3, pVersion3->cchMetadataOffset);
317
+ Assert::Equal<BOOL>(FALSE, pVersion3->fInvalid);
318
+
319
+ TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1);
320
+ TestVerutilCompareParsedVersions(pVersion1, pVersion3, -1);
321
+ }
322
+ finally
323
+ {
324
+ ReleaseVerutilVersion(pVersion1);
325
+ ReleaseVerutilVersion(pVersion2);
326
+ ReleaseVerutilVersion(pVersion3);
327
+ }
328
+ }
329
+
330
+ [Fact]
331
+ void VerCompareVersionsIgnoresLeadingZeroes()
332
+ {
333
+ HRESULT hr = S_OK;
334
+ VERUTIL_VERSION* pVersion1 = NULL;
335
+ VERUTIL_VERSION* pVersion2 = NULL;
336
+ VERUTIL_VERSION* pVersion3 = NULL;
337
+ VERUTIL_VERSION* pVersion4 = NULL;
338
+ LPCWSTR wzVersion1 = L"0.01-a.1";
339
+ LPCWSTR wzVersion2 = L"0.1.0-a.1";
340
+ LPCWSTR wzVersion3 = L"0.1-a.b.0";
341
+ LPCWSTR wzVersion4 = L"0.1.0-a.b.000";
342
+
343
+ try
344
+ {
345
+ hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1);
346
+ NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1);
347
+
348
+ hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2);
349
+ NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2);
350
+
351
+ hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3);
352
+ NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3);
353
+
354
+ hr = VerParseVersion(wzVersion4, 0, FALSE, &pVersion4);
355
+ NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion4);
356
+
357
+ NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion);
358
+ Assert::Equal<DWORD>(0, pVersion1->dwMajor);
359
+ Assert::Equal<DWORD>(1, pVersion1->dwMinor);
360
+ Assert::Equal<DWORD>(0, pVersion1->dwPatch);
361
+ Assert::Equal<DWORD>(0, pVersion1->dwRevision);
362
+ Assert::Equal<DWORD>(2, pVersion1->cReleaseLabels);
363
+
364
+ Assert::Equal<BOOL>(FALSE, pVersion1->rgReleaseLabels[0].fNumeric);
365
+ Assert::Equal<DWORD>(1, pVersion1->rgReleaseLabels[0].cchLabel);
366
+ Assert::Equal<DWORD>(5, pVersion1->rgReleaseLabels[0].cchLabelOffset);
367
+
368
+ Assert::Equal<BOOL>(TRUE, pVersion1->rgReleaseLabels[1].fNumeric);
369
+ Assert::Equal<DWORD>(1, pVersion1->rgReleaseLabels[1].dwValue);
370
+ Assert::Equal<DWORD>(1, pVersion1->rgReleaseLabels[1].cchLabel);
371
+ Assert::Equal<DWORD>(7, pVersion1->rgReleaseLabels[1].cchLabelOffset);
372
+
373
+ Assert::Equal<DWORD>(8, pVersion1->cchMetadataOffset);
374
+ Assert::Equal<BOOL>(FALSE, pVersion1->fInvalid);
375
+
376
+ NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion);
377
+ Assert::Equal<DWORD>(0, pVersion2->dwMajor);
378
+ Assert::Equal<DWORD>(1, pVersion2->dwMinor);
379
+ Assert::Equal<DWORD>(0, pVersion2->dwPatch);
380
+ Assert::Equal<DWORD>(0, pVersion2->dwRevision);
381
+ Assert::Equal<DWORD>(2, pVersion2->cReleaseLabels);
382
+
383
+ Assert::Equal<BOOL>(FALSE, pVersion2->rgReleaseLabels[0].fNumeric);
384
+ Assert::Equal<DWORD>(1, pVersion2->rgReleaseLabels[0].cchLabel);
385
+ Assert::Equal<DWORD>(6, pVersion2->rgReleaseLabels[0].cchLabelOffset);
386
+
387
+ Assert::Equal<BOOL>(TRUE, pVersion2->rgReleaseLabels[1].fNumeric);
388
+ Assert::Equal<DWORD>(1, pVersion2->rgReleaseLabels[1].dwValue);
389
+ Assert::Equal<DWORD>(1, pVersion2->rgReleaseLabels[1].cchLabel);
390
+ Assert::Equal<DWORD>(8, pVersion2->rgReleaseLabels[1].cchLabelOffset);
391
+
392
+ Assert::Equal<DWORD>(9, pVersion2->cchMetadataOffset);
393
+ Assert::Equal<BOOL>(FALSE, pVersion2->fInvalid);
394
+
395
+ NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion);
396
+ Assert::Equal<DWORD>(0, pVersion3->dwMajor);
397
+ Assert::Equal<DWORD>(1, pVersion3->dwMinor);
398
+ Assert::Equal<DWORD>(0, pVersion3->dwPatch);
399
+ Assert::Equal<DWORD>(0, pVersion3->dwRevision);
400
+ Assert::Equal<DWORD>(3, pVersion3->cReleaseLabels);
401
+
402
+ Assert::Equal<BOOL>(FALSE, pVersion3->rgReleaseLabels[0].fNumeric);
403
+ Assert::Equal<DWORD>(1, pVersion3->rgReleaseLabels[0].cchLabel);
404
+ Assert::Equal<DWORD>(4, pVersion3->rgReleaseLabels[0].cchLabelOffset);
405
+
406
+ Assert::Equal<BOOL>(FALSE, pVersion3->rgReleaseLabels[1].fNumeric);
407
+ Assert::Equal<DWORD>(1, pVersion3->rgReleaseLabels[1].cchLabel);
408
+ Assert::Equal<DWORD>(6, pVersion3->rgReleaseLabels[1].cchLabelOffset);
409
+
410
+ Assert::Equal<BOOL>(TRUE, pVersion3->rgReleaseLabels[2].fNumeric);
411
+ Assert::Equal<DWORD>(0, pVersion3->rgReleaseLabels[2].dwValue);
412
+ Assert::Equal<DWORD>(1, pVersion3->rgReleaseLabels[2].cchLabel);
413
+ Assert::Equal<DWORD>(8, pVersion3->rgReleaseLabels[2].cchLabelOffset);
414
+
415
+ Assert::Equal<DWORD>(9, pVersion3->cchMetadataOffset);
416
+ Assert::Equal<BOOL>(FALSE, pVersion3->fInvalid);
417
+
418
+ NativeAssert::StringEqual(wzVersion4, pVersion4->sczVersion);
419
+ Assert::Equal<DWORD>(0, pVersion4->dwMajor);
420
+ Assert::Equal<DWORD>(1, pVersion4->dwMinor);
421
+ Assert::Equal<DWORD>(0, pVersion4->dwPatch);
422
+ Assert::Equal<DWORD>(0, pVersion4->dwRevision);
423
+ Assert::Equal<DWORD>(3, pVersion4->cReleaseLabels);
424
+
425
+ Assert::Equal<BOOL>(FALSE, pVersion4->rgReleaseLabels[0].fNumeric);
426
+ Assert::Equal<DWORD>(1, pVersion4->rgReleaseLabels[0].cchLabel);
427
+ Assert::Equal<DWORD>(6, pVersion4->rgReleaseLabels[0].cchLabelOffset);
428
+
429
+ Assert::Equal<BOOL>(FALSE, pVersion4->rgReleaseLabels[1].fNumeric);
430
+ Assert::Equal<DWORD>(1, pVersion4->rgReleaseLabels[1].cchLabel);
431
+ Assert::Equal<DWORD>(8, pVersion4->rgReleaseLabels[1].cchLabelOffset);
432
+
433
+ Assert::Equal<BOOL>(TRUE, pVersion4->rgReleaseLabels[2].fNumeric);
434
+ Assert::Equal<DWORD>(0, pVersion4->rgReleaseLabels[2].dwValue);
435
+ Assert::Equal<DWORD>(3, pVersion4->rgReleaseLabels[2].cchLabel);
436
+ Assert::Equal<DWORD>(10, pVersion4->rgReleaseLabels[2].cchLabelOffset);
437
+
438
+ Assert::Equal<DWORD>(13, pVersion4->cchMetadataOffset);
439
+ Assert::Equal<BOOL>(FALSE, pVersion4->fInvalid);
440
+
441
+ TestVerutilCompareParsedVersions(pVersion1, pVersion2, 0);
442
+ TestVerutilCompareParsedVersions(pVersion3, pVersion4, 0);
443
+ }
444
+ finally
445
+ {
446
+ ReleaseVerutilVersion(pVersion1);
447
+ ReleaseVerutilVersion(pVersion2);
448
+ ReleaseVerutilVersion(pVersion3);
449
+ ReleaseVerutilVersion(pVersion4);
450
+ }
451
+ }
452
+
453
+ [Fact]
454
+ void VerCompareVersionsTreatsUnexpectedContentAsMetadata()
455
+ {
456
+ HRESULT hr = S_OK;
457
+ VERUTIL_VERSION* pVersion1 = NULL;
458
+ VERUTIL_VERSION* pVersion2 = NULL;
459
+ VERUTIL_VERSION* pVersion3 = NULL;
460
+ LPCWSTR wzVersion1 = L"1.2.3+abcd";
461
+ LPCWSTR wzVersion2 = L"1.2.3.abcd";
462
+ LPCWSTR wzVersion3 = L"1.2.3.-abcd";
463
+
464
+ try
465
+ {
466
+ hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1);
467
+ NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1);
468
+
469
+ hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2);
470
+ NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2);
471
+
472
+ hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3);
473
+ NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3);
474
+
475
+ NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion);
476
+ Assert::Equal<DWORD>(1, pVersion1->dwMajor);
477
+ Assert::Equal<DWORD>(2, pVersion1->dwMinor);
478
+ Assert::Equal<DWORD>(3, pVersion1->dwPatch);
479
+ Assert::Equal<DWORD>(0, pVersion1->dwRevision);
480
+ Assert::Equal<DWORD>(0, pVersion1->cReleaseLabels);
481
+ Assert::Equal<DWORD>(6, pVersion1->cchMetadataOffset);
482
+ Assert::Equal<BOOL>(FALSE, pVersion1->fInvalid);
483
+
484
+ NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion);
485
+ Assert::Equal<DWORD>(1, pVersion2->dwMajor);
486
+ Assert::Equal<DWORD>(2, pVersion2->dwMinor);
487
+ Assert::Equal<DWORD>(3, pVersion2->dwPatch);
488
+ Assert::Equal<DWORD>(0, pVersion2->dwRevision);
489
+ Assert::Equal<DWORD>(0, pVersion2->cReleaseLabels);
490
+ Assert::Equal<DWORD>(6, pVersion2->cchMetadataOffset);
491
+ Assert::Equal<BOOL>(TRUE, pVersion2->fInvalid);
492
+
493
+ NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion);
494
+ Assert::Equal<DWORD>(1, pVersion3->dwMajor);
495
+ Assert::Equal<DWORD>(2, pVersion3->dwMinor);
496
+ Assert::Equal<DWORD>(3, pVersion3->dwPatch);
497
+ Assert::Equal<DWORD>(0, pVersion3->dwRevision);
498
+ Assert::Equal<DWORD>(0, pVersion3->cReleaseLabels);
499
+ Assert::Equal<DWORD>(6, pVersion3->cchMetadataOffset);
500
+ Assert::Equal<BOOL>(TRUE, pVersion3->fInvalid);
501
+
502
+ TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1);
503
+ TestVerutilCompareParsedVersions(pVersion1, pVersion3, 1);
504
+ TestVerutilCompareParsedVersions(pVersion2, pVersion3, -1);
505
+ }
506
+ finally
507
+ {
508
+ ReleaseVerutilVersion(pVersion1);
509
+ ReleaseVerutilVersion(pVersion2);
510
+ ReleaseVerutilVersion(pVersion3);
511
+ }
512
+ }
513
+
514
+ [Fact]
515
+ void VerCompareVersionsIgnoresLeadingV()
516
+ {
517
+ HRESULT hr = S_OK;
518
+ VERUTIL_VERSION* pVersion1 = NULL;
519
+ VERUTIL_VERSION* pVersion2 = NULL;
520
+ VERUTIL_VERSION* pVersion3 = NULL;
521
+ LPCWSTR wzVersion1 = L"10.20.30.40";
522
+ LPCWSTR wzVersion2 = L"v10.20.30.40";
523
+ LPCWSTR wzVersion3 = L"V10.20.30.40";
524
+
525
+ try
526
+ {
527
+ hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1);
528
+ NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1);
529
+
530
+ hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2);
531
+ NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2);
532
+
533
+ hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3);
534
+ NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3);
535
+
536
+ NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion);
537
+ Assert::Equal<DWORD>(10, pVersion1->dwMajor);
538
+ Assert::Equal<DWORD>(20, pVersion1->dwMinor);
539
+ Assert::Equal<DWORD>(30, pVersion1->dwPatch);
540
+ Assert::Equal<DWORD>(40, pVersion1->dwRevision);
541
+ Assert::Equal<DWORD>(0, pVersion1->cReleaseLabels);
542
+ Assert::Equal<DWORD>(11, pVersion1->cchMetadataOffset);
543
+ Assert::Equal<BOOL>(FALSE, pVersion1->fInvalid);
544
+
545
+ NativeAssert::StringEqual(wzVersion1, pVersion2->sczVersion);
546
+ Assert::Equal<DWORD>(10, pVersion2->dwMajor);
547
+ Assert::Equal<DWORD>(20, pVersion2->dwMinor);
548
+ Assert::Equal<DWORD>(30, pVersion2->dwPatch);
549
+ Assert::Equal<DWORD>(40, pVersion2->dwRevision);
550
+ Assert::Equal<DWORD>(0, pVersion2->cReleaseLabels);
551
+ Assert::Equal<DWORD>(11, pVersion2->cchMetadataOffset);
552
+ Assert::Equal<BOOL>(FALSE, pVersion2->fInvalid);
553
+
554
+ NativeAssert::StringEqual(wzVersion1, pVersion3->sczVersion);
555
+ Assert::Equal<DWORD>(10, pVersion3->dwMajor);
556
+ Assert::Equal<DWORD>(20, pVersion3->dwMinor);
557
+ Assert::Equal<DWORD>(30, pVersion3->dwPatch);
558
+ Assert::Equal<DWORD>(40, pVersion3->dwRevision);
559
+ Assert::Equal<DWORD>(0, pVersion3->cReleaseLabels);
560
+ Assert::Equal<DWORD>(11, pVersion3->cchMetadataOffset);
561
+ Assert::Equal<BOOL>(FALSE, pVersion3->fInvalid);
562
+
563
+ TestVerutilCompareParsedVersions(pVersion1, pVersion2, 0);
564
+ TestVerutilCompareParsedVersions(pVersion1, pVersion3, 0);
565
+ }
566
+ finally
567
+ {
568
+ ReleaseVerutilVersion(pVersion1);
569
+ ReleaseVerutilVersion(pVersion2);
570
+ ReleaseVerutilVersion(pVersion3);
571
+ }
572
+ }
573
+
574
+ [Fact]
575
+ void VerCompareVersionsHandlesTooLargeNumbers()
576
+ {
577
+ HRESULT hr = S_OK;
578
+ VERUTIL_VERSION* pVersion1 = NULL;
579
+ VERUTIL_VERSION* pVersion2 = NULL;
580
+ LPCWSTR wzVersion1 = L"4294967295.4294967295.4294967295.4294967295";
581
+ LPCWSTR wzVersion2 = L"4294967296.4294967296.4294967296.4294967296";
582
+
583
+ try
584
+ {
585
+ hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1);
586
+ NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1);
587
+
588
+ hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2);
589
+ NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2);
590
+
591
+ NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion);
592
+ Assert::Equal<DWORD>(4294967295, pVersion1->dwMajor);
593
+ Assert::Equal<DWORD>(4294967295, pVersion1->dwMinor);
594
+ Assert::Equal<DWORD>(4294967295, pVersion1->dwPatch);
595
+ Assert::Equal<DWORD>(4294967295, pVersion1->dwRevision);
596
+ Assert::Equal<DWORD>(0, pVersion1->cReleaseLabels);
597
+ Assert::Equal<DWORD>(43, pVersion1->cchMetadataOffset);
598
+ Assert::Equal<BOOL>(FALSE, pVersion1->fInvalid);
599
+
600
+ NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion);
601
+ Assert::Equal<DWORD>(0, pVersion2->dwMajor);
602
+ Assert::Equal<DWORD>(0, pVersion2->dwMinor);
603
+ Assert::Equal<DWORD>(0, pVersion2->dwPatch);
604
+ Assert::Equal<DWORD>(0, pVersion2->dwRevision);
605
+ Assert::Equal<DWORD>(0, pVersion2->cReleaseLabels);
606
+ Assert::Equal<DWORD>(0, pVersion2->cchMetadataOffset);
607
+ Assert::Equal<BOOL>(TRUE, pVersion2->fInvalid);
608
+
609
+ TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1);
610
+ }
611
+ finally
612
+ {
613
+ ReleaseVerutilVersion(pVersion1);
614
+ ReleaseVerutilVersion(pVersion2);
615
+ }
616
+ }
617
+
618
+ [Fact]
619
+ void VerCompareVersionsIgnoresMetadataForValidVersions()
620
+ {
621
+ HRESULT hr = S_OK;
622
+ VERUTIL_VERSION* pVersion1 = NULL;
623
+ VERUTIL_VERSION* pVersion2 = NULL;
624
+ LPCWSTR wzVersion1 = L"1.2.3+abc";
625
+ LPCWSTR wzVersion2 = L"1.2.3+xyz";
626
+
627
+ try
628
+ {
629
+ hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1);
630
+ NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1);
631
+
632
+ hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2);
633
+ NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2);
634
+
635
+ NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion);
636
+ Assert::Equal<DWORD>(1, pVersion1->dwMajor);
637
+ Assert::Equal<DWORD>(2, pVersion1->dwMinor);
638
+ Assert::Equal<DWORD>(3, pVersion1->dwPatch);
639
+ Assert::Equal<DWORD>(0, pVersion1->dwRevision);
640
+ Assert::Equal<DWORD>(0, pVersion1->cReleaseLabels);
641
+ Assert::Equal<DWORD>(6, pVersion1->cchMetadataOffset);
642
+ Assert::Equal<BOOL>(FALSE, pVersion1->fInvalid);
643
+
644
+ NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion);
645
+ Assert::Equal<DWORD>(1, pVersion2->dwMajor);
646
+ Assert::Equal<DWORD>(2, pVersion2->dwMinor);
647
+ Assert::Equal<DWORD>(3, pVersion2->dwPatch);
648
+ Assert::Equal<DWORD>(0, pVersion2->dwRevision);
649
+ Assert::Equal<DWORD>(0, pVersion2->cReleaseLabels);
650
+ Assert::Equal<DWORD>(6, pVersion2->cchMetadataOffset);
651
+ Assert::Equal<BOOL>(FALSE, pVersion2->fInvalid);
652
+
653
+ TestVerutilCompareParsedVersions(pVersion1, pVersion2, 0);
654
+ }
655
+ finally
656
+ {
657
+ ReleaseVerutilVersion(pVersion1);
658
+ ReleaseVerutilVersion(pVersion2);
659
+ }
660
+ }
661
+
662
+ [Fact]
663
+ void VerCopyVersionCopiesVersion()
664
+ {
665
+ HRESULT hr = S_OK;
666
+ LPCWSTR wzVersion = L"1.2.3.4-a.b.c.d.5.+abc123";
667
+ VERUTIL_VERSION* pSource = NULL;
668
+ VERUTIL_VERSION* pCopy = NULL;
669
+ int nResult = 0;
670
+
671
+ try
672
+ {
673
+ hr = VerParseVersion(wzVersion, 0, FALSE, &pSource);
674
+ NativeAssert::Succeeded(hr, "VerParseVersion failed");
675
+
676
+ NativeAssert::StringEqual(wzVersion, pSource->sczVersion);
677
+ Assert::Equal<DWORD>(1, pSource->dwMajor);
678
+ Assert::Equal<DWORD>(2, pSource->dwMinor);
679
+ Assert::Equal<DWORD>(3, pSource->dwPatch);
680
+ Assert::Equal<DWORD>(4, pSource->dwRevision);
681
+ Assert::Equal<DWORD>(5, pSource->cReleaseLabels);
682
+
683
+ Assert::Equal<BOOL>(FALSE, pSource->rgReleaseLabels[0].fNumeric);
684
+ Assert::Equal<DWORD>(1, pSource->rgReleaseLabels[0].cchLabel);
685
+ Assert::Equal<DWORD>(8, pSource->rgReleaseLabels[0].cchLabelOffset);
686
+
687
+ Assert::Equal<BOOL>(FALSE, pSource->rgReleaseLabels[1].fNumeric);
688
+ Assert::Equal<DWORD>(1, pSource->rgReleaseLabels[1].cchLabel);
689
+ Assert::Equal<DWORD>(10, pSource->rgReleaseLabels[1].cchLabelOffset);
690
+
691
+ Assert::Equal<BOOL>(FALSE, pSource->rgReleaseLabels[2].fNumeric);
692
+ Assert::Equal<DWORD>(1, pSource->rgReleaseLabels[2].cchLabel);
693
+ Assert::Equal<DWORD>(12, pSource->rgReleaseLabels[2].cchLabelOffset);
694
+
695
+ Assert::Equal<BOOL>(FALSE, pSource->rgReleaseLabels[3].fNumeric);
696
+ Assert::Equal<DWORD>(1, pSource->rgReleaseLabels[3].cchLabel);
697
+ Assert::Equal<DWORD>(14, pSource->rgReleaseLabels[3].cchLabelOffset);
698
+
699
+ Assert::Equal<BOOL>(TRUE, pSource->rgReleaseLabels[4].fNumeric);
700
+ Assert::Equal<DWORD>(5, pSource->rgReleaseLabels[4].dwValue);
701
+ Assert::Equal<DWORD>(1, pSource->rgReleaseLabels[4].cchLabel);
702
+ Assert::Equal<DWORD>(16, pSource->rgReleaseLabels[4].cchLabelOffset);
703
+
704
+ Assert::Equal<DWORD>(18, pSource->cchMetadataOffset);
705
+ Assert::Equal<BOOL>(TRUE, pSource->fInvalid);
706
+
707
+ hr = VerCopyVersion(pSource, &pCopy);
708
+ NativeAssert::Succeeded(hr, "VerCopyVersion failed");
709
+
710
+ Assert::False(pSource == pCopy);
711
+ Assert::False(pSource->sczVersion == pCopy->sczVersion);
712
+ Assert::False(pSource->rgReleaseLabels == pCopy->rgReleaseLabels);
713
+
714
+ hr = VerCompareParsedVersions(pSource, pCopy, &nResult);
715
+ NativeAssert::Succeeded(hr, "VerCompareParsedVersions failed");
716
+
717
+ Assert::Equal<int>(nResult, 0);
718
+ }
719
+ finally
720
+ {
721
+ ReleaseVerutilVersion(pCopy);
722
+ ReleaseVerutilVersion(pSource);
723
+ }
724
+ }
725
+
726
+ [Fact]
727
+ void VerParseVersionTreatsTrailingDotsAsInvalid()
728
+ {
729
+ HRESULT hr = S_OK;
730
+ VERUTIL_VERSION* pVersion1 = NULL;
731
+ VERUTIL_VERSION* pVersion2 = NULL;
732
+ VERUTIL_VERSION* pVersion3 = NULL;
733
+ VERUTIL_VERSION* pVersion4 = NULL;
734
+ VERUTIL_VERSION* pVersion5 = NULL;
735
+ VERUTIL_VERSION* pVersion6 = NULL;
736
+ VERUTIL_VERSION* pVersion7 = NULL;
737
+ LPCWSTR wzVersion1 = L".";
738
+ LPCWSTR wzVersion2 = L"1.";
739
+ LPCWSTR wzVersion3 = L"2.1.";
740
+ LPCWSTR wzVersion4 = L"3.2.1.";
741
+ LPCWSTR wzVersion5 = L"4.3.2.1.";
742
+ LPCWSTR wzVersion6 = L"5-.";
743
+ LPCWSTR wzVersion7 = L"6-a.";
744
+
745
+ try
746
+ {
747
+ hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1);
748
+ NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1);
749
+
750
+ hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2);
751
+ NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2);
752
+
753
+ hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3);
754
+ NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3);
755
+
756
+ hr = VerParseVersion(wzVersion4, 0, FALSE, &pVersion4);
757
+ NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion4);
758
+
759
+ hr = VerParseVersion(wzVersion5, 0, FALSE, &pVersion5);
760
+ NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion5);
761
+
762
+ hr = VerParseVersion(wzVersion6, 0, FALSE, &pVersion6);
763
+ NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion6);
764
+
765
+ hr = VerParseVersion(wzVersion7, 0, FALSE, &pVersion7);
766
+ NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion7);
767
+
768
+ NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion);
769
+ Assert::Equal<DWORD>(0, pVersion1->dwMajor);
770
+ Assert::Equal<DWORD>(0, pVersion1->dwMinor);
771
+ Assert::Equal<DWORD>(0, pVersion1->dwPatch);
772
+ Assert::Equal<DWORD>(0, pVersion1->dwRevision);
773
+ Assert::Equal<DWORD>(0, pVersion1->cReleaseLabels);
774
+ Assert::Equal<DWORD>(0, pVersion1->cchMetadataOffset);
775
+ Assert::Equal<BOOL>(TRUE, pVersion1->fInvalid);
776
+
777
+ NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion);
778
+ Assert::Equal<DWORD>(1, pVersion2->dwMajor);
779
+ Assert::Equal<DWORD>(0, pVersion2->dwMinor);
780
+ Assert::Equal<DWORD>(0, pVersion2->dwPatch);
781
+ Assert::Equal<DWORD>(0, pVersion2->dwRevision);
782
+ Assert::Equal<DWORD>(0, pVersion2->cReleaseLabels);
783
+ Assert::Equal<DWORD>(2, pVersion2->cchMetadataOffset);
784
+ Assert::Equal<BOOL>(TRUE, pVersion2->fInvalid);
785
+
786
+ NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion);
787
+ Assert::Equal<DWORD>(2, pVersion3->dwMajor);
788
+ Assert::Equal<DWORD>(1, pVersion3->dwMinor);
789
+ Assert::Equal<DWORD>(0, pVersion3->dwPatch);
790
+ Assert::Equal<DWORD>(0, pVersion3->dwRevision);
791
+ Assert::Equal<DWORD>(0, pVersion3->cReleaseLabels);
792
+ Assert::Equal<DWORD>(4, pVersion3->cchMetadataOffset);
793
+ Assert::Equal<BOOL>(TRUE, pVersion3->fInvalid);
794
+
795
+ NativeAssert::StringEqual(wzVersion4, pVersion4->sczVersion);
796
+ Assert::Equal<DWORD>(3, pVersion4->dwMajor);
797
+ Assert::Equal<DWORD>(2, pVersion4->dwMinor);
798
+ Assert::Equal<DWORD>(1, pVersion4->dwPatch);
799
+ Assert::Equal<DWORD>(0, pVersion4->dwRevision);
800
+ Assert::Equal<DWORD>(0, pVersion4->cReleaseLabels);
801
+ Assert::Equal<DWORD>(6, pVersion4->cchMetadataOffset);
802
+ Assert::Equal<BOOL>(TRUE, pVersion4->fInvalid);
803
+
804
+ NativeAssert::StringEqual(wzVersion5, pVersion5->sczVersion);
805
+ Assert::Equal<DWORD>(4, pVersion5->dwMajor);
806
+ Assert::Equal<DWORD>(3, pVersion5->dwMinor);
807
+ Assert::Equal<DWORD>(2, pVersion5->dwPatch);
808
+ Assert::Equal<DWORD>(1, pVersion5->dwRevision);
809
+ Assert::Equal<DWORD>(0, pVersion5->cReleaseLabels);
810
+ Assert::Equal<DWORD>(8, pVersion5->cchMetadataOffset);
811
+ Assert::Equal<BOOL>(TRUE, pVersion5->fInvalid);
812
+
813
+ NativeAssert::StringEqual(wzVersion6, pVersion6->sczVersion);
814
+ Assert::Equal<DWORD>(5, pVersion6->dwMajor);
815
+ Assert::Equal<DWORD>(0, pVersion6->dwMinor);
816
+ Assert::Equal<DWORD>(0, pVersion6->dwPatch);
817
+ Assert::Equal<DWORD>(0, pVersion6->dwRevision);
818
+ Assert::Equal<DWORD>(0, pVersion6->cReleaseLabels);
819
+ Assert::Equal<DWORD>(2, pVersion6->cchMetadataOffset);
820
+ Assert::Equal<BOOL>(TRUE, pVersion6->fInvalid);
821
+
822
+ NativeAssert::StringEqual(wzVersion7, pVersion7->sczVersion);
823
+ Assert::Equal<DWORD>(6, pVersion7->dwMajor);
824
+ Assert::Equal<DWORD>(0, pVersion7->dwMinor);
825
+ Assert::Equal<DWORD>(0, pVersion7->dwPatch);
826
+ Assert::Equal<DWORD>(0, pVersion7->dwRevision);
827
+ Assert::Equal<DWORD>(1, pVersion7->cReleaseLabels);
828
+
829
+ Assert::Equal<BOOL>(FALSE, pVersion7->rgReleaseLabels[0].fNumeric);
830
+ Assert::Equal<DWORD>(1, pVersion7->rgReleaseLabels[0].cchLabel);
831
+ Assert::Equal<DWORD>(2, pVersion7->rgReleaseLabels[0].cchLabelOffset);
832
+
833
+ Assert::Equal<DWORD>(4, pVersion7->cchMetadataOffset);
834
+ Assert::Equal<BOOL>(TRUE, pVersion7->fInvalid);
835
+ }
836
+ finally
837
+ {
838
+ ReleaseVerutilVersion(pVersion1);
839
+ ReleaseVerutilVersion(pVersion2);
840
+ ReleaseVerutilVersion(pVersion3);
841
+ ReleaseVerutilVersion(pVersion4);
842
+ ReleaseVerutilVersion(pVersion5);
843
+ ReleaseVerutilVersion(pVersion6);
844
+ ReleaseVerutilVersion(pVersion7);
845
+ }
846
+ }
847
+
848
+ [Fact]
849
+ void VerVersionFromQwordCreatesVersion()
850
+ {
851
+ HRESULT hr = S_OK;
852
+ VERUTIL_VERSION* pVersion1 = NULL;
853
+
854
+ try
855
+ {
856
+ hr = VerVersionFromQword(MAKEQWORDVERSION(1, 2, 3, 4), &pVersion1);
857
+ NativeAssert::Succeeded(hr, "VerVersionFromQword failed");
858
+
859
+ NativeAssert::StringEqual(L"1.2.3.4", pVersion1->sczVersion);
860
+ Assert::Equal<DWORD>(1, pVersion1->dwMajor);
861
+ Assert::Equal<DWORD>(2, pVersion1->dwMinor);
862
+ Assert::Equal<DWORD>(3, pVersion1->dwPatch);
863
+ Assert::Equal<DWORD>(4, pVersion1->dwRevision);
864
+ Assert::Equal<DWORD>(0, pVersion1->cReleaseLabels);
865
+ Assert::Equal<DWORD>(7, pVersion1->cchMetadataOffset);
866
+ Assert::Equal<BOOL>(FALSE, pVersion1->fInvalid);
867
+ }
868
+ finally
869
+ {
870
+ ReleaseVerutilVersion(pVersion1);
871
+ }
872
+ }
873
+
874
+ private:
875
+ void TestVerutilCompareParsedVersions(VERUTIL_VERSION* pVersion1, VERUTIL_VERSION* pVersion2, int nExpectedResult)
876
+ {
877
+ HRESULT hr = S_OK;
878
+ int nResult = 0;
879
+
880
+ hr = VerCompareParsedVersions(pVersion1, pVersion2, &nResult);
881
+ NativeAssert::Succeeded(hr, "Failed to compare versions '{0}' and '{1}'", pVersion1->sczVersion, pVersion2->sczVersion);
882
+
883
+ Assert::Equal(nExpectedResult, nResult);
884
+
885
+ hr = VerCompareParsedVersions(pVersion2, pVersion1, &nResult);
886
+ NativeAssert::Succeeded(hr, "Failed to compare versions '{0}' and '{1}'", pVersion1->sczVersion, pVersion2->sczVersion);
887
+
888
+ Assert::Equal(nExpectedResult, -nResult);
889
+ }
890
+ };
891
+}
src/test/DUtilUnitTest/precomp.h
+1
@@ -22,6 +22,7 @@
22
#include <monutil.h>
23
#include <regutil.h>
24
#include <uriutil.h>
25
+#include <verutil.h>
26
27
#pragma managed
28
#include <vcclr.h>