feat: add a-route uia send connector
This commit is contained in:
214
native/ISphereWinHelper/UiaSendAction.cs
Normal file
214
native/ISphereWinHelper/UiaSendAction.cs
Normal file
@@ -0,0 +1,214 @@
|
||||
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<string, object> SendMessage(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");
|
||||
}
|
||||
|
||||
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<string, object>
|
||||
{
|
||||
{ "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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user