How to ensure single instance application (on multiple virtual desktops)? - c#

I'm writing a C# WinForms application that I need to ensure there's a single instance running at any given time. I thought I had it working using a Mutex.
Here is a link that I found :
How to restrict the application to just one instance.
This worked fine when I'm using a single desktop. However, when there are several virtual desktops open in Windows 10, each of those desktops can host another instance of the application.
Is there a way of limiting a single instance across ALL desktops?

If you look at Remarks section of the docs (see Note block) - you can see, that all you have to do is to prefix your mutex with "Global\". Here is an example for WinForms:
// file: Program.cs
[STAThread]
private static void Main()
{
using (var applicationMutex = new Mutex(initiallyOwned: false, name: #"Global\MyGlobalMutex"))
{
try
{
// check for existing mutex
if (!applicationMutex.WaitOne(0, exitContext: false))
{
MessageBox.Show("This application is already running!", "Already running",
MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
}
// catch abandoned mutex (previos process exit unexpectedly / crashed)
catch (AbandonedMutexException exception) { /* TODO: Handle it! There was a disaster */ }
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}

Related

How to re-start a console application if it crashes?

I have created a console application in C#. How can I program this application so that it will re-start itself after a crash?
If I understand your question correctly, you want to attempt to re-start a console app in the event of a crash. In C# console-apps the method defined as the entry point (usually static void main) is the root of the call stacks in the app. You essentially would need to call that method recursively. You will want to make sure that the app eventually fails if it is in some unintended or unrecoverable state.
For example in the main class:
static int retryCount;
const int numberOfRetries = 3;
static void Main(string[] args)
{
try
{
var theApp = new MyApplicationType(args);
theApp.StartMyAppLogic();
}
catch (ExpectedExceptionType expectThisTypeOfException)
{
thisMethodHandlesExceptions(expectThisTypeOfException);
}
catch (AnotherExpectedExceptionType alsoExpectThisTypeOfException)
{
thisMethodHandlesExceptions(alsoExpectThisTypeOfException);
}
catch (Exception unexpectedException)
{
if(retryCount < numberOfRetries)
{
retryCount++;
Main(args);
}
else
{
throw;
}
}
}
You can use a watchdog to process your monitor and restart it if crashed:
see: What's the best way to watchdog a desktop application?
You can use a windows service instead and set it's recovery options as indicated here: https://serverfault.com/questions/48600/how-can-i-automatically-restart-a-windows-service-if-it-crashes
You can use a scheduled task in task manager to start your application periodically , and set it to only start if previous run has ended:
https://support.microsoft.com/en-us/kb/323527
You could try something like this:
static void Main(string[] args)
{
try
{
// Application code goes here
}
catch (Exception)
{
var applicationPath = System.Reflection.Assembly.GetExecutingAssembly().Location;
Process.Start(applicationPath);
Environment.Exit(Environment.ExitCode);
}
}
Basically, wrap all the code in a try/catch, and if any exceptions occur, the program will retrieve the .exe location with System.Reflection.Assembly.GetExecutingAssembly().Location; and then call Process.Start to run the application again.
You should control your console app from another application (watchdog, sheduler, procmon, servman, ...).
E.g. you can create your console app as a service and control it from service manager.

Mutex kills application

This is my code:
protected override async void OnStartup(StartupEventArgs e)
{
// Used to check if we can create a new mutex
bool newMutexCreated = false;
try
{
// Create a new mutex object with a unique name
mutex = new Mutex(false, MutexName, out newMutexCreated);
}
catch (Exception ex)
when (ex is UnauthorizedAccessException ||
ex is IOException ||
ex is WaitHandleCannotBeOpenedException ||
ex is ArgumentException)
{
Logger.Error("Error while launching application. Failed to check for other instances.", ex);
Shutdown((int)ExitCode.ApplicationAlreadyRunning);
}
// When the mutex is created for the first time
// we run the program since it is the first instance.
if (newMutexCreated)
{
await ContinueStartup(e);
return;
}
else
{
// Otherwise we get the first instance with that process name,
Process[] currentProcesses = Process.GetProcessesByName(AssemblyName);
IntPtr mainWindowHandle = currentProcesses[0].MainWindowHandle;
if (mainWindowHandle != IntPtr.Zero)
{
// maximize it, if it was minimized, and set it to foreground.
Logger.Info("Another instance of the application is already running.");
ShowWindow(mainWindowHandle, WindowShowNormal);
SetForegroundWindow(mainWindowHandle);
}
// Then shutdown this instance.
Logger.Info("Shutting down.");
Shutdown((int)ConsoleModeExitCode.ApplicationAlreadyRunning);
}
}
protected override void OnExit(ExitEventArgs e)
{
Logger.Info("Exiting application.");
// Close mutex.
mutex.Dispose();
base.OnExit(e);
}
What happens here is that my application should start once. While it is running, every attempt to start a new instance should bring the first instance to the front.
But what actually happens is: after 2-10 launch-attempts the first instance's GUI is killed, the process is still running and blocking the Mutex and can only be killed in the TaskManager. If I try to debug this behaviour and run the application in VisualStudio, it just never happens. Trying to open the application 50 times never kills it, so I can't follow the events that seem to occur.
Is it normal behaviour of the GarbageCollector? It kills the first instance in case it is hanging?
Or am I missing something?
Okay, as #Luaan mentioned the problem was not the Mutex.
I fixed my code with this solution:
https://stackoverflow.com/a/9059657/3319147
ShowWindowAsync and a slightly different handling of the IntPtr-value of the handle seems to make this way more stable. Couldn't crash it since. For me this is enough stability :)

Multiple app domains on a single process winform application

I am creating app domain in my winform application using this code in program.cs file
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
///
[STAThread]
static void Main(string[] args)
{
string appGuid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), false).GetValue(0)).Value.ToString();
// unique id for global mutex - Global prefix means it is global to the machine
string mutexId = string.Format("Global\\{{{0}}}", appGuid);
using (var mutex = new Mutex(false, mutexId))
{
var allowEveryoneRule = new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), MutexRights.FullControl, AccessControlType.Allow);
var securitySettings = new MutexSecurity();
securitySettings.AddAccessRule(allowEveryoneRule);
mutex.SetAccessControl(securitySettings);
if (mutex.WaitOne(TimeSpan.Zero, true) || (args.Length > 0 && string.Compare(args[0], "secondary", true) == 0))
{
ErrorHandler errorHandler = new ErrorHandler();
DffEnvironment.Default.AppErrorHandler = errorHandler;
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(errorHandler.Application_ErrorHandler);
MainForm mainForm = new MainForm();
DffEnvironment.Default.MainForm = mainForm;
if (args.Length > 0)
{
MessageBox.Show(" CurrentDomain" + AppDomain.CurrentDomain.FriendlyName);
}
Application.Run(mainForm);
}
else
{
// send our Win32 message to make the currently running instance
// Add new app domain
NativeMethods.PostMessage(
(IntPtr)NativeMethods.HWND_BROADCAST,
NativeMethods.WM_SHOWME,
IntPtr.Zero,
IntPtr.Zero);
}
}
}
}
and in my MainForm (Form) I override the code WndProc method and wrote this
static int procNumber=0;
protected override void WndProc(ref System.Windows.Forms.Message m)
{
if (m.Msg == NativeMethods.WM_SHOWME)
{
try
{
procNumber++;
AppDomain appDomain = AppDomain.CreateDomain("MyAppDomainApplication" + procNumber.ToString(), null, setupInfo);
string[] arguments = { "secondary" };
appDomain.ExecuteAssembly("MyAppDomainApplication.exe", null, arguments);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}
base.WndProc(ref m);
}
It is working fine, it creates another app domain in the same process already running when I try to open my application.
My first question is can I create app domain of the same process in already running from other user, for example
John is working on this app and having two app domains and one process. And Steve logged in on the same machine and tried to open this application and application should not create process and it should add new app domain in already running process by John.
I detected the process running in another user by prefixing the name of the mutex with "Global\". as mentioned here
and second question is here when I edit the following code in program.cs
NativeMethods.PostMessage(
(IntPtr)NativeMethods.HWND_BROADCAST,
NativeMethods.WM_SHOWME,
IntPtr.Zero,
IntPtr.Zero);
to
try
{
procNumber++;
AppDomain appDomain = AppDomain.CreateDomain("MyAppDomainApplication" + procNumber.ToString(), null, setupInfo);
string[] arguments = { "secondary" };
appDomain.ExecuteAssembly("MyAppDomainApplication.exe", null, arguments);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
It creates another process why it is not working in Program.cs file, why should I have to send message to my Form and do the same thing in WndProc method
I'll answer to your second question.
Application domains are isolated environments inside process. Here you have two processes and they both have their own application domains. You must send a message from one process to another if you want to command the other process to create a new application domain. That is why you have to send the message.
I also suspect that the code does not work in the way you have intended. The ExecuteAssembly() is run in the same thread as the main user interface. If the executed assembly begins a new message loop, your call stack will grow after every WM_SHOWME message and you will eventually get stack overflow exception.
In that case your call stack will look more or less like this:
at Application.Run()
at Main()
at AppDomain.ExecuteAssembly()
...
at Application.Run()
at Main()
at AppDomain.ExecuteAssembly()
at Application.Run()
at Main()

How to make my app singleton application? [duplicate]

This question already has answers here:
What is the correct way to create a single-instance WPF application?
(39 answers)
Closed 6 years ago.
I have a application but currently it is not a singleton application.
I like to make it singleton application so that its another instance does not exit at the run time .
If this can be done please reply with some sample codes .
I think the following codes will be helpful for you.
Here is the related link:
http://geekswithblogs.net/chrisfalter/archive/2008/06/06/how-to-create-a-windows-form-singleton.aspx
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
/*====================================================
*
* Add codes here to set the Winform as Singleton
*
* ==================================================*/
bool mutexIsAvailable = false;
Mutex mutex = null;
try
{
mutex = new Mutex(true, "SampleOfSingletonWinForm.Singleton");
mutexIsAvailable = mutex.WaitOne(1, false); // Wait only 1 ms
}
catch (AbandonedMutexException)
{
// don't worry about the abandonment;
// the mutex only guards app instantiation
mutexIsAvailable = true;
}
if (mutexIsAvailable)
{
try
{
Application.Run(new SampleOfSingletonWinForm());
}
finally
{
mutex.ReleaseMutex();
}
}
//Application.Run(new SampleOfSingletonWinForm());
}
}
Here are some good sample applications. Below is one possible way.
public static Process RunningInstance()
{
Process current = Process.GetCurrentProcess();
Process[] processes = Process.GetProcessesByName (current.ProcessName);
//Loop through the running processes in with the same name
foreach (Process process in processes)
{
//Ignore the current process
if (process.Id != current.Id)
{
//Make sure that the process is running from the exe file.
if (Assembly.GetExecutingAssembly().Location.
Replace("/", "\\") == current.MainModule.FileName)
{
//Return the other process instance.
return process;
}
}
}
//No other instance was found, return null.
return null;
}
if (MainForm.RunningInstance() != null)
{
MessageBox.Show("Duplicate Instance");
//TODO:
//Your application logic for duplicate
//instances would go here.
}
Many other possible ways. See the examples for alternatives.
First one.
Second One.
Third One.
The approach I know of is the following. The program must attempt to open a named mutex. If that mutex existed, then exit, otherwise, create the mutex. But this seems to contradict your condition that "its another instance does not exit at the run time". Anyway, maybe this too was helpful

How can I check for a running process per user session?

I have a .NET application that I only allow to run a single process at a time of, however that app is used on Citrix boxes from time to time, and as such, can be run by multiple users on the same machine.
I want to check and make sure that the application is only running once per user session, because right now if user A is running the app, then user B gets the "App already in use" message, and should not.
This is what I have now that checks for the running process:
Process[] p = Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName);
if (p.Length > 1)
{
#if !DEBUG
allowedToOpen &= false;
errorMessage +=
string.Format("{0} is already running.{1}", Constants.AssemblyTitle, Environment.NewLine);
#endif
}
EDIT: Improved the answer according to this cw question ...
You can use a mutex for checking wether the app already runs:
using( var mutex = new Mutex( false, AppGuid ) )
{
try
{
try
{
if( !mutex.WaitOne( 0, false ) )
{
MessageBox.Show( "Another instance is already running." );
return;
}
}
catch( AbandonedMutexException )
{
// Log the fact the mutex was abandoned in another process,
// it will still get aquired
}
Application.Run(new Form1());
}
finally
{
mutex.ReleaseMutex();
}
}
Important is the AppGuid - you could make it depend on the user.
Maybe you like to read this article: the misunderstood mutex
As tanascius already say, you can use the Mutex.
On a server that is running Terminal Services, a named system mutex can have two levels of visibility. If its name begins with the prefix "Global\", the mutex is visible in all terminal server sessions. If its name begins with the prefix "Local\", the mutex is visible only in the terminal server session where it was created.
Source: msdn, Mutex Class
Just stating the obvious - although Mutex is usually considered better solution, you can still solve the single-instance-per-session issue without Mutex - just test the SessionId as well.
private static bool ApplicationIsAlreadyRunning()
{
var currentProcess = Process.GetCurrentProcess();
var processes = Process.GetProcessesByName(currentProcess.ProcessName);
// test if there's another process running in current session.
var intTotalRunningInCurrentSession = processes.Count(prc => prc.SessionId == currentProcess.SessionId);
return intTotalRunningInCurrentSession > 1;
}
Source (no Linq)
If Form1 launches non-background threads, and that Form1 exits, you've got a problem: the mutex is released but the process is still there. Something along the lines below is better IMHO:
static class Program {
private static Mutex mutex;
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main() {
bool createdNew = true;
mutex = new Mutex(true, #"Global\Test", out createdNew);
if (createdNew) {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
else {
MessageBox.Show(
"Application is already running",
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error
);
}
}
}
The mutex won't be released as long as the primary application domain is still up. And that will be around as long as the application is running.

Categories