215 lines
8.1 KiB
C#
215 lines
8.1 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|