aboutsummaryrefslogtreecommitdiff
path: root/src/api/burn/WixToolset.Mba.Core/Engine.cs
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2021-04-22 05:46:03 -0700
committerRob Mensching <rob@firegiant.com>2021-04-29 16:41:44 -0700
commitc00516901e6b67e398396b14fe7682d0376f8643 (patch)
treeb0d62089a1c5700c7f2c3e3790750bf2d8ea33c0 /src/api/burn/WixToolset.Mba.Core/Engine.cs
parent8eb98efd2175d9ece2e4639d43081667af9a4990 (diff)
downloadwix-c00516901e6b67e398396b14fe7682d0376f8643.tar.gz
wix-c00516901e6b67e398396b14fe7682d0376f8643.tar.bz2
wix-c00516901e6b67e398396b14fe7682d0376f8643.zip
Move balutil into API/burn
Diffstat (limited to 'src/api/burn/WixToolset.Mba.Core/Engine.cs')
-rw-r--r--src/api/burn/WixToolset.Mba.Core/Engine.cs541
1 files changed, 541 insertions, 0 deletions
diff --git a/src/api/burn/WixToolset.Mba.Core/Engine.cs b/src/api/burn/WixToolset.Mba.Core/Engine.cs
new file mode 100644
index 00000000..aed420d5
--- /dev/null
+++ b/src/api/burn/WixToolset.Mba.Core/Engine.cs
@@ -0,0 +1,541 @@
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
3namespace WixToolset.Mba.Core
4{
5 using System;
6 using System.ComponentModel;
7 using System.Runtime.InteropServices;
8 using System.Security;
9 using System.Text;
10
11 /// <summary>
12 /// Default implementation of <see cref="IEngine"/>.
13 /// </summary>
14 public sealed class Engine : IEngine
15 {
16 // Burn errs on empty strings, so declare initial buffer size.
17 private const int InitialBufferSize = 80;
18 private static readonly string normalizeVersionFormatString = "{0} must be less than or equal to " + UInt16.MaxValue;
19
20 private IBootstrapperEngine engine;
21
22 /// <summary>
23 /// Creates a new instance of the <see cref="Engine"/> container class.
24 /// </summary>
25 /// <param name="engine">The <see cref="IBootstrapperEngine"/> to contain.</param>
26 internal Engine(IBootstrapperEngine engine)
27 {
28 this.engine = engine;
29 }
30
31 /// <inheritdoc/>
32 public int PackageCount
33 {
34 get
35 {
36 int count;
37 this.engine.GetPackageCount(out count);
38
39 return count;
40 }
41 }
42
43 /// <inheritdoc/>
44 public void Apply(IntPtr hwndParent)
45 {
46 this.engine.Apply(hwndParent);
47 }
48
49 /// <inheritdoc/>
50 public void CloseSplashScreen()
51 {
52 this.engine.CloseSplashScreen();
53 }
54
55 /// <inheritdoc/>
56 public int CompareVersions(string version1, string version2)
57 {
58 this.engine.CompareVersions(version1, version2, out var result);
59 return result;
60 }
61
62 /// <inheritdoc/>
63 public bool ContainsVariable(string name)
64 {
65 IntPtr capacity = new IntPtr(0);
66 int ret = this.engine.GetVariableString(name, IntPtr.Zero, ref capacity);
67 return NativeMethods.E_NOTFOUND != ret;
68 }
69
70 /// <inheritdoc/>
71 public void Detect()
72 {
73 this.Detect(IntPtr.Zero);
74 }
75
76 /// <inheritdoc/>
77 public void Detect(IntPtr hwndParent)
78 {
79 this.engine.Detect(hwndParent);
80 }
81
82 /// <inheritdoc/>
83 public bool Elevate(IntPtr hwndParent)
84 {
85 int ret = this.engine.Elevate(hwndParent);
86
87 if (NativeMethods.S_OK == ret || NativeMethods.E_ALREADYINITIALIZED == ret)
88 {
89 return true;
90 }
91 else if (NativeMethods.E_CANCELLED == ret)
92 {
93 return false;
94 }
95 else
96 {
97 throw new Win32Exception(ret);
98 }
99 }
100
101 /// <inheritdoc/>
102 public string EscapeString(string input)
103 {
104 IntPtr capacity = new IntPtr(InitialBufferSize);
105 StringBuilder sb = new StringBuilder(capacity.ToInt32());
106
107 // Get the size of the buffer.
108 int ret = this.engine.EscapeString(input, sb, ref capacity);
109 if (NativeMethods.E_INSUFFICIENT_BUFFER == ret || NativeMethods.E_MOREDATA == ret)
110 {
111 capacity = new IntPtr(capacity.ToInt32() + 1); // Add one for the null terminator.
112 sb.Capacity = capacity.ToInt32();
113 ret = this.engine.EscapeString(input, sb, ref capacity);
114 }
115
116 if (NativeMethods.S_OK != ret)
117 {
118 throw new Win32Exception(ret);
119 }
120
121 return sb.ToString();
122 }
123
124 /// <inheritdoc/>
125 public bool EvaluateCondition(string condition)
126 {
127 bool value;
128 this.engine.EvaluateCondition(condition, out value);
129
130 return value;
131 }
132
133 /// <inheritdoc/>
134 public string FormatString(string format)
135 {
136 IntPtr capacity = new IntPtr(InitialBufferSize);
137 StringBuilder sb = new StringBuilder(capacity.ToInt32());
138
139 // Get the size of the buffer.
140 int ret = this.engine.FormatString(format, sb, ref capacity);
141 if (NativeMethods.E_INSUFFICIENT_BUFFER == ret || NativeMethods.E_MOREDATA == ret)
142 {
143 capacity = new IntPtr(capacity.ToInt32() + 1); // Add one for the null terminator.
144 sb.Capacity = capacity.ToInt32();
145 ret = this.engine.FormatString(format, sb, ref capacity);
146 }
147
148 if (NativeMethods.S_OK != ret)
149 {
150 throw new Win32Exception(ret);
151 }
152
153 return sb.ToString();
154 }
155
156 /// <inheritdoc/>
157 public long GetVariableNumeric(string name)
158 {
159 int ret = this.engine.GetVariableNumeric(name, out long value);
160 if (NativeMethods.S_OK != ret)
161 {
162 throw new Win32Exception(ret);
163 }
164
165 return value;
166 }
167
168 /// <inheritdoc/>
169 public SecureString GetVariableSecureString(string name)
170 {
171 var pUniString = this.getStringVariable(name, out var length);
172 try
173 {
174 return this.convertToSecureString(pUniString, length);
175 }
176 finally
177 {
178 if (IntPtr.Zero != pUniString)
179 {
180 Marshal.FreeCoTaskMem(pUniString);
181 }
182 }
183 }
184
185 /// <inheritdoc/>
186 public string GetVariableString(string name)
187 {
188 int length;
189 IntPtr pUniString = this.getStringVariable(name, out length);
190 try
191 {
192 return Marshal.PtrToStringUni(pUniString, length);
193 }
194 finally
195 {
196 if (IntPtr.Zero != pUniString)
197 {
198 Marshal.FreeCoTaskMem(pUniString);
199 }
200 }
201 }
202
203 /// <inheritdoc/>
204 public string GetVariableVersion(string name)
205 {
206 int length;
207 IntPtr pUniString = this.getVersionVariable(name, out length);
208 try
209 {
210 return Marshal.PtrToStringUni(pUniString, length);
211 }
212 finally
213 {
214 if (IntPtr.Zero != pUniString)
215 {
216 Marshal.FreeCoTaskMem(pUniString);
217 }
218 }
219 }
220
221 /// <inheritdoc/>
222 public void LaunchApprovedExe(IntPtr hwndParent, string approvedExeForElevationId, string arguments)
223 {
224 this.LaunchApprovedExe(hwndParent, approvedExeForElevationId, arguments, 0);
225 }
226
227 /// <inheritdoc/>
228 public void LaunchApprovedExe(IntPtr hwndParent, string approvedExeForElevationId, string arguments, int waitForInputIdleTimeout)
229 {
230 this.engine.LaunchApprovedExe(hwndParent, approvedExeForElevationId, arguments, waitForInputIdleTimeout);
231 }
232 /// <inheritdoc/>
233
234 public void Log(LogLevel level, string message)
235 {
236 this.engine.Log(level, message);
237 }
238
239 /// <inheritdoc/>
240 public void Plan(LaunchAction action)
241 {
242 this.engine.Plan(action);
243 }
244
245 /// <inheritdoc/>
246 public void SetUpdate(string localSource, string downloadSource, long size, UpdateHashType hashType, byte[] hash)
247 {
248 this.engine.SetUpdate(localSource, downloadSource, size, hashType, hash, null == hash ? 0 : hash.Length);
249 }
250
251 /// <inheritdoc/>
252 public void SetUpdateSource(string url)
253 {
254 this.engine.SetUpdateSource(url);
255 }
256
257 /// <inheritdoc/>
258 public void SetLocalSource(string packageOrContainerId, string payloadId, string path)
259 {
260 this.engine.SetLocalSource(packageOrContainerId, payloadId, path);
261 }
262
263 /// <inheritdoc/>
264 public void SetDownloadSource(string packageOrContainerId, string payloadId, string url, string user, string password)
265 {
266 this.engine.SetDownloadSource(packageOrContainerId, payloadId, url, user, password);
267 }
268
269 /// <inheritdoc/>
270 public void SetVariableNumeric(string name, long value)
271 {
272 this.engine.SetVariableNumeric(name, value);
273 }
274
275 /// <inheritdoc/>
276 public void SetVariableString(string name, SecureString value, bool formatted)
277 {
278 IntPtr pValue = Marshal.SecureStringToCoTaskMemUnicode(value);
279 try
280 {
281 this.engine.SetVariableString(name, pValue, formatted);
282 }
283 finally
284 {
285 Marshal.FreeCoTaskMem(pValue);
286 }
287 }
288
289 /// <inheritdoc/>
290 public void SetVariableString(string name, string value, bool formatted)
291 {
292 IntPtr pValue = Marshal.StringToCoTaskMemUni(value);
293 try
294 {
295 this.engine.SetVariableString(name, pValue, formatted);
296 }
297 finally
298 {
299 Marshal.FreeCoTaskMem(pValue);
300 }
301 }
302
303 /// <inheritdoc/>
304 public void SetVariableVersion(string name, string value)
305 {
306 IntPtr pValue = Marshal.StringToCoTaskMemUni(value);
307 try
308 {
309 this.engine.SetVariableVersion(name, pValue);
310 }
311 finally
312 {
313 Marshal.FreeCoTaskMem(pValue);
314 }
315 }
316
317 /// <inheritdoc/>
318 public int SendEmbeddedError(int errorCode, string message, int uiHint)
319 {
320 int result = 0;
321 this.engine.SendEmbeddedError(errorCode, message, uiHint, out result);
322 return result;
323 }
324
325 /// <inheritdoc/>
326 public int SendEmbeddedProgress(int progressPercentage, int overallPercentage)
327 {
328 int result = 0;
329 this.engine.SendEmbeddedProgress(progressPercentage, overallPercentage, out result);
330 return result;
331 }
332
333 /// <inheritdoc/>
334 public void Quit(int exitCode)
335 {
336 this.engine.Quit(exitCode);
337 }
338
339 /// <summary>
340 /// Gets the variable given by <paramref name="name"/> as a string.
341 /// </summary>
342 /// <param name="name">The name of the variable to get.</param>
343 /// <param name="length">The length of the Unicode string.</param>
344 /// <returns>The value by a pointer to a Unicode string. Must be freed by Marshal.FreeCoTaskMem.</returns>
345 /// <exception cref="Exception">An error occurred getting the variable.</exception>
346 internal IntPtr getStringVariable(string name, out int length)
347 {
348 IntPtr capacity = new IntPtr(InitialBufferSize);
349 bool success = false;
350 IntPtr pValue = Marshal.AllocCoTaskMem(capacity.ToInt32() * UnicodeEncoding.CharSize);
351 try
352 {
353 // Get the size of the buffer.
354 int ret = this.engine.GetVariableString(name, pValue, ref capacity);
355 if (NativeMethods.E_INSUFFICIENT_BUFFER == ret || NativeMethods.E_MOREDATA == ret)
356 {
357 // Don't need to add 1 for the null terminator, the engine already includes that.
358 pValue = Marshal.ReAllocCoTaskMem(pValue, capacity.ToInt32() * UnicodeEncoding.CharSize);
359 ret = this.engine.GetVariableString(name, pValue, ref capacity);
360 }
361
362 if (NativeMethods.S_OK != ret)
363 {
364 throw Marshal.GetExceptionForHR(ret);
365 }
366
367 // The engine only returns the exact length of the string if the buffer was too small, so calculate it ourselves.
368 int maxLength = capacity.ToInt32();
369 for (length = 0; length < maxLength; ++length)
370 {
371 if (0 == Marshal.ReadInt16(pValue, length * UnicodeEncoding.CharSize))
372 {
373 break;
374 }
375 }
376
377 success = true;
378 return pValue;
379 }
380 finally
381 {
382 if (!success && IntPtr.Zero != pValue)
383 {
384 Marshal.FreeCoTaskMem(pValue);
385 }
386 }
387 }
388
389 /// <summary>
390 /// Gets the variable given by <paramref name="name"/> as a version string.
391 /// </summary>
392 /// <param name="name">The name of the variable to get.</param>
393 /// <param name="length">The length of the Unicode string.</param>
394 /// <returns>The value by a pointer to a Unicode string. Must be freed by Marshal.FreeCoTaskMem.</returns>
395 /// <exception cref="Exception">An error occurred getting the variable.</exception>
396 internal IntPtr getVersionVariable(string name, out int length)
397 {
398 IntPtr capacity = new IntPtr(InitialBufferSize);
399 bool success = false;
400 IntPtr pValue = Marshal.AllocCoTaskMem(capacity.ToInt32() * UnicodeEncoding.CharSize);
401 try
402 {
403 // Get the size of the buffer.
404 int ret = this.engine.GetVariableVersion(name, pValue, ref capacity);
405 if (NativeMethods.E_INSUFFICIENT_BUFFER == ret || NativeMethods.E_MOREDATA == ret)
406 {
407 // Don't need to add 1 for the null terminator, the engine already includes that.
408 pValue = Marshal.ReAllocCoTaskMem(pValue, capacity.ToInt32() * UnicodeEncoding.CharSize);
409 ret = this.engine.GetVariableVersion(name, pValue, ref capacity);
410 }
411
412 if (NativeMethods.S_OK != ret)
413 {
414 throw Marshal.GetExceptionForHR(ret);
415 }
416
417 // The engine only returns the exact length of the string if the buffer was too small, so calculate it ourselves.
418 int maxLength = capacity.ToInt32();
419 for (length = 0; length < maxLength; ++length)
420 {
421 if (0 == Marshal.ReadInt16(pValue, length * UnicodeEncoding.CharSize))
422 {
423 break;
424 }
425 }
426
427 success = true;
428 return pValue;
429 }
430 finally
431 {
432 if (!success && IntPtr.Zero != pValue)
433 {
434 Marshal.FreeCoTaskMem(pValue);
435 }
436 }
437 }
438
439 /// <summary>
440 /// Initialize a SecureString with the given Unicode string.
441 /// </summary>
442 /// <param name="pUniString">Pointer to Unicode string.</param>
443 /// <param name="length">The string's length.</param>
444 internal SecureString convertToSecureString(IntPtr pUniString, int length)
445 {
446 if (IntPtr.Zero == pUniString)
447 {
448 return null;
449 }
450
451 SecureString value = new SecureString();
452 short s;
453 char c;
454 for (int charIndex = 0; charIndex < length; charIndex++)
455 {
456 s = Marshal.ReadInt16(pUniString, charIndex * UnicodeEncoding.CharSize);
457 c = (char)s;
458 value.AppendChar(c);
459 s = 0;
460 c = (char)0;
461 }
462 return value;
463 }
464
465 /// <summary>
466 /// Utility method for converting a <see cref="Version"/> into a <see cref="long"/>.
467 /// </summary>
468 /// <param name="version"></param>
469 /// <returns></returns>
470 public static long VersionToLong(Version version)
471 {
472 // In Windows, each version component has a max value of 65535,
473 // so we truncate the version before shifting it, which will overflow if invalid.
474 long major = (long)(ushort)version.Major << 48;
475 long minor = (long)(ushort)version.Minor << 32;
476 long build = (long)(ushort)version.Build << 16;
477 long revision = (long)(ushort)version.Revision;
478
479 return major | minor | build | revision;
480 }
481
482 /// <summary>
483 /// Utility method for converting a <see cref="long"/> into a <see cref="Version"/>.
484 /// </summary>
485 /// <param name="version"></param>
486 /// <returns></returns>
487 public static Version LongToVersion(long version)
488 {
489 int major = (int)((version & ((long)0xffff << 48)) >> 48);
490 int minor = (int)((version & ((long)0xffff << 32)) >> 32);
491 int build = (int)((version & ((long)0xffff << 16)) >> 16);
492 int revision = (int)(version & 0xffff);
493
494 return new Version(major, minor, build, revision);
495 }
496
497 /// <summary>
498 /// Verifies that Version can be represented in a <see cref="long"/>.
499 /// If the Build or Revision fields are undefined, they are set to zero.
500 /// </summary>
501 public static Version NormalizeVersion(Version version)
502 {
503 if (version == null)
504 {
505 throw new ArgumentNullException("version");
506 }
507
508 int major = version.Major;
509 int minor = version.Minor;
510 int build = version.Build;
511 int revision = version.Revision;
512
513 if (major > UInt16.MaxValue)
514 {
515 throw new ArgumentOutOfRangeException("version", String.Format(normalizeVersionFormatString, "Major"));
516 }
517 if (minor > UInt16.MaxValue)
518 {
519 throw new ArgumentOutOfRangeException("version", String.Format(normalizeVersionFormatString, "Minor"));
520 }
521 if (build > UInt16.MaxValue)
522 {
523 throw new ArgumentOutOfRangeException("version", String.Format(normalizeVersionFormatString, "Build"));
524 }
525 if (build == -1)
526 {
527 build = 0;
528 }
529 if (revision > UInt16.MaxValue)
530 {
531 throw new ArgumentOutOfRangeException("version", String.Format(normalizeVersionFormatString, "Revision"));
532 }
533 if (revision == -1)
534 {
535 revision = 0;
536 }
537
538 return new Version(major, minor, build, revision);
539 }
540 }
541}