如何抑制由我无法更改的代码显示的对话框?
Posted
技术标签:
【中文标题】如何抑制由我无法更改的代码显示的对话框?【英文标题】:How to suppress a dialog box displayed by code that I can't change? 【发布时间】:2012-09-14 00:15:49 【问题描述】:我有一个来自第 3 方的 Inproc COM 服务器。如果捕获特定类型的错误,我调用的函数之一将显示错误消息对话框。问题是我正在尝试批量处理数据,而我使用的数据源导致该错误对话框弹出很多。如果它生成了 1000 个对话框,这将不是问题,而是它会阻塞,并且该函数在您按 OK 之前不会返回。
如何禁止显示对话框,或以编程方式按 OK?
这是调用堆栈的副本,因为它正在等待我按 OK
[管理到本地转换] > System.Windows.Forms.dll!System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(System.IntPtr dwComponentID, int reason, int pvLoopData) Line 2198 + 0x1e bytes C# System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(int reason, System.Windows.Forms.ApplicationContext context) Line 3422 + 0x1b bytes C# System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoop(int reason, System.Windows.Forms.ApplicationContext context) Line 3306 + 0xc bytes C# System.Windows.Forms.dll!System.Windows.Forms.Application.Run(System.Windows.Forms.Form mainForm) 第 1495 行 + 0x31 字节 C# UniversalDataImporter.exe!UniversalDataImporter.Program.Main() 第 18 行 + 0x1d 字节 C# [本机到托管转换] [管理到本地转换] mscorlib.dll!System.AppDomain.ExecuteAssembly(string assemblyFile, System.Security.Policy.Evidence assemblySecurity, string[] args) 第 2023 行 + 0x18 字节 C# Microsoft.VisualStudio.HostingProcess.Utilities.dll!Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() + 0x27 字节 mscorlib.dll!System.Threading.ThreadHelper.ThreadStart_Context(object state) Line 68 + 0x27 bytes C# mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Line 581 + 0xd bytes C# mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Line 530 + 0xd bytes C# mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) Line 519 + 0xe bytes C# mscorlib.dll!System.Threading.ThreadHelper.ThreadStart() 第 105 行 + 0x20 字节 C# [本机到托管转换]我怀疑它会有所帮助(没有选项可以禁用消息框、要订阅的事件或函数的其他重载),但这里是调用代码。
for (int i = 1; i <= recordCount; i++)
//If the dialog shows up the following line blocks till you press OK.
var values = _comServer.GetValues(fileHandle, i);
sqlDataConsumer.LoadRow(values);
【问题讨论】:
【参考方案1】:消息框泵送消息循环。这是您可以利用的东西,它允许您使用 Control.BeginInvoke() 注入代码,该代码在消息框出现后立即运行。然后,您可以使用该代码找到对话窗口并关闭它。向您的项目添加一个新类并粘贴此代码:
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);
示例用法:
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");
【讨论】:
在 UI 线程上没有调用 COM 服务器,它是一个长时间运行的进程,所以我在后台任务中启动它,这个解决方案会不会有问题? 是的,这将是一个问题。 Control.BeginInvoke() 将不起作用,因为工作人员不泵送消息循环。 GetCurrentThreadId() 返回错误的线程 ID。消息框很可能是在 COM 创建的单独线程上创建的,该线程为服务器提供了一个主页,大多数 COM 组件都需要一个 STA 线程。你需要类似这样的东西:***.com/a/4271581/17034 谢谢,我不得不稍微调整一下,我遇到了一些问题(比如现在消息泵中充斥着取消的事件)但是这些问题我知道如何自己解决,我只是必须首先克服关闭窗户的障碍。如果允许,我会在 24 小时内奖励赏金。 如果您想查看我的实现,我将大部分内容发布在我posted here的后续问题中 并使用 (new DialogCloser()) MessageBox.Show("you never see this"); MessageBox.Show("你永远不会看到这个"); 将显示第二个框【参考方案2】:首先,如果 COM 服务器本身可以在不让调用者因不希望的 UI 操作而失望的情况下进入模式,那肯定会更好。假设你不能改变第三方组件的行为,你仍然可以做的是挂钩消息处理和强制关闭消息框。
在第三方服务器上进行调用之前,您可以使用SetWindowsHookEx
和WH_CALLWNDPROC
安装消息挂钩,您的回调将能够监控消息。特别是您将能够发布消息、挂钩对话框的 WindowProc 等。当然包括以编程方式关闭消息框。
【讨论】:
我从来没有使用过消息挂钩,我同意这是解决方案,但我不知道如何实现它,您能否提供一些示例代码来说明如何设置它? (不需要按照我的例子,我可以自己弄清楚,我只需要走上正轨) 我有 C++ 代码,它通过更改标准消息框来执行此操作 - 添加“不显示”复选框,并实现超时以自动关闭 - 我将很快将其上线。那里解释了类似的东西:codeproject.com/Articles/10037/…以上是关于如何抑制由我无法更改的代码显示的对话框?的主要内容,如果未能解决你的问题,请参考以下文章
Selenium Chrome 抑制/关闭客户端证书选择对话框
C#Excel Interop - 在调用Worksheet.ExportAsFixedFormat时抑制“发布”对话框