How to perform Wix Upgrade with custom bootstrapper - c#

I want to enable Upgrade in the 2nd version of our WiX custom BA installer. In my Product.wxs, Product ID is set to *, version is set to 2.0.0, and upgrade code remains the same as the 1st version's. To detect Upgrade, I used DetectRelatedBundle event handler in the Boostrapper.
The MajorUpgrade tag in the MSI looks like this:
<MajorUpgrade AllowDowngrades="no" DowngradeErrorMessage="A newer version of [ProductName] is already installed." AllowSameVersionUpgrades="no" Schedule="afterInstallInitialize" />
In my installer UI, I have an Install button. When this button is clicked during Upgrade scenario, I call PlanAction and pass LaunchAction.Install. But once installation starts, it shows another instance of BA, which I believe is the old package called by my current BA to uninstall the old version. In order to hide the new BA instance and just show installation progress, I made these changes in my Bootstrapper:
Bootstrapper.cs:
protected override void Run()
{
BootstrapperDispatcher = Dispatcher.CurrentDispatcher;
try
{
_model = new BootstrapperApplicationModel(this);
var uninstall = new UpgradeUninstall(_model);
if (uninstall.IsUpgradeUninstallation())
{
uninstall.PerformSequence();
}
else
{
//show install or uninstall main UI
this.WireUpEventHandlers();
_model.BootstrapperApplication.Engine.Detect();
Dispatcher.Run();
}
}
}
UpgradeUninstall.cs:
public class UpgradeUninstall
{
private BootstrapperApplicationModel _bootStrapperModel;
public UpgradeUninstall(BootstrapperApplicationModel model)
{
_bootStrapperModel = model;
}
public void Perform()
{
this.WireUpEventHandlers();
_bootStrapperModel.BootstrapperApplication.Engine.Detect();
}
public bool IsUpgradeUninstallation()
{
var action = _bootStrapperModel.BootstrapperApplication.Command.Action;
var display = _bootStrapperModel.BootstrapperApplication.Command.Display;
return action == LaunchAction.Uninstall && (display == Display.None || display == Display.Embedded);
}
private void WireUpEventHandlers()
{
_bootStrapperModel.BootstrapperApplication.DetectComplete += OnDetectComplete;
_bootStrapperModel.BootstrapperApplication.PlanComplete += OnPlanComplete;
_bootStrapperModel.BootstrapperApplication.ApplyComplete += OnApplyComplete;
}
private void OnDetectComplete(object sender, DetectCompleteEventArgs e)
{
this._bootStrapperModel.PlanAction(LaunchAction.Uninstall);
}
private void OnPlanComplete(object sender, PlanCompleteEventArgs e)
{
this._bootStrapperModel.ApplyAction();
}
private void OnApplyComplete(object sender, ApplyCompleteEventArgs e)
{
BootstrapperDispatcher.InvokeShutdown();
}
}
Question 1) How will I let my main BA instance (the one doing installation) know that uninstallation of old package has completed? What's happening now is that it was able to successfully uninstall the old package, but no installation of the new version is being performed.
Question 2) Is my understanding of WiX upgrade correct? :)

What is happening is your old BA is getting called in silent mode with the uninstall switch. I can see your code does have some of the plumbing to handle a command line uninstall although I can't see where you're calling Engine.Plan(LaunchAction.Uninstall).
Q1) I don't believe you have to do anything in particular to let your original BA know you're finished. You just need to exit the install in the normal way.
Q2) Yes I think you're almost there. I suggest you download the WIX source code off git to see how it implements its custom BA. Specifically look at the DetectComplete code:
private void DetectComplete(object sender, DetectCompleteEventArgs e)
{
// Parse the command line string before any planning.
this.ParseCommandLine();
this.root.InstallState = InstallationState.Waiting;
if (LaunchAction.Uninstall == WixBA.Model.Command.Action)
{
WixBA.Model.Engine.Log(LogLevel.Verbose, "Invoking automatic plan for uninstall");
WixBA.Plan(LaunchAction.Uninstall);
}
You can see it is checking for the uninstall command line option and immediately kicking off an uninstall.

Related

Querying if Office 365 is installed on button event, then executing code based on return value (WPF/C# app)

Background: I'm creating an app that allows our other techs to quickly install a dozen or so programs within one simple GUI. I am embedding the installers of those apps within my main program. (not that it matters)
I created a separate class file (IsOfficeInstalled.cs) that gets called when a user clicks on a button to install Office. Here's the code in that file:
class IsOfficeInstalled
{
public static bool check()
{
RegistryKey key = Registry.LocalMachine.OpenSubKey(#"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\Winword.exe");
if (key != null)
{
key.Close();
}
return key != null; }}
In my MainWindow.xaml file, I am trying to call the "IsOfficeInstalled" function and perform one of two actions based on the return value. This is where I am confused. The code in my button event looks like the following:
private void btn_InstallOffice_Click(object sender, RoutedEventArgs e)
if (IsOfficeInstalled.check())
{
//If yes, then perform some code
}
else
{
//If no, then perform some different code
}
Where I need help is, getting the true/false result back from the "IsOfficeInstalled" function so my code in the "btn_InstallOffice_Click" function will know which condition to execute.
I'm not sure if I'm understanding your question. Are you looking for a simple if statement?
private void btn_InstallOffice_Click(object sender, RoutedEventArgs e)
{
if (IsOfficeInstalled.check())
{
//If yes, then perform some code
}
else
{
//If no, then perform some different code
}
}

How to open a tool window of a visual studio extension programmatically?

So I've got two tool windows in my visual studio extension (package) and I'd like to open up the second window via a button on the first window.
I expected this to be explained here: "How to: Open a Tool Window Programmatically", but it wasn't.
You should use either Package.FindToolWindow or IVsUIShell.FindToolWindow to find or create a tool window.
If used from your own package (or if you have a reference to the package, just put it there instead of this):
private void OpenFromPackage()
{
ToolWindowPane window = this.FindToolWindow(typeof(MyToolWindow), 0, true); // True means: crate if not found. 0 means there is only 1 instance of this tool window
if (null == window || null == window.Frame)
throw new NotSupportedException("MyToolWindow not found");
IVsWindowFrame windowFrame = (IVsWindowFrame)window.Frame;
ErrorHandler.ThrowOnFailure(windowFrame.Show());
}
If you can't do it from your package, or don't have a reference to it, use IVSUIShell:
private void OpenWithIVsUIShell()
{
IVsUIShell vsUIShell = (IVsUIShell)Package.GetGlobalService(typeof(SVsUIShell));
Guid guid = typeof(MyToolWindow).GUID;
IVsWindowFrame windowFrame;
int result = vsUIShell.FindToolWindow((uint)__VSFINDTOOLWIN.FTW_fFindFirst, ref guid, out windowFrame); // Find MyToolWindow
if (result != VSConstants.S_OK)
result = vsUIShell.FindToolWindow((uint)__VSFINDTOOLWIN.FTW_fForceCreate, ref guid, out windowFrame); // Crate MyToolWindow if not found
if (result == VSConstants.S_OK) // Show MyToolWindow
ErrorHandler.ThrowOnFailure(windowFrame.Show());
}
When you create a new package with toolwindow support, you get a single toolwindow and a command that displays it. This command is handled in the package class with the ShowToolWindow method.
Examining that, you'll see that the base package object has a FindToolWindow method that you can use to find (and create if needed) any toolwindow you have implemented in your package. That FindToolWindow method is just a nice wrapper around the IVsUIShell.FindToolWindow method, which is what ultimately gets invoked when displaying any toolwindow.
So instead of using the old EnvDTE automation interface, I would recommend using the lower level services built into the actual package object.
Here's how I solved it, the following code is the code-behind method of the button on the first window:
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
var dte = Package.GetGlobalService(typeof(DTE)) as DTE;
if (dte == null) return;
var window = dte.Windows.Item("{WindowGUID}");
window.Visible = true;
}
You should find the "WindowGUID" in the Guids class and above the class of the ToolWindow.

Strange WPF error

I have some code that works intermittently and I can't understand why (worked perfectly until today morning when windows automatically installed some updates, but none related to .NET 4 - version used in my project).
My password box ...
<PasswordBox x:Name="TboxPassword" Grid.Row="1" Grid.Column="0"
controls:TextboxHelper.Watermark="Password ..."
controls:TextboxHelper.ClearTextButton="True"
Margin="10, 10, 0, 0">
<i:Interaction.Behaviors>
<misc:PasswordBoxBehavior Password="{Binding Path=Password, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, NotifyOnValidationError=True}"/>
</i:Interaction.Behaviors>
</PasswordBox>
My behavior:
public class PasswordBoxBehavior : Behavior<PasswordBox>
{
#region Fields
private readonly object _tryToExecuteActionSyncObject = new object();
private bool _isUpdating;
#endregion
#region Properties
public string Password
{
get { return (string)GetValue(PasswordProperty); }
set { SetValue(PasswordProperty, value); }
}
public static readonly DependencyProperty PasswordProperty =
DependencyProperty.Register("Password", typeof(string), typeof(PasswordBoxBehavior),
new PropertyMetadata(string.Empty, OnPasswordPropertyChanged));
#endregion
#region Methods
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.PasswordChanged += OnAssociatedObjectPasswordChanged;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.PasswordChanged -= OnAssociatedObjectPasswordChanged;
}
private void OnAssociatedObjectPasswordChanged(object sender, RoutedEventArgs e)
{
TryToExecuteAction(() => Password = AssociatedObject == null
? string.Empty
: AssociatedObject.Password);
}
private static void OnPasswordPropertyChanged
(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
PasswordBoxBehavior passwordBoxBehavior;
if (sender == null
|| (passwordBoxBehavior = sender as PasswordBoxBehavior) == null
|| passwordBoxBehavior.AssociatedObject == null)
{
return;
}
passwordBoxBehavior.TryToExecuteAction
(() => passwordBoxBehavior.AssociatedObject.Password =
(e.NewValue == null
? string.Empty
: (string) e.NewValue));
}
private void TryToExecuteAction(Action actionToExecute)
{
bool continueExecution;
lock (_tryToExecuteActionSyncObject)
{
continueExecution = _isUpdating == false;
_isUpdating = true;
}
if (continueExecution == false)
{
return;
}
try
{
if (actionToExecute != null)
{
actionToExecute();
}
}
finally
{
lock (_tryToExecuteActionSyncObject)
{
_isUpdating = false;
}
}
}
#endregion
}
I get 0 (zero) compilation errors. When running the application, 90% of the time I'm getting a runtime exception stating that:
{"Cannot add instance of type 'PasswordBoxBehavior' to a collection of type 'BehaviorCollection'. Only items of type 'T' are allowed."}
Debugger stops at the tag Interaction.Behaviors
Please keep in mind that I never received this error until today. Now I receive it even after I revert everything I done today.
Please advise .. :D
PS: I just commented out all the code from inside the behavior. Also removed the Password binding. Still doesn't work :(
PPS: If I close Visual Studio (2012), delete my bin folder, open VS, open project, rebuild all, the application WORKS until the first change to the code.
I know this question is very old. But I will post the answer in case someone face it again.
I just came across the same issue and I found a solution, I noticed that you are using behaviors in your code. Just make sure that your are referencing the right version of Blend SDK Windows.Interactivity
In my case the problem was that I installed the NugetPackage for Blend SDK for WPF 4.5 only on the main WPF project, and I forgot to install it on the other WPF projects in the solution that uses behaviors.
I solved the problem by installing the same NugetPackage on the project containing the code causing the problem.
Hope this helps you!
Blend SDK Windows.Interactivity has been abandoned, and the file System.Windows.Interactivity.dll has been removed from GAC. Use Microsoft.Xaml.Behaviors.Wpf instead, you can install it from Nuget.org.
If you switch the "interactive library" from Blend SDK Windows.Interactivity(System.Windows.Interactivity) to Microsoft.Xaml.Behaviors.Wpf(Microsoft.Xaml.Behaviors)
You can:
Remove System.Windows.Interactivity reference from project.
Close/Unload this solution/project, and open the solution/project directory.
Delete belows folders :.vs, bin, obj.
Reopen/Reload the solution/project.
Replace all namespace of System.Windows.Interactivity to Microsoft.Xaml.Behaviors
Build the solution/project.
Then it will be fine.

Change App language at RunTime on-the-fly

I'm currently developing a metro app in which the user can change current language at runtime and all the custom controls that are loaded must update their text regarding to the new language. Problem is that when I change the language using the following code, the app language changes but it will only update text when I restart my app because the pages and controls that are already rendered are cached.
LocalizationManager.UICulture = new System.Globalization.CultureInfo((string)((ComboBoxItem)e.AddedItems[0]).Tag);
Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = ((ComboBoxItem)e.AddedItems[0]).Tag as String;
What should I do to force updating text of all custom controls at runtime without restarting my app?
Use this:
var NewLanguage = (string)((ComboBoxItem)e.AddedItems[0]).Tag;
Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = NewLanguage;
Windows.ApplicationModel.Resources.Core.ResourceContext.GetForViewIndependentUse().Reset();
//Windows.ApplicationModel.Resources.Core.ResourceContext.GetForCurrentView().Reset();
Windows.ApplicationModel.Resources.Core.ResourceManager.Current.DefaultContext.Reset();
and then reload your Page, using Navigate method:
if (Frame != null)
Frame.Navigate(typeof(MyPage));
In order to respond right away, you would need to reset the context of the resource manager.
For Windows 8.1:
var resourceContext = Windows.ApplicationModel.Resources.Core.ResourceContext.GetForCurrentView();
resourceContext.Reset();
You will still need to force your page to redraw itself and thus re-request the resources to get the changes to take place. For Windows 8, you can see https://timheuer.com/blog/archive/2013/03/26/howto-refresh-languages-winrt-xaml-windows-store.aspx
You can change the app's language at runtime with the help of this source code. I took help from this and manipulated my app's language settings page as follows:
In languageSettings.xaml.cs:
public partial class LanguageSettings : PhoneApplicationPage
{
public LanguageSettings()
{
InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (ChangeLanguageCombo.Items.Count == 0)
{ ChangeLanguageCombo.Items.Add(LocalizationManager.SupportedLanguages.En);
ChangeLanguageCombo.Items.Add(LocalizationManager.SupportedLanguages.Bn);
}
SelectChoice();
}
private void ButtonSaveLang_OnClick(object sender, RoutedEventArgs e)
{
//Store the Messagebox result in result variable
MessageBoxResult result = MessageBox.Show("App language will be changed. Do you want to continue?", "Apply Changes", MessageBoxButton.OKCancel);
//check if user clicked on ok
if (result == MessageBoxResult.OK)
{
var languageComboBox = ChangeLanguageCombo.SelectedItem;
LocalizationManager.ChangeAppLanguage(languageComboBox.ToString());
//Application.Current.Terminate(); I am commenting out because I don't neede to restart my app anymore.
}
else
{
SelectChoice();
}
}
private void SelectChoice()
{
//Select the saved language
string lang = LocalizationManager.GetCurrentAppLang();
if(lang == "bn-BD")
ChangeLanguageCombo.SelectedItem = ChangeLanguageCombo.Items[1];
else
{
ChangeLanguageCombo.SelectedItem = ChangeLanguageCombo.Items[0];
}
}
}
***Note: Before understanding what I did on LanguageSettings page's code behind, you must implement the codes from the link as stated earlier. And also it may be noted that I am working on windows phone 8

Automatically starting a service when the installer finishes [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to automatically start your service after install?
I have a Visual Studio 2008 C# .NET 3.5 service installer project (MSI) running on Windows 7 x64.
I subscribe to the ServiceInstaller.OnAfterInstall notification to start my service when the installation finishes.
[RunInstaller(true)]
public partial class MyInstaller : Installer
{
private System.ServiceProcess.ServiceInstaller my_installer_;
private void InitializeComponent()
{
// ...
this.my_installer_.AfterInstall += new System.Configuration.Install.InstallEventHandler(this.OnAfterInstall);
// ...
}
private void OnAfterInstall(object sender, InstallEventArgs e)
{
using (System.ServiceProcess.ServiceController svc =
new System.ServiceProcess.ServiceController("MyService"))
{
svc.Start(); // completes successfully
}
}
}
Though the function succeeds without exception, my service is never running when the installer finishes.
The event log shows no failures related to service startup and if I go to the services manager, I can start the service manually (or restart the PC and it will start automatically).
What do I need to do to automatically start my service when the installer process finishes?
Using AfterInstall event
Create AfterInstall event in your Service Installer class and start service using ServiceController.
public ServiceInstaller()
{
InitializeComponent();
this.AfterInstall += new InstallEventHandler(ServiceInstaller_AfterInstall);
}
void ServiceInstaller_AfterInstall(object sender, InstallEventArgs e)
{
ServiceController sc = new ServiceController(serviceInstaller1.ServiceName);
sc.Start();
}
Using Committed event
public ServiceInstaller()
{
InitializeComponent();
this.Committed += new InstallEventHandler(ProjectInstaller_Committed);
}
void ProjectInstaller_Committed(object sender, InstallEventArgs e)
{
ServiceController sc = new ServiceController(serviceInstaller1.ServiceName);
sc.Start();
}
Or you can override OnCommitted event
protected override void OnCommitted(System.Collections.IDictionary savedState)
{
base.OnCommitted(savedState);
new ServiceController(serviceInstaller1.ServiceName).Start();
}
Other than above please check following
Installer Start type : Automatic
Account :Local System
Other than the service installer you need to have setup project which created by giving primary output of above service installer.
in the setup create Custom action at least on install by giving service installer project output.
More information from here.
I assume that Start returns immediatly, and Starts the Service in the background. Check the Docs: http://msdn.microsoft.com/en-us/library/yb9w7ytd.aspx

Categories

Resources