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

Is it possible to activate a tab in another program using an IntPtr?

提前致谢。

是否可以使用 IntPtr 在另一个程序中激活选项卡?如果是这样,如何?

SendKeys 不是一个选项。

也许我需要的是钓鱼课。我已经筋疲力尽 Google 和我的首席开发人员。 我会很感激一个彻底的解决方案或建议继续我的 Google 努力。

基本流程是:

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

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

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

我运行的基本代码是:

'blob'

root 包含类似信息:

如果 SendKeys 不是一个选项,这几乎是不可能的但阅读更多

现在更重要的部分是问题-为什么:

我们必须看看 win32 应用程序是如何工作的:它有一个 WndProc/WindowProc 方法,负责处理 "events" 形成 UI。 所以 windows 应用程序中的每个事件都必须通过上述方法。 SendKeys方法是SendMessage(MSDN)的一个特殊方法,所以你可以使用SendMessage来控制除你之外的其他exe。

简单的代码可能如下所示:

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

Whosebug 上已经有如何使用 chrome: C# - Sending messages to Google Chrome from C# application 做到这一点的示例,但这只是一个开始。您将必须找出要发送的确切消息。

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

除了 Garath 的回答之外,您可能还想调查 Windows 自动化 API,即用于为 G[= 实施编码 UI 测试的技术22=] 应用程序。作为常规功能测试的一部分,我经常使用这些 API 的一组 NUnit 测试来控制外部应用程序。

UIAVerify will give you an indication of what controls are available in the application and you can use the Invoke Pattern(以及许多其他工具)这样的工具可以在 运行 时间与控件进行交互。

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

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

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

1) 获取第一个 运行 notepad++ 进程(你必须至少启动一个)

2) 打开两个文件(注意在 notepad++ 中可能已经打开了其他选项卡)

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 的工具,但 inspect 更好。

请注意,不幸的是,notepad++ 选项卡文本内容 - 因为它基于自定义闪烁编辑器控件 - 不正确支持自动化(我们无法轻松读取它,我想我们必须使用闪烁 Windows 消息),但可以将其添加到其中(嘿,scintilla 伙计们,如果你读了这篇文章......:)。