首页 > 解决方案 > 单击其他程序工具栏中的按钮

问题描述

我正在尝试在我没有来源的遗留应用程序上自动化一些东西。所以我实际上是在尝试使用 Windows API 来单击我需要的按钮。

有一个类型如下的工具栏msvb_lib_toolbar

工具栏

我可以通过使用以下代码来处理它(我认为):

IntPtr window = FindWindow("ThunderRT6FormDC", "redacted");
IntPtr bar = FindWindowEx(window, IntPtr.Zero,"msvb_lib_toolbar",null);

查看文档,似乎我应该能够使用SendMessageTB_PRESSBUTTON单击这些按钮的消息:

[DllImport("user32.dll")]
public static extern int SendMessage(int hWnd, uint Msg, int wParam, int lParam);

但是,我不确定如何设置wParamlParam单击栏上的所需按钮。该文档似乎也没有太大帮助。

您能否提一些建议?


根据评论,我也尝试过UIAutomation. 我可以使用以下代码找到工具栏:

AutomationElement mainWindow = AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, "Migration Expert"));
AutomationElement toolbar = mainWindow.FindFirst(TreeScope.Subtree, new PropertyCondition(AutomationElement.ClassNameProperty, "msvb_lib_toolbar"));

但是从这里开始,我不确定该怎么做,因为 Spy++ 没有显示该对象的更多子对象:

间谍++

看着这个Current属性,AutomationElement我看不到任何东西向我跳来,但BoundingRectangle似乎确实表明我找到了正确的元素。

调试器

使用inspector.exe也不表示工具栏上有任何子项。

督察

标签: c#winapiui-automationuser32

解决方案


不是一个真正理想的解决方案,但我使用 和 的组合得到了一些快速而肮脏的pywinauto工作pyautogui

import pyautogui
import subprocess
import sys
import time
import os
from os import path
from glob import glob
from subprocess import check_output


from pywinauto import application


def click_at_image(image):
    location = pyautogui.locateOnScreen(image)
    buttonx, buttony = pyautogui.center(location)
    pyautogui.click(buttonx, buttony)

def get_dcf_filepaths():
    files = []
    start_dir = redacted
    pattern = "*.DCF"
    for dir, _, _ in os.walk(start_dir):
        files.extend(glob(os.path.join(dir, pattern)))
    return files

def get_csv_paths(paths):
    csv_paths = []
    for p in paths:
        csv_paths.append(p.replace(redacted,redacted).replace("DCF","csv").replace("dcf","csv"))
    return  csv_paths


def main():
    app = application.Application().start(redacted)
    files = get_dcf_filepaths()
    csv_paths = get_csv_paths(files)
    time.sleep(3)
    click_at_image("new_button.png") #Open new project
    for i in range(0, len(files)):
        if (path.exists(csv_paths[i])):
            #os.remove(csv_paths[i])
            continue
        time.sleep(1)
        # Click on nxt icon in dialog
        click_at_image("nxt_button.png")
        # Enter file path into OFD
        app.Open.Edit.SetText(files[i])
        pyautogui.press('enter')
        pyautogui.press('enter')
        time.sleep(1)
        # Click on m2c icon in toolbar
        click_at_image("m2c_button.png")
        # Wait for Excel to open
        time.sleep(6)
        # Open Save as dialog and browse
        pyautogui.press('alt')
        pyautogui.press('f')
        pyautogui.press('a')
        pyautogui.press('o')
        time.sleep(2)
        pyautogui.press('backspace')
        # Enter file path
        pyautogui.write(csv_paths[i], interval=0.01)
        #click_at_image("dummy.png")
        # Change file type to CSV and ignore any popups
        click_at_image("dd.png")
        time.sleep(1)
        
        click_at_image("csv.png")
        pyautogui.press('enter')
        pyautogui.press('enter')
        pyautogui.press('enter')
        time.sleep(2)
        # Kill excel
        pyautogui.hotkey('alt', 'f4')
        # Pull main window back to top
        app.top_window().set_focus()
        time.sleep(1)
        # New project
        click_at_image("new_button.png")
        time.sleep(0.50)
        # Don't save last one
        click_at_image("no.png")

if __name__ == "__main__":
    main()

本质上,我不得不求助于使用屏幕抓取来单击不可访问的按钮。如果这是为了需要更健壮的东西,我会直接C#使用Win32API 来处理除了屏幕抓取之外的所有内容,并通过一些额外的检查来等待窗口出现,而不是使用愚蠢的计时器。

话虽如此,这很有效,可能对未来的读者有所帮助。


推荐阅读