prevent a c# application from running more than one instance - c#

I wrote a program in c#
now I would like to know what is the proper way to prevent the program from starting if it is already running?
so if it is already running, and double-click on the program it will not start because it is already running.
I can do that, but I was thinking of a standard and proper way.

The recommended way to do this is with a system mutex.
bool createdNew;
using(var mutex = new System.Threading.Mutex(true, "MyAppName", out createdNew))
{
if (createdNew)
// first instance
Application.Run();
else
MessageBox.Show("There is already an instace running");
}
The first parameter to the Mutex ctor tells it to give create a system wide mutex for this thread. If the Mutex already exists it will return out false through the 3rd parameter.
Update
Where to put this?
I'd put this in program.cs. If you put it in form_load you'll need to keep the mutex for the life time of the application (have the mutex as a member on the form), and manually release it in the form unload.
The earlier you call this the better, before the other app opens DB connections etc. and before resources are put created for forms / controlls etc.

Quick way I did in one of the applications .. You can look at the list of running processes to see whether the current application is already running and not start the application again.
Process[] lprcTestApp = Process.GetProcessesByName("TestApplication");
if (lprcTestApp.Length > 0)
{
// The TestApplication is already running, don't run it again
}

I think enumerating the process list could potentially be slow. You could also create a Mutex using the System.Threading.Mutex class and check to see if it's already created when the process starts. However, this would require calling into Win32 system code so wouldn't be completely platform agnostic.

Take a look at Scotts blog post and don't be foolished by the assembly name. It's just a file name of a standard file in the .Net framework.
Here are more informations direct out of MSDN for the WindowsFormsApplicationBase.

You can use a system-wide Semaphore, using the Semaphore Constructor (Int32, Int32, String, Boolean%) constructor and a fairly unique name.
Cheers, Matthias

If your application produces/consumes files, then you're better registering a system wide communication mechanism (e.g. a remoting or WCF endpoint, or even a socket). Then, if the second instance of the application is being launched from double clicking one of your files, you can send the file information across to the running instance.
Otherwise, if it's a standalone program, then as others have said, a Mutex or Semaphore would server equally well.

solution in Windows form application Prohibit again run application(reopen application).
1- first add Class RunAlready.cs
2-Call method processIsRunning() with Name Process from RunAlready.cs in Program.cs
Program.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Tirage.MainStand
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
PublicClass.Class.RunAlready RunAPP = new PublicClass.Class.RunAlready();
string outApp = RunAPP.processIsRunning("Tirage.MainStand");
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
MainStand_FrmLogin fLogin = new MainStand_FrmLogin();
if (outApp.Length == 0)
{
if (fLogin.ShowDialog() == DialogResult.OK)
{
Application.Run(new MainStand_masterFrm());
}
}
else MessageBox.Show( "Instance already running");
}
}
}
class RunAlready:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PublicClass.Class
{
public class RunAlready
{
public string processIsRunning(string process)
{
string xdescription = "";
System.Diagnostics.Process[] processes =
System.Diagnostics.Process.GetProcessesByName(process);
foreach (System.Diagnostics.Process proc in processes)
{
var iddd = System.Diagnostics.Process.GetCurrentProcess().Id;
if (proc.Id != System.Diagnostics.Process.GetCurrentProcess().Id)
{
xdescription = "Application Run At time:" + proc.StartTime.ToString() + System.Environment.NewLine;
xdescription += "Current physical memory : " + proc.WorkingSet64.ToString() + System.Environment.NewLine;
xdescription += "Total processor time : " + proc.TotalProcessorTime.ToString() + System.Environment.NewLine;
xdescription += "Virtual memory size : " + proc.VirtualMemorySize64.ToString() + System.Environment.NewLine;
}
}
return xdescription;
}
}
}

Related

FolderBrowserDialog won't show in a single .cs file without Form

I am trying to code a program which is executed when a file is right clicked in windows, and then a context menu feature named 'Move to' executes a file in the windows registry HKEY ClASSES. It ought to parse in "%1" as argument when it executes, so that my program knows where the file is located. However, when I compile my single .cs file, the FolderBrowserDialog won't show. I am suspecting that it is because I haven't initialized some kind of form before I call it. Is it possible in some way to choose a folder from a single c# file without including Forms?
using System;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
public class MoveTo : Form
{
public static string current_file_path;
public static string new_file_path;
public static string file_name;
public static void Main(string[] args){
if (args.Length > 0)
{
current_file_path = (string) args[0];
file_name = (string) current_file_path.Replace(Path.GetDirectoryName(Environment.GetCommandLineArgs()[1]), "");
var browser = new FolderBrowserDialog();
if (browser.ShowDialog()==DialogResult.OK)
{
new_file_path = browser.SelectedPath + file_name;
}else
{
Environment.Exit(1);
}
try
{
File.Move(current_file_path, new_file_path);
}catch(Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}
}
If you bypass the argument check and try to show the FBD in a debugger, with this exact code, you will see System.Threading.ThreadStateException: 'Current thread must be set to single thread apartment (STA) mode before OLE calls can be made. Ensure that your Main function has STAThreadAttribute marked on it. This exception is only raised if a debugger is attached to the process.'
As per the error message, this exception won't be raised if no debugger is attached. Put an [STAThread] attribute on your Main method, like you normally see in any windows forms app:
[STAThread]
public static void Main(string[] args)
{
...
I also recommend you add an else for your outer if, to show an error if no arguments are passed (otherwise your app will exit silently

Tracking files of a particular extension

I want to track the files which are opened by the user, and select them by one particular extension. If the opened file has that extension, then I want to assign it's file path to a variable for further processing. Example applications are very cpu demanding. Is there an easy, efficient way to do that?
System wide monitoring of file-->open events (including network drives, thumb drives, etc) would require you to write a FS filter driver.
Since you have access to the machine, and you definitely need system wide access, you could simply write a simple app that will be associated with the Powerpoint extensions, perform the copy, then open Powerpoint using the filepath as a command line argument. It would look similar to the following:
using System;
using System.Windows;
using System.Diagnostics;
using System.IO;
namespace WpfApplication1
{
internal class MainWindow : Window
{
public MainWindow()
{ }
[STAThread()]
static void Main(string[] args)
{
if (args.Length == 0)
{
// [ show error or print usage ]
return;
}
if (!File.Exists(args[0]))
{
// [ show error or print usage ]
return;
}
// Perform the copy
FileInfo target = new FileInfo(args[0]);
string destinationFilename = string.Format("X:\\ExistingFolder\\{0}", target.Name);
File.Copy(target.FullName, destinationFilename);
// You may need to place the filename in quotes if it contains spaces
string targetPath = string.Format("\"{0}\"", target.FullName);
string powerpointPath = "[FullPathToPowerpointExecutable]";
Process powerpointInstance = Process.Start(powerpointPath, targetPath);
// This solution is using a wpf windows app to avoid
// the flash of the console window. However if you did
// wish to display an accumulated list then you may choose
// to uncomment the following block to display your UI.
/*
Application app = new Application();
app.MainWindow = new MainWindow();
app.MainWindow.ShowDialog();
app.Shutdown(0);
*/
Environment.Exit(0);
}
}
}
Hope this helps.

Notify when new appdomain created in the process

Consider next situation. I have injected my managed dll into the process using EasyHook. EasyHook injects dll using separate AppDomain. Now I need a way to get notifications about creation of new AppDomain in the current process.
So the question is there a way do get notifications when a new AppDomain was created in the process?
There is no event or easy way to do it, there is a COM interrupt that allows you to get a list of app domains loaded but any events etc are all hidden from us on private interfaces.
There is two ways you could do this but both require you to actively seek the information i.e. there is no event to register too.
Using Performance Counters.
Using mscoree COM interrupt.
Both there options can complement each other but it depends what level of information you need.
Using Performance Counters
CLR has numerous performance counters available but the one we care about resides in the category ".Net CLR Loading" and it is the counter called "Total Appdomains".
Using the System.Diagnostics namespace you can get the number of app domains per instance/process running in you machine.
Like the code below:
PerformanceCounter toPopulate = new PerformanceCounter(".Net CLR Loading", "Total Appdomains", "ConsoleApplication2.vshost", true);
Console.WriteLine("App domains listed = {0}", toPopulate.NextValue().ToString());
(please note the example needs the application instance name if you create your own app make sure to change this)
You can wrap this on a loop and raise an even for your app when the number changes.
(Not elegant but there is no way around it at the moment)
Using mscoree COM interrupt
Further more if you want to List all the app domains in a process you need to make use the MSCOREE.TBL library which is a COM library used by the CLRHost.
You can find the library at C:\WINDOWS\Microsoft.NET\Framework\vXXXXXX\mscoree.tlb
using mscoree;
If you are using it on window 7 or above you must make sure that the embed assembly type in the reference properties is turned off as this assembly can not be embedded like that.
See further information on this stack post: Interop type cannot be embedded
See the code below to see how you can return and list all app domains in a process (this will return the actual AppDomain instances for each app domain).
The original stack post for this can be found here: List AppDomains in Process
public static List<AppDomain> GetAppDomains()
{
List<AppDomain> _IList = new List<AppDomain>();
IntPtr enumHandle = IntPtr.Zero;
CorRuntimeHostClass host = new mscoree.CorRuntimeHostClass();
try
{
host.EnumDomains(out enumHandle);
object domain = null;
while (true)
{
host.NextDomain(enumHandle, out domain);
if (domain == null) break;
AppDomain appDomain = (AppDomain)domain;
_IList.Add(appDomain);
}
return _IList;
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
return null;
}
finally
{
host.CloseEnum(enumHandle);
Marshal.ReleaseComObject(host);
}
}
Now that you can see how many app domains exist in a process and list them let put that to the test.
Below is a fully working example using both techniques.
using System;
using System.Collections.Generic;
using System.Drawing.Printing;
using System.Linq;
using System.Printing;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Xps.Packaging;
using System.Runtime.InteropServices;
using mscoree;
using System.Diagnostics;
using System.Threading;
namespace ConsoleApplication2
{
class AppDomainWorker
{
public void DoSomeWork()
{
while (true)
{
for (int i = 0; i < 1000; i++)
{
var hello = "hello world".GetHashCode();
}
Thread.Sleep(500);
}
}
}
class Program
{
[STAThread]
static void Main(string[] args)
{
PerformanceCounter toPopulate = new PerformanceCounter(".Net CLR Loading", "Total Appdomains", "ConsoleApplication2.vshost", true);
Console.WriteLine("App domains listed = {0}", toPopulate.NextValue().ToString());
for (int i = 0; i < 10; i++)
{
AppDomain domain = AppDomain.CreateDomain("App Domain " + i);
domain.DoCallBack(() => new Thread(new AppDomainWorker().DoSomeWork).Start());
Console.WriteLine("App domains listed = {0}", toPopulate.NextValue().ToString());
}
Console.WriteLine("List all app domains");
GetAppDomains().ForEach(a => {
Console.WriteLine(a.FriendlyName);
});
Console.WriteLine("running, press any key to stop");
Console.ReadKey();
}
public static List<AppDomain> GetAppDomains()
{
List<AppDomain> _IList = new List<AppDomain>();
IntPtr enumHandle = IntPtr.Zero;
CorRuntimeHostClass host = new mscoree.CorRuntimeHostClass();
try
{
host.EnumDomains(out enumHandle);
object domain = null;
while (true)
{
host.NextDomain(enumHandle, out domain);
if (domain == null) break;
AppDomain appDomain = (AppDomain)domain;
_IList.Add(appDomain);
}
return _IList;
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
return null;
}
finally
{
host.CloseEnum(enumHandle);
Marshal.ReleaseComObject(host);
}
}
}
}
I hope this is helpful and if you need any further help let us know.

C# IO Reading and Writing file in use error

I have a library that handles reading and writing a cache file. This library is used by a Windows Service and several instances of a console application on the same machine. The console application runs when a user logs in.
I am getting occasional IO errors saying the cache file is in use by another process. I assume that collisions are occurring between the different application instances and service trying to read and write at the same time.
Is there a way to lock the file when it is in use and force all other requests to "wait in line" to access the file?
private void SaveCacheToDisk(WindowsUser user) {
string serializedCache = SerializeCache(_cache);
//encryt
serializedCache = AES.Encrypt(serializedCache);
string path = user == null ? ApplicationHelper.CacheDiskPath() :
_registry.GetCachePath(user);
string appdata = user == null ? ApplicationHelper.ClientApplicationDataFolder() :
_registry.GetApplicationDataPath(user);
if (Directory.Exists(appdata) == false) {
Directory.CreateDirectory(appdata);
}
if (File.Exists(path) == false) {
using (FileStream stream = File.Create(path)) { }
}
using (FileStream stream = File.Open(path, FileMode.Truncate)) {
using (StreamWriter writer = new StreamWriter(stream)) {
writer.Write(serializedCache);
}
}
}
private string ReadCacheFromDisk(WindowsUser user) {
//cache file path
string path = user == null ? ApplicationHelper.CacheDiskPath() :
_registry.GetCachePath(user);
using (FileStream stream = File.Open(path, FileMode.Open)) {
using (StreamReader reader = new StreamReader(stream)) {
string serializedCache = reader.ReadToEnd();
//decrypt
serializedCache = AES.Decrypt(serializedCache);
return serializedCache;
}
}
}
Sure, you could use a mutex and permit access only when holding the mutex.
You could use a cross-process EventWaitHandle. This lets you create and use a WaitHandle that's identified across processes by name. A thread is notified when it's its turn, does some work, and then indicates it's done allowing another thread to proceed.
Note that this only works if every process/thread is referring to the same named WaitHandle.
The EventWaitHandle constructors with strings in their signature create named system synchronization events.
One option you could consider is having the console applications route their file access through the service, that way there's only one process accessing the file and you can synchronise access to it there.
One way of implementing this is by remoting across an IPC channel (and here's another example from weblogs.asp.net). We used this technique in a project for the company I work for and it works well, with our specific case providing a way for a .net WebService to talk to a Windows Service running on the same machine.
Sample based on the weblogs.asp.net example
Basically what you need to do with the code below is create a Solution, add two Console Apps (one called "Server" and the other called "Client" and one Library to it. Add a reference to the Library to both console apps, paste the code below in and add a reference to System.Runtime.Remoting to both Server & Console.
Run the Server app, then run the client app. Observe the fact that the server app has a message passed to it by the client. You can extend this to any number of messages/tasks
// Server:
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Ipc;
namespace RemotingSample
{
public class Server
{
public Server()
{
}
public static int Main(string[] args)
{
IpcChannel chan = new IpcChannel("Server");
//register channel
ChannelServices.RegisterChannel(chan, false);
//register remote object
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(RemotingSample.RemoteObject),
"RemotingServer",
WellKnownObjectMode.SingleCall);
Console.WriteLine("Server Activated");
Console.ReadLine();
return 0;
}
}
}
// Client:
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Ipc;
using RemotingSample;
namespace RemotingSample
{
public class Client
{
public Client()
{
}
public static int Main(string[] args)
{
IpcChannel chan = new IpcChannel("Client");
ChannelServices.RegisterChannel(chan);
RemoteObject remObject = (RemoteObject)Activator.GetObject(
typeof(RemotingSample.RemoteObject),
"ipc://Server/RemotingServer");
if (remObject == null)
{
Console.WriteLine("cannot locate server");
}
else
{
remObject.ReplyMessage("You there?");
}
return 0;
}
}
}
// Shared Library:
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
namespace RemotingSample
{
public class RemoteObject : MarshalByRefObject
{
public RemoteObject()
{
Console.WriteLine("Remote object activated");
}
public String ReplyMessage(String msg)
{
Console.WriteLine("Client : " + msg);//print given message on console
return "Server : I'm alive !";
}
}
}
Check out the TextWriter.Synchronized method.
http://msdn.microsoft.com/en-us/library/system.io.textwriter.synchronized.aspx
This should let you do this:
TextWriter.Synchronized(writer).Write(serializedCache);

How to determine Windows.Diagnostics.Process from ServiceController

This is my first post, so let me start by saying HELLO!
I am writing a windows service to monitor the running state of a number of other windows services on the same server. I'd like to extend the application to also print some of the memory statistics of the services, but I'm having trouble working out how to map from a particular ServiceController object to its associated Diagnostics.Process object, which I think I need to determine the memory state.
I found out how to map from a ServiceController to the original image name, but a number of the services I am monitoring are started from the same image, so this won't be enough to determine the Process.
Does anyone know how to get a Process object from a given ServiceController? Perhaps by determining the PID of a service? Or else does anyone have another workaround for this problem?
Many thanks, Alex
System.Management should work for you in this case. Here's a sample to get you started:
using System;
using System.Diagnostics;
using System.ServiceProcess;
using System.Management;
class Program
{
static void Main(string[] args)
{
foreach (ServiceController scTemp in ServiceController.GetServices())
{
if (scTemp.Status == ServiceControllerStatus.Stopped)
continue; // stopped, so no process ID!
ManagementObject service = new ManagementObject(#"Win32_service.Name='" + scTemp.ServiceName + "'");
object o = service.GetPropertyValue("ProcessId");
int processId = (int) ((UInt32) o);
Process process = Process.GetProcessById(processId);
Console.WriteLine("Service: {0}, Process ID: {1}", scTemp.ServiceName, processId);
}
}
}

Categories

Resources