使用C#和UI自动化抢未知控制型的内容 [英] using C# and UI Automation to grab contents of unknown control-type

查看:1718
本文介绍了使用C#和UI自动化抢未知控制型的内容的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在图像下方有一个区域,有一个未知的(自定义)类。这不是一个网格或表。

我需要能够:


  • 在此区域内选择行

  • 来从每个单元抢值

现在的问题是,因为这不是一个常见的​​元素 - 我不知道怎么谷歌这个问题,或者解决它自己。到目前为止,code为以下内容:

 过程[] = PROC Process.GetProcessesByName(PROGRAMNAME);
AutomationElement窗口= AutomationElement.FromHandle(PROC [0] .MainWindowHandle);
PropertyCondition xEllist2 =新PropertyCondition(AutomationElement.ClassNameProperty,CustomListClass,PropertyConditionFlags.IgnoreCase);
AutomationElement targetElement = window.FindFirst(TreeScope.Children,xEllist2);

我已经试图威胁这个区域作为一个文本框,一个网格,作为一个组合框,但没有解决我的问题至今。没有任何人有任何意见如何抓住从该区域的数据,并通过行迭代?

编辑:对不起,我犯了一个错误的假设其实,包头(第1栏第2栏第3栏)和该地区的下半是的不同的控制-types 的!

由于Wininspector我能够挖掘有关这些控制类型的详细信息:


  • 头具有以下特点:HeaderControl 0x056407DC(90441692)凌:#43288为0xFFFFFFFF(-1)

  • ,下半部分有以下:列表控件0x056408A4(90441892)凌:#43288 0x02A6FDA0(44498336)

这是我之前展示的code - 回收名单元素只,所以这里是更新:

 过程[] = PROC Process.GetProcessesByName(PROGRAMNAME);
AutomationElement窗口= AutomationElement.FromHandle(PROC [0] .MainWindowHandle);
//获得头
PropertyCondition xEllist3 =新PropertyCondition(AutomationElement.ClassNameProperty,CustomHeaderClass,PropertyConditionFlags.IgnoreCase);
AutomationElement headerEl = XElAE.FindFirst(TreeScope.Children,xEllist3);
//获取列表
PropertyCondition xEllist2 =新PropertyCondition(AutomationElement.ClassNameProperty,CustomListClass,PropertyConditionFlags.IgnoreCase);
AutomationElement targetElement = window.FindFirst(TreeScope.Children,xEllist2);

给它一个进一步的思考,我试图让所有列名之后:

  AutomationElementCollection headerLines = headerEl.FindAll(TreeScope.Children,新PropertyCondition(AutomationElement.ControlTypeProperty,ControlType.HeaderItem));
字符串headertest = headerLines [0] .GetCurrentPropertyValue(AutomationElement.NameProperty)的字符串;
textBox2.AppendText(标题1:+ headertest + Environment.NewLine);

不幸的是在调试模式元素计数headerLines为0,因此该计划将引发错误。

编辑2:由于下面的答案 - 我已经安装了非托管UI自动化,它拥有比默认UIA更好的机会。 HTTP://uiacomwrapper.$c$cplex.com/
你如何使用旧模式,从未知控制型获取数据?

<$p$p><$c$c>if((bool)datagrid.GetCurrentPropertyValue(AutomationElementIdentifiers.IsLegacyIAccessiblePatternAvailableProperty))
{
变种图案=((LegacyIAccessiblePattern)datagrid.GetCurrentPattern(LegacyIAccessiblePattern.Pattern));
VAR状态= pattern.Current.State;
}

修改3。 IUIAutoamtion办法(非工作截至目前)

  _automation =新CUIAutomation();
        cacheRequest = _automation.CreateCacheRequest();
        cacheRequest.AddPattern(UiaConstants.UIA_LegacyIAccessiblePatternId);
        cacheRequest.AddProperty(UiaConstants.UIA_LegacyIAccessibleNamePropertyId);
        cacheRequest.TreeFilter = _automation.ContentViewCondition;
        trueCondition = _automation.CreateTrueCondition();
        流程[] PS = Process.GetProcessesByName(计划);
        IntPtr的HWND = PS [0] .MainWindowHandle;
        IUIAutomationElement elementMailAppWindow = _automation.ElementFromHandle(HWND);
        清单&LT; IntPtr的&GT; LS =新的List&LT; IntPtr的&GT;();        LS = GetChildWindows(HWND);        的foreach(VAR孩子LS)
        {
            IUIAutomationElement iuiae = _automation.ElementFromHandle(孩子);
            如果(iuiae.CurrentClassName ==CustomListClass)
            {
                变种outerArayOfStuff = iuiae.FindAllBuildCache(interop.UIAutomationCore.TreeScope.TreeScope_Children,trueCondition,cacheRequest.Clone());
                VAR outerArayOfStuff2 = iuiae.FindAll(interop.UIAutomationCore.TreeScope.TreeScope_Children,trueCondition);                VAR countOuter = outerArayOfStuff.Length;
                VAR countOuter2 = outerArayOfStuff2.Length;                变种uiAutomationElement = outerArayOfStuff.GetElement(0); //错误
                变种uiAutomationElement2 = outerArayOfStuff2.GetElement(0); //错误
    // ...
    //我已经删除了什么,随后接下来是因为code甚至不是现在的工作..
              }
        }

在code的实现得益于此问题:

<一个href=\"http://stackoverflow.com/questions/10799757/read-cell-items-from-data-grid-in-syslistview32-of-another-application-using-c-s\">Read使用C#从另一个应用程序的数据SysListView32网格项目

其结果


  • countOuter和countOuter2长度= 0

  • 不可能选择元素(从列表行)

  • 不可能得到任何值

  • 没有什么工作


解决方案

您可能需要使用核心UI自动化类尝试。它要求你输入在C#中使用它的dll。添加到您的pre-生成事件(或做它只有一次,等):

 为%ProgramFiles%\\微软的SDK \\ WINDOWS \\ v7.0A \\ BIN \\ tlbimp.exe是%WINDIR%\\ SYSTEM32 \\ UIAutomationCore.dll /out:..\\interop.UIAutomationCore .DLL

您可以再使用IUIAutomationLegacyIAccessiblePattern。

获取你需要的呼叫从常量:

C:\\ Program Files文件\\微软的SDK \\ WINDOWS \\ V7.1 \\包含\\ UIAutomationClient.h

我能看懂的Infragistics Ultragrids这种方式。

如果是太痛苦了,请尝试使用MSAA。我用这个项目作为与MSAA起点转换为所有UIA核心之前:的 MSSA样品code

-----编辑在12年6月25日------

我肯定会说,找到适当的标识符是使用MS UIAutomation东西最痛苦的部分。是什么使我很是创造,我可以为位置记录器使用一个简单的表格应用程序。从本质上讲,所有你需要的是两件事情:


  • 办法举办重点甚至,当你关闭你的窗体的窗口的<一个href=\"http://www.daniweb.com/software-development/csharp/threads/212456/keeping-a-form-focused-not-a-question\"相对=nofollow>举办重点


  • 使用x来ElementFromPoint(呼叫),这里的鼠标y坐标。还有就是这个在CUIAutomation类的实现。


我用CTRL键来告诉我的应用程序来获取鼠标坐标(System.Windows.Forms.Cursor.Position)。然后,我从点获得元素和递归得到元素的父,直到我到了桌面。

  VAR桌面= auto.GetRootElement();
        VAR沃克= GetRawTreeWalker();
        而(真)
        {
            元素= walker.GetParentElement(元);
            如果(auto.CompareElements(台式机,元素)== 1){打破;}
        }

-----编辑于12年6月26日-----

一旦你可以递归找到自动化识别和/或名称,可以比较容易地修改code此处的 http://blog.functionalfun.net/2009/06/introduction-to-ui-automation-with.html 与核心UI使用自动化类。这将允许您按照递归可用于识别嵌套在使用XPath语法风格的应用程序的控制建立的字符串。

In the image below there is an area, which has an unknown (custom) class. That's not a Grid or a Table.

I need to be able:

  • to select Rows in this area
  • to grab a Value from each cell

The problem is since that's not a common type element - I have no idea how to google this problem or solve it myself. So far the code is following:

Process[] proc = Process.GetProcessesByName("programname");
AutomationElement window = AutomationElement.FromHandle(proc [0].MainWindowHandle);
PropertyCondition xEllist2 = new PropertyCondition(AutomationElement.ClassNameProperty, "CustomListClass", PropertyConditionFlags.IgnoreCase);
AutomationElement targetElement = window.FindFirst(TreeScope.Children, xEllist2);

I've already tried to threat this Area as a textbox, as a grid, as a combobox, but nothing solved my problem so far. Does anybody have any advice how to grab data from this area and iterate through rows?

EDIT: sorry I've made a wrong assumption. Actually, the header(column 1, column 2, column 3) and the "lower half" of this area are different control-types!!

Thanks to Wininspector I was able to dig more information regarding these control types:

  • The header has following properties: HeaderControl 0x056407DC (90441692) Atom: #43288 0xFFFFFFFF (-1)
  • and the lower half has these: ListControl 0x056408A4 (90441892) Atom: #43288 0x02A6FDA0 (44498336)

The code that I've showed earlier - retrieved the "List" element only, so here is the update:

Process[] proc = Process.GetProcessesByName("programname");
AutomationElement window = AutomationElement.FromHandle(proc [0].MainWindowHandle);
//getting the header
PropertyCondition xEllist3 = new PropertyCondition(AutomationElement.ClassNameProperty, "CustomHeaderClass", PropertyConditionFlags.IgnoreCase);
AutomationElement headerEl = XElAE.FindFirst(TreeScope.Children, xEllist3);
//getting the list
PropertyCondition xEllist2 = new PropertyCondition(AutomationElement.ClassNameProperty, "CustomListClass", PropertyConditionFlags.IgnoreCase);
AutomationElement targetElement = window.FindFirst(TreeScope.Children, xEllist2);

After giving it a further thought I've tried to get all column names:

AutomationElementCollection headerLines = headerEl.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.HeaderItem));
string headertest = headerLines[0].GetCurrentPropertyValue(AutomationElement.NameProperty) as string;
textBox2.AppendText("Header 1: " + headertest + Environment.NewLine);

Unfortunately in debug mode element count in "headerLines" is 0 so the program throws an error.

Edit 2: Thanks to the answer below - I've installed Unmanaged UI Automation, which holds better possibilities than the default UIA. http://uiacomwrapper.codeplex.com/ How do you use the legacy pattern to grab data from unknown control-type?

if((bool)datagrid.GetCurrentPropertyValue(AutomationElementIdentifiers.IsLegacyIAccessiblePatternAvailableProperty))
{
var pattern = ((LegacyIAccessiblePattern)datagrid.GetCurrentPattern(LegacyIAccessiblePattern.Pattern));
var state = pattern.Current.State;
}

Edit 3. IUIAutoamtion approach (non-working as of now)

        _automation = new CUIAutomation();
        cacheRequest = _automation.CreateCacheRequest();
        cacheRequest.AddPattern(UiaConstants.UIA_LegacyIAccessiblePatternId);
        cacheRequest.AddProperty(UiaConstants.UIA_LegacyIAccessibleNamePropertyId);
        cacheRequest.TreeFilter = _automation.ContentViewCondition;
        trueCondition = _automation.CreateTrueCondition();


        Process[] ps = Process.GetProcessesByName("program");
        IntPtr hwnd = ps[0].MainWindowHandle;
        IUIAutomationElement elementMailAppWindow = _automation.ElementFromHandle(hwnd);


        List<IntPtr> ls = new List<IntPtr>();

        ls = GetChildWindows(hwnd);

        foreach (var child in ls)
        {
            IUIAutomationElement iuiae = _automation.ElementFromHandle(child);
            if (iuiae.CurrentClassName == "CustomListClass")
            {
                var outerArayOfStuff = iuiae.FindAllBuildCache(interop.UIAutomationCore.TreeScope.TreeScope_Children, trueCondition, cacheRequest.Clone());
                var outerArayOfStuff2 = iuiae.FindAll(interop.UIAutomationCore.TreeScope.TreeScope_Children, trueCondition);

                var countOuter = outerArayOfStuff.Length;
                var countOuter2 = outerArayOfStuff2.Length;

                var uiAutomationElement = outerArayOfStuff.GetElement(0); // error
                var uiAutomationElement2 = outerArayOfStuff2.GetElement(0); // error
    //...
    //I've erased what's followed next because the code isn't working even now..
              }
        }

The code was implemented thanks to this issue:

Read cell Items from data grid in SysListView32 of another application using C#

As the result:

  • countOuter and countOuter2 lengths = 0
  • impossible to select elements (rows from list)
  • impossible to get ANY value
  • nothing is working

解决方案

You might want to try using the core UI automation classes. It requires that you import the dll to use it in C#. Add this to your pre-build event (or do it just once, etc):

"%PROGRAMFILES%\Microsoft SDKs\Windows\v7.0A\bin\tlbimp.exe" %windir%\system32\UIAutomationCore.dll /out:..\interop.UIAutomationCore.dll"

You can then use the IUIAutomationLegacyIAccessiblePattern.

Get the constants that you need for the calls from:

C:\Program Files\Microsoft SDKs\Windows\v7.1\Include\UIAutomationClient.h

I am able to read Infragistics Ultragrids this way.

If that is too painful, try using MSAA. I used this project as a starting point with MSAA before converting to all UIA Core: MSSA Sample Code

----- Edited on 6/25/12 ------

I would definitely say that finding the proper 'identifiers' is the most painful part of using the MS UIAutomation stuff. What has helped me very much is to create a simple form application that I can use as 'location recorder'. Essentially, all you need are two things:

  • a way to hold focus even when you are off of your form's window Holding focus

  • a call to ElementFromPoint() using the x,y coordinates of where the mouse is. There is an implementation of this in the CUIAutomation class.

I use the CTRL button to tell my app to grab the mouse coordinates (System.Windows.Forms.Cursor.Position). I then get the element from the point and recursively get the element's parent until I reach the the desktop.

        var desktop = auto.GetRootElement();
        var walker = GetRawTreeWalker();
        while (true)
        {
            element = walker.GetParentElement(element);
            if (auto.CompareElements(desktop, element) == 1){ break;}
        }

----- edit on 6/26/12 -----

Once you can recursively find automation identifiers and/or names, you can rather easily modify the code here: http://blog.functionalfun.net/2009/06/introduction-to-ui-automation-with.html to be used with the Core UI Automation classes. This will allow you to build up a string as you recurse which can be used to identify a control nested in an application with an XPath style syntax.

这篇关于使用C#和UI自动化抢未知控制型的内容的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆