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);
}
}
}
Related
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 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.
I have an application that I have successfully applied a mutex to ensure only one instance of the application runs at once. It is pretty much a straight up copy of the solution here: What is the correct way to create a single-instance application?
The problem lies with the command line arguments of the second instance. I'd like to pass it to the first instance and I decided to try to use a MemoryMappedFile to accomplish this. I've modified the above mutex solution to try to accomplish this, but I get a FileNotFoundException when trying to "OpenExisting". I've tried a number of different solutions to try to resolve this, but none seem to work. What have I done wrong? (I intend to write the arg to the MMF, but for now I'm just writing in binary as a test):
namespace MyApplication
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
static Mutex mutex = new Mutex(true, "{E6FD8A59-1754-4EA5-8996-2F1BC3AC5D87}");
[STAThread]
static void Main(string[] args)
{
if (mutex.WaitOne(TimeSpan.Zero, true))
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
if (args.Length == 1)
{
Application.Run(new Form1(args[0]));
}
else
{
Application.Run(new Form1());
}
mutex.ReleaseMutex();
}
else
{
NativeMethods.PostMessage((IntPtr)NativeMethods.HWND_BROADCAST, NativeMethods.WM_SHOWME, IntPtr.Zero, IntPtr.Zero);
if (args.Length == 1)
{
if (File.Exists(args[0]))
{
MemoryMappedFile mmf = MemoryMappedFile.CreateNew("filetoopen", 10000);
using (MemoryMappedViewStream stream = mmf.CreateViewStream())
{
BinaryWriter writer = new BinaryWriter(stream);
writer.Write(1);
}
NativeMethods.PostMessage((IntPtr)NativeMethods.HWND_BROADCAST, NativeMethods.WM_OPENFILE, IntPtr.Zero, IntPtr.Zero);
}
}
}
}
}
internal class NativeMethods
{
public const int HWND_BROADCAST = 0xffff;
public static readonly int WM_SHOWME = RegisterWindowMessage("WM_SHOWME");
public static readonly int WM_OPENFILE = RegisterWindowMessage("WM_OPENFILE");
[DllImport("user32.dll")]
public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
[DllImport("user32.dll")]
public static extern int RegisterWindowMessage(string message);
}
}
And the relevant code from Form1:
protected override void WndProc(ref Message m)
{
if (m.Msg == NativeMethods.WM_SHOWME)
{
ShowMe(); //This works fine. Method excluded.
}
else if (m.Msg == NativeMethods.WM_OPENFILE)
{
try
{
MemoryMappedFile mmf = MemoryMappedFile.OpenExisting("Global\\filetoopen");
using (MemoryMappedViewStream stream = mmf.CreateViewStream())
{
BinaryReader reader = new BinaryReader(stream);
MessageBox.Show(reader.ReadBoolean().ToString());
}
}
catch (FileNotFoundException)
{
MessageBox.Show("File not found.");
}
}
base.WndProc(ref m);
}
I'm using FlashWindowEx() to flash an application window when it needs to attract a user's attention. The window caption and taskbar button flashes continuously until the application receives focus. How can I check whether the application is currently flashing (i.e. has not received focus since it was instructed to flash).
Here are two possible solutions. One uses WH_SHELL, and one uses a NativeWindow. You will have to provide your own extension method (FlashWindow()) to start the flashing.
// base class. Two different forms subclass this form to illustrate two
// different solutions.
public class FormFlash : Form {
protected Label lb = new Label { Text = "Not flashing", Dock = DockStyle.Top };
public FormFlash() {
Controls.Add(lb);
Thread t = new Thread(() => {
Thread.Sleep(3000);
if (Form.ActiveForm == this)
SetForegroundWindow(GetDesktopWindow()); // deactivate the current form by setting the desktop as the foreground window
this.FlashWindow(); // call extension method to flash window
lb.BeginInvoke((Action) delegate {
lb.Text = "Flashing";
});
});
t.IsBackground = true;
t.Start();
}
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern IntPtr GetDesktopWindow();
}
// this solution is a bit simpler. Relies on the programmer knowing when the
// flashing started. Uses a NativeWindow to detect when a WM_ACTIVATEAPP
// message happens, that signals the end of the flashing.
class FormFlashNW : FormFlash {
NW nw = null;
public FormFlashNW() {
}
protected override void OnHandleCreated(EventArgs e) {
base.OnHandleCreated(e);
nw = new NW(this.Handle, lb);
}
protected override void OnHandleDestroyed(EventArgs e) {
base.OnHandleDestroyed(e);
nw.ReleaseHandle();
}
class NW : NativeWindow {
Label lb = null;
public NW(IntPtr handle, Label lb) {
AssignHandle(handle);
this.lb = lb;
}
protected override void WndProc(ref Message m) {
base.WndProc(ref m);
const int WM_ACTIVATEAPP = 0x1C;
if (m.Msg == WM_ACTIVATEAPP) {
lb.BeginInvoke((Action) delegate {
lb.Text = "Not flashing";
});
}
}
}
}
// this solution is more complicated. Relies on setting up the hook proc.
// The 'isFlashing' bool fires true and false alternating while the flashing
// is active.
public class FormShellHook : FormFlash {
public FormShellHook() {
FlashWindowExListener.Register(this);
FlashWindowExListener.FlashEvent += FlashExListener_FlashEvent;
}
void FlashExListener_FlashEvent(Form f, bool isFlashing) {
if (f == this) {
lb.Text = DateTime.Now.ToLongTimeString() + " is flashing: " + isFlashing;
}
}
}
public class FlashWindowExListener {
private delegate IntPtr CallShellProc(int nCode, IntPtr wParam, IntPtr lParam);
private static CallShellProc procShell = new CallShellProc(ShellProc);
private static Dictionary<IntPtr,Form> forms = new Dictionary<IntPtr,Form>();
private static IntPtr hHook = IntPtr.Zero;
public static event FlashWindowExEventHandler FlashEvent = delegate {};
public delegate void FlashWindowExEventHandler(Form f, bool isFlashing);
static FlashWindowExListener() {
int processID = GetCurrentThreadId();
// we are interested in listening to WH_SHELL events, mainly the HSHELL_REDRAW event.
hHook = SetWindowsHookEx(WH_SHELL, procShell, IntPtr.Zero, processID);
System.Windows.Forms.Application.ApplicationExit += delegate {
UnhookWindowsHookEx(hHook);
};
}
public static void Register(Form f) {
if (f.IsDisposed)
throw new ArgumentException("Cannot use disposed form.");
if (f.Handle == IntPtr.Zero) {
f.HandleCreated += delegate {
forms[f.Handle] = f;
};
}
else
forms[f.Handle] = f;
f.HandleDestroyed += delegate {
Unregister(f);
};
}
public static void Unregister(Form f) {
forms.Remove(f.Handle);
}
private static IntPtr ShellProc(int nCode, IntPtr wParam, IntPtr lParam) {
if (nCode == HSHELL_REDRAW) {
Form f = null;
// seems OK not having to call f.BeginInvoke
if (forms.TryGetValue(wParam, out f))
FlashEvent(f, (int) lParam == 1);
}
return CallNextHookEx(hHook, nCode, wParam, lParam);
}
private const int WH_SHELL = 10;
private const int HSHELL_REDRAW = 6;
[DllImport("user32.dll")]
private static extern int UnhookWindowsHookEx(IntPtr idHook);
[DllImport("user32.dll")]
private static extern IntPtr SetWindowsHookEx(int idHook, CallShellProc lpfn, IntPtr hInstance, int threadId);
[DllImport("kernel32.dll")]
private static extern int GetCurrentThreadId();
[DllImport("user32.dll")]
private static extern IntPtr CallNextHookEx(IntPtr idHook, int nCode, IntPtr wParam, IntPtr lParam);
}
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.