I am using mutex to check one instance at the time. It works, but to be perfect I need to fix one bug. If program is in minimized state it will not restore it self after I click Ok. Any ideas?
This is in Program.cs :
if (process.Id != current.Id)
{
SetForegroundWindow(process.MainWindowHandle);
MessageBox.Show(new Form1 { TopMost = true }, "Application is already running!");
Form1 f1 = new Form1();
f1.WindowState = FormWindowState.Normal; // dont work
f1.BringToFront(); // dont work
f1.Focus(); // dont work
break;
}
create an extension method:
using System.Runtime.InteropServices;
namespace System.Windows.Forms
{
public static class Extensions
{
[DllImport( "user32.dll" )]
private static extern int ShowWindow( IntPtr hWnd, uint Msg );
private const uint SW_RESTORE = 0x09;
public static void Restore( this Form form )
{
if (form.WindowState == FormWindowState.Minimized)
{
ShowWindow(form.Handle, SW_RESTORE);
}
}
}
}
then use form.Restore() in your code.
Related
i have a windows desktop application with c# and framework 4.6
the app run in system tray and there is a shortcut icon on desktop
when user click the icon to run it if the app already run dont run new instance, only show (bring to front) existing app
public sealed class SingleInstance
{
private const int SW_HIDE = 0;
private const int SW_SHOW = 5;
public static bool AlreadyRunning()
{
bool running = false;
try
{
// Getting collection of process
Process currentProcess = Process.GetCurrentProcess();
// Check with other process already running
foreach (var p in Process.GetProcesses())
{
if (p.Id != currentProcess.Id) // Check running process
{
if (p.ProcessName.Equals(currentProcess.ProcessName) == true)
{
running = true;
IntPtr hFound = p.MainWindowHandle;
if (User32API.IsIconic(hFound)) // If application is in ICONIC mode then
User32API.ShowWindow(hFound, User32API.SW_RESTORE);
User32API.SetForegroundWindow(hFound); // Activate the window, if process is already running
break;
}
}
}
}
catch { }
return running;
}
}
public class User32API
{
[DllImport("User32.dll")]
public static extern bool IsIconic(IntPtr hWnd);
[DllImport("User32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("User32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
public const int SW_SHOW = 5;
public const int SW_RESTORE = 9;
}
if app run back of the any windows it can show (bring to front) but it in system tray cant show
EDIT:
minimize to tray
private void Form1_Resize(object sender, EventArgs e)
{
if (this.WindowState == FormWindowState.Minimized)
{
Hide();
}
}
to open
private void notifyIcon1_Click(object sender, EventArgs e)
{
this.WindowState = FormWindowState.Minimized;
this.Show();
this.WindowState = FormWindowState.Normal;
}
You can implement a Mutex to make sure there is only one instance of your application running. You can also use it to communicate between instances so the second instance can notify the first one that the user attempted to start your app a second time.
There is already a really good answer to this over here: https://stackoverflow.com/a/522874/14877741
It explains the Mutex concept a lot better than I could and it even has an example that brings the application window back to the foreground.
I am trying to leave my application with just one instance.
The code works fine, but I want to manipulate the window that is already open.
How can I bring the window already open to the front when I open more than one instance.
public partial class App : Application {
private static Mutex _mutex = null;
protected override void OnStartup(StartupEventArgs e)
{
const string appName = "MyAppName";
bool createdNew;
_mutex = new Mutex(true, appName, out createdNew);
if (!createdNew)
{
Application.Current.Shutdown();
}
base.OnStartup(e);
}
}
in OnStartup I tried to call the window using MainWindow.WindowState = WindowState.Normal; but it fails in that call noting the error System.Windows.Application.MainWindow.get returned null.
I created a sample project for single instance application in WPF. Maybe helpful:
Wpf Single Instance Application
You need the following native methods:
public static class NativeMethod
{
public static IntPtr BroadcastHandle => (IntPtr)0xffff;
public static int ReactivationMessage => 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);
}
In app main window add hook to window messages and when you got activation message you should bring the main window in top of other windows. Like this:
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
RegisterWndProcCallback();
}
private void RegisterWndProcCallback()
{
var handle = new WindowInteropHelper(this).EnsureHandle();
var hock = new HwndSourceHook(WndProc);
var source = HwndSource.FromHwnd(handle);
source.AddHook(hock);
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == NativeMethod.ReactivationMessage)
ReactivateMainWindow();
return IntPtr.Zero;
}
private void ReactivateMainWindow()
{
if (WindowState == WindowState.Minimized)
WindowState = WindowState.Normal;
Topmost = !Topmost;
Topmost = !Topmost;
}
}
And in app startup you should check for app instances and if needed you should send a message to the old instance and activate that. Like this:
public partial class App
{
private static readonly Mutex _singleInstanceMutex =
new Mutex(true, "{40D7FB99-C91E-471C-9E34-5D4A455E35E1}");
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
if (_singleInstanceMutex.WaitOne(TimeSpan.Zero, true))
{
Current.MainWindow = new MainWindow();
Current.MainWindow.Show();
Current.MainWindow.Activate();
_singleInstanceMutex.ReleaseMutex();
}
else
{
NativeMethod.PostMessage(
NativeMethod.BroadcastHandle,
NativeMethod.ReactivationMessage,
IntPtr.Zero,
IntPtr.Zero);
Current.Shutdown();
}
}
}
I created a program intended to run a single instance, with a form that is hidden until a broadcast message is received.
The bug is that the message is not received unless the form is shown on creation.
Why is it necessary to show the form at this stage?
I have knocked an example together. The first running instance of the program creates the form, further instances broadcast a message to it.
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
namespace HiddenProgram
{
public class Program : ApplicationContext
{
[DllImport("user32")]
static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
[DllImport("user32")]
static extern int RegisterWindowMessage(string message);
const int HWND_BROADCAST = 0xffff;
public static readonly int WM_SHOWME = Program.RegisterWindowMessage("com.holroyd.Gateway.Show");
public static Program Instance;
static Mutex mutex = new Mutex(true, "{9BFB3652-CCE9-42A2-8CDE-BBC40A0F5213}");
MyForm form;
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
if (!mutex.WaitOne(TimeSpan.Zero, true))
{
// Show the single instance window
PostMessage(
(IntPtr)HWND_BROADCAST,
WM_SHOWME,
IntPtr.Zero,
IntPtr.Zero);
}
else
{
// Create the hidden window
Instance = new Program();
Application.Run(Instance);
mutex.ReleaseMutex();
}
}
Program()
{
form = new MyForm();
form.FormClosing += form_FormClosing;
// One of the following two seem necessary to get the broadcast message
form.Show();
//MainForm = form;
}
void form_FormClosing(object sender, FormClosingEventArgs e)
{
ExitThread();
}
}
public class MyForm : Form
{
protected override void WndProc(ref Message m)
{
if (m.Msg == Program.WM_SHOWME)
Visible = !Visible;
else
base.WndProc(ref m);
}
}
}
To create a form that remains hidden until a broadcast message is received, CreateHandle() is called from the form's constructor, in order to create the underlying window so that broadcast messages from other instances of the program are received.
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
namespace HiddenProgram
{
public class Program : ApplicationContext
{
[DllImport("user32")]
static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
[DllImport("user32")]
static extern int RegisterWindowMessage(string message);
const int HWND_BROADCAST = 0xffff;
public static readonly int WM_SHOWME = Program.RegisterWindowMessage("com.holroyd.Gateway.Show");
static Mutex mutex = new Mutex(true, "{9BFB3652-CCE9-42A2-8CDE-BBC40A0F5213}");
MyForm form;
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
if (!mutex.WaitOne(TimeSpan.Zero, true))
{
// Show the single instance window
PostMessage(
(IntPtr)HWND_BROADCAST,
WM_SHOWME,
IntPtr.Zero,
IntPtr.Zero);
}
else
{
// Create the hidden window
Application.Run(new Program());
mutex.ReleaseMutex();
}
}
Program()
{
form = new MyForm();
form.FormClosing += form_FormClosing;
}
void form_FormClosing(object sender, FormClosingEventArgs e)
{
ExitThread();
}
}
public class MyForm : Form
{
public MyForm()
{
CreateHandle();
}
protected override void WndProc(ref Message m)
{
if (m.Msg == Program.WM_SHOWME)
Visible = !Visible;
else
base.WndProc(ref m);
}
}
}
Is there an easy method to restore a minimized form to its previous state, either Normal or Maximized? I'm expecting the same functionality as clicking the taskbar (or right-clicking and choosing restore).
So far, I have this, but if the form was previously maximized, it still comes back as a normal window.
if (docView.WindowState == FormWindowState.Minimized)
docView.WindowState = FormWindowState.Normal;
Do I have to handle the state change in the form to remember the previous state?
I use the following extension method:
using System.Runtime.InteropServices;
namespace System.Windows.Forms
{
public static class Extensions
{
[DllImport( "user32.dll" )]
private static extern int ShowWindow( IntPtr hWnd, uint Msg );
private const uint SW_RESTORE = 0x09;
public static void Restore( this Form form )
{
if (form.WindowState == FormWindowState.Minimized)
{
ShowWindow(form.Handle, SW_RESTORE);
}
}
}
}
Then call form.Restore() in my code.
The easiest way to restore a form to normal state is:
if (MyForm.WindowState == FormWindowState.Minimized)
{
MyForm.WindowState = FormWindowState.Normal;
}
You could simulate clicking on the taskbar button like this:
SendMessage(docView.Handle, WM_SYSCOMMAND, SC_RESTORE, 0);
For me, the code above does NOT work.
But at last I found the working code. Here it is:
CxImports.ManagedWindowPlacement placement = new CxImports.ManagedWindowPlacement();
CxImports.GetWindowPlacement(Convert.ToUInt32(Handle.ToInt64()), placement);
if (placement.flags == CxImports.WPF_RESTORETOMAXIMIZED)
WindowState = FormWindowState.Maximized;
else
WindowState = FormWindowState.Normal;
I guess, you can find all the needed "imported" functions by simple googling.
If anybody wonders how to do that with other apps windows,this code works for me:
public void UnMinimize(IntPtr handle)
{
WINDOWPLACEMENT WinPlacement = new WINDOWPLACEMENT();
GetWindowPlacement(handle, out WinPlacement);
if(WinPlacement.flags.HasFlag(WINDOWPLACEMENT.Flags.WPF_RESTORETOMAXIMIZED))
{
ShowWindow(handle, (int)SW_MAXIMIZE);
}
else
{
ShowWindow(handle, (int)SW_RESTORE);
}
}
Stuff is here:
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public Int32 Left;
public Int32 Top;
public Int32 Right;
public Int32 Bottom;
}
public struct POINT
{
public int x;
public int y;
}
public struct WINDOWPLACEMENT
{
[Flags]
public enum Flags : uint
{
WPF_ASYNCWINDOWPLACEMENT = 0x0004,
WPF_RESTORETOMAXIMIZED = 0x0002,
WPF_SETMINPOSITION = 0x0001
}
/// <summary>
/// The length of the structure, in bytes. Before calling the GetWindowPlacement or SetWindowPlacement functions, set this member to sizeof(WINDOWPLACEMENT).
/// </summary>
public uint length;
/// <summary>
/// The flags that control the position of the minimized window and the method by which the window is restored. This member can be one or more of the following values.
/// </summary>
///
public Flags flags;//uint flags;
/// <summary>
/// The current show state of the window. This member can be one of the following values.
/// </summary>
public uint showCmd;
/// <summary>
/// The coordinates of the window's upper-left corner when the window is minimized.
/// </summary>
public POINT ptMinPosition;
/// <summary>
/// The coordinates of the window's upper-left corner when the window is maximized.
/// </summary>
public POINT ptMaxPosition;
/// <summary>
/// The window's coordinates when the window is in the restored position.
/// </summary>
public RECT rcNormalPosition;
}
public class UnMinimizeClass
{
[DllImport("user32.dll")]
public static extern bool GetWindowPlacement(IntPtr hWnd, out WINDOWPLACEMENT lpwndpl);
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
const int SW_MAXIMIZE = 3;
const int SW_RESTORE = 9;
public static void UnMinimize(IntPtr handle)
{
WINDOWPLACEMENT WinPlacement = new WINDOWPLACEMENT();
GetWindowPlacement(handle, out WinPlacement);
if (WinPlacement.flags.HasFlag(WINDOWPLACEMENT.Flags.WPF_RESTORETOMAXIMIZED))
{
ShowWindow(handle, SW_MAXIMIZE);
}
else
{
ShowWindow(handle, (int)SW_RESTORE);
}
}
}
Using MainWindow.WindowState = WindowState.Normal; isn't enough
Next approach works for my WPF applcation:
MainWindow.WindowState = WindowState.Normal;
MainWindow.Show();
MainWindow.Activate();
I just added one more piece to generify the solution given by #Mesmo. It will create the instance if not created or restore and focus the form if the instance is already created from anywhere in the application. My requirement was that I didn't want to open multiple forms for some of the functionality in the application.
Utilities Class:
public static class Utilities
{
[DllImport("user32.dll")]
private static extern int ShowWindow(IntPtr hWnd, uint Msg);
private const uint SW_RESTORE = 0x09;
public static void Restore(this Form form)
{
if (form.WindowState == FormWindowState.Minimized)
{
ShowWindow(form.Handle, SW_RESTORE);
}
}
public static void CreateOrRestoreForm<T>() where T: Form
{
Form form = Application.OpenForms.OfType<T>().FirstOrDefault();
if (form == null)
{
form = Activator.CreateInstance<T>();
form.Show();
}
else
{
form.Restore();
form.Focus();
}
}
}
Usage:
Utilities.CreateOrRestoreForm<AboutForm>();
This is simple and works if you don't want to use any PInvoke or API trickery. Keep track of when the form has been resized, ignoring when it is minimised.
FormWindowState _PreviousWindowState;
private void TestForm_Resize(object sender, EventArgs e)
{
if (WindowState != FormWindowState.Minimized)
_PreviousWindowState = WindowState;
}
Later when you want to restore it -- for example if a tray icon is clicked:
private void Tray_MouseClick(object sender, MouseEventArgs e)
{
Activate();
if (WindowState == FormWindowState.Minimized)
WindowState = _PreviousWindowState; // former glory
}
[DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool GetWindowRect(IntPtr hWnd, ref wndRect lpRect);
[DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll")] public static extern bool EnumWindows(WNDENUMPROC lpEnumFunc, int lParam);//用来遍历所有窗口
[DllImport("user32.dll")] public static extern int GetWindowTextW(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)]StringBuilder lpString, int nMaxCount);//获取窗口Text
[DllImport("user32.dll")] public static extern int GetClassNameW(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)]StringBuilder lpString, int nMaxCount);//获取窗口类名
public static List<wndInfo> GetAllDesktopWindows(bool? isVisitable_)
{
//用来保存窗口对象
List<wndInfo> wndList = new List<wndInfo>();
//enum all desktop windows
EnumWindows(delegate (IntPtr hWnd, int lParam)
{
wndInfo wnd = new wndInfo();
StringBuilder sb = new StringBuilder(256);
//get hwnd
wnd.hWnd = hWnd;
if (isVisitable_ == null || IsWindowVisible(wnd.hWnd) == isVisitable_)
{
//get window name
GetWindowTextW(hWnd, sb, sb.Capacity);
wnd.szWindowName = sb.ToString();
//get window class
GetClassNameW(hWnd, sb, sb.Capacity);
wnd.szClassName = sb.ToString();
wndList.Add(wnd);
}
return true;
}, 0);
return wndList;
}
private void Btn_Test5_Click(object sender, RoutedEventArgs e)
{
var ws = WSys.GetAllDesktopWindows(true);
foreach (var w in ws)
{
if (w.szWindowName == "计算器")
{
WSys.ShowWindow(w.hWnd, 5);
WSys.ShowWindow(w.hWnd, 9);
Log.WriteLine(w.szWindowName);
}
}
}
The above code did not quite work for me in all situations
After checking the flags I also have to check showcmd=3 and if so maximise else restore
I have a "Switch to window" button on my main form that I'd like to be enabled only when other windows (in my app) are open. Is there some sort of event that is raised whenever a form is opened or closed that my main form can hook? (For example, perhaps some sort of way to track when Application.OpenForms has changed?)
(I understand that if this functionality were in a menu item, I could simply do the Application.OpenForms check when the menu was clicked, but this button is not in a menu.)
You could define your own type FooForm which inherits from Form. In the constructor of FooForm, fire a statically defined FormOpened event.
Then, all your forms would have a base type of FooForm instead of Form. The event will be fired each time one of your forms is opened.
I ended up building a window manager class and when a new form is created, it adds itself to the Window Manager's collection. You could encapsulate this functionality in a base Form class so you don't have to remember to do it. Then you can create events in the window manager class to notify you of things like this. You also get the benefit of being able to query the collection of windows in the manager class. I then used this class to be able to consolidate the functionality that builds a menu of open windows into a utility class.
You could use a MessageFilter and monitor for WM_SHOWWINDOW and WM_CLOSE messages.
UPDATE: You can also use a windows hook similar to what's at https://blogs.msdn.microsoft.com/calvin_hsia/2016/11/30/its-easy-to-use-windows-hooks-even-from-c/
Below is code that will write to the Console whenever a Form is opened or closed.
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
class Form1 : Form
{
public Form1()
{
var newWindowBtn = new Button { Text = "New Window" };
newWindowBtn.Click += (s, e) => new Form { Text = Guid.NewGuid().ToString() }.Show(this);
Controls.Add(newWindowBtn);
}
}
static class NativeMethods
{
[StructLayout(LayoutKind.Sequential)]
public struct CWPSTRUCT
{
public IntPtr lparam;
public IntPtr wparam;
public int message;
public IntPtr hwnd;
}
public delegate IntPtr CBTProc(int code, IntPtr wParam, IntPtr lParam);
public enum HookType
{
WH_CALLWNDPROC = 4,
}
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool UnhookWindowsHookEx(IntPtr hookPtr);
[DllImport("kernel32.dll")]
public static extern uint GetCurrentThreadId();
[DllImport("user32.dll")]
public static extern IntPtr CallNextHookEx(IntPtr hookPtr, int nCode, IntPtr wordParam, IntPtr longParam);
[DllImport("user32.dll")]
public static extern IntPtr SetWindowsHookEx(HookType hookType, CBTProc hookProc, IntPtr instancePtr, uint threadID);
}
static class Program
{
[STAThread]
static void Main()
{
var hook = NativeMethods.SetWindowsHookEx(NativeMethods.HookType.WH_CALLWNDPROC, Callback, IntPtr.Zero, NativeMethods.GetCurrentThreadId());
try
{
Application.Run(new Form1());
}
finally
{
NativeMethods.UnhookWindowsHookEx(hook);
}
}
static IntPtr Callback(int code, IntPtr wParam, IntPtr lParam)
{
var msg = Marshal.PtrToStructure(lParam);
if (msg.message == 0x0018)//WM_SHOWWINDOW
{
var form = Control.FromHandle(msg.hwnd) as Form;
if (form != null)
{
Console.WriteLine($"Opened form [{form.Handle}|{form.Text}]");
}
}
if (msg.message == 0x0010)//WM_CLOSE
{
var form = Control.FromHandle(msg.hwnd) as Form;
if (form != null)
{
Console.WriteLine($"Closed form [{form.Handle}|{form.Text}]");
}
}
return NativeMethods.CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
}
}
}
A simple global form counter:
public static class AppForms
{
public static int OpenForms { get; private set; }
public static event EventHandler FormShown;
public static event EventHandler FormClosed;
public static void Watch(Form form)
{
form.Shown += (sender, e) => { OpenForms++; FormShown?.Invoke(sender, e); };
form.Closed += (sender, e) => { OpenForms--; FormClosed?.Invoke(sender, e); };
}
}
The next step is to make a project-width search for InitializeComponent and add one line of code bellow it:
InitializeComponent();
AppForms.Watch(this);
Finally subscribe to the global events FormShown and FormClosed in your main form's constructor.
AppForms.FormShown += (sender, e) =>
{
this.Text = $"OpenForms: {AppForms.OpenForms}";
};
AppForms.FormClosed += (sender, e) =>
{
this.Text = $"OpenForms: {AppForms.OpenForms}";
};
You don't need to worry about your forms not being garbage collected because of the event subscriptions. The class AppForms is a subscriber, and subscribers don't keep references of publishers. Only the publishers keep references of subscribers.