如何获取 VBA MsgBox 的窗口句柄?

Posted

技术标签:

【中文标题】如何获取 VBA MsgBox 的窗口句柄?【英文标题】:How to get the window handle for a VBA MsgBox? 【发布时间】:2016-02-27 23:01:49 【问题描述】:

我在大量 Excel 工作簿上运行 C# 脚本,其中涉及在每个工作簿中调用宏;由于错误处理程序,宏有时会产生一个 MsgBox,并且会暂停脚本的执行,直到我在 MsgBox 中单击“确定”。

MsgBox 的标题文本为“Error in processSub”,正文为“Error (Type Mismatch)”。

我想也许我可以有一个并发线程来查找所有当前打开的窗口,如果找到 MsgBox,请单击“确定”。我正在尝试使用以下方式查找窗口:

using System.Diagnostics;
public Process getErrorWindow()
    
        Process[] processList = Process.GetProcesses();
        foreach (Process process in processList)
        

            if (process.MainWindowTitle=="Error in processSub")
            
                return process;
            
        

    

但这并没有找到任何东西。当我查看processList[] 时,它似乎只找到了主 Excel 窗口,而不是其 VBA 代码生成的任何子窗口。有没有办法找到 MsgBox 并点击它的 OK 按钮?

【问题讨论】:

【参考方案1】:

您可以使用 winapi 函数FindWindow 通过标题和类检索窗口句柄。将以下代码添加到您的程序中:

[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetForegroundWindow(IntPtr hWnd);

public static IntPtr FindExcelErrorPopup()

    return FindWindow(null, "Error in processSub");

点击按钮:

IntPtr hwnd = FindExcelErrorPopup();
if (hwnd != IntPtr.Zero)

    SetForegroundWindow(hwnd); // activates the window
    SendKeys.SendWait("ENTER"); // send ENTER key

如果默认按钮不是“确定”,请在 ENTER 之前发送一些 TAB 笔划以选择它。 不要忘记将using System.Runtime.InteropServices; 放入DllImport

编辑: 对于远程桌面,试试这个原生方法:

[DllImport("user32.dll")]
private static extern void keybd_event(Keys bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo);

private const uint KEYEVENTF_EXTENDEDKEY = 0x0001;
private const uint KEYEVENTF_KEYUP = 0x0002;
const uint KEYEVENTF_EXTENDEDKEY = 0x0001;
const uint KEYEVENTF_KEYUP = 0x0002;

然后像这样举起钥匙:

keybd_event(Keys.Enter, 0x45, KEYEVENTF_EXTENDEDKEY, UIntPtr.Zero); // key down
keybd_event(Keys.Enter, 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, UIntPtr.Zero); // key up

【讨论】:

这可以成功,但是当我在远程桌面上运行它时,SendKeys.SendInput() 会抛出一个System.ComponentModel.Win32Exception: Access is denied。关于如何解决此问题的任何想法,因为我将在远程桌面上运行脚本,以便即使我关闭笔记本电脑也可以继续该过程? 你不能添加一个 try/catch 来捕获该错误并在不让窗口弹出任何消息框的情况下处理它吗? 如果你仍然在做整个 FindWindow 舞蹈,只需找到默认按钮并按下它。跳过注入键盘事件的困难。 (如果你坚持这样做,你应该完全使用 SendInput,而不是 keybd_event。) 我最终通过在远程机器上运行 AutoIt 脚本来解决它。

以上是关于如何获取 VBA MsgBox 的窗口句柄?的主要内容,如果未能解决你的问题,请参考以下文章

C# 根据进程ID获取进程主窗口句柄

C# 根据进程ID获取进程主窗口句柄

VC++中怎样获取到一个窗体的句柄?

Qt:子窗口中如何获取主窗体ui中的控件

vb如何获取到窗口的句柄

主窗口中如何获取子窗口某控件句柄?