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

后端 未结 3 1120
萌比男神i
萌比男神i 2021-01-25 12:15

Thanks in advance.

Is it possible to activate a tab in another program using an IntPtr? If so, how?

SendKeys is not an option.

Perhaps what I need is a

3条回答
  •  青春惊慌失措
    2021-01-25 13:05

    As others pointed out, the standard way of doing this is to use UI Automation. Notepad++ does support UI Automation (to some extent, as it's somehow automatically provided by the UI Automation Windows layers).

    Here is a sample C# console app that demonstrates the following sceanrio (you need to reference UIAutomationClient.dll, UIAutomationProvider.dll and UIAutomationTypes.dll):

    1) get the first running notepad++ process (you must start at least one)

    2) open two files (note there may be already other opened tabs in notepad++)

    3) selects all tabs in an infinite loop

    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 GetTabsNames(AutomationElement window)
        {
            List list = new List();
    
            // 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())
                {
                    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
                {
                }
            }
        }
    }
    

    If you wonder how this has been made, then you must read about UI Automation. The mother of all tools is called Inspect: https://msdn.microsoft.com/library/windows/desktop/dd318521.aspx Make sure you get version at least 7.2.0.0. Note there is another one called UISpy but inspect is better.

    Note, unfortunately, notepad++ tab text content - because it's based on the custom scintilla editor control - does not properly supports automation (we can't read from it easily, I suppose we'd have to use scintilla Windows messages for this), but it could be added to it (hey, scintilla guys, if you read this ... :).

提交回复
热议问题