4

我正在开发一个应用程序来检测弹出对话框,然后自动将其关闭。我将其编写为 C++/Win32 应用程序。该对话框是由 IE 7 生成的,我可以检测到该窗口,但是几种让“确定”按钮“单击”的方法都失败了。

搜索其他人的解决方案,将这些消息发送到按钮句柄似乎在很多情况下都有效:

PostMessage( handle, WM_LBUTTONDOWN, 0, 0 );
PostMessage( handle, WM_LBUTTONUP, 0, 0 );
PostMessage( handle, BM_SETSTATE, 1, 0 );

不过,它对我的​​测试中的按钮状态没有影响。

我可以将制表符发送到主窗口并看到 OK 按钮获得焦点,但随后发送返回字符什么也不做。

为了了解更多信息,我使用 Spy++ 来获取有关窗口层次结构的信息,以及当我手动单击“确定”按钮时传递了哪些消息。

查看消息日志并阅读有关 WM_MOUSEACTIVATE 的信息以提供解决方案。日志信息显示 0002166C 是按钮窗口。所以在我的代码中我尝试了这个:

GetClassNameA( handle, str, str_size );

if( strcmp( str, "Internet Explorer_Server" ) != 0 )
    return TRUE; // Not the window we're interested in.

// Send a message to activate the button window and have it process a mouse click.
PostMessage( handle, WM_MOUSEACTIVATE, (WPARAM) dialog_handle, MAKELPARAM( HTCLIENT, WM_LBUTTONDOWN );

基于窗口层次结构和消息日志,我认为类名为“Internet Explorer_Server”的窗口是按钮。也许我错了,因为它看起来确实是一个奇怪的按钮类名......

下面是窗口层次结构图像的链接,当我手动单击“确定”按钮时的消息日志。最后是在 1 秒计时器票上执行的代码,用于查找窗口。

任何见解和帮助表示赞赏!

窗口层次结构、源、窗口消息和测试对话框源的图像可在此处获得:

https://sites.google.com/site/matthewmillersmiscellanea/Home/

4

2 回答 2

3

理想情况下,您应该创建一个导出全局CBT窗口挂钩的 DLL。这将允许您在将要创建对话框时获得早期通知。这将避免通过不断轮询来消耗资源。

一旦您检测到将要创建一个对话框,您有两个选择:

1) 防止创建对话框。

我不推荐这样做,它会导致代码出现各种问题,这些问题完全期望 CreateDialog() 返回有效的 HWND;

2)异步控制对话框。

我们通过使用带有注册用户消息的 PostMessage 并通过挂钩 WNDPROC 来获取它来实现这一点。当您收到此消息时,您必须决定如何终止您所在的对话框。

有多种退出对话框的方法:

a) 使用 WM_COMMAND(BN_CLICKED) 模拟按下 OK、Cancel、Abort、No 按钮(如 Chris 评论)。您可以使用 GetDlgItem(),查找 WindowText 并做出选择。但是,这不适用于非美国英语。不过,在这里利用 Accessibility API 可能还有一些距离。

b) 使用 PostMessage(WM_CLOSE, m_hWnd) 模拟关闭对话框。这并不总是按预期工作 - 一些对话框没有 [X] 关闭按钮,并且它们的客户端代码期望按下特定按钮。

c) 使用SendInput() API模拟用户输入。这解决了其中包含反弹出杀手代码的对话框:)

我们的最终解决方案是一种基于规则+启发式的方法,它有一个配置文件,当应用程序/IE 对话框更改其 ID、类名或父类名时,我们可以调整该配置文件。

于 2012-10-01T15:57:58.930 回答
2

在您知道窗口类名称和窗口标题的情况下不断关闭特定的弹出窗口

  #define UNICODE

  #include <windows.h>

  #pragma comment(lib, "user32")

  int main (int nn, char ** aa)
  {
     while (true) {
        HWND iHandle = FindWindow (L"theWindowClassName", L"theWindowCaption");
        if (iHandle > 0) SendMessage(iHandle, WM_SYSCOMMAND, SC_CLOSE, 0);
        Sleep (200); // check 5 times per second
     }
     return 0;
  }

如果一个未知或太通用(例如“对话框”),您可以通过传递一个 null 来省略它

 HWND iHandle = FindWindow (L"theWindowClassName", 0);
 or
 HWND iHandle = FindWindow (0, L"theWindowCaption");

当然,这将关闭所有具有给定名称的窗口。

于 2016-12-18T12:36:46.413 回答