using System; using System.Collections.Generic; using System.Globalization; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Text; using System.Windows.Automation; namespace ISphereWinHelper { internal static class UiaSendAction { private const int WM_SETTEXT = 0x000C; private const int BM_CLICK = 0x00F5; [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, string lParam); [DllImport("user32.dll", SetLastError = true)] private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam); public static Dictionary SendMessage(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"); } string editorAutomationId = HelperProtocol.GetString(args, "send_editor_automation_id", "rtbSendMessage"); string buttonAutomationId = HelperProtocol.GetString(args, "send_button_automation_id", "btnSend"); string contentText = HelperProtocol.GetString(args, "content_text", ""); string contentSha256 = HelperProtocol.GetString(args, "content_sha256", "").Trim().ToLowerInvariant(); string targetRef = HelperProtocol.GetString(args, "target_ref", ""); if (string.IsNullOrWhiteSpace(editorAutomationId)) { return HelperProtocol.Failure(requestId, op, "UIA_SEND_CONFIG_MISSING", "send_editor_automation_id is required"); } if (string.IsNullOrWhiteSpace(buttonAutomationId)) { return HelperProtocol.Failure(requestId, op, "UIA_SEND_CONFIG_MISSING", "send_button_automation_id is required"); } if (string.IsNullOrWhiteSpace(contentText)) { return HelperProtocol.Failure(requestId, op, "UIA_SEND_CONTENT_MISSING", "content_text is required"); } if (string.IsNullOrWhiteSpace(contentSha256) || contentSha256 != Sha256Hex(contentText)) { return HelperProtocol.Failure(requestId, op, "UIA_SEND_HASH_MISMATCH", "content_sha256 does not match content_text"); } try { AutomationElement root = AutomationElement.FromHandle(hwnd); if (root == null) { return HelperProtocol.Failure(requestId, op, "WINDOW_NOT_FOUND", "no UI Automation element for hwnd"); } AutomationElement editor = FindByAutomationId(root, editorAutomationId); if (editor == null) { return HelperProtocol.Failure(requestId, op, "UIA_SEND_EDITOR_NOT_FOUND", "send editor control not found: " + editorAutomationId); } AutomationElement button = FindByAutomationId(root, buttonAutomationId); if (button == null) { return HelperProtocol.Failure(requestId, op, "UIA_SEND_BUTTON_NOT_FOUND", "send button control not found: " + buttonAutomationId); } bool typed = SetElementText(editor, contentText); if (!typed) { return HelperProtocol.Failure(requestId, op, "UIA_SEND_SET_TEXT_FAILED", "could not set send editor text"); } bool clicked = InvokeElement(button); if (!clicked) { return HelperProtocol.Failure(requestId, op, "UIA_SEND_CLICK_FAILED", "could not invoke send button"); } string ackRef = "uia:" + WindowScanner.FormatHwnd(hwnd) + ":" + buttonAutomationId + ":" + ShortHash(contentSha256); var data = new Dictionary { { "action_mode", "uia_send_message" }, { "hwnd", WindowScanner.FormatHwnd(hwnd) }, { "target_ref", targetRef ?? "" }, { "content_sha256", contentSha256 }, { "content_length", contentText.Length }, { "editor_automation_id", editorAutomationId }, { "button_automation_id", buttonAutomationId }, { "editor_found", true }, { "button_found", true }, { "typed_text", true }, { "clicked_ui", true }, { "sent_message", true }, { "uploaded_file", false }, { "sent_file", false }, { "captured_network", false }, { "attached_hook", false }, { "modified_client_data", false }, { "ack_ref", ackRef } }; return HelperProtocol.Success(requestId, op, data); } catch (Exception ex) { return HelperProtocol.Failure(requestId, op, "UIA_SEND_FAILED", ex.Message); } } private static AutomationElement FindByAutomationId(AutomationElement root, string automationId) { if (root == null || string.IsNullOrWhiteSpace(automationId)) { return null; } try { return root.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.AutomationIdProperty, automationId)); } catch { return null; } } private static bool SetElementText(AutomationElement element, string text) { try { object pattern; if (element.TryGetCurrentPattern(ValuePattern.Pattern, out pattern)) { ((ValuePattern)pattern).SetValue(text); return true; } } catch { } try { int nativeHandle = element.Current.NativeWindowHandle; if (nativeHandle != 0) { SendMessage(new IntPtr(nativeHandle), WM_SETTEXT, IntPtr.Zero, text); return true; } } catch { } return false; } private static bool InvokeElement(AutomationElement element) { try { object pattern; if (element.TryGetCurrentPattern(InvokePattern.Pattern, out pattern)) { ((InvokePattern)pattern).Invoke(); return true; } } catch { } try { int nativeHandle = element.Current.NativeWindowHandle; if (nativeHandle != 0) { SendMessage(new IntPtr(nativeHandle), BM_CLICK, IntPtr.Zero, IntPtr.Zero); return true; } } catch { } return false; } private static string Sha256Hex(string value) { using (SHA256 sha = SHA256.Create()) { byte[] hash = sha.ComputeHash(Encoding.UTF8.GetBytes(value ?? "")); StringBuilder sb = new StringBuilder(hash.Length * 2); foreach (byte b in hash) { sb.Append(b.ToString("x2", CultureInfo.InvariantCulture)); } return sb.ToString(); } } private static string ShortHash(string value) { if (string.IsNullOrEmpty(value)) { return ""; } return value.Length <= 12 ? value : value.Substring(0, 12); } } }