Skip to content

Commit

Permalink
Separate automation responsibility
Browse files Browse the repository at this point in the history
* Separate functions for detecting the login window state and executing login window actions.
* Don't check against string values for the AutomationId element as that could be subject to system language. #181
* Make sure to focus the window before trying to find window elements.
* Add debug menu item for triggering the GetLoginWindowState function.
  • Loading branch information
rex706 committed Aug 29, 2023
1 parent 5624266 commit ceb62ae
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 148 deletions.
227 changes: 83 additions & 144 deletions Core/WindowUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -191,9 +191,13 @@ public static LoginWindowState GetLoginWindowState(WindowHandle loginWindow)
return LoginWindowState.Invalid;
}

window.Focus();
#if DEBUG
AutomationElement[] descendants = window.FindAllChildren();
#endif
AutomationElement document = window.FindFirstDescendant(e => e.ByControlType(ControlType.Document));

if (document == null || document.FindAllChildren().Length == 0)
if (document.FindAllChildren().Length == 0)
{
return LoginWindowState.Invalid;
}
Expand All @@ -210,216 +214,151 @@ public static LoginWindowState GetLoginWindowState(WindowHandle loginWindow)
AutomationElement[] groups = document.FindAllChildren(e => e.ByControlType(ControlType.Group));
AutomationElement[] images = document.FindAllChildren(e => e.ByControlType(ControlType.Image));

if (inputs != null)
if (inputs.Length == 0 && images.Length > 0 && buttons.Length > 0)
{
if (inputs.Length == 0 && images.Length > 0 && images[0].AutomationId == "Layer_2")
{
return LoginWindowState.Selection;
}
if (inputs.Length == 0 && buttons.Length == 1)
{
return LoginWindowState.Error;
}
else if (inputs.Length == 5)
{
return LoginWindowState.Code;
}
else if (inputs.Length == 2 && buttons.Length == 1)
{
return LoginWindowState.Login;
}
return LoginWindowState.Selection;
}
else if (inputs.Length == 0 && images.Length == 0 && buttons.Length == 1)
{
return LoginWindowState.Error;
}
else if (inputs.Length == 5)
{
return LoginWindowState.Code;
}
else if (inputs.Length == 2 && buttons.Length == 1)
{
return LoginWindowState.Login;
}
}
catch
catch (Exception e)
{
Console.WriteLine(e.Message);
return LoginWindowState.Error;
}
}

return LoginWindowState.Invalid;
}

public static LoginWindowState TryCredentialsEntry(WindowHandle loginWindow, string username, string password, bool remember)
public static LoginWindowState HandleAccountSelection(WindowHandle loginWindow)
{
if (!loginWindow.IsValid)
using (var automation = new UIA3Automation())
{
return LoginWindowState.Invalid;
}
try
{
AutomationElement window = automation.FromHandle(loginWindow.RawPtr);

window.Focus();

AutomationElement document = window.FindFirstDescendant(e => e.ByControlType(ControlType.Document));
AutomationElement[] groups = document.FindAllChildren(e => e.ByControlType(ControlType.Group));

Button addAccountButton = groups[groups.Length - 1].AsButton();
addAccountButton.Invoke();

SetForegroundWindow(loginWindow.RawPtr);
return LoginWindowState.Login;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return LoginWindowState.Invalid;
}
}
}

public static LoginWindowState TryCredentialsEntry(WindowHandle loginWindow, string username, string password, bool remember)
{
using (var automation = new UIA3Automation())
{
try
{
AutomationElement window = automation.FromHandle(loginWindow.RawPtr);

if (window == null)
{
return LoginWindowState.Invalid;
}
window.Focus();

AutomationElement document = window.FindFirstDescendant(e => e.ByControlType(ControlType.Document));
AutomationElement[] children = document.FindAllChildren();

if (document == null || children.Length == 0)
{
return LoginWindowState.Invalid;
}

AutomationElement[] inputs = document.FindAllChildren(e => e.ByControlType(ControlType.Edit));
AutomationElement[] buttons = document.FindAllChildren(e => e.ByControlType(ControlType.Button));
AutomationElement[] groups = document.FindAllChildren(e => e.ByControlType(ControlType.Group));
AutomationElement[] images = document.FindAllChildren(e => e.ByControlType(ControlType.Image));

if (inputs != null)
Button signInButton = buttons[0].AsButton();

if (signInButton.IsEnabled)
{
if (inputs.Length == 0 && images.Length > 0 && images[0].AutomationId == "Layer_2")
{
Button addAccountButton = groups[groups.Length - 1].AsButton();
addAccountButton.Invoke();
TextBox usernameBox = inputs[0].AsTextBox();
usernameBox.WaitUntilEnabled();
usernameBox.Text = username;

Console.WriteLine("Window State: Selection");
return LoginWindowState.Selection;
}
if (inputs.Length == 0 && buttons.Length == 1)
{
Console.WriteLine("Window State: Error");
return LoginWindowState.Error;
}
TextBox passwordBox = inputs[1].AsTextBox();
passwordBox.WaitUntilEnabled();
passwordBox.Text = password;

if (inputs.Length == 5)
{
Console.WriteLine("Window State: Code");
return LoginWindowState.Code;
}
Button checkBoxButton = groups[0].AsButton();
bool isChecked = checkBoxButton.FindFirstChild(e => e.ByControlType(ControlType.Image)) != null;

if (inputs.Length == 2 && buttons.Length == 1 && groups.Length == 1)
if (remember != isChecked)
{
Button signInButton = buttons[0].AsButton();

if (signInButton.IsEnabled)
{
SetForegroundWindow(loginWindow.RawPtr);

TextBox usernameBox = inputs[0].AsTextBox();
usernameBox.WaitUntilEnabled();
usernameBox.Text = username;

TextBox passwordBox = inputs[1].AsTextBox();
passwordBox.WaitUntilEnabled();
passwordBox.Text = password;

Button checkBoxButton = groups[0].AsButton();
bool isChecked = checkBoxButton.FindFirstChild(e => e.ByControlType(ControlType.Image)) != null;

if (remember != isChecked)
{
checkBoxButton.Focus();
checkBoxButton.WaitUntilEnabled();
checkBoxButton.Invoke();
}

signInButton.Focus();
signInButton.WaitUntilEnabled();
signInButton.Invoke();
}

return LoginWindowState.Success;
checkBoxButton.Focus();
checkBoxButton.WaitUntilEnabled();
checkBoxButton.Invoke();
}

signInButton.Focus();
signInButton.WaitUntilEnabled();
signInButton.Invoke();

return LoginWindowState.Success;
}

return LoginWindowState.Invalid;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return LoginWindowState.Invalid;
}
}

return LoginWindowState.Invalid;
}

public static LoginWindowState TryCodeEntry(WindowHandle loginWindow, string secret)
{
if (!loginWindow.IsValid)
{
return LoginWindowState.Invalid;
}

SetForegroundWindow(loginWindow.RawPtr);

using (var automation = new UIA3Automation())
{
try
{
AutomationElement window = automation.FromHandle(loginWindow.RawPtr);

if (window == null)
{
return LoginWindowState.Invalid;
}
window.Focus();

AutomationElement document = window.FindFirstDescendant(e => e.ByControlType(ControlType.Document));
AutomationElement[] children = document.FindAllChildren();

if (document == null || children.Length == 0)
{
return LoginWindowState.Invalid;
}

Console.WriteLine(children.Length);

if (children.Length == 2)
{
return LoginWindowState.Loading;
}

AutomationElement[] inputs = document.FindAllChildren(e => e.ByControlType(ControlType.Edit));
AutomationElement[] buttons = document.FindAllChildren(e => e.ByControlType(ControlType.Button));
AutomationElement[] groups = document.FindAllChildren(e => e.ByControlType(ControlType.Group));
AutomationElement[] images = document.FindAllChildren(e => e.ByControlType(ControlType.Image));

if (inputs != null)
{
if (inputs.Length == 0 && buttons.Length == 1)
{
return LoginWindowState.Error;
}
else if (inputs.Length == 2 && buttons.Length == 1)
{
return LoginWindowState.Login;
}
string code = Generate2FACode(secret);

if (inputs.Length == 5)
try
{
for (int i = 0; i < inputs.Length; i++)
{
string code = Generate2FACode(secret);

try
{
for (int i = 0; i < inputs.Length; i++)
{
TextBox textBox = inputs[i].AsTextBox();
textBox.Text = code[i].ToString();
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return LoginWindowState.Code;
}

return LoginWindowState.Success;
TextBox textBox = inputs[i].AsTextBox();
textBox.Text = code[i].ToString();
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return LoginWindowState.Code;
}

return LoginWindowState.Success;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return LoginWindowState.Invalid;
}
}

return LoginWindowState.Invalid;
}

public static Process WaitForSteamProcess(WindowHandle windowHandle)
Expand Down
41 changes: 37 additions & 4 deletions Views/AccountsWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,19 @@ private async void MainWindow_Loaded(object sender, RoutedEventArgs e)
// Version number from assembly
AssemblyVer = Assembly.GetExecutingAssembly().GetName().Version.ToString();

var ver = new MenuItem();
var newExistMenuItem = (MenuItem)FileMenu.Items[2];
MenuItem ver = new MenuItem();
MenuItem newExistMenuItem = (MenuItem)FileMenu.Items[2];
ver.Header = "v" + AssemblyVer;
ver.IsEnabled = false;
newExistMenuItem.Items.Add(ver);

#if DEBUG
MenuItem windowStateMenuItem = new MenuItem();
windowStateMenuItem.Header = "Get Window State";
windowStateMenuItem.Click += WindowStateMenuItem_Click;
newExistMenuItem.Items.Add(windowStateMenuItem);
#endif

if (settings.User.CheckForUpdates)
{
UpdateResponse response = await UpdateHelper.CheckForUpdate(updateCheckUrl);
Expand Down Expand Up @@ -180,6 +187,16 @@ private async void MainWindow_Loaded(object sender, RoutedEventArgs e)
}
}

private void WindowStateMenuItem_Click(object sender, RoutedEventArgs e)
{
WindowHandle windowHandle = WindowUtils.GetSteamLoginWindow();

if (windowHandle.IsValid)
{
WindowUtils.GetLoginWindowState(windowHandle);
}
}

private string VerifyAndSetPassword()
{
MessageBoxResult messageBoxResult = MessageBoxResult.No;
Expand Down Expand Up @@ -1621,7 +1638,18 @@ private void EnterCredentials(Process steamProcess, Account account, int tryCoun

Thread.Sleep(100);

state = WindowUtils.TryCredentialsEntry(steamLoginWindow, account.Name, account.Password, settings.User.RememberPassword);
state = WindowUtils.GetLoginWindowState(steamLoginWindow);

if (state == LoginWindowState.Selection)
{
WindowUtils.HandleAccountSelection(steamLoginWindow);
continue;
}

if (state == LoginWindowState.Login)
{
state = WindowUtils.TryCredentialsEntry(steamLoginWindow, account.Name, account.Password, settings.User.RememberPassword);
}
}

if (account.SharedSecret != null && account.SharedSecret.Length > 0)
Expand Down Expand Up @@ -1710,7 +1738,12 @@ private void EnterReact2FA(Process steamProcess, Account account, int tryCount)

Thread.Sleep(100);

state = WindowUtils.TryCodeEntry(steamLoginWindow, secret);
state = WindowUtils.GetLoginWindowState(steamLoginWindow);

if (state == LoginWindowState.Code)
{
state = WindowUtils.TryCodeEntry(steamLoginWindow, secret);
}
}

Thread.Sleep(settings.User.SleepTime);
Expand Down

0 comments on commit ceb62ae

Please sign in to comment.