是否可以使用IntPtr激活另一个程序中的选项卡?

时间:2015-04-29 18:15:51

标签: c# ui-automation user32

提前致谢。

是否可以使用IntPtr激活其他程序中的选项卡?如果是这样,怎么样?

SendKeys不是一个选项。

也许我需要的是钓鱼课程。我已经筋疲力尽了谷歌和我的首席开发人员。 我希望得到彻底解决方案或建议继续我的Google努力。

基本流程是:

我将快捷方式图标拖到启动器

Launcher window ~ C# source code

这将打开目标应用程序(Notepad ++)并抓取IntPtr等

我想以编程方式选择Notepad ++中的各种项目,例如编辑,编辑下的菜单项或文档选项卡。

Screen grab of Notepad++

我正在运行的基本代码是:

enter image description here

' blob'

  • 第1项:项目的IntPtr
  • 第2项:itemsChild的IntPtr
  • 第3项:第1项的控制案文
  • 第4项:是第1项的矩形参数

enter image description here

root包含类似的信息:

enter image description here

3 个答案:

答案 0 :(得分:2)

正如其他人所指出的,这样做的标准方法是使用UI Automation。 Notepad ++确实支持UI自动化(在某种程度上,因为它以某种方式由UI自动化Windows层自动提供)。

以下是一个示例C#控制台应用程序,它演示了以下sceanrio(您需要引用UIAutomationClient.dll,UIAutomationProvider.dll和UIAutomationTypes.dll):

1)获得第一个运行的记事本++进程(你必须至少启动一个)

2)打开两个文件(注意在记事本++中可能已经有其他打开的标签)

3)选择无限循环中的所有标签

class Program
{
    static void Main(string[] args)
    {
        // this presumes notepad++ has been started somehow
        Process process = Process.GetProcessesByName("notepad++").FirstOrDefault();
        if (process == null)
        {
            Console.WriteLine("Cannot find any notepad++ process.");
            return;
        }
        AutomateNpp(process.MainWindowHandle);
    }

    static void AutomateNpp(IntPtr handle)
    {
        // get main window handle
        AutomationElement window = AutomationElement.FromHandle(handle);

        // display the title
        Console.WriteLine("Title: " + window.Current.Name);

        // open two arbitrary files (change this!)
        OpenFile(window, @"d:\my path\file1.txt");
        OpenFile(window, @"d:\my path\file2.txt");

        // selects all tabs in sequence for demo purposes
        // note the user can interact with n++ (for example close tabs) while all this is working
        while (true)
        {
            var tabs = GetTabsNames(window);
            if (tabs.Count == 0)
            {
                Console.WriteLine("notepad++ process seems to have gone.");
                return;
            }

            for (int i = 0; i < tabs.Count; i++)
            {
                Console.WriteLine("Selecting tab:" + tabs[i]);
                SelectTab(window, tabs[i]);
                Thread.Sleep(1000);
            }
        }
    }

    static IList<string> GetTabsNames(AutomationElement window)
    {
        List<string> list = new List<string>();

        // get tab bar
        var tab = window.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Tab));
        if (tab != null)
        {
            foreach (var item in tab.FindAll(TreeScope.Children, PropertyCondition.TrueCondition).OfType<AutomationElement>())
            {
                list.Add(item.Current.Name);
            }
        }
        return list;
    }

    static void SelectTab(AutomationElement window, string name)
    {
        // get tab bar
        var tab = window.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Tab));

        // get tab
        var item = tab.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, name));
        if (item == null)
        {
            Console.WriteLine("Tab item '" + name + "' has been closed.");
            return;
        }

        // select it
        ((SelectionItemPattern)item.GetCurrentPattern(SelectionItemPattern.Pattern)).Select();
    }

    static void OpenFile(AutomationElement window, string filePath)
    {
        // get menu bar
        var menu = window.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.MenuBar));

        // get the "file" menu
        var fileMenu = menu.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, "File"));

        // open it
        SafeExpand(fileMenu);

        // get the new File menu that appears (this is quite specific to n++)
        var subFileMenu = fileMenu.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Menu));

        // get the "open" menu
        var openMenu = subFileMenu.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, "Open..."));

        // click it
        ((InvokePattern)openMenu.GetCurrentPattern(InvokePattern.Pattern)).Invoke();

        // get the new Open dialog (from root)
        var openDialog = WaitForDialog(window);

        // get the combobox
        var cb = openDialog.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.ComboBox));

        // fill the filename
        ((ValuePattern)cb.GetCurrentPattern(ValuePattern.Pattern)).SetValue(filePath);

        // get the open button
        var openButton = openDialog.FindFirst(TreeScope.Children, new AndCondition(
            new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button),
            new PropertyCondition(AutomationElement.NameProperty, "Open")));

        // press it
        ((InvokePattern)openButton.GetCurrentPattern(InvokePattern.Pattern)).Invoke();
    }

    static AutomationElement WaitForDialog(AutomationElement element)
    {
        // note: this should be improved for error checking (timeouts, etc.)
        while(true)
        {
            var openDialog = element.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Window));
            if (openDialog != null)
                return openDialog;
        }
    }

    static void SafeExpand(AutomationElement element)
    {
        // for some reason, menus in np++ behave badly
        while (true)
        {
            try
            {
                ((ExpandCollapsePattern)element.GetCurrentPattern(ExpandCollapsePattern.Pattern)).Expand();
                return;
            }
            catch
            {
            }
        }
    }
}

如果您想知道如何做到这一点,那么您必须阅读有关UI自动化的内容。所有工具的母亲称为Inspect:https://msdn.microsoft.com/library/windows/desktop/dd318521.aspx 确保您的版本至少为7.2.0.0。请注意,还有一个名为UISpy,但检查更好。

请注意,遗憾的是,notepad ++标签文本内容 - 因为它基于自定义scintilla编辑器控件 - 不能正确支持自动化(我们无法轻松读取它,我想我们已经&#39; d必须使用scintilla Windows消息,但它可以添加到它(嘿,scintilla家伙,如果你读这个... :)。

答案 1 :(得分:1)

如果SendKeys不是选项,那么几乎不可能,但阅读更多

现在更重要的部分问题 - 原因:

我们必须看看win32应用程序是如何工作的:它有一个WndProc / WindowProc方法,可以从UI处理“事件”。 因此,Windows应用程序中的每个事件都必须通过上述方法。 SendKeys方法是SendMessageMSDN)的特殊方法,因此您可以使用SendMessage来控制除您之外的其他exe。

简单代码可能如下所示:

IntPtr hwnd = FindWindow("Notepad++", null);
SendMessageA(hwnd, WM_COMMAND, SOMETHING1, SOMETHING2);

StackOverflow示例中已经有如何使用chrome:C# - Sending messages to Google Chrome from C# application执行此操作,但这只是一个开始。您必须找出您要发送的确切消息。

在您描述的确切情况下,我将尝试将WM_MOUSE和WM_KEYBORD事件发送到Notepad ++事件,但这只是一个想法:)

答案 2 :(得分:1)

除了Garath的答案之外,您可能还想研究Windows自动化API,即用于实现GUI应用程序的编码UI测试的技术。作为常规功能测试的一部分,我通常使用这些API从一组NUnit测试中控制外部应用程序。

UIAVerify之类的工具会告诉您应用程序中可用的控件,并且可以在运行时使用Invoke Pattern(和许多其他控件)与控件进行交互。

如果您想要一个如何使用自动化API的详细示例,那么开源TestStack White项目非常方便。