162 lines
5.8 KiB
C#
162 lines
5.8 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Windows.Automation;
|
|
|
|
namespace ISphereWinHelper
|
|
{
|
|
internal static class UiaDumper
|
|
{
|
|
public static Dictionary<string, object> Dump(string requestId, string op, Dictionary<string, object> args)
|
|
{
|
|
string hwndText = HelperProtocol.GetString(args, "hwnd", "");
|
|
IntPtr hwnd;
|
|
if (!WindowScanner.TryParseHwnd(hwndText, out hwnd))
|
|
{
|
|
return HelperProtocol.Failure(requestId, op, "WINDOW_NOT_FOUND", "invalid or empty hwnd");
|
|
}
|
|
|
|
int maxDepth = HelperProtocol.GetInt(args, "max_depth", 8);
|
|
if (maxDepth < 0) maxDepth = 0;
|
|
if (maxDepth > 20) maxDepth = 20;
|
|
bool includeText = HelperProtocol.GetBool(args, "include_text", true);
|
|
int maxChildren = HelperProtocol.GetInt(args, "max_children", 200);
|
|
if (maxChildren < 1) maxChildren = 1;
|
|
if (maxChildren > 1000) maxChildren = 1000;
|
|
|
|
try
|
|
{
|
|
AutomationElement rootElement = AutomationElement.FromHandle(hwnd);
|
|
if (rootElement == null)
|
|
{
|
|
return HelperProtocol.Failure(requestId, op, "WINDOW_NOT_FOUND", "no UI Automation element for hwnd");
|
|
}
|
|
|
|
var data = new Dictionary<string, object>
|
|
{
|
|
{ "hwnd", WindowScanner.FormatHwnd(hwnd) },
|
|
{ "root", DumpElement(rootElement, 0, maxDepth, includeText, maxChildren) }
|
|
};
|
|
return HelperProtocol.Success(requestId, op, data);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return HelperProtocol.Failure(requestId, op, "UIA_DUMP_FAILED", ex.Message);
|
|
}
|
|
}
|
|
|
|
private static Dictionary<string, object> DumpElement(AutomationElement element, int depth, int maxDepth, bool includeText, int maxChildren)
|
|
{
|
|
var node = new Dictionary<string, object>
|
|
{
|
|
{ "name", SafeString(delegate { return element.Current.Name; }) },
|
|
{ "control_type", SafeControlType(element) },
|
|
{ "automation_id", SafeString(delegate { return element.Current.AutomationId; }) },
|
|
{ "class_name", SafeString(delegate { return element.Current.ClassName; }) },
|
|
{ "localized_control_type", SafeString(delegate { return element.Current.LocalizedControlType; }) },
|
|
{ "framework_id", SafeString(delegate { return element.Current.FrameworkId; }) },
|
|
{ "is_enabled", SafeBool(delegate { return element.Current.IsEnabled; }) },
|
|
{ "is_offscreen", SafeBool(delegate { return element.Current.IsOffscreen; }) },
|
|
{ "bounds", SafeBounds(element) }
|
|
};
|
|
|
|
if (includeText)
|
|
{
|
|
node["value"] = SafeValue(element);
|
|
}
|
|
|
|
var children = new List<Dictionary<string, object>>();
|
|
if (depth < maxDepth)
|
|
{
|
|
try
|
|
{
|
|
AutomationElementCollection found = element.FindAll(TreeScope.Children, Condition.TrueCondition);
|
|
int limit = Math.Min(found.Count, maxChildren);
|
|
for (int i = 0; i < limit; i++)
|
|
{
|
|
children.Add(DumpElement(found[i], depth + 1, maxDepth, includeText, maxChildren));
|
|
}
|
|
if (found.Count > limit)
|
|
{
|
|
node["truncated_children"] = found.Count - limit;
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
node["children_error"] = true;
|
|
}
|
|
}
|
|
node["children"] = children;
|
|
return node;
|
|
}
|
|
|
|
private delegate string StringGetter();
|
|
private delegate bool BoolGetter();
|
|
|
|
private static string SafeString(StringGetter getter)
|
|
{
|
|
try { return getter() ?? ""; }
|
|
catch { return ""; }
|
|
}
|
|
|
|
private static bool SafeBool(BoolGetter getter)
|
|
{
|
|
try { return getter(); }
|
|
catch { return false; }
|
|
}
|
|
|
|
private static string SafeControlType(AutomationElement element)
|
|
{
|
|
try
|
|
{
|
|
string name = element.Current.ControlType.ProgrammaticName ?? "";
|
|
return name.StartsWith("ControlType.", StringComparison.Ordinal) ? name.Substring("ControlType.".Length) : name;
|
|
}
|
|
catch
|
|
{
|
|
return "";
|
|
}
|
|
}
|
|
|
|
private static Dictionary<string, object> SafeBounds(AutomationElement element)
|
|
{
|
|
try
|
|
{
|
|
System.Windows.Rect rect = element.Current.BoundingRectangle;
|
|
return new Dictionary<string, object>
|
|
{
|
|
{ "x", rect.X },
|
|
{ "y", rect.Y },
|
|
{ "width", rect.Width },
|
|
{ "height", rect.Height }
|
|
};
|
|
}
|
|
catch
|
|
{
|
|
return new Dictionary<string, object>
|
|
{
|
|
{ "x", 0 },
|
|
{ "y", 0 },
|
|
{ "width", 0 },
|
|
{ "height", 0 }
|
|
};
|
|
}
|
|
}
|
|
|
|
private static string SafeValue(AutomationElement element)
|
|
{
|
|
try
|
|
{
|
|
object pattern;
|
|
if (element.TryGetCurrentPattern(ValuePattern.Pattern, out pattern))
|
|
{
|
|
return ((ValuePattern)pattern).Current.Value ?? "";
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
return "";
|
|
}
|
|
}
|
|
}
|