33 internal class FormHelper
41 public static void FillInFrom(
GPALControl controlEntry,
string useText, WriteMode writeMode)
43 Control control = controlEntry.WindowsControl as Control;
47 $
"Control [{controlEntry.Name}] is not on a form yet, so nothing was written to it",
48 null, GPALObjectType.GPALForm);
49 else if (FormControlType.Input != controlEntry.
ControlType
50 && FormControlType.TextArea != controlEntry.
ControlType)
52 $
"A [{controlEntry.ControlType}] does not take text, so nothing was written to [{controlEntry.Name}]",
53 null, GPALObjectType.GPALForm);
55 GPALControl.OnUi(control, () => control.Text = TextFor(control, useText, writeMode));
68 private static string TextFor(Control control,
string useText, WriteMode writeMode)
70 string current = control.Text ??
string.Empty;
71 string retVal = useText;
73 if (WriteMode.Append == writeMode)
74 retVal = current + useText;
75 else if (WriteMode.Insert == writeMode)
78 int at = control is TextBoxBase box ? box.SelectionStart : current.Length;
80 retVal = current.Insert(Math.Min(Math.Max(at, 0), current.Length), useText);
85 public static void FillInWithTokens(
GPALForm myForm, List<IGPALGrid<string>> tokenList, WriteMode writeMode)
87 foreach (IGPALGrid<string> tokens
in tokenList)
90 for (
int tokenIdx = 0; tokenIdx < tokens.Count(); tokenIdx++)
93 List<string> currentRow = tokens[tokenIdx];
95 foreach (
string token
in currentRow)
97 bool textFilled =
false;
99 while (
false == textFilled && elementIdx < myForm.ControlList.Count)
101 switch (myForm.ControlList[elementIdx].ControlType)
103 case FormControlType.TextArea:
104 case FormControlType.Input:
105 myForm.Form.SwitchToTab(myForm.ControlList[elementIdx]);
106 FillInFrom(myForm.ControlList[elementIdx], token, writeMode);
117 if (elementIdx == myForm.ControlList.Count &&
false == textFilled)
118 GPAL.
PublishSimpleEvent(GPALEventType.WARNING, $
"Unused token: [{token}]", myForm, GPALObjectType.GPALForm);
122 while (elementIdx < myForm.ControlList.Count)
124 switch (myForm.ControlList[elementIdx].ControlType)
126 case FormControlType.TextArea:
127 case FormControlType.Input:
128 GPAL.
PublishSimpleEvent(GPALEventType.WARNING, $
"No token for control: [{myForm.ControlList[elementIdx].Name}]", myForm, GPALObjectType.GPALForm);
138 if (
null != myForm.Form.CallAfterFillIn)
140 CallIfStatus handled = myForm.Form.CallAfterFillIn(myForm, tokens, tokenIdx);
142 if (CallIfStatus.Terminate == handled)
144 GPAL.
PublishSimpleEvent(GPALEventType.INFO, $
"CallAfterFillIn handler [{myForm.Form.CallAfterFillIn.GetInvocationList()[0].Method.Name}] requested program termination.", myForm, GPALObjectType.GPALForm);
145 throw new GPALException($
"{GPAL.MyMethodName()}: CallAfterFillIn handler [{myForm.Form.CallAfterFillIn.GetInvocationList()[0].Method.Name}] requested program termination.");
151 public static HWND WaitForWindow(
string windowTitle,
int waitTimeInSeconds = 10)
153 return WaitForWindowGeneric(windowTitle,
false, waitTimeInSeconds);
156 public static HWND WaitForWindowRegex(
string windowTitleRegex,
int waitTimeInSeconds)
158 return WaitForWindowGeneric(windowTitleRegex,
true, waitTimeInSeconds);
161 public static HWND WaitForWindowGeneric(
string waitForTitle,
bool useRegex,
int waitTimeInSeconds)
164 HWND retHWND = IntPtr.Zero;
166 for (
int cnt = 0; cnt < waitTimeInSeconds &&
false == found; cnt++)
169 IDictionary<HWND, string> windows = GetOpenWindows();
170 foreach (var entry
in windows)
172 if (
true == useRegex)
174 MatchCollection mc = Regex.Matches(entry.Value, waitForTitle);
184 if (
true == waitForTitle.Equals(entry.Value))
209 public static HWND WaitForCommonDialog(
int processId,
int waitTimeInSeconds = 10)
211 HWND retHWND = IntPtr.Zero;
212 Stopwatch waited = Stopwatch.StartNew();
218 var before =
new HashSet<HWND>(CommonDialogsOf(0));
220 while (IntPtr.Zero == retHWND && waited.ElapsedMilliseconds < waitTimeInSeconds * 1000)
222 List<HWND> appeared =
new List<HWND>();
227 foreach (HWND window
in CommonDialogsOf(0))
228 if (
false == before.Contains(window) ||
true == LooksLikeAFileDialog(window))
229 appeared.Add(window);
232 foreach (HWND window
in appeared)
234 GetWindowThreadProcessId(window, out
int owner);
236 if (IntPtr.Zero == retHWND && owner == processId)
243 if (IntPtr.Zero == retHWND && 0 < appeared.Count)
245 GetWindowThreadProcessId(appeared[0], out
int owner);
247 GPAL.
PublishSimpleEvent(GPALEventType.DEBUG, $
"A common dialog appeared belonging to process [{owner}] rather than [{processId}], and is taken as the one that was waited for.",
null, GPALObjectType.None);
249 retHWND = appeared[0];
252 if (IntPtr.Zero == retHWND)
256 if (IntPtr.Zero == retHWND)
257 GPAL.
PublishSimpleEvent(GPALEventType.DEBUG, $
"No common dialog for process [{processId}] in [{waitTimeInSeconds}]s. {VisibleWindows()}",
null, GPALObjectType.None);
271 public static string TitleOf(HWND window)
273 var title =
new StringBuilder(256);
275 GetWindowText(window, title, title.Capacity);
277 return title.ToString();
288 public static string VisibleWindows()
290 var seen =
new List<string>();
292 EnumWindows(delegate (HWND hWnd,
int lParam)
294 if (
true == IsWindowVisible(hWnd))
296 var className =
new StringBuilder(256);
297 var title =
new StringBuilder(256);
299 GetClassName(hWnd, className, className.Capacity);
300 GetWindowText(hWnd, title, title.Capacity);
301 GetWindowThreadProcessId(hWnd, out
int owner);
303 if (0 < className.Length)
304 seen.Add($
"[{className}] '{title}' (process {owner})");
311 return 0 == seen.Count ?
"No visible windows were found at all." : $
"What is on screen: {string.Join(" |
", seen)}";
319 public static List<HWND> CommonDialogsOf(
int processId)
321 var found =
new List<HWND>();
323 EnumWindows(delegate (HWND hWnd,
int lParam)
325 if (
true == IsWindowVisible(hWnd))
327 var className =
new StringBuilder(64);
329 GetClassName(hWnd, className, className.Capacity);
331 if (COMMON_DIALOG_CLASS == className.ToString())
333 GetWindowThreadProcessId(hWnd, out
int owner);
335 if (0 == processId || owner == processId)
358 public static bool LooksLikeAFileDialog(HWND dialog)
363 EnumChildWindows(dialog, delegate (HWND child,
int lParam)
365 if (ACCEPT_CONTROL_ID == GetDlgCtrlID(child))
368 var className =
new StringBuilder(64);
370 GetClassName(child, className, className.Capacity);
372 string name = className.ToString();
374 if (
"Edit" == name ||
true == name.StartsWith(
"ComboBox"))
381 return true == accept &&
true == typing;
385 private const string COMMON_DIALOG_CLASS =
"#32770";
388 private const int ACCEPT_CONTROL_ID = 1;
390 [DllImport(
"USER32.DLL")]
391 private static extern bool EnumChildWindows(HWND parent, EnumWindowsProc enumFunc,
int lParam);
393 [DllImport(
"USER32.DLL")]
394 private static extern int GetDlgCtrlID(HWND hWnd);
396 [DllImport(
"USER32.DLL")]
397 private static extern int GetClassName(HWND hWnd, StringBuilder lpClassName,
int nMaxCount);
399 [DllImport(
"USER32.DLL")]
400 private static extern int GetWindowThreadProcessId(HWND hWnd, out
int processId);
406 public static IDictionary<HWND, string> GetOpenWindows()
408 HWND shellWindow = GetShellWindow();
409 Dictionary<HWND, string> windows =
new Dictionary<HWND, string>();
411 EnumWindows(delegate (HWND hWnd,
int lParam)
413 if (hWnd == shellWindow)
return true;
414 if (!IsWindowVisible(hWnd))
return true;
416 int length = GetWindowTextLength(hWnd);
417 if (length == 0)
return true;
419 StringBuilder builder =
new StringBuilder(length);
420 GetWindowText(hWnd, builder, length + 1);
422 windows[hWnd] = builder.ToString();
430 private delegate
bool EnumWindowsProc(HWND hWnd,
int lParam);
432 [DllImport(
"USER32.DLL")]
433 private static extern bool EnumWindows(EnumWindowsProc enumFunc,
int lParam);
435 [DllImport(
"USER32.DLL")]
436 private static extern int GetWindowText(HWND hWnd, StringBuilder lpString,
int nMaxCount);
438 [DllImport(
"USER32.DLL")]
439 private static extern int GetWindowTextLength(HWND hWnd);
441 [DllImport(
"USER32.DLL")]
442 private static extern bool IsWindowVisible(HWND hWnd);
444 [DllImport(
"USER32.DLL")]
445 private static extern IntPtr GetShellWindow();