How to Suppress a Dialog Box Displayed by Code That I Can't Change

How to suppress a dialog box displayed by code that I can't change?

A message box pumps a message loop. That's something you can take advantage of, it allows you to inject code with Control.BeginInvoke() that runs as soon as the message box appears. You can then use that code to find the dialog window and close it. Add a new class to your project and paste this code:

using System;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

public class DialogCloser : IDisposable {
public DialogCloser() {
if (Application.OpenForms.Count == 0) throw new InvalidOperationException();
Application.OpenForms[0].BeginInvoke(new Action(() => {
// Enumerate windows to find dialogs
if (cancelled) return;
EnumThreadWndProc callback = new EnumThreadWndProc(checkWindow);
EnumThreadWindows(GetCurrentThreadId(), callback, IntPtr.Zero);
GC.KeepAlive(callback);
}));
}

public void Dispose() {
cancelled = true;
}

private static bool checkWindow(IntPtr hWnd, IntPtr lp) {
// Checks if <hWnd> is a Windows dialog
StringBuilder sb = new StringBuilder(260);
GetClassName(hWnd, sb, sb.Capacity);
if (sb.ToString() == "#32770") {
// Close it by sending WM_CLOSE to the window
SendMessage(hWnd, 0x0010, IntPtr.Zero, IntPtr.Zero);
}
return true;
}

private bool cancelled;

// P/Invoke declarations
private delegate bool EnumThreadWndProc(IntPtr hWnd, IntPtr lp);
[DllImport("user32.dll")]
private static extern bool EnumThreadWindows(int tid, EnumThreadWndProc callback, IntPtr lp);
[DllImport("kernel32.dll")]
private static extern int GetCurrentThreadId();
[DllImport("user32.dll")]
private static extern int GetClassName(IntPtr hWnd, StringBuilder buffer, int buflen);
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
}

Sample usage:

private void button1_Click(object sender, EventArgs e) {
using (new DialogCloser()) {
// Replace this with the call to the COM server method:
MessageBox.Show("you never see this");
}
}

How to stop multiple dialog boxes displaying?

It seems to me that you have some very ugly bug in your code! I'd focus on fixing this bug first. The easy way to do this is to set a breakpoint on the line that opens up the dialog. When the line is hit the second time (while the first dialog box is still open), view the stacktrace at that point and check all active threads (View Threads and View Stack windows) for the cause of the issue.

In addition, change your code to open a modal dialog window instead, where the parent window must be the owner. A modal dialog window will stop execution and prevent the parent window from processing user input (which could've lead to this behavior).

Edit

From your comments I gather that you have several threads that you cannot control and that you want one thread at a time to access the messagebox code. When working with threads, there are several synchronisation primitives available. Expanding on each of them requires a book (you may try Concurrent Programming on Windows, one that's thorough while the book has some flaws in its structure), knowing the right one for you requires knowledge of your code.

That said, you may wish to do something like the following, using a Mutex which at least prevents other threads to access the code (meaning: it will put them in a suspend state until the Mutex is released). Add a static boolean flag variable (or add a checkbox on the popupped form "show box only once") if you want to prevent the popup box to show more than once. Mutex plus flag solves two problems in one: only one thread will ever run that code, and the code will only run once, ever.

// as static class variable, create a mutex
private static Mutex dialogMutex = new Mutex();

// a static flag preventing the dialog box to show more than once
// (you may wish to resolve this differently, depending on req's)
private static boolean dialogIsShownOnce = false;

public static void ShowDialogBox()
{
// Wait until it is safe to enter, this makes the current thread
// the exclusive user of this code and other threads may only enter
// after the current thread finishes.
dialogMutex.WaitOne();

// depending on your requirements, you may not want this
// must come _after_ WaitOne to prevent entering before another
// thread that entered hasn't yet changed this variable
if(dialogIsShownOnce)
return;

// show your dialog box as a modal box
// if you are unsure: add a breakpoint just after the ShowDialog
// it should only be hit _after_ you dismiss the dialog box
yourForm.ShowDialog();

// set the flag, or the counter, or whatever you wish:
dialogIsShownOnce = true;

// Release the Mutex, this will remove the "roadblock" and allow
// other threads to enter this piece of code
dialogMutex.ReleaseMutex();
}

The code above is not tested and should be considered a hint on how to tackle this issue. There are so many ways to solve this, but I think the above method may just be what you need.

Getting the text from a dialog box that does not use a label control?

Thanks to Sertac's comment I found out that the text in Delphi's message boxes are not window objects, they are drawn on with the 'DrawText' methods. I used EasyHook to intercept the Windows API calls and I am now able to grab the text I care about.

////It appears that DrawText always calls DrawTextEx so it is getting intercepted twice.
//// Only need to hook DrawTextEx
static EasyHook.LocalHook _drawTextExAHook;

//Snip...

public override void Run()
{
//Snip...

IntPtr drawTextExAPtr = EasyHook.LocalHook.GetProcAddress("user32", "DrawTextExA");
_drawTextExAHook = EasyHook.LocalHook.Create(drawTextExAPtr, new DrawTextExDelegate(DrawTextEx_Hooked), null);

//The COM stuff must be run in a STA Thread so we can intercept the message boxes that it throws up.
var staThread = new Thread(() =>
{
try
{
var threadID = new[] { GetCurrentThreadId() };
//Enable the hook on the current thread.
_drawTextExAHook.ThreadACL.SetInclusiveACL(threadID);

//Tell the dummy form to start ComThread
_dummyForm = new DummyForm(ComThread);
Application.Run(_dummyForm);
}
finally
{
if(_drawTextExAHook != null)
_drawTextExAHook.Dispose();
}
});
staThread.SetApartmentState(ApartmentState.STA);
staThread.Name = "Com Thread";
staThread.Start();

//Wait for the Com Thread to finish.
staThread.Join();

}

//Snip...

private delegate int DrawTextExDelegate(IntPtr hdc, string lpchText, int cchText,
ref Rect lprc, uint dwDTFormat, ref DRAWTEXTPARAMS lpDTParams);

private int DrawTextEx_Hooked(IntPtr hdc, string lpchText, int cchText, ref Rect lprc,
uint dwDTFormat, ref DRAWTEXTPARAMS lpDTParams)
{
LogErrorText(lpchText);
return DrawTextEx(hdc, lpchText, cchText, ref lprc, dwDTFormat, ref lpDTParams);
}

[DllImport("user32.dll")]
static extern int DrawTextEx(IntPtr hdc, string lpchText, int cchText,
ref Rect lprc, uint dwDTFormat, ref DRAWTEXTPARAMS lpDTParams);

How to dismiss flutter dialog?

https://docs.flutter.io/flutter/material/showDialog.html says

The dialog route created by this method is pushed to the root navigator. If the application has multiple Navigator objects, it may be necessary to call Navigator.of(context, rootNavigator: true).pop(result) to close the dialog rather just Navigator.pop(context, result).

so I'd assume one of these two should do what you want.

How can I prevent a third party library from displaying a MessageBox?

Check out Detours from Microsoft Research. It allows you to detour arbitrary Windows API functions. C/C++ programming is required to make it work though. You won't need much.



Related Topics



Leave a reply



Submit