As I know Application.Restart() restarts an application and creates new Instance of an Application. Does this instance will creates in new process, or old process will be used?
Thanks for an answer.
It runs in a new process. The documentation seems a little unclear on whether or not the process is reused but it can be verified by showing the process ID in a text box at start up.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Application.Restart();
}
private void Form1_Load(object sender, EventArgs e)
{
textBox1.Text = Process.GetCurrentProcess().Id.ToString();
}
}
You can also see using .NET Reflector that a new process is created:
public static void Restart()
{
// ...
ExitInternal(); // Causes the application to exit.
Process.Start(startInfo); // Starts a new process.
// ...
}
According to the documentation it will start a new instance of the application and thus new process. If there were command line arguments supplied when starting the application those same arguments will be supplied to the new process.
It starts a new instance. You could run into issue where if your original application still have worker thread running, the original process may not be killed soon enough that you will end up having 2 instances running at the same time (which will be shown in task manager).
Related
This is an incredibly simple task tray app - using ApplicationContext and a few guides I found online.
The purpose of the app is to query a small REST API and show a message box to the user on a given result. I need to essentially have the API query in a background loop, running every 10 seconds or something similar. This is to report on data that I've made accessible via another service.
I've done some reading and it seems a BackgroundWorker and Timer is an appropriate option, but I'm lost on where to go next. How exactly can I achieve this? I initially tried adding a while(true) loop to the TaskTrayApplicationContext but it just created an infinite loop whereby you couldn't do anything else with the app.
namespace TaskTrayApplication
{
public class TaskTrayApplicationContext : ApplicationContext
{
NotifyIcon notifyIcon = new NotifyIcon();
Configuration configWindow = new Configuration();
public TaskTrayApplicationContext()
{
MenuItem configMenuItem = new MenuItem("Configuration", new EventHandler(ShowConfig));
MenuItem exitMenuItem = new MenuItem("Exit", new EventHandler(Exit));
notifyIcon.Icon = TaskTrayApplication.Properties.Resources.AppIcon;
notifyIcon.DoubleClick += new EventHandler(ShowMessage);
notifyIcon.ContextMenu = new ContextMenu(new MenuItem[] { configMenuItem, exitMenuItem });
notifyIcon.Visible = true;
}
void ShowMessage(object sender, EventArgs e)
{
// Only show the message if the settings say we can.
if (TaskTrayApplication.Properties.Settings.Default.ShowMessage)
MessageBox.Show("This is the Serenity TaskTray Agent.");
}
void ShowConfig(object sender, EventArgs e)
{
// If we are already showing the window meerly focus it.
if (configWindow.Visible)
configWindow.Focus();
else
configWindow.ShowDialog();
}
void Exit(object sender, EventArgs e)
{
// We must manually tidy up and remove the icon before we exit.
// Otherwise it will be left behind until the user mouses over.
notifyIcon.Visible = false;
Application.Exit();
}
}
}
And the Program.cs
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace TaskTrayApplication
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// Instead of running a form, we run an ApplicationContext.
Application.Run(new TaskTrayApplicationContext());
}
}
}
Threading is hard, concurrency is hard. Background worker and System.Timers are both constructs that run in their own thread.
winforms won't allow for interaction between threads that own a control (read: that created a control) and threads that don't. This is a whole subject apart i wont get into now - theres good stuff to read out there why this is and how to go about it: https://visualstudiomagazine.com/articles/2010/11/18/multithreading-in-winforms.aspx
There are tools to help, one is the dispatchertimer:
https://learn.microsoft.com/en-us/dotnet/api/system.windows.threading.dispatchertimer?view=netcore-3.1
This is a special timer that instead of its own thread, schedules tasks on the main thread. The main thread in a winforms application handles the drawing of controls, showing of the different windows etc. e.g. this 'owns' all controls.
A sample can be seen on msdn, i adopted it here to show you what you could do:
public class TaskTrayApplicationContext : ApplicationContext
{
...
DispatcherTimer dispatcherTimer;
public TaskTrayApplicationContext()
{
...
dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
dispatcherTimer.Interval = new TimeSpan(0,0,1);
dispatcherTimer.Start();
}
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
// Fetch your data via a rest api
var myData = MyDataFunction();
// check and show dialog if the data is not okay
if(myData.Result.Value = 'NOT_OKAY!')
ShowMessage(this, myData.Result); // or something.
}
...
Now since this does not utilize a second thread, this means the main ui thread could be blocked from drawing the windows, reacting to user input etc. because its busy doing work in the timer_tick function. This would for example happen if your rest call takes a long time.
This will make your application freeze and irresponsive. This could be a problem but most likely wont, so lets burn that bridge when we get to it.
Basically, this question also summarizes my issue:
SystemEvents.SessionEnding not fired until a Process (opened before) gets closed
But there is no answer to it yet. I have a Console app that starts another process from within itself. The app also listens for SystemEvents.SessionSwitch. If I comment out the code that starts the additional process, the event handler for SessionSwitch is hit. However, if I uncomment the code that starts the additional process, the handler is not hit. I'm 100% confident that the event handler not being hit is due to starting a new process from within my app... I just don't know why.
I tagged this as a possible multithreading issue because that's what some of the comments made in the question posted above seemed to indicate. However, I'm not sure at all what could be causing it.
Here's some of the code.
[STAThread]
static void Main(string[] args)
{
SystemEvents.SessionSwitch += SystemEvents_SessionSwitch;
_myFoo = new _myFoo();
_processManager = new ProcessManager();
// If I comment out this code block, the SessionSwitch event handler is hit
// ------------------------------------------------------
if (args.Length == 0)
{
// creates a duplicate process to monitor the current (main) process
_processManager.StartObserverProcess();
}
else
{
// start monitoring the main process
_processManager.ObserveMainProcess(int.Parse(args[0]));
}
// ----------------------------------------------------
_myFoo.Start();
}
// this method does not get hit if we start the 'duplicate'
// monitoring process from within ProcessManager
private static void SystemEvents_SessionSwitch(object sender, SessionSwitchEventArgs e)
{
if (e.Reason == SessionSwitchReason.SessionLock)
{
// Do something when session locked
}
if (e.Reason == SessionSwitchReason.SessionUnlock)
{
// Do something when session unlocked
}
}
The ProcessManager basically starts another 'duplicate' process that watches to see if the current process exits (I know the term 'duplicate' here is probably not accurate). Here's an excerpt:
public class ProcessManager
{
// create a new process to monitor the current process
// passing in the current process id as args
public void StartObserverProcess()
{
_mainProcess = Process.GetCurrentProcess();
_mainProcessId = _mainProcess.Id;
_observerProcess = new Process
{
StartInfo =
{
FileName = _mainProcess.MainModule.FileName,
Arguments = _mainProcessId.ToString()
},
EnableRaisingEvents = true
};
_observerProcess.Exited += OnObserverProcessExit;
_observerProcess.Start();
}
private void OnObserverProcessExit(object sender, EventArgs e)
{
// do something on main process exit
}
}
I'm trying to build a single instance application using the approach outlined here.
The reason I tried going with that solution is that I need to pass on the commandlines from the second attempt to start the app to the first instance, and this seemed the easiest way to accomplish that.
OS flavours I need to support:
Windows XP SP3
Windows 7 32 Bit
Windows 7 64 Bit
I've got it working on all three OS versions, however, I have one machine with Windows 7 32Bit where this crashes with a CantStartSingleInstanceException.
Here's the code:
SingleInstanceController.cs:
using System;
using Microsoft.VisualBasic.ApplicationServices;
namespace SingleInstanceTest
{
public class SingleInstanceController : WindowsFormsApplicationBase
{
public SingleInstanceController()
{
IsSingleInstance = true;
}
protected override void OnCreateMainForm()
{
base.OnCreateMainForm();
Form1 f = new Form1();
MainForm = f;
// process first command line
f.SetCommandLine(Environment.GetCommandLineArgs());
}
protected override void OnStartupNextInstance(StartupNextInstanceEventArgs eventArgs)
{
base.OnStartupNextInstance(eventArgs);
Form1 f = MainForm as Form1;
// process subsequent command lines
f.SetCommandLine(eventArgs.CommandLine);
}
}
}
Program.cs:
using System;
using System.Windows.Forms;
namespace SingleInstanceTest
{
static class Program
{
[STAThread]
static void Main()
{
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
SingleInstanceController si = new SingleInstanceController();
// This triggers the crash on one machine when starting the
// app for the second time
si.Run(Environment.GetCommandLineArgs());
}
static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
{
// this is triggered with CantStartSingleInstanceException
MessageBox.Show(e.ToString(),"ThreadException");
MessageBox.Show(e.Exception.ToString(), "ThreadException");
}
static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
MessageBox.Show(e.ToString(), "UnhandledException");
MessageBox.Show(e.ExceptionObject.ToString(), "UnhandledException");
}
}
}
For testing purposes, the form is just a plain form containing a listbox that displays the command line arguments.
Any ideas why this doesn't work on that one machine? I've been fiddling with this for two days now and can't figure it out ...
I ran into the same Problem, but I don't think it has something to do with Windows 7 or 32bit. In my case it turned out, it was a performance issue. Unfortunately, I can't find the source code of WindowsFormsApplicationBase
but it uses network to communicate with the main application, so there might be timeouts involved. It is especially bad, when the main application has to do a lot of network I/O anyways. When the main application does not answer the call to Run fast enough, this exception is thrown.
I solved it by fine tuning the processes, tasks ans threads, so the call gets answered first.
And getting rid of WindowsFormsApplicationBase by using mutexes and proper IPC, where I can actually not only choose the time-out, but also catch any exceptions! Actually, for some sorts of IPC, there isn't even a need for a mutex.
See this fine article for more on that topic:
https://www.codeproject.com/Articles/1089841/SingleInstance-NET
The two dirtymost workarounds I choose:
Catching the exception and trying again a couple of milliseconds later.
After some testing, spawning a new thread with a low priority in the base application seems to be a good idea (at least it was in my scenario).
public void SetCommandLineInThread(string[] args) {
new Thread(() => {
SetCommandLine(args);
}) { IsBackground = true, Priority = ThreadPriority.Lowest }.Start();
}
Note, that I make a copy of the command line args as soon as possible.
var args = e.CommandLine.ToArray();
I'm a learn-by-example C# coder who isn't very advanced, which is why this problem is completely stumping me regardless of the amount of information on the internet.
I'm essentially creating a program that is, on a timer, repeatedly polling a website to get some information. During this process, a WebBrowser control is created to navigate to the information (needed for authentication). The program runs this series of events at startup, then using a System.Timers.Timer set to every 10 minutes (less for debugging of course) to do that same series of events yet when my Timer.Elapsed event triggers that process, I get a:
ThreadStateException with the description as ActiveX control '8856f961-340a-11d0-a96b-00c04fd705a2' cannot be instantiated because the current thread is not in a single-threaded apartment.
Here is a slimmed down version of my program.
private void Form1_Load(object sender, EventArgs e)
{
GetDataFromWebBrowser();
Set_Auto_Refresh_Timer();
}
private void Set_Auto_Refresh_Timer()
{
System.Timers.Timer TimerRefresh = new System.Timers.Timer(10000);
TimerRefresh.Elapsed += new System.Timers.ElapsedEventHandler(TimerRefresh_Elapsed);
TimerRefresh.AutoReset = true;
TimerRefresh.Start();
}
private void TimerRefresh_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
GetDataFromWebBrowser();
}
private void GetDataFromWebBrowser()
{
WebBrowser wb = new WebBrowser(); <--This is where the error is thrown.
...get web data...
}
I think I got enough code in there to paint the picture. As you can see, when it gets to creating another WebBrowser, it throws the error.
I'm really stumped and I'm just starting to scrape the surface on Threading which is probably why I'm so stumped.
//Solution for me/
I ended up moving the WebBrowser creation out of the method as well as making it static to just reuse the WebBrowser control. I also swapped my System.Timers.Timer to System.Threading.Timer. Seemed to fix the problem.
The MSDN documentation for WebBrowser states that:
The WebBrowser class can only be used in threads set to single thread apartment (STA) mode. To use this class, ensure that your Main method is marked with the [STAThread] attribute.
Also, change your System.Timers.Timer to a System.Windows.Forms.Timer if you want to interact with UI controls in regular intervals. Alternatively, set the SynchronizingObject property of your System.Timers.Timer to a parent control to force your timer to invoke calls on the right thread. All WinForms controls can only be accessed from the same, one and only UI thread.
There are three types of timers in .NET's BCL, each of them acting very differently. Check this MSDN article for a comparison: Comparing the Timer Classes in the .NET Framework Class Library (web archive) or this brief comparison table.
I would recommend using WebClient class instead of WebBrowser. Also it seems to be better to store already created instance as a private property instead of creating new instance each time you need to poll a web site.
As following:
private WebClient webClient;
private void Form1_Load(object sender, EventArgs e)
{
GetDataFromWebBrowser();
Set_Auto_Refresh_Timer();
this.webClient = new WebClient();
}
private void Set_Auto_Refresh_Timer()
{
System.Timers.Timer.TimerRefresh = new System.Timers.Timer(10000);
TimerRefresh.Elapsed += new System.Timers.ElapsedEventHandler(TimerRefresh_Elapsed);
TimerRefresh.AutoReset = true;
TimerRefresh.Start();
}
private void Set_Auto_Refresh_Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
GetDataFromWebBrowser();
}
private void GetDataFromWebBrowser()
{
...perform required work with webClient...
...get web data...
}
As Groo said, you should use System.Windows.Forms.Timer, or if you really want to do you operation in another thread, you should use the Invoke method to do any UI related stuff:
private void GetWebData()
{
...get web data...
}
private void ShowWebData()
{
WebBrowser wb = new WebBrowser();
// other UI stuff
}
private void TimerRefresh_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
GetDataFromWebBrowser();
}
private void GetDataFromWebBrowser()
{
GetWebData();
if (this.InvokeRequired)
this.Invoke(new Action(ShowWebData));
else
ShowWebData();
}
I am faced with a problem. I am clicking a button that is calling several methods, although the main thread is locking up, so I created an instance of my class (which is Form1) e.g. Form1Object and then the button called the methods as so: Form1Object.Check1 and so on.
Although the thread still locked up (i.e. the GUI became unresponsive for a period) Is there anyway of getting around this, any examples would be greatly appreciated.
The code in question is below:
private void StartChecks_Click(object sender, EventArgs e)
{
Form1 Form1Object = new Form1();
Form1Object.InitChecks();
}
public void InitChecks()
{
Check1();
Check2();
Check3();
Check4();
Check5();
Check6();
Check7();
}
Creating a new Form does not start a new Thread.
You will have to move those CheckN() methods to a BackgroundWorker.
private void StartChecks_Click(object sender, EventArgs e)
{
Form1 Form1Object = new Form1();
var worker = new BackgroundWorker();
worker.DoWork += (s, arg) =>
{
Form1Object.InitChecks();
};
// add progress, completed events
worker.RunWorkerAsync();
}
But note that this require that the checks are independent and do not interact with any Control.
What you need to do is start a parallel thread to do the check, so you won't lock up the main thread:
private void StartChecks_Click(object sender, EventArgs e)
{
Form1 Form1Object = new Form1();
Thread t = new Thread(
o =>
{
Form1Object.InitChecks();
});
t.Start();
}
Hopefully you don't need to actually retrieve anything from those calculations, so you can just fire and forget about it.
You have several options here, and use them depending of your skill/preference/requirement:
if you don't update anything on the form while you process, start another thread and call everything on that thread, and update UI when appropriate (when everything is finished)
if you need to update things on your form while processing, you have several options:
either use Application.DoEvents() from the processing loop of every method you use
start a new thread then update form controls with Invoke() - if you try to update them directly, you'll be in trouble
If you care to comment and decide for one of the options, I can provide more info on just that...