Checking if my Windows application is running - c#

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.

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.

Change a List to a Button ? (modificate ProcessFreezer by )

I just downloaded this Open-Source Program:
http://fabi.me/tools/processfreezer/
and I want to modify something in it. But I really don't know how...
This program allows you to freeze a process. You select the process through a Listbox.
But I just want to freeze a one specific program which is manually given in a string.
For example:
string process = "C:\\Program Files (x86)\\yolo\\yolo.exe";
How can I override the Listbox and give that information to the suspend function?
Here's the code
Process[] GetCheckedProcesses()
{
Process[] procs = new Process[lvProcesses.CheckedItems.Count];
for (int i = 0; i < procs.Length; i++)
procs[i] = (Process)lvProcesses.CheckedItems[i].Tag;
return procs;
}
private void btnSuspend_Click(object sender, EventArgs e)
{
Process[] procs = GetCheckedProcesses();
for (int i = 0; i < procs.Length; i++)
{
try { ProcessFreezer.SuspendProcess(procs[i]); ; }
catch (Exception ex) { MessageBox.Show(ex.ToString(), "FEHLER"); }
}
}
Here's a snippet of ProcessIsSuspend
public static bool ProcessIsSuspended(Process proc)
{
bool suspended = true;
foreach (ProcessThread pT in proc.Threads)
suspended &= ( pT.ThreadState == System.Diagnostics.ThreadState.Wait
&& pT.WaitReason == ThreadWaitReason.Suspended );
return suspended;
}
So the Button should just freeze the process without asking which...
Hopefully its understandable. I just started with C# and its a bit different to C++
A process to file name mapping is an n-to-1 relationship (consider two instances of Visual Studio for example).
In order to find processes based on a file name you can use the file of the 'main module':
Process proc;
try
{
string fileName = proc.MainModule.FileName
//check if the file matches what you are looking for.
}
catch (Win32Exception e)
{
//some processes are protected, you
//will not be allowed to access the file name
//even as an administrator
}
So basically filter the list of processes based on your file name.

How to close a file in Autocad using C# keeping acad.exe running?

I am using visual studio 2010 and I am having a .DWG file which I want to open in autocad. Till now I have used this.
Process p = new Process();
ProcessStartInfo s = new ProcessStartInfo("D:/Test File/" + fileName);
p.StartInfo = s;
p.Start();
But what I want is to close the file inside the Autocad but not the autocad itself. (Means atocad.exe should be kept running).
Till now I hve used this but its closing the acad.exe not the file.
foreach (Process Proc in Process.GetProcesses())
{
if (Proc.ProcessName.Equals("acad"))
{
Proc.CloseMainWindow();
Proc.Kill();
}
}
Take the Autocad .NET libraries from Autodesk Sites (http://usa.autodesk.com/adsk/servlet/index?id=773204&siteID=123112)
Then you will be able to use Application and Document classes.
They will give you full control over opening and closing documents within the application.
You can find many articles on that, and can ask further questions.
AutoCAD does have an api. there are 4 assemblys. Two for in-process and two for COM.
inprocess :
acdbmgd.dll
acmgd.dll
COMInterop :
Autodesk.Autocad.Interop.dll
Autodesk.Autocad.Interop.Common.dll
this is a method that will open a new instance of AutoCAD or it will connect to an existing running instance of AutoCAD.
you will need to load these .dlls into your project references.
using Autodesk.AutoCAD.Interop;
using Autodesk.AutoCAD.Interop.Common;
namespace YourNameSpace {
public class YourClass {
AcadApplication AcApp;
private const string progID = "AutoCAD.Application.18.2";// this is AutoCAD 2012 program id
private string profileName = "<<Unnamed Profile>>";
private const string acadPath = #"C:\Program Files\Autodesk\AutoCAD 2012 - English\acad.exe";
public void GetAcApp()
{
try
{
AcApp = (AcadApplication)Marshal.GetActiveObject(progID);
} catch {
try {
var acadProcess = new Process();
acadProcess.StartInfo.Arguments = string.Format("/nologo /p \"{0}\"", profileName);
acadProcess.StartInfo.FileName = (#acadPath);
acadProcess.Start();
while(AcApp == null)
{
try { AcApp = (AcadApplication)Marshal.GetActiveObject(progID); }
catch { }
}
} catch(COMException) {
MessageBox.Show(String.Format("Cannot create object of type \"{0}\"",progID));
}
}
try {
int i = 0;
var appState = AcApp.GetAcadState();
while (!appState.IsQuiescent)
{
if(i == 120)
{
Application.Exit();
}
// Wait .25s
Thread.Sleep(250);
i++;
}
if(AcApp != null){
// set visibility
AcApp.Visible = true;
}
} catch (COMException err) {
if(err.ErrorCode.ToString() == "-2147417846"){
Thread.Sleep(5000);
}
}
}
}
}
closeing it is as simple as
Application.Exit();
and forgive the code. its atrocious, this was one of my first methods when i just started developing...
I doubt you will be able to do this unless AutoCAD has an API that you can hook into and ask it to close the file for you.
Your c# app can only do things to the process (acad.exe) , it doesn't have access to the internal operations of that process.
Also, you shouldn't use Kill unless the process has become unresponsive and certainly not immediately after CloseMainWindow.
CloseMainWindow is the polite way to ask an application to close itself. Kill is like pulling the power lead from the socket. You aren't giving it the chance to clean up after itself and exit cleanly.
There is one other possibility - this will only work if your C# code is running on the same machine as the AutoCAD process and it is not really recommended, but, if you are really stuck and are prepared to put up with the hassle of window switching you can send key strokes to an application using the SendKeys command.
MSDN articles here:
http://msdn.microsoft.com/EN-US/library/ms171548(v=VS.110,d=hv.2).aspx
http://msdn.microsoft.com/en-us/library/system.windows.forms.sendkeys.send.aspx
Using this you could send the key strokes to simulate the user using the menu commands to close the file.
To perform the closing of file, best way out is to follow the steps at this ObjectARX SDK for c# and change the following code with the below code.
[CommandMethod("CD", CommandFlags.Session)]
static public void CloseDocuments()
{
DocumentCollection docs = Application.DocumentManager;
foreach (Document doc in docs)
{
// First cancel any running command
if (doc.CommandInProgress != "" &&
doc.CommandInProgress != "CD")
{
AcadDocument oDoc =
(AcadDocument)doc.AcadDocument;
oDoc.SendCommand("\x03\x03");
}
if (doc.IsReadOnly)
{
doc.CloseAndDiscard();
}
else
{
// Activate the document, so we can check DBMOD
if (docs.MdiActiveDocument != doc)
{
docs.MdiActiveDocument = doc;
}
int isModified =
System.Convert.ToInt32(
Application.GetSystemVariable("DBMOD")
);
// No need to save if not modified
if (isModified == 0)
{
doc.CloseAndDiscard();
}
else
{
// This may create documents in strange places
doc.CloseAndSave(doc.Name);
}
}
}

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

open file with a running process

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.

Categories

Resources