Is there a way to cancel a message box displayed by MessageBox.Show()
?
I am thinking that it is possible by sending a close "message" (WM_?) to the native win32 message queue? How exactly?
Is there a way to cancel a message box displayed by MessageBox.Show()
?
I am thinking that it is possible by sending a close "message" (WM_?) to the native win32 message queue? How exactly?
The simplest solution using WinApi would be:
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
private const int WM_CLOSE = 0x10;
private const string MessageBoxTitle = "UniqueTitle123";
void CloseMessageBox()
{
var hwnd = FindWindow(null, MessageBoxTitle);
if (hwnd != IntPtr.Zero)
{
PostMessage(hwnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
}
}
You need to invoke messagebox with MessageBoxTitle:
MessageBox.Show("This gonna close itself", MessageBoxTitle);
And then somewhere:
CloseMessageBox();
Example use case:
Task.Run(async () =>
{
await Task.Delay(2000).ConfigureAwait(false);
CloseMessageBox();
});
MessageBox.Show("This closes in 2 seconds", MessageBoxTitle);
Be aware that if there is different window with the same title as your message box then calling CloseMessageBox() will close that window instead of your message box. The solution is simple, but choose your msgbox title in a way that there will be very small probability of name collison with other windows in the system f.ex. YourAppName-51245
should be fine.
I thought I would post a 'windows hook' solution to this as I believe it is the best way of doing it. The answer above is fragile. I have provided code in native C++ since I lack the .net skills to answer in C#. Perhaps someone who has those skills would translate. Code is not threadsafe, note. If you create message boxes on more than one thread then you will have to be a bit cleverer.
static HHOOK my_hook;
static HWND message_box_hwnd;
// Hook proc
LRESULT CALLBACK MyHookProc (int code, WPARAM wParam, LPARAM lParam)
{
if (code < 0)
return CallNextHookEx (tdata->tcpHook, code, wParam, lParam);
CWPSTRUCT *msg = (CWPSTRUCT *) lParam;
LRESULT result = CallNextHookEx (tdata->tcpHook, code, wParam, lParam);
if (msg->message == WM_INITDIALOG)
{
message_box_hwnd = msg->hwnd;
UnhookWindowsHookEx (my_hook);
my_hook = NULL;
}
return result;
}
my_hook = SetWindowsHookEx (WH_CALLWNDPROC, MyHookProc, NULL, GetCurrentThreadId ());
MessageBox (...);
And, of course, in your timer proc or whatever:
PostMessage (message_box_hwnd, WM_CLOSE, 0, 0);
or (say):
PostMessage (message_box_hwnd, WM_COMMAND, IDCANCEL, 0);
Not much to it, really.