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

一笑奈何 提交于 2019-12-02 09:04:33

问题


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

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.

The basic code I am running is:

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

root contains similar info:


回答1:


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




回答2:


In addition to the answer from Garath, you might also want to investigate the Windows automation API's i.e. the technology used to implement coded UI tests for GUI applications. As part of regular functional testing, I routinely control an external application from a set of NUnit tests using these API's.

Tools like UIAVerify will give you an indication of what controls are available in the application and you can use the Invoke Pattern (and many others) to interact with the controls at run-time.

If you want a detailed example of how to use the automation API's, the open source TestStack White project is pretty handy.




回答3:


It is almost not possible if SendKeys is not an option but read more

Now more important part of the question- why:

We have to look how win32 application works: it has a WndProc/WindowProc method which is resposible for processing "events" form the UI. So every event in the windows application must go through above method. SendKeys method is a special of SendMessage (MSDN), so you can use SendMessage to control other exe than your.

Simple code could look like:

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

There is already on StackOverflow example how to do that with chrome: C# - Sending messages to Google Chrome from C# application , but this is only a start. You will have to find out what exactly message you want to send.

In exactly situation which you described I will try to send WM_MOUSE and WM_KEYBORD events to Notepad++ events, but it is only an idea :)



来源:https://stackoverflow.com/questions/29951432/is-it-possible-to-activate-a-tab-in-another-program-using-an-intptr

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!