1 // =========================================================================
2 //
3 // INTEL CONFIDENTIAL
4 // Copyright 2005 - 2015
5 // Intel Corporation All Rights Reserved.
6 //
7 // =========================================================================
8 // The source code contained or described herein and all documents
9 // related to the source code ("Material") are owned by Intel Corporation
10 // or its suppliers or licensors. Title to the Material remains with
11 // Intel Corporation or its suppliers and licensors. The Material contains
12 // trade secrets and proprietary and confidential information of Intel or
13 // its suppliers and licensors. The Material is protected by worldwide
14 // copyright and trade secret laws and treaty provisions. No part of the
15 // Material may be used, copied, reproduced, modified, published, uploaded,
16 // posted, transmitted, distributed, or disclosed in any way without Intel’s
17 // prior express written permission.
18 //
19 // No license under any patent, copyright, trade secret or other intellectual
20 // property right is granted to or conferred upon you by disclosure or
21 // delivery of the Materials, either expressly, by implication, inducement,
22 // estoppel or otherwise. Any license under such intellectual property rights
23 // must be express and approved by Intel in writing.
24 // ==========================================================================
25
26 using System;
27 using System.ComponentModel;
28 using System.Windows;
29 using System.Windows.Threading;
30 using Intel.Deployment.Bootstrapper.Models;
31 using Intel.Deployment.Bootstrapper.Views;
32 using Microsoft.Tools.WindowsInstallerXml.Bootstrapper;
33
34 namespace Intel.Deployment.Bootstrapper.ViewModels
35 {
36 public class MainViewModel : MainViewModelBase
37 {
38 #region Constructor
39 public MainViewModel()
40 {
41 this.Window = new MainView(this);
42 this.Dispatcher = Dispatcher.CurrentDispatcher;
43
44 this.Wizard = new Wizard();
45 this.ProgressMutex = new object();
46 this.CancelSetup = false;
47 }
48 #endregion
49
50 #region Fields
51 internal object ProgressMutex;
52 private string _BannerTitle;
53 private Wizard _Wizard;
54
55 /// <summary>
56 /// Gets or sets a value indicating whether the user is attempting
57 /// to cancel the setup from the bootstrapper application UI.
58 /// </summary>
59 internal volatile bool CancelSetup;
60
61 /// <summary>
62 /// Gets and sets whether the view model considers this install to be a downgrade.
63 /// </summary>
64 private volatile bool Downgrade;
65
66 private DetectModel _DetectModel;
67 private PlanModel _PlanModel;
68 private ApplyModel _ApplyModel;
69 private ExtractModel _ExtractModel;
70 #endregion
71
72 #region Properties
73 public Dispatcher Dispatcher { get; set; }
74
75 public Command Command
76 { get { return BootstrapperApp.Current.Command; } }
77 public Engine Engine
78 { get { return BootstrapperApp.Current.Engine; } }
79
80 public string BannerTitle
81 {
82 get { return this._BannerTitle; }
83 set
84 {
85 if (this._BannerTitle != value)
86 {
87 this._BannerTitle = value;
88 base.OnPropertyChanged("BannerTitle");
89 }
90 }
91 }
92
93 public string InstallTypeMessage
94 {
95 get
96 {
97 switch (Settings.Current.RunMode)
98 {
99 case RunMode.Repair:
100 return Resource.IntroView_RepairMessage;
101 case RunMode.Uninstall:
102 return Resource.IntroView_UninstallMessage;
103 case RunMode.Install:
104 default:
105 return Resource.IntroView_InstallMessage;
106 }
107 }
108 }
109
110 public bool IsUninstall => Settings.Current.RunMode == RunMode.Uninstall;
111
112 public bool IsNotUninstall => !IsUninstall;
113
114 public Wizard Wizard
115 {
116 get { return this._Wizard; }
117 set
118 {
119 if (this._Wizard != value)
120 {
121 this._Wizard = value;
122 base.OnPropertyChanged("Wizard");
123 }
124 }
125 }
126
127 public NoInstallView NoInstallView { get; set; }
128
129 public virtual DetectModel DetectModel
130 { get { return this._DetectModel ?? (this._DetectModel = new DetectModel()); } }
131 public virtual PlanModel PlanModel
132 { get { return this._PlanModel ?? (this._PlanModel = new PlanModel()); } }
133 public virtual ApplyModel ApplyModel
134 { get { return this._ApplyModel ?? (this._ApplyModel = new ApplyModel()); } }
135 internal virtual ExtractModel ExtractModel
136 { get { return this._ExtractModel ?? (this._ExtractModel = new ExtractModel()); } }
137 #endregion
138
139 #region Methods
140 public override void Run()
141 {
142 this.BannerTitle = this.Engine.StringVariables["WixBundleNameBanner"];
143
144 InitializeModels();
145
146 switch (Settings.Current.RunMode)
147 {
148 case RunMode.Uninstall:
149 this.Wizard.AddView(new DetectProgressView(this));
150 if (Settings.Current.Display == Display.Full)
151 {
152 this.Wizard.AddView(new IntroView(this));
153 }
154 this.Wizard.AddView(new ApplyProgressView(this));
155
156 this.DetectState();
157 break;
158 case RunMode.Install:
159 case RunMode.Modify:
160 case RunMode.Repair:
161 this.Wizard.AddView(new DetectProgressView(this));
162 if (Settings.Current.Display == Display.Full)
163 {
164 this.Wizard.AddView(new IntroView(this));
165 this.Wizard.AddView(new LicenseView(this));
166 #if NDABUILD
167 this.Wizard.AddView(new ReadmeViewNda(this));
168 #else
169 this.Wizard.AddView(new ReadmeViewPublic(this));
170 #endif
171 }
172 this.Wizard.AddView(new ApplyProgressView(this));
173
174 this.DetectState();
175 break;
176 case RunMode.Help:
177 this.Wizard.AddView(new HelpView(this));
178 break;
179 case RunMode.DisplayVersion: //TODO IMPORTANT: This.
180 break;
181 case RunMode.NoInstall:
182 this.InitializeNoInstall();
183 this.NoInstallView.Show();
184 break;
185 case RunMode.Extract:
186 this.Wizard.AddView(new ExtractProgressView(this));
187 this.ExtractModel.BeginExtract(Settings.Current.TargetPath);
188 break;
189 case RunMode.ExtractMup:
190 this.Wizard.AddView(new ExtractProgressView(this));
191 this.ExtractModel.BeginExtractMup(Settings.Current.TargetPath);
192 break;
193 default:
194 break;
195 }
196
197 if (Settings.Current.Display > Display.None)
198 {
199 this.Wizard.NextView();
200 this.Window.Show();
201
202 if (Settings.Current.RunMode == RunMode.NoInstall)
203 {
204 this.NoInstallView.Focus();
205 }
206 }
207
208 Dispatcher.Run();
209 }
210
211 public override void RunToError(int error)
212 {
213 this.BannerTitle = this.Engine.StringVariables["WixBundleNameBanner"];
214
215 ErrorView errView = new ErrorView(this, error);
216
217 this.Wizard.Interrupt(errView);
218
219 this.Window.Show();
220
221 Dispatcher.Run();
222 }
223
224 public override void RunToErrorDispatched(int error)
225 {
226 Dispatcher.Invoke(() => RunToError(error));
227 }
228
229 private void InitializeModels()
230 {
231 BootstrapperApp.Current.Error += this.BootstrapperApplication_Error;
232
233 this.DetectModel.DowngradeDetected += this.DetectModel_DowngradeDetected;
234 this.DetectModel.DetectComplete += this.DetectModel_DetectComplete;
235
236 this.PlanModel.PlanComplete += this.PlanModel_PlanComplete;
237
238 this.ApplyModel.ApplyComplete += this.ApplyModel_ApplyComplete;
239
240 this.ExtractModel.ExtractComplete += this.ExtractModel_ExtractComplete;
241 }
242
243 private void InitializeNoInstall()
244 {
245 //Create the noinstall navigation window
246 this.NoInstallView = new NoInstallView(this);
247
248 //Add each view
249 this.Wizard.AddView(new HelpView(this));
250 this.Wizard.AddView(new DetectProgressView(this));
251 this.Wizard.AddView(new DowngradePromptView(this));
252 this.Wizard.AddView(new IntroView(this));
253 this.Wizard.AddView(new LicenseView(this));
254 this.Wizard.AddView(new ReadmeViewNda(this));
255 this.Wizard.AddView(new ReadmeViewPublic(this));
256 this.Wizard.AddView(new ApplyProgressView(this));
257 this.Wizard.AddView(new FinishView(this, RunMode.Install) { NoInstallViewName = "Install Completion" });
258 this.Wizard.AddView(new RestartView(this));
259 this.Wizard.AddView(new ExtractProgressView(this));
260 this.Wizard.AddView(new FinishView(this, RunMode.Extract) { NoInstallViewName = "Extract Completion" });
261 this.Wizard.AddView(new CancelSetupView(this));
262
263 //Add the error view with each error
264 this.Wizard.AddView(new ErrorView(this, 1613));
265 this.Wizard.AddView(new ErrorView(this, Error.RebootPending));
266 this.Wizard.AddView(new ErrorView(this, Error.InvalidOperatingSystem));
267 this.Wizard.AddView(new ErrorView(this, Error.NoDeviceMatch));
268 this.Wizard.AddView(new ErrorView(this, Error.Extract));
269 this.Wizard.AddView(new ErrorView(this, Error.BadPath));
270 this.Wizard.AddView(new ErrorView(this, Win32Error.AccessDenied));
271 this.Wizard.AddView(new ErrorView(this, Win32Error.InvalidCommandLine));
272 this.Wizard.AddView(new ErrorView(this, int.MaxValue)); //An unknown error.
273 }
274
275 #region Methods - Event Handlers
276 private void DetectModel_DowngradeDetected(object sender, EventArgs e)
277 {
278 this.Window.Invoke(() => { this.OnDowngradeDetected(e); });
279 }
280 private void DetectModel_DetectComplete(object sender, SuccessEventArgs e)
281 {
282 this.Window.Invoke(() => { this.OnDetectComplete(e); });
283 }
284 private void PlanModel_PlanComplete(object sender, SuccessEventArgs e)
285 {
286 this.Window.BeginInvoke(() => { this.OnPlanComplete(e); });
287 }
288 private void ApplyModel_ApplyComplete(object sender, ApplyCompleteEventArgs e)
289 {
290 this.Window.BeginInvoke(() => {
291 this.OnApplyComplete(e);
292 //force UI refresh to avoid freezing on the end of installation,
293 //it happens in some 4k screens
294 this.Window.WindowState = WindowState.Minimized;
295 this.Window.WindowState = WindowState.Normal;
296 });
297 }
298 private void ExtractModel_ExtractComplete(object sender, SuccessEventArgs e)
299 {
300 this.Window.BeginInvoke(() => { this.OnExtractComplete(e); });
301 }
302 #endregion
303
304 #region Detect
305 public void DetectState()
306 {
307 this.CancelSetup = false;
308 this.DetectModel.DetectState();
309 }
310
311 protected virtual void OnDowngradeDetected(EventArgs e)
312 {
313 this.Downgrade = true;
314 }
315
316 protected virtual void OnDetectComplete(SuccessEventArgs e)
317 {
318 if (e.Success)
319 {
320 //If it's installing a downgrade that is not forced...
321 if (Settings.Current.RunMode == RunMode.Install && this.Downgrade && !Settings.Current.ForceDowngrade)
322 {
323 //...then indicate a conflict.
324 this.OnDowngradeConflictDetected();
325 }
326 //..otherwise there's no downgrade issue, so continue as normal.
327 else
328 {
329 if (Settings.Current.Display > Display.None)
330 {
331 this.Wizard.NextView();
332 }
333
334 //If the UI is not interactive, begin planning.
335 if (Settings.Current.Display < Display.Full)
336 {
337 switch (Settings.Current.RunMode)
338 {
339 case RunMode.Install:
340 case RunMode.Uninstall:
341 case RunMode.Repair:
342 this.Plan(Settings.Current.RunMode);
343 break;
344 default:
345 //No other RunMode should ever be able get to this point, so the below code should never run.
346 Log.WriteException(new Exception(" Error, invalid value: Settings.Current.RunMode = " + Settings.Current.RunMode));
347 break;
348 }
349 }
350 }
351
352 }
353 else
354 {
355 this.OnError(e.ErrorCode);
356 }
357 }
358
359 /// <summary>
360 /// Occurs when installing a downgrade that is not forced.
361 /// </summary>
362 protected virtual void OnDowngradeConflictDetected()
363 {
364 //If the UI is in interactive mode...
365 if (Settings.Current.Display == Display.Full)
366 {
367 //...then go to the downgrade prompt view.
368 this.Wizard.Interrupt(new DowngradePromptView(this));
369 }
370 else
371 {
372 //...otherwise, log an exception and quit.
373 Log.WriteException(new Exception("Downgrade detected in non-interactive mode, and is not forced.", new Win32Exception(Error.DowngradeRefused)));
374 this.OnError(Error.DowngradeRefused);
375 }
376 }
377 #endregion
378
379 #region Plan
380 private void Plan(RunMode runMode)
381 {
382 this.CancelSetup = false;
383
384 this.PlanModel.Plan(runMode.ToLaunchAction());
385 }
386
387 protected virtual void OnPlanComplete(SuccessEventArgs e)
388 {
389 if (e.Success)
390 {
391 //TODO: Put common stuff into a common library, such as the string "IIF_MSI_SWITCHES".
392 // And have that library read and write the switches to and from a Dictionary<string, string>.
393 // Intel.Tools namespace
394 this.Engine.StringVariables["IIF_MSI_SWITCHES"] = Settings.Current.ToMsiPropertyString();
395 this.ApplyModel.Apply(this.Window);
396 }
397 else
398 {
399 this.OnError(e.ErrorCode);
400 }
401 }
402 #endregion
403
404 internal int? OverrideError = null;
405
406 #region Apply
407 /// <summary>
408 ///
409 /// </summary>
410 /// <param name="e"></param>
411 /// <remarks>
412 /// e.Status possible values are ERROR_SUCCESS (0), ERROR_INSTALL_USEREXIT (1602), and ERROR_INSTALL_FAILURE (1603).
413 /// </remarks>
414 protected virtual void OnApplyComplete(ApplyCompleteEventArgs e)
415 {
(1) Event missing_lock: |
Accessing "this.OverrideError" without holding lock "MainViewModel.ProgressMutex". Elsewhere, "MainViewModel.OverrideError" is written to with "MainViewModel.ProgressMutex" held 2 out of 2 times. |
Also see events: |
[lock_acquire][example_access][lock_acquire][example_access] |
416 int error = this.OverrideError ?? (e.Status & 0x0000FFFF);
417
418 //TODO: if the user specifies /norestart, will e.Restart be affected here???
419 BootstrapperApp.Current.Result = error;
420
421 if (Settings.Current.Display == Display.Full)
422 {
423 if (error == Win32Error.Success)
424 {
425 this.Wizard.NextView(e.Restart == ApplyRestart.None ? new FinishView(this) : new RestartView(this) as View);
426 }
427 else
428 {
429 this.OnError(error);
430 }
431 }
432 else
433 {
434 //TODO: How do I handle restarts from embedded runs???
435 if (e.Restart != ApplyRestart.None)
436 {
437 if (Settings.Current.Restart == Restart.Always || Settings.Current.Restart == Restart.Automatic)
438 {
439 BootstrapperApp.Current.Result = Win32Error.RebootInitiated;
440 }
441 else
442 {
443 BootstrapperApp.Current.Result = Win32Error.RebootRequired;
444 }
445 }
446
447 if (Settings.Current.Display <= Display.None)
448 {
449 //Stop the dispatcher, allowing code to resume from 'Dispatcher.Run();'
450 this.Dispatcher.InvokeShutdown();
451 }
452 else if (Settings.Current.Display == Display.Passive)
453 {
454 //Close the window, stopping the dispatcher, allowing code to resume from 'Dispatcher.Run();'
455 this.Window.Close();
456 }
457 }
458 }
459 #endregion
460
461 #region Extract
462 protected virtual void OnExtractComplete(SuccessEventArgs e)
463 {
464 switch (Settings.Current.Display)
465 {
466 case Display.None:
467 this.Dispatcher.InvokeShutdown();
468 break;
469 case Display.Passive:
470 this.Window.Close();
471 break;
472 case Display.Full:
473 if (e.ErrorCode == Win32Error.Success)
474 {
475 this.Wizard.AddView(new FinishView(this));
476 this.Wizard.NextView();
477 }
478 else
479 {
480 this.OnError(e.ErrorCode);
481 }
482 break;
483 default:
484 break;
485 }
486 }
487 #endregion
488
489 #region Error
490 protected virtual void OnError(int errorCode)
491 {
492 BootstrapperApp.Current.Result = errorCode;
493
494 switch (Settings.Current.Display)
495 {
496 case Display.None:
497 this.Dispatcher.InvokeShutdown();
498 break;
499 case Display.Passive:
500 this.Window.Close();
501 break;
502 case Display.Full:
503 this.Wizard.Interrupt(new ErrorView(this, errorCode));
504 break;
505 default:
506 break;
507 }
508 }
509
510 private void BootstrapperApplication_Error(object sender, ErrorEventArgs e)
511 {
512 Log.WriteLine("BootstrapperApplication_Error");
(2) Event lock_acquire: |
Example 1: Acquiring lock "MainViewModel.ProgressMutex". |
(4) Event lock_acquire: |
Example 2: Acquiring lock "MainViewModel.ProgressMutex". |
Also see events: |
[missing_lock][example_access][example_access] |
513 lock (this.ProgressMutex)
514 {
515
516 //If the user cancelled the setup...
517 if (this.IsCancelled(e.ErrorCode))
518 {
519 Log.WriteLine("Cancelled");
520 //...then return Cancel.
521 e.Result = Result.Cancel;
522 this.OverrideError = BootstrapperApp.Current.Result = Win32Error.InstallUserExit;
523 }
524 else
525 {
526 if (Settings.Current.Display == Display.Full)
527 {
528 int error;
529
530 //If it's an error passed back from a custom action...
531 if (IsCustomActionError(e, out error))
532 {
533 //...then set the override error.
534 this.OverrideError = BootstrapperApp.Current.Result = error;
535 }
536 //Else if some input is required...
537 else if (ModalInputView.IsRequired(e))
538 {
539 //...then get it.
540
541 //Create a modal input view on the UI thread...
542 ModalInputView modalInput = null;
543
544 this.Window.Invoke(() =>
545 {
546 modalInput = new ModalInputView(this, e);
547 this.Wizard.Interrupt(modalInput);
548 });
549
550 //...and get its result.
551 var result = modalInput.GetResult();
552 if (result > Result.None)
553 {
554 e.Result = result;
555 }
556
557 //Go back to the previous view.
558 //Note: So far, this method only supports being called during
559 // the Engine.Apply() step. If it gets use elsewhere in the
560 // future, then we may possibly need to also implement NextView().
561 this.Window.Invoke(() => { this.Wizard.PreviousView(); });
562 }
563 //else don't change the result.
564 }
565 }
566 }
567 }
568
569 private bool IsCustomActionError(ErrorEventArgs e, out int error)
570 {
571 //TODO: Generalize the errors...Chipset error needs to go in Chipset project.
572
573 //Initialize error to zero.
574 error = 0;
575 //Check if it's an error passed back from a custom action.
576 return e.Data.Count == 2 && e.Data[0] == "IIF_CustomActionError" && int.TryParse(e.Data[1], out error);
577 }
578
579 private bool IsCancelled(int error)
580 {
581 return
582
583 //The user cancelled from the bootstrapper UI.
584 this.CancelSetup ||
585
586 //The user cancelled from the msi.
587 error == Win32Error.InstallUserExit ||
588
589 //The user cancelled in some other way.
590 error == Win32Error.Cancelled;
591 }
592 #endregion
593
594 #region Do Commands
595 internal void DoBackCommand(object param)
596 {
597 this.Wizard.PreviousView();
598 }
599
600 internal void DoNextCommand(object param)
601 {
602 this.Wizard.NextView();
603 }
604
605 internal void DoNextApplyCommand(object param)
606 {
607 this.Wizard.NextView();
608 this.Plan(Settings.Current.RunMode);
609 }
610
611 internal void DoCancelSetupCommand(object param)
612 {
613 this.Wizard.Interrupt(new CancelSetupView(this));
614 }
615
616 internal void DoCancelSetupCloseCommand(object param)
617 {
618 BootstrapperApp.Current.Result = Win32Error.InstallUserExit;
619 this.Window.Close();
620 }
621
622 internal void DoCloseCommand(object param)
623 {
624 this.Window.Close();
625 }
626
627 internal void DoBackCommand_NoInstall(object param)
628 {
629 if (this.Wizard.HasPreviousView)
630 {
631 this.Wizard.PreviousView();
632 }
633 }
634
635 internal void DoNextCommand_NoInstall(object param)
636 {
637 if (this.Wizard.HasNextView)
638 {
639 this.Wizard.NextView();
640 }
641 else
642 {
643 this.NoInstallView.Close();
644 this.Window.Close();
645 }
646 }
647
648 internal void DoCloseCommand_NoInstall(object param)
649 {
650 this.NoInstallView.Close();
651 this.Window.Close();
652 }
653 #endregion
654
655 #endregion
656 }
657 }
658