如何抑制我无法更改的代码显示的对话框? [英] How to suppress a dialog box displayed by code that I can't change?

查看:19
本文介绍了如何抑制我无法更改的代码显示的对话框?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个来自第 3 方的 Inproc COM 服务器.如果我调用的其中一个函数捕获特定类型的错误,它将显示一个错误消息对话框.问题是我正在尝试批量处理数据,而我正在使用的数据源导致该错误对话框弹出很多.如果它产生了 1000 个对话框,这不会是一个问题,但它会阻塞并且该函数在您按下 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) Line 1495 + 0x31 bytes C#UniversalDataImporter.exe!UniversalDataImporter.Program.Main() Line 18 + 0x1d bytes C#[本机到托管过渡][管理到本机过渡]mscorlib.dll!System.AppDomain.ExecuteAssembly(string assemblyFile, System.Security.Policy.Evidence assemblySecurity, string[] args) Line 2023 + 0x18 bytes 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() Line 105 + 0x20 bytes C#[本机到托管过渡]

我怀疑它会有所帮助(没有选项可以禁用消息框、要订阅的事件或函数的其他重载),但这是调用代码.

for (int i = 1; i <= recordCount; i++){//如果对话框显示以下行块,直到您按确定.var values = _comServer.GetValues(fileHandle, i);sqlDataConsumer.LoadRow(values);}

解决方案

消息框泵出消息循环.这是您可以利用的东西,它允许您使用 Control.BeginInvoke() 注入代码,该代码在消息框出现时立即运行.然后,您可以使用该代码查找对话框窗口并将其关闭.向您的项目添加一个新类并粘贴以下代码:

使用系统;使用 System.Text;使用 System.Windows.Forms;使用 System.Runtime.InteropServices;公共类 DialogCloser :IDisposable {公共 DialogCloser() {if (Application.OpenForms.Count == 0) throw new InvalidOperationException();Application.OpenForms[0].BeginInvoke(new Action(() => {//枚举窗口以查找对话框如果(取消)返回;EnumThreadWndProc callback = new EnumThreadWndProc(checkWindow);EnumThreadWindows(GetCurrentThreadId(), callback, IntPtr.Zero);GC.KeepAlive(回调);}));}公共无效处置(){取消 = 真;}private static bool checkWindow(IntPtr hWnd, IntPtr lp) {//检查是否<hWnd>是一个 Windows 对话框StringBuilder sb = new StringBuilder(260);GetClassName(hWnd, sb, sb.Capacity);if (sb.ToString() == "#32770") {//通过向窗口发送 WM_CLOSE 来关闭它SendMessage(hWnd, 0x0010, IntPtr.Zero, IntPtr.Zero);}返回真;}私人布尔取消;//P/调用声明私人委托 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")]私有静态外部 IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);}

示例用法:

private void button1_Click(object sender, EventArgs e) {使用(新的DialogCloser()){//将此替换为对 COM 服务器方法的调用:MessageBox.Show("你永远不会看到这个");}}

I have a Inproc COM Server from a 3rd party. One of the functions I call will display a error message dialog box if it traps a specific type of error. The issue is I am trying to process data in bulk, and the data source I am using is causing that error dialog to pop up a lot. This would not be a issue if it spawned 1000 dialog boxes, but instead it blocks and the function does not return until you press OK.

How can I suppress the dialog from showing up, or programmatically press OK?

Here is a copy of the call stack as it is waiting for me to press OK

    [Managed to Native Transition]  
>   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) Line 1495 + 0x31 bytes    C#
    UniversalDataImporter.exe!UniversalDataImporter.Program.Main() Line 18 + 0x1d bytes C#
    [Native to Managed Transition]  
    [Managed to Native Transition]  
    mscorlib.dll!System.AppDomain.ExecuteAssembly(string assemblyFile, System.Security.Policy.Evidence assemblySecurity, string[] args) Line 2023 + 0x18 bytes  C#
    Microsoft.VisualStudio.HostingProcess.Utilities.dll!Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() + 0x27 bytes  
    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() Line 105 + 0x20 bytes  C#
    [Native to Managed Transition]  

I doubt it will help (there are no options to disable the message box, events to subscribe to, or other overloads to the function) but here is the calling code.

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);
}

解决方案

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");
    }
}

这篇关于如何抑制我无法更改的代码显示的对话框?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆