diff options
Diffstat (limited to '')
-rw-r--r-- | src/WixToolset.WixBA/InstallationViewModel.cs | 670 |
1 files changed, 670 insertions, 0 deletions
diff --git a/src/WixToolset.WixBA/InstallationViewModel.cs b/src/WixToolset.WixBA/InstallationViewModel.cs new file mode 100644 index 00000000..6bec427a --- /dev/null +++ b/src/WixToolset.WixBA/InstallationViewModel.cs | |||
@@ -0,0 +1,670 @@ | |||
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.UX | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections.Generic; | ||
7 | using System.ComponentModel; | ||
8 | using System.Linq; | ||
9 | using System.Reflection; | ||
10 | using System.Windows; | ||
11 | using System.Windows.Input; | ||
12 | using IO = System.IO; | ||
13 | using WixToolset.Bootstrapper; | ||
14 | |||
15 | /// <summary> | ||
16 | /// The states of detection. | ||
17 | /// </summary> | ||
18 | public enum DetectionState | ||
19 | { | ||
20 | Absent, | ||
21 | Present, | ||
22 | Newer, | ||
23 | } | ||
24 | |||
25 | /// <summary> | ||
26 | /// The states of installation. | ||
27 | /// </summary> | ||
28 | public enum InstallationState | ||
29 | { | ||
30 | Initializing, | ||
31 | Detecting, | ||
32 | Waiting, | ||
33 | Planning, | ||
34 | Applying, | ||
35 | Applied, | ||
36 | Failed, | ||
37 | } | ||
38 | |||
39 | /// <summary> | ||
40 | /// The model of the installation view in WixBA. | ||
41 | /// </summary> | ||
42 | public class InstallationViewModel : PropertyNotifyBase | ||
43 | { | ||
44 | private RootViewModel root; | ||
45 | |||
46 | private Dictionary<string, int> downloadRetries; | ||
47 | private bool downgrade; | ||
48 | private string downgradeMessage; | ||
49 | |||
50 | private ICommand licenseCommand; | ||
51 | private ICommand launchHomePageCommand; | ||
52 | private ICommand launchNewsCommand; | ||
53 | private ICommand launchVSExtensionPageCommand; | ||
54 | private ICommand installCommand; | ||
55 | private ICommand repairCommand; | ||
56 | private ICommand uninstallCommand; | ||
57 | private ICommand openLogCommand; | ||
58 | private ICommand openLogFolderCommand; | ||
59 | private ICommand tryAgainCommand; | ||
60 | |||
61 | private string message; | ||
62 | private DateTime cachePackageStart; | ||
63 | private DateTime executePackageStart; | ||
64 | |||
65 | /// <summary> | ||
66 | /// Creates a new model of the installation view. | ||
67 | /// </summary> | ||
68 | public InstallationViewModel(RootViewModel root) | ||
69 | { | ||
70 | this.root = root; | ||
71 | this.downloadRetries = new Dictionary<string, int>(); | ||
72 | |||
73 | this.root.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(this.RootPropertyChanged); | ||
74 | |||
75 | WixBA.Model.Bootstrapper.DetectBegin += this.DetectBegin; | ||
76 | WixBA.Model.Bootstrapper.DetectRelatedBundle += this.DetectedRelatedBundle; | ||
77 | WixBA.Model.Bootstrapper.DetectComplete += this.DetectComplete; | ||
78 | WixBA.Model.Bootstrapper.PlanPackageBegin += this.PlanPackageBegin; | ||
79 | WixBA.Model.Bootstrapper.PlanComplete += this.PlanComplete; | ||
80 | WixBA.Model.Bootstrapper.ApplyBegin += this.ApplyBegin; | ||
81 | WixBA.Model.Bootstrapper.CacheAcquireBegin += this.CacheAcquireBegin; | ||
82 | WixBA.Model.Bootstrapper.CacheAcquireComplete += this.CacheAcquireComplete; | ||
83 | WixBA.Model.Bootstrapper.ExecutePackageBegin += this.ExecutePackageBegin; | ||
84 | WixBA.Model.Bootstrapper.ExecutePackageComplete += this.ExecutePackageComplete; | ||
85 | WixBA.Model.Bootstrapper.Error += this.ExecuteError; | ||
86 | WixBA.Model.Bootstrapper.ResolveSource += this.ResolveSource; | ||
87 | WixBA.Model.Bootstrapper.ApplyComplete += this.ApplyComplete; | ||
88 | } | ||
89 | |||
90 | void RootPropertyChanged(object sender, PropertyChangedEventArgs e) | ||
91 | { | ||
92 | if (("DetectState" == e.PropertyName) || ("InstallState" == e.PropertyName)) | ||
93 | { | ||
94 | base.OnPropertyChanged("RepairEnabled"); | ||
95 | base.OnPropertyChanged("InstallEnabled"); | ||
96 | base.OnPropertyChanged("IsComplete"); | ||
97 | base.OnPropertyChanged("IsSuccessfulCompletion"); | ||
98 | base.OnPropertyChanged("IsFailedCompletion"); | ||
99 | base.OnPropertyChanged("StatusText"); | ||
100 | base.OnPropertyChanged("UninstallEnabled"); | ||
101 | } | ||
102 | } | ||
103 | |||
104 | /// <summary> | ||
105 | /// Gets the version for the application. | ||
106 | /// </summary> | ||
107 | public string Version | ||
108 | { | ||
109 | get { return String.Concat("v", WixBA.Model.Version.ToString()); } | ||
110 | } | ||
111 | |||
112 | /// <summary> | ||
113 | /// The Publisher of this bundle. | ||
114 | /// </summary> | ||
115 | public string Publisher | ||
116 | { | ||
117 | get | ||
118 | { | ||
119 | string company = "[AssemblyCompany]"; | ||
120 | return WixDistribution.ReplacePlaceholders(company, typeof(WixBA).Assembly); | ||
121 | } | ||
122 | } | ||
123 | |||
124 | /// <summary> | ||
125 | /// The Publisher of this bundle. | ||
126 | /// </summary> | ||
127 | public string SupportUrl | ||
128 | { | ||
129 | get | ||
130 | { | ||
131 | return WixDistribution.SupportUrl; | ||
132 | } | ||
133 | } | ||
134 | public string VSExtensionUrl | ||
135 | { | ||
136 | get | ||
137 | { | ||
138 | return WixDistribution.VSExtensionsLandingUrl; | ||
139 | } | ||
140 | } | ||
141 | |||
142 | public string Message | ||
143 | { | ||
144 | get | ||
145 | { | ||
146 | return this.message; | ||
147 | } | ||
148 | |||
149 | set | ||
150 | { | ||
151 | if (this.message != value) | ||
152 | { | ||
153 | this.message = value; | ||
154 | base.OnPropertyChanged("Message"); | ||
155 | } | ||
156 | } | ||
157 | } | ||
158 | |||
159 | /// <summary> | ||
160 | /// Gets and sets whether the view model considers this install to be a downgrade. | ||
161 | /// </summary> | ||
162 | public bool Downgrade | ||
163 | { | ||
164 | get | ||
165 | { | ||
166 | return this.downgrade; | ||
167 | } | ||
168 | |||
169 | set | ||
170 | { | ||
171 | if (this.downgrade != value) | ||
172 | { | ||
173 | this.downgrade = value; | ||
174 | base.OnPropertyChanged("Downgrade"); | ||
175 | } | ||
176 | } | ||
177 | } | ||
178 | |||
179 | public string DowngradeMessage | ||
180 | { | ||
181 | get | ||
182 | { | ||
183 | return this.downgradeMessage; | ||
184 | } | ||
185 | set | ||
186 | { | ||
187 | if (this.downgradeMessage != value) | ||
188 | { | ||
189 | this.downgradeMessage = value; | ||
190 | base.OnPropertyChanged("DowngradeMessage"); | ||
191 | } | ||
192 | } | ||
193 | } | ||
194 | |||
195 | public ICommand LaunchHomePageCommand | ||
196 | { | ||
197 | get | ||
198 | { | ||
199 | if (this.launchHomePageCommand == null) | ||
200 | { | ||
201 | this.launchHomePageCommand = new RelayCommand(param => WixBA.LaunchUrl(this.SupportUrl), param => true); | ||
202 | } | ||
203 | |||
204 | return this.launchHomePageCommand; | ||
205 | } | ||
206 | } | ||
207 | |||
208 | public ICommand LaunchNewsCommand | ||
209 | { | ||
210 | get | ||
211 | { | ||
212 | if (this.launchNewsCommand == null) | ||
213 | { | ||
214 | this.launchNewsCommand = new RelayCommand(param => WixBA.LaunchUrl(WixDistribution.NewsUrl), param => true); | ||
215 | } | ||
216 | |||
217 | return this.launchNewsCommand; | ||
218 | } | ||
219 | } | ||
220 | |||
221 | public ICommand LaunchVSExtensionPageCommand | ||
222 | { | ||
223 | get | ||
224 | { | ||
225 | if (this.launchVSExtensionPageCommand == null) | ||
226 | { | ||
227 | this.launchVSExtensionPageCommand = new RelayCommand(param => WixBA.LaunchUrl(WixDistribution.VSExtensionsLandingUrl), param => true); | ||
228 | } | ||
229 | |||
230 | return this.launchVSExtensionPageCommand; | ||
231 | } | ||
232 | } | ||
233 | |||
234 | public ICommand LicenseCommand | ||
235 | { | ||
236 | get | ||
237 | { | ||
238 | if (this.licenseCommand == null) | ||
239 | { | ||
240 | this.licenseCommand = new RelayCommand(param => this.LaunchLicense(), param => true); | ||
241 | } | ||
242 | |||
243 | return this.licenseCommand; | ||
244 | } | ||
245 | } | ||
246 | |||
247 | public bool LicenseEnabled | ||
248 | { | ||
249 | get { return this.LicenseCommand.CanExecute(this); } | ||
250 | } | ||
251 | |||
252 | public ICommand CloseCommand | ||
253 | { | ||
254 | get { return this.root.CloseCommand; } | ||
255 | } | ||
256 | |||
257 | public bool IsComplete | ||
258 | { | ||
259 | get { return IsSuccessfulCompletion || IsFailedCompletion; } | ||
260 | } | ||
261 | |||
262 | public bool IsSuccessfulCompletion | ||
263 | { | ||
264 | get { return InstallationState.Applied == this.root.InstallState; } | ||
265 | } | ||
266 | |||
267 | public bool IsFailedCompletion | ||
268 | { | ||
269 | get { return InstallationState.Failed == this.root.InstallState; } | ||
270 | } | ||
271 | |||
272 | public ICommand InstallCommand | ||
273 | { | ||
274 | get | ||
275 | { | ||
276 | if (this.installCommand == null) | ||
277 | { | ||
278 | this.installCommand = new RelayCommand(param => WixBA.Plan(LaunchAction.Install), param => this.root.DetectState == DetectionState.Absent && this.root.InstallState == InstallationState.Waiting); | ||
279 | } | ||
280 | |||
281 | return this.installCommand; | ||
282 | } | ||
283 | } | ||
284 | |||
285 | public bool InstallEnabled | ||
286 | { | ||
287 | get { return this.InstallCommand.CanExecute(this); } | ||
288 | } | ||
289 | |||
290 | public ICommand RepairCommand | ||
291 | { | ||
292 | get | ||
293 | { | ||
294 | if (this.repairCommand == null) | ||
295 | { | ||
296 | this.repairCommand = new RelayCommand(param => WixBA.Plan(LaunchAction.Repair), param => this.root.DetectState == DetectionState.Present && this.root.InstallState == InstallationState.Waiting); | ||
297 | } | ||
298 | |||
299 | return this.repairCommand; | ||
300 | } | ||
301 | } | ||
302 | |||
303 | public bool RepairEnabled | ||
304 | { | ||
305 | get { return this.RepairCommand.CanExecute(this); } | ||
306 | } | ||
307 | |||
308 | public ICommand UninstallCommand | ||
309 | { | ||
310 | get | ||
311 | { | ||
312 | if (this.uninstallCommand == null) | ||
313 | { | ||
314 | this.uninstallCommand = new RelayCommand(param => WixBA.Plan(LaunchAction.Uninstall), param => this.root.DetectState == DetectionState.Present && this.root.InstallState == InstallationState.Waiting); | ||
315 | } | ||
316 | |||
317 | return this.uninstallCommand; | ||
318 | } | ||
319 | } | ||
320 | |||
321 | public bool UninstallEnabled | ||
322 | { | ||
323 | get { return this.UninstallCommand.CanExecute(this); } | ||
324 | } | ||
325 | |||
326 | public ICommand OpenLogCommand | ||
327 | { | ||
328 | get | ||
329 | { | ||
330 | if (this.openLogCommand == null) | ||
331 | { | ||
332 | this.openLogCommand = new RelayCommand(param => WixBA.OpenLog(new Uri(WixBA.Model.Engine.StringVariables["WixBundleLog"]))); | ||
333 | } | ||
334 | return this.openLogCommand; | ||
335 | } | ||
336 | } | ||
337 | |||
338 | public ICommand OpenLogFolderCommand | ||
339 | { | ||
340 | get | ||
341 | { | ||
342 | if (this.openLogFolderCommand == null) | ||
343 | { | ||
344 | string logFolder = IO.Path.GetDirectoryName(WixBA.Model.Engine.StringVariables["WixBundleLog"]); | ||
345 | this.openLogFolderCommand = new RelayCommand(param => WixBA.OpenLogFolder(logFolder)); | ||
346 | } | ||
347 | return this.openLogFolderCommand; | ||
348 | } | ||
349 | } | ||
350 | |||
351 | public ICommand TryAgainCommand | ||
352 | { | ||
353 | get | ||
354 | { | ||
355 | if (this.tryAgainCommand == null) | ||
356 | { | ||
357 | this.tryAgainCommand = new RelayCommand(param => | ||
358 | { | ||
359 | this.root.Canceled = false; | ||
360 | WixBA.Plan(WixBA.Model.PlannedAction); | ||
361 | }, param => IsFailedCompletion); | ||
362 | } | ||
363 | |||
364 | return this.tryAgainCommand; | ||
365 | } | ||
366 | } | ||
367 | |||
368 | public string StatusText | ||
369 | { | ||
370 | get | ||
371 | { | ||
372 | switch(this.root.InstallState) | ||
373 | { | ||
374 | case InstallationState.Applied: | ||
375 | return "Complete"; | ||
376 | case InstallationState.Failed: | ||
377 | return this.root.Canceled ? "Cancelled" : "Failed"; | ||
378 | default: | ||
379 | return "Unknown"; // this shouldn't be shown in the UI. | ||
380 | } | ||
381 | } | ||
382 | } | ||
383 | |||
384 | /// <summary> | ||
385 | /// Launches the license in the default viewer. | ||
386 | /// </summary> | ||
387 | private void LaunchLicense() | ||
388 | { | ||
389 | string folder = IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); | ||
390 | WixBA.LaunchUrl(IO.Path.Combine(folder, "License.txt")); | ||
391 | } | ||
392 | |||
393 | private void DetectBegin(object sender, DetectBeginEventArgs e) | ||
394 | { | ||
395 | this.root.DetectState = e.Installed ? DetectionState.Present : DetectionState.Absent; | ||
396 | WixBA.Model.PlannedAction = LaunchAction.Unknown; | ||
397 | } | ||
398 | |||
399 | private void DetectedRelatedBundle(object sender, DetectRelatedBundleEventArgs e) | ||
400 | { | ||
401 | if (e.Operation == RelatedOperation.Downgrade) | ||
402 | { | ||
403 | this.Downgrade = true; | ||
404 | } | ||
405 | |||
406 | if (!WixBA.Model.Bootstrapper.BAManifest.Bundle.Packages.ContainsKey(e.ProductCode)) | ||
407 | { | ||
408 | WixBA.Model.Bootstrapper.BAManifest.Bundle.AddRelatedBundleAsPackage(e); | ||
409 | } | ||
410 | } | ||
411 | |||
412 | private void DetectComplete(object sender, DetectCompleteEventArgs e) | ||
413 | { | ||
414 | // Parse the command line string before any planning. | ||
415 | this.ParseCommandLine(); | ||
416 | this.root.InstallState = InstallationState.Waiting; | ||
417 | |||
418 | if (LaunchAction.Uninstall == WixBA.Model.Command.Action && | ||
419 | ResumeType.Arp != WixBA.Model.Command.Resume) // MSI and WixStdBA require some kind of confirmation before proceeding so WixBA should, too. | ||
420 | { | ||
421 | WixBA.Model.Engine.Log(LogLevel.Verbose, "Invoking automatic plan for uninstall"); | ||
422 | WixBA.Plan(LaunchAction.Uninstall); | ||
423 | } | ||
424 | else if (Hresult.Succeeded(e.Status)) | ||
425 | { | ||
426 | if (this.Downgrade) | ||
427 | { | ||
428 | this.root.DetectState = DetectionState.Newer; | ||
429 | IEnumerable<PackageInfo> relatedPackages = WixBA.Model.Bootstrapper.BAManifest.Bundle.Packages.Values.Where(p => p.Type == PackageType.UpgradeBundle); | ||
430 | Version installedVersion = relatedPackages.Any() ? new Version(relatedPackages.Max(p => p.Version)) : null; | ||
431 | if (installedVersion != null && installedVersion < new Version(4, 1) && installedVersion.Build > 10) | ||
432 | { | ||
433 | this.DowngradeMessage = "You must uninstall WiX v" + installedVersion + " before you can install this."; | ||
434 | } | ||
435 | else | ||
436 | { | ||
437 | this.DowngradeMessage = "There is already a newer version of WiX installed on this machine."; | ||
438 | } | ||
439 | } | ||
440 | |||
441 | if (LaunchAction.Layout == WixBA.Model.Command.Action) | ||
442 | { | ||
443 | WixBA.PlanLayout(); | ||
444 | } | ||
445 | else if (WixBA.Model.Command.Display != Display.Full) | ||
446 | { | ||
447 | // If we're not waiting for the user to click install, dispatch plan with the default action. | ||
448 | WixBA.Model.Engine.Log(LogLevel.Verbose, "Invoking automatic plan for non-interactive mode."); | ||
449 | WixBA.Plan(WixBA.Model.Command.Action); | ||
450 | } | ||
451 | } | ||
452 | else | ||
453 | { | ||
454 | this.root.InstallState = InstallationState.Failed; | ||
455 | } | ||
456 | |||
457 | // Force all commands to reevaluate CanExecute. | ||
458 | // InvalidateRequerySuggested must be run on the UI thread. | ||
459 | root.Dispatcher.Invoke(new Action(CommandManager.InvalidateRequerySuggested)); | ||
460 | } | ||
461 | |||
462 | private void PlanPackageBegin(object sender, PlanPackageBeginEventArgs e) | ||
463 | { | ||
464 | // If we're able to run our BA, we don't want to install the .NET Framework since the framework on the machine is already good enough. | ||
465 | if ( e.PackageId.StartsWith("NetFx4", StringComparison.OrdinalIgnoreCase)) | ||
466 | { | ||
467 | e.State = RequestState.None; | ||
468 | } | ||
469 | } | ||
470 | |||
471 | private void PlanComplete(object sender, PlanCompleteEventArgs e) | ||
472 | { | ||
473 | if (Hresult.Succeeded(e.Status)) | ||
474 | { | ||
475 | this.root.PreApplyState = this.root.InstallState; | ||
476 | this.root.InstallState = InstallationState.Applying; | ||
477 | WixBA.Model.Engine.Apply(this.root.ViewWindowHandle); | ||
478 | } | ||
479 | else | ||
480 | { | ||
481 | this.root.InstallState = InstallationState.Failed; | ||
482 | } | ||
483 | } | ||
484 | |||
485 | private void ApplyBegin(object sender, ApplyBeginEventArgs e) | ||
486 | { | ||
487 | this.downloadRetries.Clear(); | ||
488 | } | ||
489 | |||
490 | private void CacheAcquireBegin(object sender, CacheAcquireBeginEventArgs e) | ||
491 | { | ||
492 | this.cachePackageStart = DateTime.Now; | ||
493 | } | ||
494 | |||
495 | private void CacheAcquireComplete(object sender, CacheAcquireCompleteEventArgs e) | ||
496 | { | ||
497 | this.AddPackageTelemetry("Cache", e.PackageOrContainerId ?? String.Empty, DateTime.Now.Subtract(this.cachePackageStart).TotalMilliseconds, e.Status); | ||
498 | } | ||
499 | |||
500 | private void ExecutePackageBegin(object sender, ExecutePackageBeginEventArgs e) | ||
501 | { | ||
502 | lock (this) | ||
503 | { | ||
504 | this.executePackageStart = e.ShouldExecute ? DateTime.Now : DateTime.MinValue; | ||
505 | } | ||
506 | } | ||
507 | |||
508 | private void ExecutePackageComplete(object sender, ExecutePackageCompleteEventArgs e) | ||
509 | { | ||
510 | lock (this) | ||
511 | { | ||
512 | if (DateTime.MinValue < this.executePackageStart) | ||
513 | { | ||
514 | this.AddPackageTelemetry("Execute", e.PackageId ?? String.Empty, DateTime.Now.Subtract(this.executePackageStart).TotalMilliseconds, e.Status); | ||
515 | this.executePackageStart = DateTime.MinValue; | ||
516 | } | ||
517 | } | ||
518 | } | ||
519 | |||
520 | private void ExecuteError(object sender, ErrorEventArgs e) | ||
521 | { | ||
522 | lock (this) | ||
523 | { | ||
524 | if (!this.root.Canceled) | ||
525 | { | ||
526 | // If the error is a cancel coming from the engine during apply we want to go back to the preapply state. | ||
527 | if (InstallationState.Applying == this.root.InstallState && (int)Error.UserCancelled == e.ErrorCode) | ||
528 | { | ||
529 | this.root.InstallState = this.root.PreApplyState; | ||
530 | } | ||
531 | else | ||
532 | { | ||
533 | this.Message = e.ErrorMessage; | ||
534 | |||
535 | if (Display.Full == WixBA.Model.Command.Display) | ||
536 | { | ||
537 | // On HTTP authentication errors, have the engine try to do authentication for us. | ||
538 | if (ErrorType.HttpServerAuthentication == e.ErrorType || ErrorType.HttpProxyAuthentication == e.ErrorType) | ||
539 | { | ||
540 | e.Result = Result.TryAgain; | ||
541 | } | ||
542 | else // show an error dialog. | ||
543 | { | ||
544 | MessageBoxButton msgbox = MessageBoxButton.OK; | ||
545 | switch (e.UIHint & 0xF) | ||
546 | { | ||
547 | case 0: | ||
548 | msgbox = MessageBoxButton.OK; | ||
549 | break; | ||
550 | case 1: | ||
551 | msgbox = MessageBoxButton.OKCancel; | ||
552 | break; | ||
553 | // There is no 2! That would have been MB_ABORTRETRYIGNORE. | ||
554 | case 3: | ||
555 | msgbox = MessageBoxButton.YesNoCancel; | ||
556 | break; | ||
557 | case 4: | ||
558 | msgbox = MessageBoxButton.YesNo; | ||
559 | break; | ||
560 | // default: stay with MBOK since an exact match is not available. | ||
561 | } | ||
562 | |||
563 | MessageBoxResult result = MessageBoxResult.None; | ||
564 | WixBA.View.Dispatcher.Invoke((Action)delegate() | ||
565 | { | ||
566 | result = MessageBox.Show(WixBA.View, e.ErrorMessage, "WiX Toolset", msgbox, MessageBoxImage.Error); | ||
567 | } | ||
568 | ); | ||
569 | |||
570 | // If there was a match from the UI hint to the msgbox value, use the result from the | ||
571 | // message box. Otherwise, we'll ignore it and return the default to Burn. | ||
572 | if ((e.UIHint & 0xF) == (int)msgbox) | ||
573 | { | ||
574 | e.Result = (Result)result; | ||
575 | } | ||
576 | } | ||
577 | } | ||
578 | } | ||
579 | } | ||
580 | else // canceled, so always return cancel. | ||
581 | { | ||
582 | e.Result = Result.Cancel; | ||
583 | } | ||
584 | } | ||
585 | } | ||
586 | |||
587 | private void ResolveSource(object sender, ResolveSourceEventArgs e) | ||
588 | { | ||
589 | int retries = 0; | ||
590 | |||
591 | this.downloadRetries.TryGetValue(e.PackageOrContainerId, out retries); | ||
592 | this.downloadRetries[e.PackageOrContainerId] = retries + 1; | ||
593 | |||
594 | e.Action = retries < 3 && !String.IsNullOrEmpty(e.DownloadSource) ? BOOTSTRAPPER_RESOLVESOURCE_ACTION.Download : BOOTSTRAPPER_RESOLVESOURCE_ACTION.None; | ||
595 | } | ||
596 | |||
597 | private void ApplyComplete(object sender, ApplyCompleteEventArgs e) | ||
598 | { | ||
599 | WixBA.Model.Result = e.Status; // remember the final result of the apply. | ||
600 | |||
601 | // Set the state to applied or failed unless the state has already been set back to the preapply state | ||
602 | // which means we need to show the UI as it was before the apply started. | ||
603 | if (this.root.InstallState != this.root.PreApplyState) | ||
604 | { | ||
605 | this.root.InstallState = Hresult.Succeeded(e.Status) ? InstallationState.Applied : InstallationState.Failed; | ||
606 | } | ||
607 | |||
608 | // If we're not in Full UI mode, we need to alert the dispatcher to stop and close the window for passive. | ||
609 | if (Bootstrapper.Display.Full != WixBA.Model.Command.Display) | ||
610 | { | ||
611 | // If its passive, send a message to the window to close. | ||
612 | if (Bootstrapper.Display.Passive == WixBA.Model.Command.Display) | ||
613 | { | ||
614 | WixBA.Model.Engine.Log(LogLevel.Verbose, "Automatically closing the window for non-interactive install"); | ||
615 | WixBA.Dispatcher.BeginInvoke(new Action(WixBA.View.Close)); | ||
616 | } | ||
617 | else | ||
618 | { | ||
619 | WixBA.Dispatcher.InvokeShutdown(); | ||
620 | } | ||
621 | return; | ||
622 | } | ||
623 | else if (Hresult.Succeeded(e.Status) && LaunchAction.UpdateReplace == WixBA.Model.PlannedAction) // if we successfully applied an update close the window since the new Bundle should be running now. | ||
624 | { | ||
625 | WixBA.Model.Engine.Log(LogLevel.Verbose, "Automatically closing the window since update successful."); | ||
626 | WixBA.Dispatcher.BeginInvoke(new Action(WixBA.View.Close)); | ||
627 | return; | ||
628 | } | ||
629 | else if (root.AutoClose) | ||
630 | { | ||
631 | // Automatically closing since the user clicked the X button. | ||
632 | WixBA.Dispatcher.BeginInvoke(new Action(WixBA.View.Close)); | ||
633 | return; | ||
634 | } | ||
635 | |||
636 | // Force all commands to reevaluate CanExecute. | ||
637 | // InvalidateRequerySuggested must be run on the UI thread. | ||
638 | root.Dispatcher.Invoke(new Action(CommandManager.InvalidateRequerySuggested)); | ||
639 | } | ||
640 | |||
641 | private void ParseCommandLine() | ||
642 | { | ||
643 | // Get array of arguments based on the system parsing algorithm. | ||
644 | string[] args = WixBA.Model.Command.GetCommandLineArgs(); | ||
645 | for (int i = 0; i < args.Length; ++i) | ||
646 | { | ||
647 | if (args[i].StartsWith("InstallFolder=", StringComparison.InvariantCultureIgnoreCase)) | ||
648 | { | ||
649 | // Allow relative directory paths. Also validates. | ||
650 | string[] param = args[i].Split(new char[] {'='}, 2); | ||
651 | this.root.InstallDirectory = IO.Path.Combine(Environment.CurrentDirectory, param[1]); | ||
652 | } | ||
653 | } | ||
654 | } | ||
655 | |||
656 | private void AddPackageTelemetry(string prefix, string id, double time, int result) | ||
657 | { | ||
658 | lock (this) | ||
659 | { | ||
660 | string key = String.Format("{0}Time_{1}", prefix, id); | ||
661 | string value = time.ToString(); | ||
662 | WixBA.Model.Telemetry.Add(new KeyValuePair<string, string>(key, value)); | ||
663 | |||
664 | key = String.Format("{0}Result_{1}", prefix, id); | ||
665 | value = String.Concat("0x", result.ToString("x")); | ||
666 | WixBA.Model.Telemetry.Add(new KeyValuePair<string, string>(key, value)); | ||
667 | } | ||
668 | } | ||
669 | } | ||
670 | } | ||