Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.0k views
in Technique[技术] by (71.8m points)

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

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 fishing lesson. I have exhausted Google and my lead developer. I would appreciate an outright solution OR a recommendation to continue my Google efforts.

basic process is:

I drag a shortcut icon to the launcher

Launcher window ~ C# source code

This opens the target application (Notepad++) and grabs IntPtr, etc.

I would like to programmatically select various items in Notepad++ such as Edit, menu items under Edit, or a doc tab.

Screen grab of Notepad++

The basic code I am running is:

enter image description here

the 'blob'

  • item 1: IntPtr of item
  • item 2: IntPtr of itemsChild
  • item 3: control text of item 1
  • item 4: is rectangle parameters of item 1

enter image description here

root contains similar info:

enter image description here

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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 pathfile1.txt");
        OpenFile(window, @"d:my pathfile2.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
            {
            }
        }
    }
}

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 ... :).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

57.0k users

...