using System; using System.Collections.Generic; using System.Windows.Automation; namespace ISphereWinHelper { internal static class UiaDumper { public static Dictionary Dump(string requestId, string op, Dictionary 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 { { "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 DumpElement(AutomationElement element, int depth, int maxDepth, bool includeText, int maxChildren) { var node = new Dictionary { { "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>(); 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 SafeBounds(AutomationElement element) { try { System.Windows.Rect rect = element.Current.BoundingRectangle; return new Dictionary { { "x", rect.X }, { "y", rect.Y }, { "width", rect.Width }, { "height", rect.Height } }; } catch { return new Dictionary { { "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 ""; } } }