使用 Delphi 通过 UIAutomation 获取基础菜单项列表
Getting list of underlying menu items via UIAutomation with Delphi
我一直在尝试使用作为 TLB 导入 Delphi 的 UIAutomationCore 库从标准 Windows 应用程序获取菜单子项列表 - 即
File -> New | Exit
Help -> About
我可以获取应用程序菜单,然后将顶级项目放入列表中(即在上面的示例中,'File' 和 'Help',但我无法获取任何控件的列表在这些菜单项下。我的代码如下 - FElement 代表我正在检查的实际菜单项。
FindAll返回的collection的长度始终为0。我在这段代码之前尝试过扩展menuitem,但似乎没有效果。
UIAuto.CreateTrueCondition(condition);
FItems := TObjectList<TAutomationMenuItem>.create;
self.Expand;
sleep(3000);
// Find the elements
self.FElement.FindAll(TreeScope_Descendants, condition, collection);
collection.Get_Length(length);
for count := 0 to length -1 do
begin
collection.GetElement(count, itemElement);
itemElement.Get_CurrentControlType(retVal);
if (retVal = UIA_MenuItemControlTypeId) then
begin
item := TAutomationMenuItem.Create(itemElement);
FItems.Add(item);
end;
end;
我可以在 C# 中看到这方面的示例,它们实际上与上面的代码没有任何不同(据我所知)
提前致谢
更新 : 看起来很像这个 question
Update2 :在本例中,它试图为另一个 Delphi 应用程序执行此操作。但是,如果我在记事本上尝试同样的事情(例如),就会出现同样的问题。
Update3:使用 Inspect(然后使用 UI Automation),我有以下结构 ...
名称 = 退出
Ancestors = 文件(菜单)Form1(窗格)
展开菜单(文件)后我也试过这个,同样的事情正在发生(或没有发生)。
我认为你有以下两个问题:
- 除非展开菜单,否则菜单不会列出子菜单项
- 如果您尝试自动化您自己的应用程序,则必须在一个线程中进行。
以下对我有用:
// Careful: the code might not be 100% threadsafe, but should work for the purpose of demonstration
const
UIA_MenuItemControlTypeId = 50011;
UIA_ControlTypePropertyId = 30003;
UIA_NamePropertyId = 30005;
UIA_ExpandCollapsePatternId = 10005;
procedure TForm1.Button1Click(Sender: TObject);
begin
TThread.CreateAnonymousThread(procedure begin CoInitializeEx(nil, 2); FindItems(true); CoUninitialize; end).Start;
end;
procedure TForm1.FindItems(Recurse: Boolean);
var
UIAuto: TCUIAutomation;
condition: IUIAutomationCondition;
collection: IUIAutomationElementArray;
Length: Integer;
Count: Integer;
itemElement: IUIAutomationElement;
retVal: Integer;
val: WideString;
ExpandCollapsePattern: IUIAutomationExpandCollapsePattern;
FElement: IUIAutomationElement;
begin
UIAuto := TCUIAutomation.Create(nil);
UIAuto.CreateTrueCondition(condition);
// Find the elements
UIAuto.ElementFromHandle(Pointer(Handle), FElement);
FElement.FindAll(TreeScope_Descendants, condition, collection);
collection.Get_Length(length);
for Count := 0 to length - 1 do
begin
collection.GetElement(Count, itemElement);
itemElement.Get_CurrentControlType(retVal);
if (retVal = UIA_MenuItemControlTypeId) then
begin
ItemElement.Get_CurrentName(val);
TThread.Synchronize(nil,
procedure
begin
memo1.lines.Add(val);
end);
itemElement.GetCurrentPattern(UIA_ExpandCollapsePatternId, IInterface(ExpandCollapsePattern));
if Assigned(ExpandCollapsePattern) then
begin
ExpandCollapsePattern.Expand;
if Recurse = True then
FindItems(False);
end;
end;
end;
UIAuto.Free;
end;
我一直在尝试使用作为 TLB 导入 Delphi 的 UIAutomationCore 库从标准 Windows 应用程序获取菜单子项列表 - 即
File -> New | Exit
Help -> About
我可以获取应用程序菜单,然后将顶级项目放入列表中(即在上面的示例中,'File' 和 'Help',但我无法获取任何控件的列表在这些菜单项下。我的代码如下 - FElement 代表我正在检查的实际菜单项。
FindAll返回的collection的长度始终为0。我在这段代码之前尝试过扩展menuitem,但似乎没有效果。
UIAuto.CreateTrueCondition(condition);
FItems := TObjectList<TAutomationMenuItem>.create;
self.Expand;
sleep(3000);
// Find the elements
self.FElement.FindAll(TreeScope_Descendants, condition, collection);
collection.Get_Length(length);
for count := 0 to length -1 do
begin
collection.GetElement(count, itemElement);
itemElement.Get_CurrentControlType(retVal);
if (retVal = UIA_MenuItemControlTypeId) then
begin
item := TAutomationMenuItem.Create(itemElement);
FItems.Add(item);
end;
end;
我可以在 C# 中看到这方面的示例,它们实际上与上面的代码没有任何不同(据我所知)
提前致谢
更新 : 看起来很像这个 question
Update2 :在本例中,它试图为另一个 Delphi 应用程序执行此操作。但是,如果我在记事本上尝试同样的事情(例如),就会出现同样的问题。
Update3:使用 Inspect(然后使用 UI Automation),我有以下结构 ...
名称 = 退出 Ancestors = 文件(菜单)Form1(窗格)
展开菜单(文件)后我也试过这个,同样的事情正在发生(或没有发生)。
我认为你有以下两个问题:
- 除非展开菜单,否则菜单不会列出子菜单项
- 如果您尝试自动化您自己的应用程序,则必须在一个线程中进行。
以下对我有用:
// Careful: the code might not be 100% threadsafe, but should work for the purpose of demonstration
const
UIA_MenuItemControlTypeId = 50011;
UIA_ControlTypePropertyId = 30003;
UIA_NamePropertyId = 30005;
UIA_ExpandCollapsePatternId = 10005;
procedure TForm1.Button1Click(Sender: TObject);
begin
TThread.CreateAnonymousThread(procedure begin CoInitializeEx(nil, 2); FindItems(true); CoUninitialize; end).Start;
end;
procedure TForm1.FindItems(Recurse: Boolean);
var
UIAuto: TCUIAutomation;
condition: IUIAutomationCondition;
collection: IUIAutomationElementArray;
Length: Integer;
Count: Integer;
itemElement: IUIAutomationElement;
retVal: Integer;
val: WideString;
ExpandCollapsePattern: IUIAutomationExpandCollapsePattern;
FElement: IUIAutomationElement;
begin
UIAuto := TCUIAutomation.Create(nil);
UIAuto.CreateTrueCondition(condition);
// Find the elements
UIAuto.ElementFromHandle(Pointer(Handle), FElement);
FElement.FindAll(TreeScope_Descendants, condition, collection);
collection.Get_Length(length);
for Count := 0 to length - 1 do
begin
collection.GetElement(Count, itemElement);
itemElement.Get_CurrentControlType(retVal);
if (retVal = UIA_MenuItemControlTypeId) then
begin
ItemElement.Get_CurrentName(val);
TThread.Synchronize(nil,
procedure
begin
memo1.lines.Add(val);
end);
itemElement.GetCurrentPattern(UIA_ExpandCollapsePatternId, IInterface(ExpandCollapsePattern));
if Assigned(ExpandCollapsePattern) then
begin
ExpandCollapsePattern.Expand;
if Recurse = True then
FindItems(False);
end;
end;
end;
UIAuto.Free;
end;