Displaying a WPF window from an Installer class - c#

I'm having a similar problem as this issue. I'm trying to display a WPF window from an Installer class from System.Configuration.Install. My window, corresponding to my software license manager window, should ideally pop up during or after the installation process to install the license as well. However, the installation finishes without the window ever showing and I'm not sure why.
Here is my code:
[RunInstaller(true)]
public partial class Installer1 : System.Configuration.Install.Installer
{
public Installer1()
{
InitializeComponent();
}
// INSTALL EVENT //////////
public override void Install(System.Collections.IDictionary stateSaver)
{
StaThreadWrapper(() =>
{
LicenseActivationWindow activationWindow = new LicenseActivationWindow();
activationWindow.ShowDialog();
});
}
// Method to call the xaml in a thread safe way
private static void StaThreadWrapper(Action action)
{
var t = new Thread(o =>
{
action();
System.Windows.Threading.Dispatcher.Run();
});
t.SetApartmentState(ApartmentState.STA);
t.Start();
}
// UNINSTALL EVENT //////////
public override void Uninstall(System.Collections.IDictionary stateSaver)
{
}
}
I had to add the StaThreadWrapper method to fix the "The calling thread must be STA, because many UI components require this." error from calling the wpf window directly from the installer thread and I'm no longer getting the error message but I'm also not getting the window to show up. I thought this would solve the issue like [1] but it didn't.
What am I doing wrong?

Displaying a window in a System.Configuration.Install.Installer is probably not a very good idea but if you still want to try it out, then create an Application class on the STA thread. Something like this:
var t = new Thread(o =>
{
var app = new System.Windows.Application();
app.Run(new LicenseActivationWindow());
});
t.SetApartmentState(ApartmentState.STA);
t.Start();

Related

Syncing data between two Window in WPF

I am developing an application in WPF. I need to load an instance of the Window class (which I call Win1 here) with which a form is filled. Then, when the Submit button is clicked, Win1 closes and only then can a new Win2 window be loaded (another class, also inherited from Window). The problem is that both of them open and I can not synchronize the data obtained from the first Win1 and pass them to the second Win2. I'm just messing up.
Someone can give me a generic idea indicating the tools and the pattern I need to do the above. For the specifications given to me, it is necessary that Win2 appears only after Win1 has finished its work.
Even though the application is more complex than I described it now, I would like to post some code, but I manage to confuse the ideas of who is reading me, so I tell you that at the moment I'm managing the windows inside the constructor of App.cs, while MainWindow.cs corresponds to Win2 and I created a new class to implement Win1.
public partial class App : Application
{
// Params...
public App()
{
Client = LoadNetwork();
User = LoadUser(Client); // Shows Win1
Games = LoadMinigames();
mainWindow = new MainWindow(User, Games);
Application.Current.MainWindow = mainWindow; // On XAML default is Hidden
mainWindow.Show(); // Shows Win2
}
// Other methods...
}
The biggest problem for me is to pass User data to MainWindow and I do not have many ideas on how to deal with this case.
Update
public partial class MainWindow : Window
{
public UserLoading ul;
public UserRegistering ur;
public User.UserProfile User;
private List<Game.Game> Games;
public Label Username;
public MainWindow(User.UserProfile user, List<Game.Game> games)
{
User = new UserProfile();
InitializeComponent();
User = user;
Games = games;
Username.Content = User.Username;
DrawList(Games);
}
//...
}
I realize I have explained myself a bit 'badly rereading my question several times. So I update it trying to be clearer by reporting here my answer to one of the comments.
The UserLoad method is not blocking, because inside it are instantiated classes that inherit Window (other windows for login and registration in other words) then the flow of execution proceeds and instantiates the MainWindow where naturally the argument "user" will result null because the forms have not been filled yet. I realize now that perhaps I had explained myself badly. The call of Win1 is not blocking and I would like it to return only when the user data is ready to be passed as an argument to Win2.
I have done this in the past. here is my solution:
Set Your Launch Window to Win1. Let It launch. Create a Static Method in App.cs to launch Win2. When Win1 is ok to shut down and you want Win2 to open call App.ShowMainWindow(this) from within Win1.
App.cs
public partial class App : Application
{
static internal void ShowWin2(Win1 win1)
{
Win2 win2 = new Win2();
// Copy Win1 stuff to Win2 here
Application.Current.MainWindow = win2;
win2.Show();
}
}
Win1
public partial class Win1 : Window
{
public Win1()
{
InitializeComponent();
}
private void CloseAndLaunchWin2()
{
App.ShowWin2(this);
this.Close();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
CloseAndLaunchWin2();
}
}
As User Nawed mentioned, you should read into MVVM. Syncing can be achieved by using the same model for two different views.
You could do something like this.
var sharedContext = new MyViewModel();
var viewOne = new MyWindow();
var viewTwo = new MyUserControl();
viewOne.DataContext = viewTwo.DataContext = sharedContext;

Can a UWP App Self Activate Its Window?

public class MainViewModel MyViewModel : INotifyPropertyChanged
{
Window window { get; set; }
public MyViewModel()
{
window = Window.Current;
}
async void MyWebSocketService_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "ActivateWindow"
ActivateWindow();
break;
}
}
void ActivateWindow()
{
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
window.Activate();
window.CoreWindow.Activate();
});
}
}
I'm trying to get a hidden or minimized UWP app to regain focus in the OS upon a WebSocket notification. This is the code I tried. It fires but nothing happens. Is there a way to achieve what I'm trying to do?
A UWP app cannot resume itself from minimized state because its execution state is suspended (and therefore can't run any code). The resume needs to be triggered externally, either by the user or by another running app, which can activate the suspended UWP app via any of the supported app activation APIs, for example doing a launch via protocol - as you have already figured out.

Stop service without error "Service cannot be started. The handle is invalid"

I have WIndows Service app and want to stop service when main code is executed. I am trying to execute ServiceBase.Stop() in OnStart event, everything works fine, the service is stopped but I get annoying error message in event viewer
"Service cannot be started. The handle is invalid"
Any ideas how to stop windows service without errors?
public partial class VirtualServerInitService : ServiceBase
{
public ILogger EventLogger = new EventLogger();
public VirtualServerInitService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
EventLogger.Write("Starting service!");
new VirtualServerInit(EventLogger).Run();
EventLogger.Write("VirtualServerInit code was executed");
Stop();//This code works and also gives error in event viewer
}
protected override void OnStop()
{
EventLogger.Write("Stopping service!");
}
}
Use a thread to make sure the OnStart method finishes. The threadpool will put your workitem on the queue and once a thread it is available it will execute your Run method and then calls Stop on the service. By then the windows service manager already handled the succesful start of your service and hence no error will be sent to the Eventlog when Stop is called.
protected override void OnStart(string[] args)
{
EventLogger.Write("Starting service!");
ThreadPool.QueueUserWorkItem( _ => {
new VirtualServerInit(EventLogger).Run();
EventLogger.Write("VirtualServerInit code was executed");
this.Stop();
});
}
You might consider leaving the service running and use a CustomCommand to control if actual work needs to be done. For that you can override OnCustomCommand and send a pre determined integer to ask the service to perform a particular task for example by calling sc virtualsvc control 128 from the commandline.
protected virtual void OnCustomCommand(int command)
{
switch(command)
{
case 128:
new VirtualServerInit(EventLogger).Run();
EventLogger.Write("VirtualServerInit code was executed");
// maybe keep state if this only can be run once
break;
default:
EventLogger.Write(String.Format("Unknown control code:{0}", command));
break;
}
}

Where to Break the Chain with Task, ContinueWith, Lock

I have MVP application C#, .NET 4, WinForms. It uses Bridge class which communicate with third party app via NamedPipe.
The command flow is like this: View → Presenter → Manager → Bridge → Client
And back in the reverse order. View is prepared for multitasking. I split reverse chain in Manager by rising event with the result, but it doesn't help.
// View class
public void AccountInfo_Clicked() { presenter.RequestAccountInfo(); }
public void UpdateAccountInfo(AccountInfo info)
{
if (pnlInfo.InvokeRequired)
pnlInfo.BeginInvoke(new InfoDelegate(UpdateAccountInfo), new object[] {info});
else
pnlInfo.Update(info);
}
// Presenter class
public void RequestAccountInfo() { manager.RequestAccountInfo(); }
private void Manager_AccountInfoUpdated(object sender, AccountInfoEventArgs e)
{
view.UpdateAccountInfo(e.AccountInfo);
}
// Manager class
public void RequestAccountInfo()
{
AccountInfo accountInfo = bridge.GetAccountInfo();
OnAccountInfoUpdated(new AccountInfoEventArgs(accountInfo));
}
// Bridge class
public AccountInfo GetAccountInfo() { return client.GetAccountInfo(); }
// Client class
public AccountInfo GetAccountInfo()
{
string respond = Command("AccountInfo");
return new AccountInfo(respond);
}
private string Command(string command)
{
var pipe = new ClientPipe(pipeName);
pipe.Connect();
return pipe.Command(command);
}
I want to unfreeze the UI during command processing. There are also other commands that can be executed. Finally all commands reach Command(string command) method in Client.
I tried to break the chain in Manager by using task and ContinueWith but it results to pipe failing to connect. The reason is that client is not thread safe.
// Manager class
public void RequestAccountInfo()
{
var task = Task<AccountInfo>.Factory.StartNew(() => bridge.GetAccountInfo());
task.ContinueWith(t => { OnAccountInfoUpdated(new AccountInfoEventArgs(t.Result)); });
}
My question is: Where to use Task, ContinueWith and where to Lock?
I assume I can lock only Command(string command) because it is the ultimate method.
private string Command(string command)
{
lock (pipeLock)
{
var pipe = new ClientPipe(pipeName);
pipe.Connect();
return pipe.Command(command);
}
}
Can I use Task, Wait in Command in Client class?
I think the problem you are having is that bridge.GetAccountInfo() is trying to extract information from the UI itself - hence the UI thread. This code
public void RequestAccountInfo()
{
var task = Task<AccountInfo>.Factory.StartNew(() => bridge.GetAccountInfo());
task.ContinueWith(t => { OnAccountInfoUpdated(new AccountInfoEventArgs(t.Result)); });
}
is attempting to execute the bridge.GetAccountInfo() method (accessing the UI) from a background thread-pool thread.
My first question here would be how expensive is the call to bridge.GetAccountInfo()? If it is not expensive, it makes no sense to put working into multi-threading this aspect. If it is expensive, you will have to think about a way to make this operation thread safe (I can't advise without more information).
Another thing to do would assess the expense of a move to WCF. This handles most synchronisation problems for you... I am sorry I can't be of more help. I wrote the above before I read your last comment.
I hope this is of some use.
Aside: something to be aware of is SynchronizationContext. Using a TaskScheduler you can launch a Task on the UI thread (this is not what you want here as this again will just block the UI - however, this can be good to know when reporting [in .NET 4.0]. To launch your code above on the UI thread you can do
public void RequestAccountInfo()
{
var task = Task<AccountInfo>.Factory.StartNew(() =>
bridge.GetAccountInfo(),
TaskScheduler.FromCurrentSynchronizationContext());
task.ContinueWith(t => { OnAccountInfoUpdated(new AccountInfoEventArgs(t.Result)); });
}
I locked Command in Client class. It appears that it works perfectly in that way. No blocking UI, no pipe errors. I lock on pipeName because each copy of View is using a unique pipe name.
I applied Task<Type>, ContinueWith to all commands in Manager class.
// Manager class
public void RequestSomeInfo()
{
var task = Task<SomeInfo>.Factory.StartNew(() => bridge.GetSomeInfo());
task.ContinueWith(t => { OnInfoUpdated(new InfoEventArgs(t.Result)); });
}
// Client class
private string Command(string command)
{
lock (pipeName)
{
var pipe = new ClientPipe(pipeName);
pipe.Connect();
return pipe.Command(command);
}
}

Testing the Application class doesn't shutdown the application after the test is done

My WPF application has an App class, and it inherits from System.Windows.Application.
Inside this class I have my exception handling that handles DispatcherUnhandledException, so any un handled exceptions may be caught and be presented to the user.
To test this I start my test with
//Arrange
app = new App();
and then I continue with the rest of the test setup.
Later on in my test I run this code:
//Act
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Normal, new MyDlg(RaiseArgumentNullException));
This triggers ApplicationStartup to be executed in the App class.
When my next test starts it also creates a new App class, and also uses the CurrentDispatcher.Invoke to create another type of exception. However, the first Application seems to be running.
If I run my two tests one by one, they work just fine. But if I run them in sequence the second one fails.
Does anyone have any idea of how to properly shut down the Application after the test is done?
I have tried the following ways to shut the application down, but it doesn't work.
static void CloseApp()
{
Application.Current.Shutdown();
}
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Send, new MyDlg(CloseApp));
app.Shutdown();
Dispatcher.CurrentDispatcher.BeginInvokeShutdown(DispatcherPriority.Send);
This is how my code looks like:
delegate void MyDlg();
static void RaiseArgumentNullException()
{
throw new ArgumentNullException();
}
[Test]
public void MyTest()
{
//Arrange
app = new App();
//...
//Act
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Normal, new MyDlg(RaiseArgumentNullException));
//Assert....
//Tear Down
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Send, new MyDlg(CloseApp));
app.Shutdown();
Dispatcher.CurrentDispatcher.BeginInvokeShutdown(DispatcherPriority.Send);
}
And this is my App class:
public partial class App : Application
{
public App()
{
Startup += ApplicationStartup;
DispatcherUnhandledException += AppDispatcherUnhandledException;
Bootstrapper.InitializeIoc();
}
private void ApplicationStartup(object sender, StartupEventArgs e)
{
//Do startup stuff
}
void AppDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
HandleException(e.Exception);
e.Handled = true;
}
//....
}
The Application class does not allow more than one instance of Application (or any subclass) to be created per AppDomain. This is regardless of whether the Application is still running or not. From MSDN:
Application implements the singleton pattern to provide shared access
to its window, property, and resource scope services. Consequently,
only one instance of the Application class can be created per
AppDomain.
To work around this, you could create an AppDomain in each of your tests.

Categories

Resources