fix: expand local session results and reject unsafe search targets
This commit is contained in:
@@ -40,7 +40,8 @@ public static partial class WechatChatClient
|
||||
public static async Task<IReadOnlyList<WechatSessionSearchResult>> SearchSessionsAsync(
|
||||
string query,
|
||||
bool exactOnly,
|
||||
CancellationToken cancellationToken)
|
||||
CancellationToken cancellationToken,
|
||||
string? uiOutput = null)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(query);
|
||||
await CommandQueue.WaitAsync(cancellationToken);
|
||||
@@ -55,18 +56,9 @@ public static partial class WechatChatClient
|
||||
var previous = box.Text;
|
||||
try
|
||||
{
|
||||
box.Text = query;
|
||||
if (!string.Equals(box.Text, query, StringComparison.Ordinal))
|
||||
{
|
||||
throw new WxAgentException(WxAgentErrorCode.ResultUnconfirmed, "The search box did not contain the exact query.");
|
||||
}
|
||||
|
||||
await Task.Delay(500, cancellationToken);
|
||||
var results = WechatSessionParser.ParseSearchResults(
|
||||
window.FindAllDescendants()
|
||||
.Where(element => SafeControlType(element) == ControlType.ListItem)
|
||||
.Select(element => (SafeAutomationId(element), SafeName(element))),
|
||||
query);
|
||||
var results = await SearchLocalSessionsCoreAsync(window, query, cancellationToken).ConfigureAwait(false);
|
||||
if (uiOutput is not null)
|
||||
await WechatUiInspector.CaptureAsync(uiOutput, cancellationToken).ConfigureAwait(false);
|
||||
return exactOnly ? results.Where(result => result.IsExactMatch).ToArray() : results;
|
||||
}
|
||||
finally
|
||||
@@ -90,14 +82,20 @@ public static partial class WechatChatClient
|
||||
: new WechatSessionSnapshot(name, SafeAutomationId(element!), true));
|
||||
}, cancellationToken);
|
||||
|
||||
public static async Task<WechatSessionSnapshot> OpenSessionAsync(string name, CancellationToken cancellationToken)
|
||||
public static async Task<WechatSessionSnapshot> OpenSessionAsync(string name, CancellationToken cancellationToken, string? query = null)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(name);
|
||||
if (query is not null) ArgumentException.ThrowIfNullOrWhiteSpace(query);
|
||||
await CommandQueue.WaitAsync(cancellationToken);
|
||||
try
|
||||
{
|
||||
using var automation = new UIA3Automation();
|
||||
var window = AttachWindow(automation, cancellationToken);
|
||||
if (query is not null)
|
||||
{
|
||||
var selected = await OpenLocalSearchSessionAsync(window, name, cancellationToken, query).ConfigureAwait(false);
|
||||
return new WechatSessionSnapshot(name, selected.AutomationId, true);
|
||||
}
|
||||
if (SafeName(FindByAutomationId(window, WechatLocators.CurrentChatName)) == name)
|
||||
{
|
||||
return new WechatSessionSnapshot(name, SafeAutomationId(FindByAutomationId(window, WechatLocators.CurrentChatName)!), true);
|
||||
@@ -106,23 +104,8 @@ public static partial class WechatChatClient
|
||||
var target = FindByAutomationId(window, "session_item_" + name);
|
||||
if (target is null)
|
||||
{
|
||||
var search = window.FindAllDescendants().FirstOrDefault(element =>
|
||||
SafeControlType(element) == ControlType.Edit && SafeName(element) == WechatLocators.Search)
|
||||
?? throw new WxAgentException(WxAgentErrorCode.ControlNotFound, "WeChat search box was not found.");
|
||||
search.AsTextBox().Text = name;
|
||||
var deadline = DateTimeOffset.UtcNow + TimeSpan.FromSeconds(5);
|
||||
while (DateTimeOffset.UtcNow < deadline && target is null)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
target = window.FindAllDescendants().FirstOrDefault(element =>
|
||||
SafeAutomationId(element).StartsWith("search_item_", StringComparison.Ordinal) && SessionNameMatches(SafeName(element), name));
|
||||
if (target is null) await Task.Delay(150, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
if (target is null)
|
||||
{
|
||||
throw new WxAgentException(WxAgentErrorCode.ControlNotFound, "The requested session was not found.");
|
||||
var selected = await OpenLocalSearchSessionAsync(window, name, cancellationToken).ConfigureAwait(false);
|
||||
return new WechatSessionSnapshot(name, selected.AutomationId, true);
|
||||
}
|
||||
|
||||
ClickCenter(target);
|
||||
@@ -400,6 +383,9 @@ public static partial class WechatChatClient
|
||||
IDisposable? subscription = null;
|
||||
var connected = false;
|
||||
nint subscribedWindow = 0;
|
||||
var bindingAttempted = false;
|
||||
var observedSnapshot = false;
|
||||
Exception? lastReadError = null;
|
||||
var announceReconnect = recoveredScan;
|
||||
var elapsed = System.Diagnostics.Stopwatch.StartNew();
|
||||
using var readDeadline = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
||||
@@ -416,6 +402,7 @@ public static partial class WechatChatClient
|
||||
await CommandQueue.WaitAsync(readDeadline.Token);
|
||||
try
|
||||
{
|
||||
bindingAttempted = true;
|
||||
window = await BindListeningWindowAsync(automation, session, independentWindow, readDeadline.Token).ConfigureAwait(false);
|
||||
var handle = window.Properties.NativeWindowHandle.Value;
|
||||
if (subscribedWindow != 0 && handle != subscribedWindow)
|
||||
@@ -440,6 +427,7 @@ public static partial class WechatChatClient
|
||||
if (!IsListeningSession(window, session, independentWindow))
|
||||
throw new WxAgentException(WxAgentErrorCode.InvalidOperationState, "Listener session changed during snapshot.");
|
||||
visible = snapshot;
|
||||
observedSnapshot = true;
|
||||
snapshotObserved?.Invoke();
|
||||
}
|
||||
finally
|
||||
@@ -455,8 +443,9 @@ public static partial class WechatChatClient
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch
|
||||
catch (Exception exception)
|
||||
{
|
||||
lastReadError = exception;
|
||||
visible = null;
|
||||
connected = false;
|
||||
subscription?.Dispose();
|
||||
@@ -551,6 +540,9 @@ public static partial class WechatChatClient
|
||||
{
|
||||
subscription?.Dispose();
|
||||
}
|
||||
if (bindingAttempted && !observedSnapshot)
|
||||
throw new WxAgentException(lastReadError is WxAgentException error ? error.Code : WxAgentErrorCode.ControlNotFound,
|
||||
$"Listener never obtained a valid snapshot (stage: {lastReadError?.TargetSite?.Name ?? "initial binding"}).", lastReadError);
|
||||
}
|
||||
|
||||
private static AutomationElement AttachWindow(UIA3Automation automation, CancellationToken cancellationToken)
|
||||
|
||||
Reference in New Issue
Block a user