Run the current application as Single Instance and show the previous instance - c#

I just implemented this code that is guarding the Single Instance of the Application, in order to not run the application twice.
Now I am wondering how I can show the original Application process that is already running.
Here is my code in the program class:
static class Program
{
[STAThread]
static void Main()
{
const string appName = "MyappName";
bool createdNew;
mutex = new Mutex(true, appName, out createdNew);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Form form = new Form1();
if (!createdNew)
{
form.Show(); <<=========================== NOT WORKING
form.Visible = true; <<===================== None
form.TopMost = true; <<===================== of
form.BringToFront(); <<===================== these working!
form.WindowState = FormWindowState.Maximized;
return;
}
Application.Run(form);
} private static Mutex mutex = null;
}

I propose you a different method, using a combination of the System.Threading.Mutex class and UIAutomation AutomationElement class.
A Mutex can be, as you already know, a simple string. You can assign an application a Mutex in the form of a GUID, but it can be anything else.
Let's assume this is the current Application Mutex:
string ApplicationMutex = "BcFFcd23-3456-6543-Fc44abcd1234";
//Or
string ApplicationMutex = "Global\BcFFcd23-3456-6543-Fc44abcd1234";
Note:
Use the "Global\" Prefix to define the scope of the Mutex. If no prefix is specified, the "Local\" prefix is assumed and used instead. This will prevent a single instance of the process when multiple desktops are active or Terminal Services is running on the server.
If we want to verify whether another running Process has already registered the same Mutex, we try to register our Mutex and if it fails, another instance of our Application is already running.
We let the user know that the Application supports only a single instance, then switch to the running process, showing its interface and finally exit the duplicate Application, disposing of the Mutex.
The method to activate a previous instance of the Application may vary based on the type of the Application, but only some details change.
We can use Process..GetProcesses() to retrieve a list of the running processes and verify if one of them has the same details as ours.
Here, you have a windowed Application (it has an UI), so it's already possible to filter the list, excluding those processes that do not have a MainWindowHandle.
Process[] windowedProcesses =
Process.GetProcesses().Where(p => p.MainWindowHandle != IntPtr.Zero).ToArray();
To identify the right one, we could test if the Process.ProcessName is the same.
But this name is tied to the executable name. If the file name changes (someone changes it for some reason), we will never identify the Process this way.
One possible way to identify the right Process is to test the Process.MainModule.FileVersionInfo.ProductName and check whether it's the same.
When found, it's possible to bring the original Application to front with an AutomationElement created using the MainWindowHandle of the identified Process.
The AutomationElement can automate different Patterns (sort of controls that provide automation functionalities for UI elements).
A WindowPattern allows to control a window-base control (the Platform is irrelevant, could be a WinForms' Form or a WPF's Window).
AutomationElement element = AutomationElement.FromHandle(process.MainWindowHandle);
WindowPattern wPattern = element.GetCurrentPattern(WindowPattern.Pattern) as WindowPattern;
wPattern.SetWindowVisualState(WindowVisualState.Normal);
To use the UIAutomation functionalities, you have to add these refereneces in your Project:
- UIAutomationClient
- UIAutomationTypes
UPDATE:
Since the Application's Form might be hidden, Process.GetProcesses() will not find it's Window handle, thus AutomationElement.FromHandle() cannot be used to identify the Form Window.
A possible workaround, without dismissing the UIAutomation "pattern", is to register an Automation event, using Automation.AddAutomationEventHandler, which allows to receive a notification when an UI Automation events occurs, such as a new Window is about to be shown (a Program is run).
The event is registerd only if the Application needs to run as Single Instance. When the event is raised, the new Process AutomationElement Name (the Windows Title Text) is compared to the current and, if it's the same, the hidden Form will un-hide and show itself in Normal state.
As a fail-safe measure, we present an information MessageBox. The MessageBox caption has the same caption as the Application MainForm.
(Tested with a Form with its WindowsState set to Minimized and its Visible property set to false).
After the orginal Process has been brought to front, we just neeed to close the current thread and release the resources we created (mainly the Mutex, in this case).
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Windows.Automation;
using System.Windows.Forms;
static class Program
{
static Mutex mutex = null;
[STAThread]
static void Main()
{
Application.ThreadExit += ThreadOnExit;
string applicationMutex = #"Global\BcFFcd23-3456-6543-Fc44abcd1234";
mutex = new Mutex(true, applicationMutex);
bool singleInstance = mutex.WaitOne(0, false);
if (!singleInstance)
{
string appProductName = Process.GetCurrentProcess().MainModule.FileVersionInfo.ProductName;
Process[] windowedProcesses =
Process.GetProcesses().Where(p => p.MainWindowHandle != IntPtr.Zero).ToArray();
foreach (Process process in windowedProcesses.Where(p => p.MainModule.FileVersionInfo.ProductName == appProductName))
{
if (process.Id != Process.GetCurrentProcess().Id)
{
AutomationElement wElement = AutomationElement.FromHandle(process.MainWindowHandle);
if (wElement.Current.IsOffscreen)
{
WindowPattern wPattern = wElement.GetCurrentPattern(WindowPattern.Pattern) as WindowPattern;
#if DEBUG
WindowInteractionState state = wPattern.Current.WindowInteractionState;
Debug.Assert(!(state == WindowInteractionState.NotResponding), "The application is not responding");
Debug.Assert(!(state == WindowInteractionState.BlockedByModalWindow), "Main Window blocked by a Modal Window");
#endif
wPattern.SetWindowVisualState(WindowVisualState.Normal);
break;
}
}
}
Thread.Sleep(200);
MessageBox.Show("Application already running", "MyApplicationName",
MessageBoxButtons.OK, MessageBoxIcon.Information,
MessageBoxDefaultButton.Button1, MessageBoxOptions.ServiceNotification);
}
if (SingleInstance) {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MyAppMainForm());
}
else {
Application.ExitThread();
}
}
private static void ThreadOnExit(object s, EventArgs e)
{
mutex.Dispose();
Application.ThreadExit -= ThreadOnExit;
Application.Exit();
}
}
In the Application MainForm constructor:
(this is used in case the Application's Main Window is hidden when a new instance is run, hence the procedure in Program.cs cannot find its handle)
public partial class MyAppMainForm : Form
{
public MyAppMainForm()
{
InitializeComponent();
Automation.AddAutomationEventHandler(WindowPattern.WindowOpenedEvent,
AutomationElement.RootElement,
TreeScope.Subtree, (uiElm, evt) =>
{
AutomationElement element = uiElm as AutomationElement;
string windowText = element.Current.Name;
if (element.Current.ProcessId != Process.GetCurrentProcess().Id && windowText == this.Text)
{
this.BeginInvoke(new MethodInvoker(() =>
{
this.WindowState = FormWindowState.Normal;
this.Show();
}));
}
});
}
}

Run Only One time :
static class Program
{
[STAThread]
static void Main()
{
bool createdNew = true;
using (Mutex mutex = new Mutex(true, "samplename", out createdNew))
{
if (createdNew)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
Application.Run(new Form1());
}
else
{
ProcessUtils.SetFocusToPreviousInstance("samplename");
}
}
}
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
}
private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
{
}
}
ProcessUtils :
public static class ProcessUtils
{
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
static extern bool IsIconic(IntPtr hWnd);
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
const int SW_RESTORE = 9;
[DllImport("user32.dll")]
static extern IntPtr GetLastActivePopup(IntPtr hWnd);
[DllImport("user32.dll")]
static extern bool IsWindowEnabled(IntPtr hWnd);
public static void SetFocusToPreviousInstance(string windowCaption)
{
IntPtr hWnd = FindWindow(null, windowCaption);
if (hWnd != null)
{
IntPtr hPopupWnd = GetLastActivePopup(hWnd);
if (hPopupWnd != null && IsWindowEnabled(hPopupWnd))
{
hWnd = hPopupWnd;
}
SetForegroundWindow(hWnd);
if (IsIconic(hWnd))
{
ShowWindow(hWnd, SW_RESTORE);
}
}
}
}
Normal Run :
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}

If you are still looking for an answer. There is a good example here that
uses windows messages to restore the previous instance. It work even if the first instance is minimized contrary to FindWindow witch does not work in that case.

Related

Giving focus to an external application (Chrome)

Good morning,
I'm trying to figure out how to put the focus on Google Chrome. That is to say to make as a click on the software in the taskbar (already open).
Thank you for helping me to put the focus on Google Chrome.
You can use the following function to bring chrome to front and to focus it.
The code uses 2 windows API calls because what you need is not directly provided by the .Net framework.
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
public class Program
{
[DllImport("user32.dll")]
[return: MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
private static extern bool ShowWindow(IntPtr hWnd, int flags);
[DllImport("user32.dll")]
public static extern int SetForegroundWindow(IntPtr hwnd);
private static bool FocusChromeWindow()
{
foreach (Process chrome in Process.GetProcessesByName("chrome"))
{
// In case the process did not reveal a main window handle
// try to restore it in case it is hidden
if (chrome.MainWindowHandle == IntPtr.Zero)
{
ShowWindow(chrome.Handle, 9); // 9 = Restore
}
// If main window handle is still zero,
// this chrome process is one of the background
// workers chrome starts. Skip it.
if (chrome.MainWindowHandle != IntPtr.Zero)
{
SetForegroundWindow(chrome.MainWindowHandle);
return true;
}
}
return false;
}
static void Main(string[] args)
{
FocusChromeWindow();
Console.ReadLine();
}
}

Kill Process in Panel

I have a Win Forms app in which I am starting a process from a different .exe file, then setting it's Handle property to a panel in my Win Form app. This gives the effect of the other app running inside of the Win Forms app.
Here is how I accomplish that:
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr SetParent(IntPtr hwc, IntPtr hwp);
string exepath = "myProgram.exe";
ProcessStartInfo p = new ProcessStartInfo(exepath);
process = Process.Start(p);
Thread.Sleep(500); //sleep to allow program to start up properly
SetParent(process.MainWindowHandle, pictureBox1.Handle); //then set the handle to give the effect of being run inside the win forms app
Now I know I can call process.Kill() in a FormClosed event, which terminates the process when this form is closed, but how would I go about killing the process if my Win Form app is forcefully quit? Is that even possible?
Because the process's handle is set to a panel in the Win Form app, it doesn't appear on the taskbar but it will still continue to run if process.Kill() is not called, which happens when the Win Form is forcefully closed. This means each time I have to shut it off via task manager, which is a pain..
If this is not possible, I will not bother with setting the Handle to the panel, and I will just have it open in a new window.
Thanks
Try with this code, I was using it for a long time:
public partial class Launcher : Form
{
/// <summary>Collection of process. </summary>
private Dictionary<IntPtr, Process> _processCollection = new Dictionary<IntPtr, Process>();
#region DLL Import
[DllImport("user32.dll")]
public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
#endregion
/// <summary>Default constructor. </summary>
public Launcher()
{
InitializeComponent();
try {
FormClosing += Launcher_FormClosing;
StartInstances();
}
catch (Exception ex) {
MessageBox.Show(ex.Message);
}
}
/// <summary>Starts the instances. </summary>
private void StartInstances()
{
var path = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase);
var numberOfInstances = Int32.Parse(ConfigurationManager.AppSettings["NumerOfInstances"]);
for (int i = 0; i < numberOfInstances; i++) {
StartInstance(i, path);
}
}
/// <summary>Starts an instance. </summary>
private void StartInstance(int instanceId, string path)
{
Process proc = Process.Start(path + "\\foo.exe", instanceId.ToString());
IntPtr handlerDocked = IntPtr.Zero;
Panel panel = new Panel();
panel.Size = new Size(flwPanel.Width / 3, flwPanel.Height / 2);
flwPanel.Controls.Add(panel);
do {
try {
proc.WaitForInputIdle(1000); //wait for the window to be ready for input;
proc.Refresh(); //update process info
if (proc.HasExited) {
return; //abort if the process finished before we got a handle.
}
handlerDocked = proc.MainWindowHandle; //cache the window handle
}
catch {
Thread.Sleep(500);
}
} while (handlerDocked == IntPtr.Zero);
//hWndOriginalParent = SetParent(hWndDocked, panel1.Handle);
SetParent(handlerDocked, panel.Handle);
var docked = new DockedElement(handlerDocked, panel);
panel.SizeChanged += new EventHandler(Panel_Resize);
Panel_Resize(docked, new EventArgs());
_processCollection.Add(handlerDocked, proc);
}
private void Panel_Resize(object sender, EventArgs e)
{
var docked = (DockedElement)sender;
//Change the docked windows size to match its parent's size.
MoveWindow(docked.Handle, 0, 0, docked.Container.Width, docked.Container.Height, true);
}
/// <summary>Finallize instances. </summary>
public void FinallizeInstances()
{
foreach (var docked in _processCollection) {
docked.Value.Close();
}
_processCollection.Clear();
}
private void Launcher_FormClosing(object sender, FormClosingEventArgs e)
{
FinallizeInstances();
}
protected override void Dispose(bool disposing)
{
FinallizeInstances();
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
}
To be sure your child processes are properly wiped as soon as your application closes (gracefully or forcefully), you can use Job Objects.
A job object allows groups of processes to be managed as a unit. Job
objects are namable, securable, sharable objects that control
attributes of the processes associated with them. Operations performed
on a job object affect all processes associated with the job object.
Examples include enforcing limits such as working set size and process
priority or terminating all processes associated with a job.
There is a similar question on StackOverflow with a detailed implementation in C#: Working example of CreateJobObject/SetInformationJobObject pinvoke in .net?

how to detect a new application being launched from C#?

I have a .dll library for c# that loves to pop out a 'Welcome' screen when it starts.
This screen appears as an application in the task manager.
Is there some way to automatically detect this application/form being launched and close it?
Thanks! :)
Here us simple console application that will monitor and close specified window
class Program
{
static void Main(string[] args)
{
while(true)
{
FindAndKill("Welcome");
Thread.Sleep(1000);
}
}
private static void FindAndKill(string caption)
{
Process[] processes = Process.GetProcesses();
foreach (Process p in processes)
{
IntPtr pFoundWindow = p.MainWindowHandle;
StringBuilder windowText = new StringBuilder(256);
GetWindowText(pFoundWindow, windowText, windowText.Capacity);
if (windowText.ToString() == caption)
{
p.CloseMainWindow();
Console.WriteLine("Excellent kill !!!");
}
}
}
[DllImport("user32.dll", EntryPoint = "GetWindowText",ExactSpelling = false, CharSet = CharSet.Auto, SetLastError = true)]
private static extern int GetWindowText(IntPtr hWnd,StringBuilder lpWindowText, int nMaxCount);
}
If it's running within your process and opening a Form (not a Dialog), you can use something like this to close all Forms which aren't opened by your own Assembly.
foreach (Form form in Application.OpenForms)
if (form.GetType().Assembly != typeof(Program).Assembly)
form.Close();
What is your own Assembly is defined by the class Program, you could also use Assembly.GetExecutingAssembly or Assembly.GetCallingAssembly, but I'm not sure it will behave correctly, if you run the Application inside Visual Studio (since it might return the VS Assembly).

Problem with SendMessage

I am developing an application using C# having similar functionality of copy,paste as in Windows.
I have added menu items and linked with respective applications.
Please look at the following image for getting more idea.
Items added to shell menu http://softwaregenius.net/myimages/menu.jpg
Like we select multiple items in windows explorer, you need to select multiple files and/or folders and then select OS Util->FastCopy. A form is opened as shown below
Form shown on FastCopy http://softwaregenius.net/myimages/fastcopy1.jpg
The application is working perfectly. The major problem here is that after selecting the files all these files are opening up within there respective softwares. That is if i selected word document then the filename is added to FastCopy form but the is also opening up within Word also.
When i investigate i found that this problem is due to SendMessage. I have to use PostMessage instead of SendMessage. But when i do so the application is not working.
Below is my Main function coding in C# 2005
static class Program
{
static Mutex mutex = new Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE92}");
[STAThread]
static void Main(string[] args)
{
string fileName = "";
if (args.Length > 0)
{
fileName = args[0];
}
if (mutex.WaitOne(TimeSpan.Zero, true))
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
frmFastCopy frm = new frmFastCopy();
frm.AddItemToList(fileName);
Application.Run(frm);
}
else
{
//The following message is sent just to show up the form
NativeMethods.PostMessage(
(IntPtr)NativeMethods.HWND_BROADCAST,
NativeMethods.WM_SHOWME,
IntPtr.Zero,
IntPtr.Zero);
//Send the filename
SendFileName(fileName);
}
}
static void SendFileName(string s)
{
Win32.CopyDataStruct cds = new Win32.CopyDataStruct();
cds.cbData = (s.Length + 1) * 2;
cds.lpData = Win32.LocalAlloc(0x40, cds.cbData);
Marshal.Copy(s.ToCharArray(), 0, cds.lpData, s.Length);
cds.dwData = (IntPtr)1;
Win32.SendMessage((IntPtr)NativeMethods.HWND_BROADCAST, Win32.WM_COPYDATA, IntPtr.Zero, ref cds);
//NativeMethods.PostMessage((IntPtr)NativeMethods.HWND_BROADCAST, Win32.WM_COPYDATA, cds.lpData, IntPtr.Zero);
}
}
}
Below is the copy for WndProc and other code from within the Form
public partial class frmFastCopy : Form
{
delegate void AddItemToListDelegate(string itm);
public frmFastCopy()
{
InitializeComponent();
}
public void AddItemToList(string itm)
{
if (lvFilesAndFolders.InvokeRequired)
{
AddItemToListDelegate m = new AddItemToListDelegate(AddItemToList);
this.Invoke(m, new object[] { itm });
}
else
{
lvFilesAndFolders.Items.Add(itm);
}
}
protected override void WndProc(ref Message m)
{
if (m.Msg==NativeMethods.WM_SHOWME)
{
ShowMe();
}
if (m.Msg==Win32.WM_COPYDATA)
{
//string s = Marshal.PtrToStringUni(m.LParam);
MessageBox.Show("Got message");
Win32.CopyDataStruct st = (Win32.CopyDataStruct)Marshal.PtrToStructure(m.LParam, typeof(Win32.CopyDataStruct));
string strData = Marshal.PtrToStringUni(st.lpData);
AddItemToList(strData);
}
base.WndProc(ref m);
}
private void ShowMe()
{
this.Show();
if (WindowState == FormWindowState.Minimized)
{
WindowState = FormWindowState.Normal;
}
// get our current "TopMost" value (ours will always be false though)
bool top = TopMost;
// make our form jump to the top of everything
TopMost = true;
// set it back to whatever it was
TopMost = top;
}
Here is the NativeCode class
internal class NativeMethods
{
public const int HWND_BROADCAST = 0xffff;
public static readonly int WM_SHOWME = RegisterWindowMessage("WM_SHOWME");
[DllImport("user32")]
public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
[DllImport("user32")]
public static extern int RegisterWindowMessage(string message);
}
I know you guys are genius. Could someone tell me where should i make changes to that the selected files should be opened or rather how i should use postmessage.
Thanks for sharing your valuable time.
Regards
Irfan
Please look at my comment (I wonder why you don't use the Clipboard class here). But ignoring that: Why do you broadcast the message?
Can you locate your application (by name, window class, whatever) and only send the message to your own application?
To elaborate on the message handling:
You say regarding HWND_BROADCAST in the comments below:
Thats nothing but the global handle to
my application.
No, it's not. It is a special value that tells Windows "this message is for all applications". You are sending a WM_SHOWME to all applications. Which is why I asked why you would want to do that?
Please see this post on the old new things blog regarding message broadcasts.

Activating the main form of a single instance application

In a C# Windows Forms application I want to detect if another instance of the application is already running.
If so, activate the main form of the running instance and exit this instance.
What is the best way to achieve this?
Scott Hanselman answers on you question in details.
Here is what I'm currently doing in the application's Program.cs file.
// Sets the window to be foreground
[DllImport("User32")]
private static extern int SetForegroundWindow(IntPtr hwnd);
// Activate or minimize a window
[DllImportAttribute("User32.DLL")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
private const int SW_RESTORE = 9;
static void Main()
{
try
{
// If another instance is already running, activate it and exit
Process currentProc = Process.GetCurrentProcess();
foreach (Process proc in Process.GetProcessesByName(currentProc.ProcessName))
{
if (proc.Id != currentProc.Id)
{
ShowWindow(proc.MainWindowHandle, SW_RESTORE);
SetForegroundWindow(proc.MainWindowHandle);
return; // Exit application
}
}
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
catch (Exception ex)
{
}
}
You can use such detection and activate your instance after it:
// Detect existing instances
string processName = Process.GetCurrentProcess().ProcessName;
Process[] instances = Process.GetProcessesByName(processName);
if (instances.Length > 1)
{
MessageBox.Show("Only one running instance of application is allowed");
Process.GetCurrentProcess().Kill();
return;
}
// End of detection
Aku, that is a good resource. I answered a question similar to this one a while back. You can check my answer here. Even though this was for WPF, you can use the same logic in WinForms.

Categories