以编程方式按下另一个应用程序(C、Windows)上的按钮

Posted

技术标签:

【中文标题】以编程方式按下另一个应用程序(C、Windows)上的按钮【英文标题】:Programmatically press a button on another application (C, Windows) 【发布时间】:2010-11-16 11:15:55 【问题描述】:

我正在尝试使用以下代码来按下我的其他应用程序上的按钮:

HWND ButtonHandle;
if( (wnd = FindWindow(0, "Do you want to save?")) )
   
   ButtonHandle = FindWindowEx(wnd, 0, "SaveButton", "&Save");
   SendMessage(wnd, WM_COMMAND, MAKEWORD(GetDlgCtrlID(ButtonHandle), BN_CLICKED ), (LPARAM)ButtonHandle);


它不起作用。我尝试将不同的句柄传递给 MAKEWORD 并更改 WPARM 和 LPARAM 但没有。

关于如何单击另一个应用程序窗口上的按钮的任何想法?

感谢代码。 谢谢。

编辑: 它似乎没有工作权限的原因。我发送了一个 PostMessage(),结果是 GetLastError() = 5 错误(或拒绝访问)。 有什么想法吗?

EDIT2 我并不是要粗鲁,但请拜托,我已经搜索了所有 API,包括获取和设置按钮的区域,然后发送一个按钮向下和按钮向上,得到控制 ID,获得类 ID 和更多。 我首先在这里问这个问题的原因是因为我已经在互联网上用尽了我的搜索。 如果您知道答案PLEASE POST CODE,请不要建议 API,仅此而已,请告诉我该 API 如何解决问题。这并不难。 谢谢。

编辑 3:问题的答案是在赏金完成后自动选择的。这个问题仍然没有答案。

【问题讨论】:

这适用于哪个版本的 windows? 可能不相关,但我建议您以管理员身份运行程序或在管理员模式下运行 Visual Studio 并尝试是否可行。这也是我在 PostMessage 文档中看到的:Microsoft Windows Vista然后。当消息被 UIPI 阻止时,使用 GetLastError 检索的最后一个错误设置为 5(拒绝访问)。 这是在 Vista 还是 7 中?它在XP中工作吗? (从 Microsoft 下载要在 Virtual PC 中运行的映像并进行测试。)您是否尝试过使用 sendkeys? 咕。这就是为什么所有图形应用程序也应该有 CLI 的原因。像 DBUS 这样的东西就更好了。 鉴于按钮具有非标准按钮类,它可能不会将 WM_COMMAND 发送到其父级。它甚至可能不支持 Active Accessibility。所以建议使用SendInput 发送鼠标事件可能是你最好的选择。 【参考方案1】:
    您确定“SaveButton”类名有效吗?你得到按钮了吗 处理? 尝试将消息发送到 ButtonHandle 窗口(直接发送到按钮)。

更新:我相信这应该可行,

SendMessage(ButtonHandle, BM_CLICK, 0, 0);

【讨论】:

1) 是的。我确定。我使用 Spy++ 来获取这些信息。 2)我尝试将消息发送到按钮的句柄,但没有发生任何事情。 确实如此。我得到了窗口、按钮的句柄,甚至得到了按钮类 在大多数情况下,您发布的 sendmessage() 有效,但在一种情况下,我最需要它,但它似乎不起作用。我确实得到了按钮句柄,甚至得到了按钮类。我尝试了不同的方法,但没有... 很奇怪。它至少成功过一次吗?每次发送消息时都应该获取句柄,而不是(存储)重用它。按钮手柄可能并不总是相同的。还有一种情况是自定义按钮控件,不过还是……我现在想不出别的了。 请注意,在更高版本的 windows (Vista+) 上,您不能向以更高权限运行的 windows 发送消息。【参考方案2】:
SendMessage(btnHandle, WM_LBUTTONDOWN, 0, 0);
SendMessage(btnHandle, WM_LBUTTONUP, 0, 0);
SendMessage(btnHandle, WM_LBUTTONDOWN, 0, 0);
SendMessage(btnHandle, WM_LBUTTONUP, 0, 0);

你必须发送一个按钮点击两次。不知道为什么(也许第一次点击只会激活按钮的窗口),但我使用这段代码很长时间了,它总是对我有用。

【讨论】:

【参考方案3】:

也许这会有所帮助: http://www.cplusplus.com/forum/beginner/8806/

【讨论】:

【参考方案4】:

看下面的解决方案,你也可以使用

SendMessage(ButtonHandle, WM_LBUTTONDOWN, 0, 0);
SendMessage(ButtonHandle, WM_LBUTTONUP, 0, 0);

或者

SendMessage(ButtonHandle, BM_CLICK, 0, 0);

HWND buttonHandle = 0;

BOOL CALLBACK GetButtonHandle(HWND handle, LPARAM)

 char label[100];
 int size = GetWindowTextA(handle,label,sizeof(label));
 if(strcmp(label,"&Save") == 0)
 
  buttonHandle = handle;
  return false;
 
 return true;

void main()

 HWND windowHandle = FindWindowA(NULL,"Do you want to Save?");
 if(windowHandle != 0)
 
  BOOL ret = EnumChildWindows(windowHandle,GetButtonHandle,0);

  if(buttonHandle != 0)
  
   LRESULT res = SendMessage(buttonHandle,BM_CLICK,0,0);
   //SendMessage(buttonHandle,WM_LBUTTONDOWN,0,0); 
   //SendMessage(buttonHandle,WM_LBUTTONUP,0,0);
  

 




注意: 从窗口文本、按钮文本中确定(检查窗口标题末尾是否有空格)

【讨论】:

【参考方案5】:

除非发送消息的进程以低于目标进程的完整性级别运行,否则 SendMessage 或 PostMessage 上的访问被拒绝错误毫无意义。

除非拥有目标窗口的进程正在以“管理员身份”运行或者是服务,否则不应发生这种情况。而且服务很难在 Windows 6 及更高版本的交互式桌面上创建窗口。

你可以阅读一些关于Integrity Levels Here 的内容,如果它们甚至可以远程应用到这种情况。 Internet Explorer 是唯一一个“选择加入”完整性安全模型的应用程序,它通过故意降低自身的完整性级别以更有效地进行沙箱化。

【讨论】:

奇怪。这两个进程都以管理员身份运行。但是,我尝试单击的过程是服务的前端 (GUI)。现在,GUI 不是服务,它是一个普通的 win32 GUI 应用程序,它所做的只是调用服务管理器并更新服务......我想知道这是否导致 GUI 丢弃消息......【参考方案6】:

如果您可以抬起包含按钮的窗口,您可以将原始鼠标事件发送到按钮边界内的位置。

有两个函数可以模拟鼠标事件SendInput和mouse_event。我推荐使用mouse_event 函数。要提高窗口,您可以使用ShowWindow。我不知道如何获得按钮的句柄,但如果你有它的 hWnd,它很容易使用GetWindowRect 函数找到它的绝对位置。尝试使用这些,如果您遇到任何问题,我很乐意提供帮助。

或者在您的应用程序窗口中定义一个自定义 WM 来处理保存请求。 WM_CUSTOMWM_USER(不记得是哪个)标志着用户定义的窗口消息的开始。

【讨论】:

1) 例子? 2) 我无法在应用程序上定义任何自定义消息处理程序。 由于示例太长,我已经更新了答案,请查看。 好的,再次。我无法在我的应用程序或我的应用程序“窗口”上设置自定义消息处理程序,因为我特别要求提供代码示例,如果您能提供示例,我将不胜感激。我问的原因是因为我已经通过了我自己能找到的所有答案,包括通常的“尝试 X 和 Y”。好吧,如果你认为 X 和 Y 有效,那么请给我看代码。否则我非常感谢你的努力,但这不是我想要的。 很抱歉在额外定义之前提交评论,我不得不做一些其他任务,现在只能编辑它。检查附加段落,我已包含您需要的功能。没有我可以提供的样本,但我确信你会使用这些函数来计算它 我也很确定mouse_event,因为它使用的是设备驱动程序。如果您阅读 MSDN 文章,您会看到他们推荐平板电脑制造商在他们的驱动程序中使用它。【参考方案7】:

当我必须做这些事情时,我会使用 SendKeys。它是 VB 风格的,C# 提供了一个很好的使用界面,但对于 C/C++,你必须这样做 this way>。它的好处是您可以编写脚本并运行它们,而不是在代码中硬编码。

【讨论】:

【参考方案8】:

Microsoft 现在正在推动 Active Accessibility (MSAA) 用于 UI 自动化, (多年来已多次重命名) 见

UI Automation and Microsoft Active Accessibility Using UI Automation for Automated Testing UI Automation Clients for Managed Code

抱歉,我没有任何简单的代码可以帮助您入门。由于“SendMessage()”似乎对你不起作用,我不知道除了“UI 自动化”之外的其他选项

我假设您已使用 Spy++(与 MsDev 一起安装)检查您的消息正在发送到正确的按钮等 - 并且该按钮是标准的 Windows 按钮。我的第一个瞬间会说使用“SendMessage()”或“PostMessage()”,但考虑到“SendMessage()”的答案数量以及它对你不起作用的事实。我希望有人在继续……

【讨论】:

【参考方案9】:

//向已经打开的calc.exe发送数字4

HWND windowHandle;

windowHandle = FindWindowA(NULL,"Calculator");

if(windowHandle != 0)
   ret = EnumChildWindows(windowHandle,GetButtonHandle,0);

BOOL CALLBACK GetButtonHandle(HWND handle, LPARAM)

char label[100];
int size = GetWindowTextA(handle,label,sizeof(label));

if(strcmp(label,"4") == 0)

PostMessage(handle ,WM_LBUTTONDOWN,(WPARAM)0x0001,0);
PostMessage(handle ,WM_LBUTTONUP,(WPARAM)0x0001,0);

PostMessage(handle ,WM_LBUTTONDOWN,(WPARAM)0x0001,0);
PostMessage(handle ,WM_LBUTTONUP,(WPARAM)0x0001,0);

return false;

return true;

【讨论】:

【参考方案10】:

您可以使用 sendkeys(如 tr3 所说)发送鼠标点击,这与使用 SendMessage 不同。它也不太直接且更 hack-ish,但对于自动化(在 VBS 中)很有用。

另外,这只是一个猜测,但问题可能是您的消息处理在某处因未调用基类成员而被破坏。示例:

void CMyClass::OnMessageY(CWnd *cwnd)

    CBaseClass::OnMessageY(cwnd);
    //... my code

【讨论】:

【参考方案11】:

如果您确定 ButtonHandle 是有效句柄,您可以使用 WM_LBUTTONDOWN 和 WM_LBUTTONUP 消息对而不是 BN_CLICKED

HWND ButtonHandle;
if( (wnd = FindWindow(0, "Do you want to save?")) )
   
    SendMessage(ButtonHandle, WM_LBUTTONDOWN, MK_LBUTTON, 0);
    SendMessage(ButtonHandle, WM_LBUTTONUP, MK_LBUTTON, 0);

【讨论】:

已经试过了。它不起作用。我猜那个按钮不想被按下。当我使用 SendMessage 时,我得到一个错误“拒绝访问”是我使用 postmessage() 或 C000022 或类似的东西。我看了那个错误,基本上也是访问被拒绝。 你能以非编程方式按下按钮吗 ;) - 可能是程序挂钩鼠标事件,以防止其他程序点击它的按钮 我当然可以。那里没问题。我开始怀疑由于某种原因应用程序忽略了消息【参考方案12】:

非 C 方法:使用 Java 和 java.awt.Robot 类来移动鼠标执行真正的点击(我猜在 Windows World 中也有这样的东西)。 问题:你必须知道你的按钮在哪里:D

【讨论】:

以上是关于以编程方式按下另一个应用程序(C、Windows)上的按钮的主要内容,如果未能解决你的问题,请参考以下文章

如何更改 UIpickerView 上的元素按下按钮或按下另一个按钮

当按下另一个可触摸的不透明度时,一个可触摸的不透明度不会按下

int 16h/ah=1 即使在用户按下另一个键后也重复给出相同的按键

UITableView 复选框(按下另一个按钮时选择一个按钮)

如何在按下另一个按钮之前更改按钮状态?

didDeselectRowAtIndexPath 仅在按下另一个单元格后调用[重复]