19using System.Collections.Generic;
20using System.Diagnostics;
24using System.Threading;
25using System.Threading.Tasks;
26using System.Windows.Automation;
51 public class Application
88 public delegate CallIfStatus
CallIfDelegate(Application application, List<IGPALAutomationElement> foundElements, List<IGPALAutomationElement> matchedElements,
Selector selector,
bool matchedAll);
116 #region <Working Variables>
120 internal ApplicationSettings applicationSettings {
get;
set; }
121 private dynamic persistenceList;
125 internal UnitOfWork CurrentUOW {
get;
set; } =
null;
127 #endregion <Working Variables>
167 persistenceList =
new List<UnitOfWork>
171 applicationSettings =
new ApplicationSettings(
this);
179 private bool CheckWaitFor()
181 bool matchFound =
true;
182 List<GPALAutomationElement> foundElements =
null;
187 if (
null != CurrentUOW.WaitForSatisfied)
189 GPAL.PublishSimpleEvent(GPALEventType.DEBUG, $
"Selectors for this unit of work already found at [{CurrentUOW.WaitForSatisfied:HH:mm:ss.fff}], skipping redundant wait.",
this, GPALObjectType.Application);
193 if (WaitTime.Never < CurrentUOW.WaitForInMs)
196 foundElements = ElementHelper.WaitFor(
this, CurrentUOW.WaitForInMs, out matchFound);
198 if (0 < foundElements?.Count)
199 CurrentUOW.WaitForSatisfied = DateTime.Now;
210 private bool InAction(
bool checkWaitFor)
212 ApplicationHelper.TopProcess(this.
Process);
214 CurrentUOW.ActionCalled =
true;
216 SelectorListMaintenanceSweep(CurrentUOW);
217 SelectorListMaintenanceSweep(persistentUOW);
218 CheckPersistentSelectors();
221 if (
true == checkWaitFor)
234 private static void SelectorListMaintenanceSweep(dynamic CurrentUOW)
236 for (
int idx = 0; idx < CurrentUOW.WithSelectorList.Count; idx++)
237 if (
true == CurrentUOW.WithSelectorList[idx].DeleteMe)
238 CurrentUOW.WithSelectorList.RemoveAt(idx);
240 for (
int idx = 0; idx < CurrentUOW.InSelectorList.Count; idx++)
241 if (
true == CurrentUOW.InSelectorList[idx].DeleteMe)
242 CurrentUOW.InSelectorList.RemoveAt(idx);
248 private void CheckPersistentSelectors()
251 if (0 < (persistentUOW.WithSelectorList?.Count ?? 0))
252 foreach (Selector selector
in persistentUOW.WithSelectorList)
253 ElementHelper.FindElements(
this, persistentUOW, selector, out
bool matchedAll, out List<GPALAutomationElement> elems);
256 #region <Application Actions>
296 if (
true == InAction(
true))
297 ApplicationHelper.ResizeWindow(ApplicationSettings.LastWindowHandle, width, height);
305 if (
true == InAction(
true))
319 DatabaseHelper.TokenizeDatabase(CurrentUOW, inputDatabase);
320 if (
true == InAction(
true))
334 ((IGPALFileInternal)inputFile).TokenList =
FileHelper.TokenizeFile(CurrentUOW, inputFile);
336 if (
true == InAction(
true))
351 if (
true == InAction(
true))
364 if (
true == InAction(
true))
365 ElementHelper.FillInFrom(
this, textToUse, WriteMode.Append);
377 IntPtr processHandle = IntPtr.Zero;
379 StringBuilder parameters =
new StringBuilder();
382 foreach (
string parameter
in applicationSettings.Parameters)
385 parameters.Append(
" ");
388 parameters.Append(parameter);
393 GPAL.
PublishSimpleEvent(GPALEventType.ERROR,
"Unable to Open/control more than one program. Please use another Application.",
this, GPALObjectType.Application);
402 if (
true == applicationSettings.HiddenDesktop)
404 applicationSettings.NewInstance =
true;
406 if (
false == Desktops.Ensure(applicationSettings.HiddenDesktopName))
408 GPAL.
PublishSimpleEvent(GPALEventType.WARNING, $
"Windows would not make the desktop [{applicationSettings.HiddenDesktopName}], so [{applicationPath}] runs on the one you are looking at.",
this, GPALObjectType.Application);
410 applicationSettings.HiddenDesktop =
false;
412 else if (
false == applicationSettings.HiddenDesktopReported)
414 applicationSettings.HiddenDesktopReported =
true;
416 GPAL.
PublishSimpleEvent(GPALEventType.INFO, $
"[{applicationPath}] runs on [{applicationSettings.HiddenDesktopName}], where UIAutomation drives it and hardware input cannot reach it. Peek at the desktop to watch it or to use hardware there.",
this, GPALObjectType.Application);
424 processes =
Process.GetProcessesByName(Path.GetFileNameWithoutExtension(applicationPath));
425 bool attached =
false;
429 if (
false == applicationSettings.NewInstance &&
null != processes && 0 < processes.Length)
433 foreach (
Process process
in processes)
435 applicationSettings.Process = process;
436 AutomationElement existingRoot = WaitForRootAutomationElement(process, 2000);
437 if (
null != existingRoot)
439 applicationSettings.RootAutomationElement = existingRoot;
440 processHandle = process.MainWindowHandle;
447 if (
false == attached)
449 applicationSettings.Process =
true == applicationSettings.HiddenDesktop
450 ? StartOnHiddenDesktop(applicationPath, parameters.ToString())
451 :
Process.Start(applicationPath, parameters.ToString());
455 AutomationElement root = WaitForRootAutomationElement(applicationSettings.Process,
456 Math.Max(1, applicationSettings.WaitForWindowTimeout) * 1000);
460 applicationSettings.RootAutomationElement = root;
461 processHandle = applicationSettings.Process.MainWindowHandle;
466 else if (
true == applicationSettings.Process?.HasExited)
467 GPAL.
PublishSimpleEvent(GPALEventType.WARNING, $
"[{applicationPath}] exited as soon as it started, which is what a launcher does. Use WaitForWindow or WaitForWindowRegex to settle on the window it opened.",
this, GPALObjectType.Application);
470 string msg = $
"Timed out after [{applicationSettings.WaitForWindowTimeout}]s waiting for the main window / root automation element of [{applicationPath}]{(true == applicationSettings.HiddenDesktop ? $" on [{applicationSettings.HiddenDesktopName}]
" : string.Empty)}.";
478 GPAL.
PublishSimpleEvent(GPALEventType.EXCEPTION, $
"Unable to launch [{applicationPath}].",
this, GPALObjectType.Application, ex);
482 if (
null == applicationSettings.Process)
483 GPAL.
PublishSimpleEvent(GPALEventType.ERROR, $
"Unable to launch [{applicationPath}].",
this, GPALObjectType.Application);
489 if (
false == applicationSettings.Process.HasExited &&
false == applicationSettings.HiddenDesktop)
492 if (
true == applicationSettings.HiddenDesktop &&
false == applicationSettings.HiddenDesktopChecked)
494 applicationSettings.HiddenDesktopChecked =
true;
496 if (
false == Desktops.HasWindows(applicationSettings.HiddenDesktopName, 10_000))
497 GPAL.
PublishSimpleEvent(GPALEventType.WARNING, $
"Nothing arrived on [{applicationSettings.HiddenDesktopName}], so this program is on the desktop you are working on.",
this, GPALObjectType.Application);
500 CurrentUOW.ActionCalled =
true;
513 private AutomationElement WaitForRootAutomationElement(
Process process,
int timeoutMs)
518 Stopwatch stopwatch = Stopwatch.StartNew();
522 try { process.WaitForInputIdle(timeoutMs); }
catch { }
524 while (stopwatch.ElapsedMilliseconds < timeoutMs)
532 IntPtr handle =
true == applicationSettings.HiddenDesktop
533 ? Desktops.MainWindowOf(applicationSettings.HiddenDesktopName, process.Id)
534 : process.MainWindowHandle;
535 if (IntPtr.Zero != handle)
537 AutomationElement root = AutomationElement.FromHandle(handle);
566 if (
true == ApplicationSettings.HiddenDesktop)
567 Desktops.CloseWindowsOf(ApplicationSettings.HiddenDesktopName,
Process.Id);
571 if (
false ==
Process.WaitForExit(CLOSING_MS))
573 GPAL.
PublishSimpleEvent(GPALEventType.WARNING, $
"[{ApplicationPath}] did not close within [{CLOSING_MS / 1000}]s of being asked, so it is being ended.",
this, GPALObjectType.Application);
584 private const int CLOSING_MS = 5000;
607 private Process StartOnHiddenDesktop(
string applicationPath,
string parameters)
611 cb = (uint)System.Runtime.InteropServices.Marshal.SizeOf<Native.STARTUPINFO>(),
612 lpDesktop = Desktops.StartupNameFor(applicationSettings.HiddenDesktopName)
615 string quoted =
"\"" + applicationPath +
"\"";
616 string commandLine =
true ==
string.IsNullOrWhiteSpace(parameters) ? quoted : quoted +
" " + parameters;
618 if (
false == Native.CreateProcess(
null, commandLine, IntPtr.Zero, IntPtr.Zero,
false, 0, IntPtr.Zero,
619 Path.GetDirectoryName(applicationPath), ref startup, out Native.PROCESS_INFORMATION started))
620 throw new System.ComponentModel.Win32Exception(System.Runtime.InteropServices.Marshal.GetLastWin32Error(), $
"Could not start [{applicationPath}] on [{applicationSettings.HiddenDesktopName}]");
622 Process retVal =
Process.GetProcessById((
int)started.dwProcessId);
624 Native.CloseHandle(started.hProcess);
625 Native.CloseHandle(started.hThread);
630 private static bool HasGone(
Process process)
636 retVal = process.HasExited;
638 catch (InvalidOperationException)
655 DatabaseHelper.TokenizeDatabase(CurrentUOW, inputDatabase);
657 if (
true == InAction(
true))
672 ((IGPALFileInternal)inputFile).TokenList =
FileHelper.TokenizeFile(CurrentUOW, inputFile);
673 if (
true == InAction(
true))
687 if (
true == InAction(
true))
699 if (
true == InAction(
true))
700 ElementHelper.FillInFrom(
this, textToUse, WriteMode.Overwrite);
712 if (
true == InAction(
true))
713 ElementHelper.Focus(
this);
726 if (
true == InAction(
true))
727 ElementHelper.Hover(
this, CurrentUOW);
739 if (
true == InAction(
true))
740 ElementHelper.FillInFrom(
this, textToUse, WriteMode.Insert);
753 DatabaseHelper.TokenizeDatabase(CurrentUOW, inputDatabase);
754 if (
true == InAction(
true))
768 if (
true == InAction(
true))
782 ((IGPALFileInternal)inputFile).TokenList =
FileHelper.TokenizeFile(CurrentUOW, inputFile);
783 if (
true == InAction(
true))
796 if (
true == InAction(
true))
797 ElementHelper.LeftClick(
this);
807 if (
true == InAction(
true))
808 ElementHelper.LeftClick(
this, modifierKeys);
819 if (
true == InAction(
true))
820 ElementHelper.GenericClick(
this, ClickType.LeftDoubleClick);
831 if (
true == InAction(
true))
832 ElementHelper.GenericClick(
this, ClickType.MiddleClick);
843 if (
true == InAction(
true))
844 ElementHelper.MoveTo(
this);
869 return ClickAndAnswer(file);
896 return ClickAndAnswer(file);
903 private void ClearTheWay(
GPALFile file)
905 foreach (
string name
in file?.Filenames ??
new List<string>())
906 if (
true == System.IO.File.Exists(name))
908 GPAL.
PublishSimpleEvent(GPALEventType.INFO, $
"Deleting [{name}] before saving over it, so the dialog has nothing to ask about",
this, GPALObjectType.Application);
910 System.IO.File.Delete(name);
919 private IAllowSelectorActionOrAnySelector ClickAndAnswer(
GPALFile file)
921 if (
true == InAction(
true))
923 ElementHelper.LeftClick(
this, ModifierKeys.NONE);
925 FileDialogHelper.Answer(
this, file, ApplicationSettings.WaitForWindowTimeout);
942 if (
true == InAction(
true))
943 FileDialogHelper.Answer(
this, file, ApplicationSettings.WaitForWindowTimeout);
956 if (
true == InAction(
true))
957 ElementHelper.DragAndDrop(
this);
968 if (
true == InAction(
true))
969 ElementHelper.DragAndDrop(
this, modifierKeys);
980 HardwareHelper.PressModifierKey(modifierKeys);
991 HardwareHelper.ReleaseModifierKeys(modifierKeys);
1016 if (
true == InAction(
true))
1018 HardwareHelper.SendKey(VKCode);
1022 ElementHelper.SettleInput(
this);
1036 ElementHelper.SettleInput(
this);
1048 if (
true == InAction(
true))
1049 ElementHelper.SwitchToTab(
this);
1059 if (
true == InAction(
true))
1060 ElementHelper.SetRange(
this, rangeValue);
1072 if (
true == InAction(
true))
1073 ElementHelper.GenericClick(
this, ClickType.RightClick);
1089 if (
true == CurrentUOW.ActionCalled)
1090 Thread.Sleep(timeoutInTicks);
1092 CurrentUOW.WaitForInMs = timeoutInTicks;
1105 if (
true == InAction(
true))
1106 SettleOnWindow(
true == ApplicationSettings.HiddenDesktop
1107 ? Desktops.WaitForWindowOn(ApplicationSettings.HiddenDesktopName, title => windowTitle.Equals(title), ApplicationSettings.WaitForWindowTimeout)
1108 : FormHelper.WaitForWindow(windowTitle, ApplicationSettings.WaitForWindowTimeout));
1120 if (
true == InAction(
true))
1121 SettleOnWindow(
true == ApplicationSettings.HiddenDesktop
1122 ? Desktops.WaitForWindowOn(ApplicationSettings.HiddenDesktopName, title => 0 < System.Text.RegularExpressions.Regex.Matches(title, windowTitleRegex).Count, ApplicationSettings.WaitForWindowTimeout)
1123 : FormHelper.WaitForWindowRegex(windowTitleRegex, ApplicationSettings.WaitForWindowTimeout));
1138 private void SettleOnWindow(IntPtr windowHandle)
1140 ApplicationSettings.LastWindowHandle = windowHandle;
1142 if (IntPtr.Zero != windowHandle)
1144 AutomationElement window = AutomationElement.FromHandle(windowHandle);
1147 ApplicationSettings.RootAutomationElement = window;
1162 if (
true == InAction(
true))
1164 for (; 0 < iterations; iterations--)
1165 ElementHelper.Scroll(
this, ScrollAmount.SmallIncrement, ScrollTypes.Increments, ScrollTypes.Horizontal);
1167 for (; 0 > iterations; iterations++)
1168 ElementHelper.Scroll(
this, ScrollAmount.SmallDecrement, ScrollTypes.Increments, ScrollTypes.Horizontal);
1185 if (
true == InAction(
true))
1187 for (; 0 < iterations; iterations--)
1188 ElementHelper.Scroll(
this, ScrollAmount.LargeIncrement, ScrollTypes.Increments, ScrollTypes.Horizontal);
1190 for (; 0 > iterations; iterations++)
1191 ElementHelper.Scroll(
this, ScrollAmount.LargeDecrement, ScrollTypes.Increments, ScrollTypes.Horizontal);
1207 if (
true == InAction(
true))
1208 ElementHelper.Scroll(
this, InRange(percent), ScrollTypes.Percent, ScrollTypes.Horizontal);
1222 private int InRange(
int percent)
1224 int retVal = 0 > percent ? 0 : (100 < percent ? 100 : percent);
1226 if (retVal != percent)
1227 GPAL.
PublishSimpleEvent(GPALEventType.WARNING, $
"A scroll percentage is 0 to 100, so [{percent}] was taken as [{retVal}]",
this, GPALObjectType.Application);
1243 if (
true == InAction(
true))
1245 for (; 0 < iterations; iterations--)
1246 ElementHelper.Scroll(
this, ScrollAmount.SmallIncrement, ScrollTypes.Increments, ScrollTypes.Vertical);
1248 for (; 0 > iterations; iterations++)
1249 ElementHelper.Scroll(
this, ScrollAmount.SmallDecrement, ScrollTypes.Increments, ScrollTypes.Vertical);
1266 if (
true == InAction(
true))
1268 for (; 0 < iterations; iterations--)
1269 ElementHelper.Scroll(
this, ScrollAmount.LargeIncrement, ScrollTypes.Increments, ScrollTypes.Vertical);
1271 for (; 0 > iterations; iterations++)
1272 ElementHelper.Scroll(
this, ScrollAmount.LargeDecrement, ScrollTypes.Increments, ScrollTypes.Vertical);
1288 if (
true == InAction(
true))
1289 ElementHelper.Scroll(
this, InRange(percent), ScrollTypes.Percent, ScrollTypes.Vertical);
1293 #endregion <Application Actions>
1301 CurrentUOW.AppCallIfFound.Remove(func);
1302 CurrentUOW.AppCallIfNotFound.Remove(func);
1305 foreach (
Selector selector
in CurrentUOW.WithSelectorList)
1307 selector.SelectorSettings.AppCallIfFound.Remove(func);
1308 selector.SelectorSettings.AppCallIfNotFound.Remove(func);
1311 foreach (
Selector selector
in CurrentUOW.InSelectorList)
1313 selector.SelectorSettings.AppCallIfFound.Remove(func);
1314 selector.SelectorSettings.AppCallIfNotFound.Remove(func);
1317 persistentUOW.AppCallIfFound.Remove(func);
1318 persistentUOW.AppCallIfNotFound.Remove(func);
1320 foreach (
Selector selector
in persistentUOW.WithSelectorList)
1322 selector.SelectorSettings.AppCallIfFound.Remove(func);
1323 selector.SelectorSettings.AppCallIfNotFound.Remove(func);
1326 #endregion <Helpers>
1327 #region <Application Settings>
1349 persistentUOW.AppCallIfFound.Add(persistentCallIfFound);
1364 persistentUOW.AppCallIfNotFound.Add(persistentCallIfNotFound);
1381 if (
true == CurrentUOW.ActionCalled)
1384 persistenceList.Add(persistentUOW);
1387 selector.Application =
this;
1389 persistentUOW.WithSelectorList.Add(selector);
1402 selector.ElementsFoundAndMatchedCount = 0;
1403 selector.DeleteMe =
false;
1404 selector.RemovedMethods.Clear();
1406 if (
true == CurrentUOW.ActionCalled)
1414 ApplicationSettings.MySelector = selector;
1416 CurrentUOW.WithSelectorList.Add(ApplicationSettings.MySelector);
1435 CurrentUOW.WithAllThatMatch = rowCount;
1439 #region <GetGrid / SaveTo / Pagination>
1460 CurrentUOW.ActionCalled =
true;
1461 CurrentUOW.GetGridCalled =
true;
1462 CurrentUOW.RetGrid.Clear();
1464 if (!TryGetGridFromControl())
1465 GetGridFromSelectors();
1470 if (
null == returnGrid)
1471 returnGrid = (IGPALGrid<string>)CurrentUOW.RetGrid;
1473 returnGrid.Add((IGPALGrid<string>)CurrentUOW.RetGrid);
1481 private bool TryGetGridFromControl()
1483 if (1 != CurrentUOW.WithSelectorList.Count
1484 || SelectorType.Selector != CurrentUOW.WithSelectorList[0].selectorSettings.SelectorType)
1487 Selector sel = CurrentUOW.WithSelectorList[0];
1488 bool matchedAll; List<GPALAutomationElement> matched;
1489 var found = ElementHelper.FindElements(
this, CurrentUOW, sel, out matchedAll, out matched);
1490 AutomationElement ae = (
null != found && found.Count > 0) ? found[0].Ae : null;
1492 if (
null == ae || !ae.TryGetCurrentPattern(GridPattern.Pattern, out gpObj))
1495 GridPattern grid = (GridPattern)gpObj;
1496 int rows = grid.Current.RowCount;
1497 int cols = grid.Current.ColumnCount;
1499 List<string> headers =
null;
1501 if (ae.TryGetCurrentPattern(TablePattern.Pattern, out tpObj))
1503 AutomationElement[] colHeaders = ((TablePattern)tpObj).Current.GetColumnHeaders();
1504 if (
null != colHeaders && colHeaders.Length > 0)
1506 headers =
new List<string>();
1511 foreach (AutomationElement h
in colHeaders)
1512 headers.Add(
true ==
string.IsNullOrWhiteSpace(h.Current.Name) ? (h.GetText() ??
string.Empty) : h.Current.Name);
1519 if (
null == headers)
1520 headers = ColumnHeadersUnder(ae);
1522 int cap = (int.MaxValue == CurrentUOW.WithAllThatMatch)
1525 for (
int r = 0; r < rows && CurrentUOW.RetGrid.Count() < cap; r++)
1527 var row =
new List<string>(cols);
1528 for (
int c = 0; c < cols; c++)
1533 AutomationElement cell = grid.GetItem(r, c);
1534 text = (
null != cell) ? (cell.GetText() ??
string.Empty) : GPAL.ErrorPlaceholder;
1538 text = GPAL.ErrorPlaceholder;
1542 CurrentUOW.RetGrid.AddRow(row);
1545 if (
null != headers && headers.Count > 0)
1547 CurrentUOW.ControlHeaderList = headers;
1549 if (
null == CurrentUOW.HeaderList || 0 == CurrentUOW.HeaderList.Count)
1550 CurrentUOW.HeaderList = headers;
1555 ColumnNames =
null == headers ?
new List<string>() : new List<string>(headers);
1557 GPAL.PublishSimpleEvent(GPALEventType.INFO, $
"GetGrid: [{sel.Name}] resolved to a UIA grid ([{rows}] x [{cols}]).",
this, GPALObjectType.Application);
1572 private static List<string> ColumnHeadersUnder(AutomationElement element)
1574 List<string> retVal =
null;
1576 AutomationElement header = element.FindFirst(TreeScope.Descendants,
1577 new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Header));
1583 AutomationElementCollection items = header.FindAll(TreeScope.Children, Condition.TrueCondition);
1585 if (0 < items.Count)
1587 retVal =
new List<string>();
1589 foreach (AutomationElement item
in items)
1590 retVal.Add(
true ==
string.IsNullOrWhiteSpace(item.Current.Name) ? (item.GetText() ??
string.Empty) : item.Current.Name);
1600 private void GetGridFromSelectors()
1602 var names =
new List<string>();
1603 foreach (Selector s
in CurrentUOW.WithSelectorList)
1606 var seenRows =
new HashSet<string>();
1607 bool capReached =
false;
1608 int cap = (int.MaxValue == CurrentUOW.WithAllThatMatch)
1611 for (
int page = 0; page < CurrentUOW.PageCount && !capReached; page++)
1613 var columns =
new List<List<string>>();
1616 foreach (Selector sel
in CurrentUOW.WithSelectorList)
1618 var col =
new List<string>();
1619 if (SelectorType.Selector == sel.selectorSettings.
SelectorType)
1621 bool matchedAll; List<GPALAutomationElement> matched;
1622 var found = ElementHelper.FindElements(
this, CurrentUOW, sel, out matchedAll, out matched);
1623 List<GPALAutomationElement> elems =
1624 (!matchedAll &&
null != matched && matched.Count > 0)
1626 : (null != found ? found.ToList() : new List<GPALAutomationElement>());
1627 foreach (GPALAutomationElement e
in elems)
1628 col.Add((
null != e.Ae) ? (e.Ae.GetText() ??
string.Empty) : GPAL.ErrorPlaceholder);
1630 else if (SelectorType.Data == sel.selectorSettings.
SelectorType)
1632 else if (SelectorType.DataFunc == sel.selectorSettings.
SelectorType)
1633 col.Add(sel.selectorSettings.DataFunction());
1636 if (col.Count > maxRows) maxRows = col.Count;
1640 for (
int i = 0; i < columns.Count; i++)
1641 while (columns[i].Count < maxRows)
1642 columns[i].Add($
"{GPAL.ErrorPlaceholder} : {names[i]}");
1644 for (
int r = 0; r < maxRows && !capReached; r++)
1646 var row =
new List<string>(columns.Count);
1647 for (
int c = 0; c < columns.Count; c++)
1648 row.Add(columns[c][r]);
1650 string key =
string.Join(
"", row);
1651 if (!seenRows.Add(key))
1653 CurrentUOW.RetGrid.AddRow(row);
1654 if (CurrentUOW.RetGrid.Count() >= cap)
1658 if (!capReached && page < CurrentUOW.PageCount - 1)
1659 if (!ApplicationHelper.GotoNextPage(
this))
1661 GPAL.PublishSimpleEvent(GPALEventType.INFO, $
"GetGrid: no next page after page [{page + 1}].",
this, GPALObjectType.Application);
1666 if (
null == CurrentUOW.HeaderList || 0 == CurrentUOW.HeaderList.Count)
1667 CurrentUOW.HeaderList = names;
1673 if (
true == CurrentUOW.GetGridCalled)
1675 CurrentUOW.GetGridCalled =
false;
1676 CurrentUOW.HeaderList.Clear();
1678 CurrentUOW.HeaderList.Add(header);
1691 if (
true == CurrentUOW.ActionCalled)
1694 CurrentUOW.RetGrid = gridToSave;
1702 CurrentUOW.NextPageButton = selector;
1711 CurrentUOW.InfiniteScroll =
true;
1719 CurrentUOW.PageCount = numberOfPages;
1736 private List<string> ResolveHeaderColumns(
GPALFile file)
1738 List<string> selectorNames = CurrentUOW.WithSelectorList?.Where(s =>
null != s.
Name).Select(s => s.
Name).ToList()
1739 ??
new List<string>();
1741 List<string> fileColumns =
new List<string>();
1742 IGPALGrid<string> columnList = ((IGPALFileInternal)file).FileSettings.ColumnList;
1743 if (
null != columnList && 0 < columnList.Count() &&
null != columnList[0])
1744 fileColumns =
new List<string>(columnList[0]);
1746 List<string> resolved = (
null != CurrentUOW.HeaderList && 0 < CurrentUOW.HeaderList.Count)
1747 ?
new List<string>(CurrentUOW.HeaderList)
1748 :
new List<string>();
1749 for (
int i = resolved.Count; i < fileColumns.Count; i++)
1750 resolved.Add(fileColumns[i]);
1751 for (
int i = resolved.Count; i < selectorNames.Count; i++)
1752 resolved.Add(selectorNames[i]);
1756 List<string> controlHeaders = CurrentUOW.ControlHeaderList ??
new List<string>();
1758 for (
int i = resolved.Count; i < controlHeaders.Count; i++)
1759 resolved.Add(controlHeaders[i]);
1766 if (
null != CurrentUOW.RetGrid)
1767 foreach (List<string> row
in CurrentUOW.RetGrid)
1768 if (
null != row && widest < row.Count)
1771 for (
int i = resolved.Count; i < widest; i++)
1772 resolved.Add($
"column{i + 1}");
1774 return UniqueColumns(resolved);
1789 private static List<string> UniqueColumns(List<string> columns)
1791 var used =
new HashSet<string>(StringComparer.OrdinalIgnoreCase);
1792 var result =
new List<string>();
1794 for (
int i = 0; i < columns.Count; i++)
1796 string name =
true ==
string.IsNullOrWhiteSpace(columns[i]) ? $
"column{i + 1}" : columns[i].Trim();
1797 string candidate = name;
1800 while (
false == used.Add(candidate))
1803 candidate = $
"{name}{repeat}";
1806 result.Add(candidate);
1818 GPAL.
PublishSimpleEvent(GPALEventType.INFO, $
"Saving [{CurrentUOW.RetGrid?.Rows ?? 0}] rows of window data to [{file?.Filename}].",
this, GPALObjectType.Application);
1820 var converter =
GPAL.
Converter.WithInput(CurrentUOW.RetGrid).WithColumnNames(ResolveHeaderColumns(file));
1823 if (
null != CurrentUOW.HeaderList && 0 < CurrentUOW.HeaderList.Count)
1824 converter.WithFirstLineHasColumnNames(
true);
1825 converter.SaveTo(file);
1835 GPAL.
PublishSimpleEvent(GPALEventType.INFO, $
"Appending [{CurrentUOW.RetGrid?.Rows ?? 0}] rows of window data to [{file?.Filename}].",
this, GPALObjectType.Application);
1837 GPAL.
Converter.WithInput(CurrentUOW.RetGrid).WithColumnNames(ResolveHeaderColumns(file)).AppendTo(file);
1840 #endregion <GetGrid / SaveTo / Pagination>
1848 ApplicationSettings.WaitForWindowTimeout = waitTimeInSeconds;
1858 ApplicationSettings.Parameters.Add(parameter);
1868 ApplicationSettings.UseHardware = trueFalse;
1884 ApplicationSettings.NewInstance = trueFalse;
1916 ApplicationSettings.HiddenDesktop = hiddenDesktop;
1920 if (
true == hiddenDesktop)
1921 ApplicationSettings.HiddenDesktopName = Desktops.NextName();
1943 ApplicationSettings.HiddenDesktop =
true;
1947 ApplicationSettings.HiddenDesktopName =
true ==
string.IsNullOrWhiteSpace(desktopName)
1948 ? Desktops.NextName()
1965 CurrentUOW.AppCallIfFound.Add(callIfFoundDelegate);
1980 CurrentUOW.AppCallIfNotFound.Add(callIfNotFoundDelegate);
1992 CurrentUOW.AppCallAfterFillIn = callAfterFillIn;
2016 ScrollPattern view = ScrollableView();
2020 ScrollPattern.ScrollPatternInformation at = view.Current;
2023 retVal =
false == at.VerticallyScrollable || 100 <= Math.Round(at.VerticalScrollPercent);
2034 internal ScrollPattern ScrollableView()
2036 ScrollPattern retVal =
null;
2041 AutomationElement focused = AutomationElement.FocusedElement;
2043 if (
null != focused && focused.Current.ProcessId != root.Current.ProcessId)
2047 for (AutomationElement at = focused;
null != at &&
null == retVal; at = TreeWalker.RawViewWalker.GetParent(at))
2048 if (
true == at.TryGetCurrentPattern(ScrollPattern.Pattern, out
object pattern) &&
true == ((ScrollPattern)pattern).Current.VerticallyScrollable)
2049 retVal = (ScrollPattern)pattern;
2053 AutomationElement first = root.FindFirst(TreeScope.Subtree,
new AndCondition(
2054 new PropertyCondition(AutomationElement.IsScrollPatternAvailableProperty,
true),
2055 new PropertyCondition(ScrollPattern.VerticallyScrollableProperty,
true)));
2058 retVal = (ScrollPattern)first.GetCurrentPattern(ScrollPattern.Pattern);
2065 #region <Getters/Setters>
2071 ?
new GPALHiddenDesktop(ApplicationSettings.HiddenDesktopName)
2078 public IReadOnlyList<string>
ColumnNames {
get;
private set; } =
new List<string>();
2089 return applicationSettings.Process;
2093 applicationSettings.Process = value;
2103 return applicationSettings.ManagedRootAutomationElement;
2108 applicationSettings.ManagedRootAutomationElement = value;
2118 return applicationSettings.RootAutomationElement;
2123 applicationSettings.RootAutomationElement = value;
2136 return ApplicationSettings.Name;
2141 ApplicationSettings.Name = value;
2153 return applicationSettings.ApplicationPath;
2157 applicationSettings.ApplicationPath = value;
2164 internal ApplicationSettings ApplicationSettings
2168 return applicationSettings;
2171 #endregion <Getters/Setters>
2172 #endregion <Application Settings>
Application object that contains the fluent methods to create your Application workflow....
string ApplicationPath
The path of the program you Opened. This get is intended for debugging only.
IAllowSelectorActionOrAnySelector SendKey(byte VKCode)
Type special characters like Enter, Page Up, Page Down, Tab, etc. Use GPAL.VK constants for ease of ...
IAllowSelectorActionOrAnySelector FillInFrom(GPALFile inputFile)
FillIn data from the file overwriting text in the input Selector(s) defined in the UOW One token (on...
IAllowAfterWaitForAndSizeControl WithWaitForWindowTimeout(int waitTimeInSeconds)
Define the time in seconds to WaitForWindow/Regex.
IAllowApplicationSettingsOrOpen WithParameter(string parameter)
Define the parameters to use in the Open command.
IAllowSelectorActionOrAnySelector ScrollHorizontalPercent(int percent)
Element's horizontal scroll position as a percentage of the total content area within the AutomationE...
IAllowApplicationSettingsOrOpen WithHiddenDesktop(bool hiddenDesktop)
Run the program on a Win32 desktop object of its own, so it has a real window, real rendering and not...
IAllowAllActionAndAnySelector SwitchToTab(Selector selector=null)
Switch to any TabControl defined in the current UOW. This will hardware click the tab control if the ...
IAllowSelectorActionOrAnySelector DragAndDrop(ModifierKeys modifierKeys)
Performs a drag from the source element to the target element, both found via Selector(s) defined in ...
object ManagedRootAutomationElement
The root UI Automation element (AutomationElement) for the managed application's main window.
IAllowApplicationSettingsOrOpen WithName(string appName)
Sets the application's name, used when publishing events.
IAllowWithPagesAndGridActions WithInfiniteScroll
Scroll to load more rows between pages. Not supported for desktop apps (single page).
IAllowSelectorActionOrAnySelector ScrollHorizontalSmall(int iterations=1)
Scroll the element horizontally in a small increment. https://docs.microsoft.com/en-us/windows/win32...
IAllowAfterAnySelector CallIfNotFound(Application.CallIfDelegate callIfNotFoundDelegate)
Add a Unit of Work level CallIfNotFound handler to be called if any defined selectors DO NOT find/mat...
IAllowSelectorActionOrAnySelector DragAndDrop(Selector selector=null)
Performs a drag from the source element to the target element, both found via Selector(s) defined in ...
Application ToGPALObject()
Return a GPAL.Application object so that GPAL.Application does not have to be cast.
IAllowAllActionAndAnySelector SetRange(double rangeValue)
Set the value of a range-based control (e.g. a slider) found via Selector(s) defined in the UOW.
IAllowPersistentCallBack PersistentCallIfFound(CallIfDelegate persistentCallIfFound)
Add a persistent CallIfFound handler to be called if any defined persistent selectors find/match elem...
IAllowAllApplicationAndAllSelector Restore
Restore the application to 'original' size, unminimize.
IAllowSelectorActionOrAnySelector LeftClickAndOpen(GPALFile file)
Clicks the element that raises an Open dialog, waits for the dialog, and answers it with the given fi...
IAllowAfterAnySelector CallIfFound(Application.CallIfDelegate callIfFoundDelegate)
Add a Unit of Work level CallIfFound handler to be called if any defined selectors find/match element...
IAllowSelectorActionOrAnySelector PressModifierKey(ModifierKeys modifierKeys)
Press and hold a modifier key (Alt, Control, Shift) using hardware emulation.
IAllowSelectorActionOrAnySelector SendString(string textToSend)
Send the string using hardware emulation.
IAllowSelectorActionOrAnySelector Hover(Selector selector=null)
Hover the mouse over the current element found via the Selector. Only the first element per Selector ...
IAllowAfterWaitForAndSizeControl WaitForWindowRegex(string windowTitleRegex)
Wait for a Windows window with title that matches the regex pattern to appear on the desktop....
IAllowAfterWaitForAndSizeControl WaitFor(int timeoutInTicks)
Either wait the amount of time specified or wait UP TO the amount of time specified waiting for eleme...
IAllowSelectorActionOrAnySelector ReleaseModifierKey(ModifierKeys modifierKeys)
Release a previously pressed modifier key (Alt, Control, Shift) using hardware emulation.
IAllowSelectorActionOrAnySelector InsertFrom(IGPALGrid< string > inputGrid)
Insert the data from the grid at the beginning of the text in the input Selector(s) defined in the UO...
IAllowAfterAnySelector CallAfterFillIn(CallAfterFillInDelegate callAfterFillIn)
Add a handler to call after a row of tokens is consumed and after all inputs are filled in with data....
IAllowSelectorActionOrAnySelector LeftClick(Enums.ModifierKeys modifierKeys)
Left click the element found for Selector(s) defined in the UOW, optionally emulating pressing a modi...
string Name
A name you provide to be used when publishing events. Use .WithName to provide your own friendly nam...
IAllowSelectorActionOrAnySelector AnswerFileDialog(GPALFile file)
Answers a file dialog that is already on screen with the given file or folder.
IAllowApplicationSettingsOrOpen WithHiddenDesktop(string desktopName)
Run the program on the named Win32 desktop object. Programs naming the same desktop share it,...
IAllowSelectorActionOrAnySelector InsertFrom(GPALDatabase inputDatabase)
Insert the text from the database at the beginning of the input Selector(s) defined in the UOW One t...
IAllowApplicationSettingsOrOpen WithNewInstance(bool trueFalse)
Launch a new copy of the program rather than attaching to one that is already running.
IAllowAfterWaitForAndSizeControl WaitForWindow(string windowTitle)
Wait for a Windows window with title to appear on the desktop. Used in conjunction with ....
IAllowSelectorActionOrAnySelector LeftClickAndSave(GPALFile file)
Clicks the element that raises a Save dialog, waits for the dialog, and answers it with the given fil...
IAllowSelectorActionOrAnySelector FillInFrom(GPALDatabase inputDatabase)
FillIn data from the database overwriting text in the input Selector(s) defined in the UOW One token...
IAllowApplicationSettingsOrOpen WithUseHardware(bool trueFalse)
Force all workflow element interactions to use hardware emulation (UIAutomation is default).
IAllowAllApplicationAndAllSelector MoveWindow(int x, int y)
Move the window last matched by WaitForWindow to the specified screen coordinates.
IAllowSelectorActionOrAnySelector FillInFrom(string textToUse)
FillIn the text from the string overwriting text in the first input in the UOW. Should probably only ...
IReadOnlyList< string > ColumnNames
The column names the last GetGrid read off the control itself, so a workflow can see what it is about...
delegate CallIfStatus CallAfterFillInDelegate(Application application, IGPALGrid< string > tokens, int tokenIdx)
Delegate callback for the CallAfterFillIn EventHandler which will be invoked after each row of tokens...
IAllowSelectorActionOrAnySelector InsertFrom(string textToUse)
Insert the text from the string at the beginning of the first input in the UOW. Should probably only ...
IAllowSelectorActionOrAnySelector Focus(Selector selector=null)
Focus the current element found via the Selector. Only the first element per Selector found will be f...
delegate CallIfStatus CallIfDelegate(Application application, List< IGPALAutomationElement > foundElements, List< IGPALAutomationElement > matchedElements, Selector selector, bool matchedAll)
Delegate callback for the CallIfFound/CallIfNotFound EventHandlers which will be invoked when a selec...
IAllowSelectorActionOrAnySelector LeftClick(Selector selector=null)
Left click the element found for Selector(s) defined in the UOW One left click for the first element...
IAllowSelectorActionOrAnySelector RightClick(Selector selector=null)
Right click the element found for Selector(s) defined in the UOW One right click for the first eleme...
IAllowSelectorActionOrAnySelector MiddleClick(Selector selector=null)
Middle click the element found for Selector(s) defined in the UOW.
IAllowAllApplicationAndAllSelector Minimize
Minimize the application to the taskbar.
IAllowWithHeaderOrFileActions WithGridToSave(IGPALGrid< string > gridToSave)
Supply a GPALGrid (perhaps built elsewhere) to be saved by a following SaveTo*.
IAllowAllActionAndAnySelector AppendTo(GPALFile file)
Append the retrieved grid. The output format and delimiter are determined by the GPALFile (its extens...
IAllowSelectorActionOrAnySelector WithSelector(Selector selector)
Add a selector to the current UOW. Selectors make up a Unit of Work (UOW) to perform actions upon....
IAllowSelectorActionOrAnySelector ScrollVerticalLarge(int iterations=1)
Scroll the element vertically in a large increment. https://docs.microsoft.com/en-us/windows/win32/a...
IAllowAfterAnySelectorExceptWithAll WithAllThatMatch(int rowCount=int.MaxValue)
Indicates the Selectors refer to/match repeating, multiple elements on the page. Use after all your ...
IAllowSelectorActionOrAnySelector FillInFrom(IGPALGrid< string > inputGrid)
FillIn data from the grid overwriting text in the input Selector(s) defined in the UOW One token (on...
IAllowGetGridAndFillInFrom WithPages(int numberOfPages)
Number of result pages to retrieve. Use with WithNextPageButton.
IAllowSelectorActionOrAnySelector ScrollVerticalPercent(int percent)
Element's vertical scroll position as a percentage of the total content area within the AutomationEle...
IAllowSelectorActionOrAnySelector LeftDoubleClick(Selector selector=null)
Left double click the element found for Selector(s) defined in the UOW.
IAllowAllApplicationAndAllSelector ResizeWindow(int width, int height)
Resize the window last matched by WaitForWindow to the specified pixel dimensions.
Process Process
The current Windows process for the program run via Open. Only one program can be Opened/controlled ...
IAllowAllActionAndAnySelector SaveTo(GPALFile file)
Save the retrieved grid. The output format and delimiter are determined by the GPALFile (its extensio...
IAllowSelectorActionOrAnySelector AppendFrom(GPALDatabase inputDatabase)
Append the data from the database to the end of the input Selectors defined in the UOW One token (on...
IAllowSelectorActionOrAnySelector AppendFrom(GPALFile inputFile)
Append the data from the input file to the end of the input Selectors defined in the UOW One token (...
IAllowSelectorActionOrAnySelector AppendFrom(string textToUse)
Append the given text to the first input in the UOW. Should probably only be used with one selector a...
AutomationElement RootAutomationElement
The starting AutomationElement for the application. All xpath searches start from this AutomationElem...
IAllowSelectorActionOrAnySelector InsertFrom(GPALFile inputFile)
Insert the data from the file at the beginning of the text in the input Selector(s) defined in the UO...
IAllowAllExceptParameters Open([Directory()] string applicationPath)
Launch the application specified in the path. Find the root automation element and place into applic...
bool IsEndOfPage()
Whether the application's scrollable view is at the bottom, the way the browser answers it for a page...
IAllowSelectorActionOrAnySelector ScrollHorizontalLarge(int iterations=1)
Scroll the element horizontally in a large increment. https://docs.microsoft.com/en-us/windows/win32...
IAllowPersistence WithPersistentSelector(Selector selector)
Add a persistent selector to the persistent UOW. Persistent selectors are always looked for whenever ...
IAllowSelectorActionOrAnySelector MoveTo(Selector selector=null)
Move the mouse cursor to the element found for Selector(s) defined in the UOW.
IAllowAllApplicationAndAllSelector Maximize
Maximize the application to full-screen.
IAllowPersistentCallBack PersistentCallIfNotFound(CallIfDelegate persistentCallIfNotFound)
Add a persistent CallIfNotFound handler to be called if any defined persistent selectors DO NOT find/...
IAllowSelectorActionOrAnySelector AppendFrom(IGPALGrid< string > inputGrid)
Append the data from the input grid to the end of the input Selectors defined in the UOW One token (...
void RemoveCallIfHandlerEverywhere(Application.CallIfDelegate func)
Helper method for GPAL applications to remove a handler from all UOWs. Envisioned to be used in a per...
IAllowWithHeaderOrFileActions GetGrid(ref IGPALGrid< string > returnGrid)
Retrieve the current selectors' data (or a table/grid control's data) into a GPALGrid....
IHiddenDesktop HiddenDesktop
The hidden desktop this program runs on, or null when it is on the desktop you are looking at....
IAllowWithHeaderOrFileActions WithHeader(string header)
Column header for exported data. Overrides selector names / table headers, in order.
IAllowSelectorActionOrAnySelector ScrollVerticalSmall(int iterations=1)
Scroll the element vertically in a small increment. https://docs.microsoft.com/en-us/windows/win32/a...
IAllowWithPagesAndGridActions WithNextPageButton(Selector selector)
The control to click to load the next page when paging through results.
void Close()
Close/terminate the application. This terminates the fluent interface and nothing can be chained to ...
static void TopProcess(Process process)
Brings the given process's main window to the foreground/top of the z-order, if it is not already.
static void FillInWithTokens(Application.Application application, List< IGPALGrid< string > > tokenList, WriteMode writeMode)
Enter tokens into form controls for each grid of tokens in the list.
File-side plumbing behind the fluent chain: writing a unit of work's data out in a delimited format,...
Class to define database usage. Currently only used for input from a table, sql or stored procedure....
IGPALGrid< string > Tokens
The rows/columns returned from the database. Tokens are loaded upon calling any [Append/FillIn/Inser...
Thrown where GPAL deliberately ends the workflow, such as a CallIf handler returning CallIfStatus....
GPAL File object instantied with GPAL.File Used to load tokens into a GPALGrid [rows/columns].
Everything starts here, all the GPAL controls and global settings using fluent syntax are here....
static IAllowConverterInput Converter
New GPAL Convertor.
static void PublishSimpleEvent(GPALEventType gPALEventType, string msg, dynamic gPALObject=null, Enums.GPALObjectType gPALObjectType=GPALObjectType.None, Exception ex=null)
Publish a message to either the information channel or exception channel (if exception passed in) Pub...
GPAL Selector used to locate Application and Browser elements. Instantiated with GPAL....
string SelectorPath
Returns the first defined selector path string.
string Name
The name you gave this selector, or one assigned by GPAL [selector1, selector2...] Used in Informati...
SelectorType SelectorType
Type of selector: A selector for an element, or literal data or a data function for dynamic data not ...
Everything revolves around the Unit of Work. A Unit of Work is defined as one or more selectors betw...
IAllowSelectorActionOrAnySelector SendString(string textToSend)
Simulates typing via hardware key events, one character at a time. Honors GPAL.TypingDelay (see GPAL....
One Win32 desktop object a browser is running on. Reached from the browser that is on it,...