open file with a running process - c#

I am making an application that can open a custom document. I connected the document extension to the application (using registry), but when I open the document, it is always opened with a new instance of the application.
I want some logic that can open a document running the current process if it exists. I dont mean a single instance. It should be able to run by multiple instances. Like IE or chrome, it should be able to open an HTML file with tab when the process is running, but it can also run a new instance.
How can I do it?

This article contains a good description (images taken from there as well).
The approach uses ThreadPool object with EventWaitHandle object to pass messages (objects) between processes (.Net Remoting).
When the application starts, it uses CreateSingleInstance() to call the existing instance OR register itself as single instance application.
public static bool CreateSingleInstance( string name, EventHandler<InstanceCallbackEventArgs> callback )
{
EventWaitHandle eventWaitHandle = null;
int curSessionId = System.Diagnostics.Process.GetCurrentProcess().SessionId;
name += curSessionId;
string eventName = string.Format( "{0}-{1}", Environment.MachineName, name );
// If there is another instance
InstanceProxy.IsFirstInstance = false;
InstanceProxy.CommandLineArgs = Environment.GetCommandLineArgs();
try
{
//try to open a handle with the eventName
eventWaitHandle = EventWaitHandle.OpenExisting( eventName );
}
catch
{
InstanceProxy.IsFirstInstance = true;
}
if( InstanceProxy.IsFirstInstance )
{
eventWaitHandle = new EventWaitHandle( false, EventResetMode.AutoReset, eventName );
// register wait handle for this instance (process)
ThreadPool.RegisterWaitForSingleObject( eventWaitHandle, WaitOrTimerCallback, callback, Timeout.Infinite, false );
eventWaitHandle.Close();
// register shared type (used to pass data between processes)
RegisterRemoteType( name );
}
else
{
// here will be the code for the second instance/
}
return InstanceProxy.IsFirstInstance;
}
private static void RegisterRemoteType( string uri )
{
// register remote channel (net-pipes)
var serverChannel = new IpcServerChannel( Environment.MachineName + uri );
ChannelServices.RegisterChannel( serverChannel, true );
// register shared type
RemotingConfiguration.RegisterWellKnownServiceType(
typeof( InstanceProxy ), uri, WellKnownObjectMode.Singleton );
// close channel, on process exit
Process process = Process.GetCurrentProcess();
process.Exited += delegate
{
ChannelServices.UnregisterChannel( serverChannel );
};
}
[Serializable]
[System.Security.Permissions.PermissionSet( System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust" )]
internal class InstanceProxy : MarshalByRefObject
{
private static bool firstInstance;
private static string[] arrCommandLineArgs;
public static bool IsFirstInstance
{
get
{
return firstInstance;
}
set
{
firstInstance = value;
}
}
public static string[] CommandLineArgs
{
get
{
return arrCommandLineArgs;
}
set
{
arrCommandLineArgs = value;
}
}
public void SetCommandLineArgs( bool isFirstInstance, string[] commandLineArgs )
{
firstInstance = isFirstInstance;
arrCommandLineArgs = commandLineArgs;
}
}
public class InstanceCallbackEventArgs : EventArgs
{
private bool firstInstance;
private string[] arrCommandLineArgs;
internal InstanceCallbackEventArgs( bool isFirstInstance, string[] commandLineArgs )
{
firstInstance = isFirstInstance;
arrCommandLineArgs = commandLineArgs;
}
public bool IsFirstInstance
{
get
{
return firstInstance;
}
set
{
firstInstance = value;
}
}
public string[] CommandLineArgs
{
get
{
return arrCommandLineArgs;
}
set
{
arrCommandLineArgs = value;
}
}
}

There are many options here, a few them are:
Try use DDE which is ancient history but it is still used by many applications like MS Office. DDE commands are registered on open command for file extension (look HKEY_CLASSES_ROOT\Excel.Sheet.8\shell\Open for example). If application hasn't already been started, it is launched by OS, and DDE command is submitted. If launched, DDE command is submitted to running instance which is registered as a DDE server.
When your process starts try to create an IpcChannel with a predefined name. If your process is launched with file argument, pass file name to running process via IpcChannel. Problem is only one process can create IpcChannel with same name. If that process quits, other processes are left without an open channel.
Every process creates an IpcChannel using process id. When your process starts with a file argument, you enumerate processes where process' path is same as yours, then connect to that process using IpcChannel (where name can be obtained by looking at process id), and then pass filename to it.
Enumerate processes where process' path is same as yours, and send a WM_COPYDATA message containing your filename.

Related

Get process names of installed programs

How can one get the corresponding process name of the installed programs in Windows (10)? For now, I'm using this
string uninstallKey = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
using (RegistryKey rk = Registry.LocalMachine.OpenSubKey(uninstallKey))
{
foreach (string skName in rk.GetSubKeyNames())
{
using (RegistryKey sk = rk.OpenSubKey(skName))
{
//returns installed programs
}
}
}
to return the installed software. Despite not every installed program being shown, how can I get the name of the process, like it would be shown in Task Manager, that the program would start if it was started?
I want to make an application blacklist. If an application gets started it compares its process with the blacklist. If the process matches with an entry in the list, the process gets killed.
Use static method GetProcesses of Process class to create component for each running process on the local computer.
You can get their names like this:
var processNames = Process.GetProcesses().Select(x => x.ProcessName).ToList();
More about Process class here:
https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process?view=net-6.0
You should consider to use the Windows integrated feature to block applications via the registry. You can create such entries programmatically.
However, you can implement your own, but you must know that you can't prevent applications from starting using your approach. You can only kill it after it was started and after it has allocated resources.
Create your blacklist first: collect all installed application paths and let the user pick the application to blacklist (see CreateInstalledApplicationIndex method).
Use WMI to observe any process starts by registering a corresponding event handler.
In the event handler retrieve the started Process and compare its filename to your blacklisted filenames to identify and handle a forbidden process.
private List<FileInfo> InstallationInfos { get; } = new List<FileInfo>();
private List<FileInfo> BlacklistedExecutables { get; } = new List<FileInfo>();
public void ApplyBlacklist()
{
CreateInstalledApplicationIndex();
WatchProcessStarts();
}
private void CreateInstalledApplicationIndex()
{
string uninstallKey = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
using RegistryKey registryKey = Registry.LocalMachine.OpenSubKey(uninstallKey);
foreach (string subKeyName in registryKey.GetSubKeyNames())
{
using RegistryKey subKey = registryKey.OpenSubKey(subKeyName);
var installationPath = subKey.GetValue("InstallLocation") as string;
if (string.IsNullOrWhiteSpace(installationPath))
{
continue;
}
IEnumerable<FileInfo> fileInfos = Enumerable.Empty<FileInfo>();
try
{
var installationDirectoryInfo = new DirectoryInfo(installationPath);
fileInfos = installationDirectoryInfo.EnumerateFiles("*.exe", new EnumerationOptions());
}
catch (IOException)
{
continue;
}
foreach (FileInfo fileInfo in fileInfos)
{
this.InstallationInfos.Add(fileInfo);
// For demo, all executables are blacklisted.
// TODO::Let user fill Blacklisted collection.
this.BlacklistedExecutables.Add(fileInfo);
}
}
}
private void WatchProcessStarts()
{
WqlEventQuery query = new WqlEventQuery("SELECT * FROM Win32_ProcessStartTrace");
ManagementEventWatcher watcher = new ManagementEventWatcher(query);
watcher.EventArrived += OnProcessStarted;
// Start listening for process start events
watcher.Start();
// Stop listening for process start events
//watcher.Stop();
}
private void OnProcessStarted(object sender, EventArrivedEventArgs e)
{
uint startedProcessId = (uint)e.NewEvent["ProcessID"];
// Note: Convert.ToInt32 will throw an OverflowException
// in case uint does not fit into an int.
// You must decide whether to handle this particular exception or to let it crash your application.
// Since it is very very unlikely that a machine runs Int32.MaxValue processes,
// I recommend not to handle this exception.
Process startedProcess = Process.GetProcessById(Convert.ToInt32(startedProcessId));
bool isProcessBlacklisted = this.BlacklistedExecutables
.Select(fileInfo => fileInfo.FullName)
.Contains(startedProcess.MainModule.FileName);
// TODO::Handle blacklisted process e.g., by killing it
if (isProcessBlacklisted)
{
startedProcess.Kill(entireProcessTree: true);
}
}
It is possible that you have to run your application as administrator in order to observe process starts and to kill them. In this case ensure to prompt the user to elevate your application's rights by restarting it with administrator permissions.
I got a solution which looks like this:
First I get all installed programs based on this
public static void LoadInstalledPrograms()
{
var FOLDERID_AppsFolder = new Guid("{1e87508d-89c2-42f0-8a7e-645a0f50ca58}");
ShellObject appsFolder = (ShellObject)KnownFolderHelper.FromKnownFolderId(FOLDERID_AppsFolder);
foreach (var app in (IKnownFolder)appsFolder)
{
//regular installed programs
if (app.Properties.System.Link.TargetParsingPath.Value != null)
{
AddToInstalledProgramsList(app.Name, app.Properties.System.Link.TargetParsingPath.Value, "reg");
}
//Windows apps/Microsoft store apps
/*else
{
AddToInstalledProgramsList(app.Name, app.Properties.GetProperty("System.AppUserModel.PackageInstallPath").ValueAsObject.ToString(), "win");
}*/
}
}
and then write them to a dictionary which is observed by a BackgroundWorker who kills every process from the list
static Dictionary<String, String> programs = new Dictionary<String, String>();
public static void AddToInstalledProgramsList(string programName, string programPath, string programType)
{
string processName = "";
if (programType == "reg")
{
programPath = programPath.Replace("/", "\\");
processName = programPath.Split("\\").Last();
if (!programs.ContainsKey(programName))
{
programs.Add(programName, processName);
}
else
{
AddDuplicateEntry(programName, processName, 1);
}
}
else if (programType == "win")
{
//...
}
Debug.WriteLine(programName + ": " + processName);
}
If I stumble across problems with this approach I will update this thread.

How to figure out what IIS Express instance is using a port?

I want to kill a running IIS Instance programmatically that is occupying a specific port, but it seems there is no way to figure out what IIS Instance is using a specific port.
netstat.exe just shows that the process is having the PID 4, but that's the system process. "netsh http show urlacl" does not display the occupied port at all.
The IIS Express Tray program knows this somehow. When I try to start another IIS Express instance while the port is occupied I get the following error:
"Port '40000' is already being used by process 'IIS Express' (process ID '10632').
Anyone got a clue how I can get this information?
It seems like the PID is 4 (System) because the actual listening socket is under a service called http.
I looked at what iisexpresstray.exe was using to provide a list of all running IISExpress applications. Thankfully it's managed .NET code (all in iisexpresstray.dll) that's easily decompiled.
It appears to have at least three different ways of getting the port number for a process:
Reading /port from the command-line arguments (unreliable as we know)
Running netsh http show servicestate view=requestq and parsing the output
Calling Microsoft.Web.RuntimeStatusClient.GetWorkerProcess(pid) and parsing the site URL
Unfortunately, most of the useful stuff in iisexpresstray.dll like the IisExpressHelper class is declared internal (although I imagine there're tools to generate wrappers or copy the assembly and publicize everything).
I opted to use Microsoft.Web.dll. It was in my GAC, though for some reason wasn't appearing in the list of assemblies available to add as a reference in Visual Studio, so I just copied the file out from my GAC. Once I had Microsoft.Web.dll it was just a matter of using this code:
using (var runtimeStatusClient = new RuntimeStatusClient())
{
var workerProcess = runtimeStatusClient.GetWorkerProcess(process.Id);
// Apparently an IISExpress process can run multiple sites/applications?
var apps = workerProcess.RegisteredUrlsInfo.Select(r => r.Split('|')).Select(u => new { SiteName = u[0], PhysicalPath = u[1], Url = u[2] });
// If we just assume one app
return new Uri(apps.FirstOrDefault().Url).Port;
}
You can also call RuntimeClient.GetAllWorkerProcesses to retrieve only actual worker processes.
I looked into RegisteredUrlsInfo (in Microsoft.Web.dll) as well and found that it's using two COM interfaces,
IRsca2_Core (F90F62AB-EE00-4E4F-8EA6-3805B6B25CDD)
IRsca2_WorkerProcess (B1341209-7F09-4ECD-AE5F-3EE40D921870)
Lastly, I read about a version of Microsoft.Web.Administration apparently being able to read IISExpress application info, but information was very scarce, and the one I found on my system wouldn't even let me instantiate ServerManager without admin privileges.
Here is a C# implementation of calling netsh.exe as recommended within the answer by #makhdumi:
Usage:
static public bool TryGetCurrentProcessRegisteredHttpPort(out List<int> ports, out Exception ex)
{
NetshInvoker netsh = new NetshInvoker();
return netsh.TryGetHttpPortUseByProcessId(Process.GetCurrentProcess().Id, out ports, out ex);
}
Implementation:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace YourCompanyName.Server.ServerCommon.Utility
{
/// <summary>
/// Invoke netsh.exe and extract information from its output.
/// Source: #crokusek, https://stackoverflow.com/questions/32196188
/// #GETah, https://stackoverflow.com/a/8274758/538763
/// </summary>
public class NetshInvoker
{
const string NetshHttpShowServiceStateViewRequestqArgs = "http show servicestate view=requestq";
public NetshInvoker()
{
}
/// <summary>
/// Call netsh.exe to determine the http port number used by a given windowsPid (e.g. an IIS Express process)
/// </summary>
/// <param name="windowsPid">For example an IIS Express process</param>
/// <param name="port"></param>
/// <param name="ex"></param>
/// <returns></returns>
public bool TryGetHttpPortUseByProcessId(Int32 windowsPid, out List<Int32> ports, out Exception ex)
{
ports = null;
try
{
if (!TryQueryProcessIdRegisteredUrls(out Dictionary<Int32, List<string>> pidToUrlMap, out ex))
return false;
if (!pidToUrlMap.TryGetValue(windowsPid, out List<string> urls))
{
throw new Exception(String.Format("Unable to locate windowsPid {0} in '{1}' output.",
windowsPid, "netsh " + NetshHttpShowServiceStateViewRequestqArgs));
}
if (!urls.Any())
{
throw new Exception(String.Format("WindowsPid {0} did not reference any URLs in '{1}' output.",
windowsPid, "netsh " + NetshHttpShowServiceStateViewRequestqArgs));
}
ports = urls
.Select(u => new Uri(u).Port)
.ToList();
return true;
}
catch (Exception ex_)
{
ex = ex_;
return false;
}
}
private bool TryQueryProcessIdRegisteredUrls(out Dictionary<Int32, List<string>> pidToUrlMap, out Exception ex)
{
if (!TryExecNetsh(NetshHttpShowServiceStateViewRequestqArgs, out string output, out ex))
{
pidToUrlMap = null;
return false;
}
bool gotRequestQueueName = false;
bool gotPidStart = false;
int currentPid = 0;
bool gotUrlStart = false;
pidToUrlMap = new Dictionary<int, List<string>>();
foreach (string line in output.Split('\n').Select(s => s.Trim()))
{
if (!gotRequestQueueName)
{
gotRequestQueueName = line.StartsWith("Request queue name:");
}
else if (!gotPidStart)
{
gotPidStart = line.StartsWith("Process IDs:");
}
else if (currentPid == 0)
{
Int32.TryParse(line, out currentPid); // just get the first Pid, ignore others.
}
else if (!gotUrlStart)
{
gotUrlStart = line.StartsWith("Registered URLs:");
}
else if (line.ToLowerInvariant().StartsWith("http"))
{
if (!pidToUrlMap.TryGetValue(currentPid, out List<string> urls))
pidToUrlMap[currentPid] = urls = new List<string>();
urls.Add(line);
}
else // reset
{
gotRequestQueueName = false;
gotPidStart = false;
currentPid = 0;
gotUrlStart = false;
}
}
return true;
}
private bool TryExecNetsh(string args, out string output, out Exception exception)
{
output = null;
exception = null;
try
{
// From #GETah, https://stackoverflow.com/a/8274758/538763
Process p = new Process();
p.StartInfo.FileName = "netsh.exe";
p.StartInfo.Arguments = args;
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.Start();
output = p.StandardOutput.ReadToEnd();
return true;
}
catch (Exception ex)
{
exception = ex;
return false;
}
}
}
}
In my case I just output "Command line" column in Task manager and it's getting obvious, which IISExpress is that:
You can run below command to get the information of the executable and its PID
netstat -a -n -o -b | find "iisexpress.exe"

Performance Counter Instance Name vs. Process Name

I am connecting to various performance counters in the Process category. I am using the following c# method to determine the instance name to use when acquiring the counters:
private const string _categoryName = "Process";
private const string _processIdCounter = "ID Process";
public static bool TryGetInstanceName(Process process, out string instanceName)
{
PerformanceCounterCategory processCategory = new PerformanceCounterCategory(_categoryName);
string[] instanceNames = processCategory.GetInstanceNames();
foreach (string name in instanceNames)
{
using (PerformanceCounter processIdCounter = new PerformanceCounter(_categoryName, _processIdCounter, name, true))
{
if (process.Id == (int)processIdCounter.RawValue)
{
instanceName = name;
return true;
}
}
}
instanceName = null;
return false;
}
Now, I have noticed that the instance name returned usually matches the value of Process.ProcessName.
How are the instance name and process name related?
I ask because I want to simplify the foreach loop in the routine so that I do not have to acquire the ID Process counter for instances that cannot match the current process. I envision a final method that might look like this:
public static bool TryGetInstanceName(Process process, out string instanceName)
{
PerformanceCounterCategory processCategory = new PerformanceCounterCategory(_categoryName);
string[] instanceNames = processCategory.GetInstanceNames();
foreach (string name in instanceNames)
{
if (name /* more or less matches */ process.ProcessName)
{
using (PerformanceCounter processIdCounter = new PerformanceCounter(_categoryName, _processIdCounter, name, true))
{
// ...
}
}
}
instanceName = null;
return false;
}
Seeing that an answer was not forthcoming, I did some more trial and error testing and observed the following behaviour:
Regular Processes
It appears that, for the first regular process with a given name, the process name matches the instance name. For subsequent processes with the same name, the instance name is modified by appending #1, #2, ...
Alarmingly, it also appears to be possible for the instance name associated with the process to change. This appears to happen when processes earlier in the numeric sequence end. There is a race-condition between determining the instance name and acquiring the relevant counters!
Service Processes
Windows NT Services running under the Service Control Manager appear to behave in the same way that regular processes behave. The instance name also appears to change if you end a service-process earlier in the numeric sequence.
ASP.NET Applications
The same assumptions work for applications hosted under IIS except that the process name is w3wp. Different app. pools definitely get different processes and, by starting and stopping app. pools, I ascertained that the instance name changes in the same way, under the same circumstances as above.
Conclusion
My conclusion is that the instance name always starts with the process name and the method can be modified as follows:
public static bool TryGetInstanceName(Process process, out string instanceName)
{
PerformanceCounterCategory processCategory = new PerformanceCounterCategory(_categoryName);
string[] instanceNames = processCategory.GetInstanceNames();
foreach (string name in instanceNames)
{
if (name.StartsWith(process.ProcessName))
{
using (PerformanceCounter processIdCounter = new PerformanceCounter(_categoryName, _processIdCounter, name, true))
{
if (process.Id == (int)processIdCounter.RawValue)
{
instanceName = name;
return true;
}
}
}
}
instanceName = null;
return false;
}
Additionally, it is vitally important that one acknowledges the presence of the race-condition mentioned above when using the instance name returned.
(In the absence of further input, I will accept this as an answer. Feel free to correct me.)
Also note that if you are monitoring muliple instances of the same process then the instance naming isn't consistent across different categories. So the solution given above only works if you are pulling counters from the same category that the pid was was pulled from. I did find the pid was in some other categores - but not all - and not with consistent naming.
Wouldn't this solution be a little bit faster:
public static bool TryGetInstanceName(Process process, out string instanceName)
{
PerformanceCounterCategory processCategory = new PerformanceCounterCategory(_categoryName);
string processName = Path.GetFileNameWithoutExtension(process.ProcessName);
string[] instanceNames = processCategory.GetInstanceNames()
.Where(inst => inst.StartsWith(processName))
.ToArray();
foreach (string name in instanceNames)
{
using (PerformanceCounter processIdCounter = new PerformanceCounter(_categoryName, _processIdCounter, name, true))
{
if (process.Id == (int)processIdCounter.RawValue)
{
instanceName = name;
return true;
}
}
}
instanceName = null;
return false;
}
(Copied from Rick Strahl's blog and modified a bit).
Nevertheless, you need to take care: If there a multiple processes with the same name and one of them exits, the naming of all of them changes:
One thing to mention related to windows process instance names is that they change dynamically when one of the processes exits.
For example if chrome#8 exits, chrome#9 will become chrome#8 and chrome#10 >will become chrome#9. At this point getting the value of the counter >previously created for chrome#10 will throw an exception. This
is really annoying if you want to to monitor multiple instances of
multiple processes as it gets down to monitoring process exits and
recreating all the counters (really ugly).
One way would be to change the way process instance names are generated >(see http://support.microsoft.com/kb/281884) but this has
the potential of affecting other apps using the perfmon api.

Mutex results are varying in systems

I have a console application in C# and I want to restrict my application to run only one instance at a time.It's work fine in one system.When i try to run the exe in another system it's not working.The problem is In one pc i can open only one exe. When i try to run on another pc i can open more than one exe.How can i resolve this issue? Below are the code i have written.
string mutexId = Application.ProductName;
using (var mutex = new Mutex(false, mutexId))
{
if (!mutex.WaitOne(0, false))
{
MessageBox.Show("Instance Already Running!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Hand);
return;
}
//Remaining Code here
}
I would use this approach instead anyway:
// Use a named EventWaitHandle to determine if the application is already running.
bool eventWasCreatedByThisInstance;
using (new EventWaitHandle(false, EventResetMode.ManualReset, Application.ProductName, out eventWasCreatedByThisInstance))
{
if (eventWasCreatedByThisInstance)
{
runTheProgram();
return;
}
else // This instance didn't create the event, therefore another instance must be running.
{
return; // Display warning message here if you need it.
}
}
My good old solution:
private static bool IsAlreadyRunning()
{
string strLoc = Assembly.GetExecutingAssembly().Location;
FileSystemInfo fileInfo = new FileInfo(strLoc);
string sExeName = fileInfo.Name;
bool bCreatedNew;
Mutex mutex = new Mutex(true, "Global\\"+sExeName, out bCreatedNew);
if (bCreatedNew)
mutex.ReleaseMutex();
return !bCreatedNew;
}
Source

Checking if my Windows application is running

How do I check if my C# Windows application is running ?
I know that I can check the process name but the name can be changed if the exe changes.
Is there any way to have a hash key or something to make my application unique?
public partial class App : System.Windows.Application
{
public bool IsProcessOpen(string name)
{
foreach (Process clsProcess in Process.GetProcesses())
{
if (clsProcess.ProcessName.Contains(name))
{
return true;
}
}
return false;
}
protected override void OnStartup(StartupEventArgs e)
{
// Get Reference to the current Process
Process thisProc = Process.GetCurrentProcess();
if (IsProcessOpen("name of application.exe") == false)
{
//System.Windows.MessageBox.Show("Application not open!");
//System.Windows.Application.Current.Shutdown();
}
else
{
// Check how many total processes have the same name as the current one
if (Process.GetProcessesByName(thisProc.ProcessName).Length > 1)
{
// If ther is more than one, than it is already running.
System.Windows.MessageBox.Show("Application is already running.");
System.Windows.Application.Current.Shutdown();
return;
}
base.OnStartup(e);
}
}
The recommended way is to use a Mutex. You can check out a sample here :
http://www.codeproject.com/KB/cs/singleinstance.aspx
In specific the code:
///
/// check if given exe alread running or not
///
/// returns true if already running
private static bool IsAlreadyRunning()
{
string strLoc = Assembly.GetExecutingAssembly().Location;
FileSystemInfo fileInfo = new FileInfo(strLoc);
string sExeName = fileInfo.Name;
bool bCreatedNew;
Mutex mutex = new Mutex(true, "Global\\"+sExeName, out bCreatedNew);
if (bCreatedNew)
mutex.ReleaseMutex();
return !bCreatedNew;
}
For my WPF application i've defined global app id and use semaphore to handle it.
public partial class App : Application
{
private const string AppId = "c1d3cdb1-51ad-4c3a-bdb2-686f7dd10155";
//Passing name associates this sempahore system wide with this name
private readonly Semaphore instancesAllowed = new Semaphore(1, 1, AppId);
private bool WasRunning { set; get; }
private void OnExit(object sender, ExitEventArgs e)
{
//Decrement the count if app was running
if (this.WasRunning)
{
this.instancesAllowed.Release();
}
}
private void OnStartup(object sender, StartupEventArgs e)
{
//See if application is already running on the system
if (this.instancesAllowed.WaitOne(1000))
{
new MainWindow().Show();
this.WasRunning = true;
return;
}
//Display
MessageBox.Show("An instance is already running");
//Exit out otherwise
this.Shutdown();
}
}
Checkout: What is a good pattern for using a Global Mutex in C#?
// unique id for global mutex - Global prefix means it is global to the machine
const string mutex_id = "Global\\{B1E7934A-F688-417f-8FCB-65C3985E9E27}";
static void Main(string[] args)
{
using (var mutex = new Mutex(false, mutex_id))
{
// edited by Jeremy Wiebe to add example of setting up security for multi-user usage
// edited by 'Marc' to work also on localized systems (don't use just "Everyone")
var allowEveryoneRule = new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), MutexRights.FullControl, AccessControlType.Allow);
var securitySettings = new MutexSecurity();
securitySettings.AddAccessRule(allowEveryoneRule);
mutex.SetAccessControl(securitySettings);
//edited by acidzombie24
var hasHandle = false;
try
{
try
{
// note, you may want to time out here instead of waiting forever
//edited by acidzombie24
//mutex.WaitOne(Timeout.Infinite, false);
hasHandle = mutex.WaitOne(5000, false);
if (hasHandle == false) return;//another instance exist
}
catch (AbandonedMutexException)
{
// Log the fact the mutex was abandoned in another process, it will still get aquired
}
// Perform your work here.
}
finally
{
//edit by acidzombie24, added if statemnet
if (hasHandle)
mutex.ReleaseMutex();
}
}
}
you need a way to say that "i am running" from the app,
1) open a WCF ping service
2) write to registry/file on startup and delete on shutdown
3) create a Mutex
... i prefer the WCF part because you may not clean up file/registry correctly and Mutex seems to have its own issues
Mutex and Semaphore didn't work in my case (I tried them as suggested, but it didn't do the trick in the application I developed). The answer abramlimpin provided worked for me, after I made a slight modification.
This is how I got it working finally.
First, I created some helper functions:
public static class Ext
{
private static string AssemblyFileName(this Assembly myAssembly)
{
string strLoc = myAssembly.Location;
FileSystemInfo fileInfo = new FileInfo(strLoc);
string sExeName = fileInfo.Name;
return sExeName;
}
private static int HowManyTimesIsProcessRunning(string name)
{
int count = 0;
name = name.ToLowerInvariant().Trim().Replace(".exe", "");
foreach (Process clsProcess in Process.GetProcesses())
{
var processName = clsProcess.ProcessName.ToLowerInvariant().Trim();
// System.Diagnostics.Debug.WriteLine(processName);
if (processName.Contains(name))
{
count++;
};
};
return count;
}
public static int HowManyTimesIsAssemblyRunning(this Assembly myAssembly)
{
var fileName = AssemblyFileName(myAssembly);
return HowManyTimesIsProcessRunning(fileName);
}
}
Then, I added the following to the main method:
[STAThread]
static void Main()
{
const string appName = "Name of your app";
// Check number of instances running:
// If more than 1 instance, cancel this one.
// Additionally, if it is the 2nd invocation, show a message and exit.
var numberOfAppInstances = Assembly.GetExecutingAssembly().HowManyTimesIsAssemblyRunning();
if (numberOfAppInstances == 2)
{
MessageBox.Show("The application is already running!
+"\nClick OK to close this dialog, then switch to the application by using WIN + TAB keys.",
appName, MessageBoxButtons.OK, MessageBoxIcon.Warning);
};
if (numberOfAppInstances >= 2)
{
return;
};
}
If you invoke the application a 3rd, 4th ... time, it does not show the warning any more and just exits immediately.
I really simplistic way I guess would be, for every exe that is running, you could create/open a file on disk in a known location (c:\temp) with a special name "yourapp.lock" and then just count how many of those there are.
A harder way, would be to open up some inter-process communication, or sockets, so with the process list you could interrogate each process to see if it was your application.
Enter a guid in your assembly data.
Add this guid to the registry.
Enter a reg key where the application read it's own name and add the name as value there.
The other task watcher read the reg key and knows the app name.
you can simply use varialbles and one file to check for running your program.
when open the file contain a value and when program closes changes this value to another one.

Categories

Resources