public override void RunCommand(object sender) { var engine = (IAutomationEngineInstance)sender; string windowName = v_WindowName.ConvertUserVariableToString(engine); int timeout = Int32.Parse(v_Timeout); DateTime timeToEnd = DateTime.Now.AddSeconds(timeout); while (timeToEnd >= DateTime.Now) { try { if (engine.IsCancellationPending) { break; } User32Functions.ActivateWindow(windowName); if (!User32Functions.GetActiveWindowTitle().Equals(windowName)) { throw new Exception($"Window '{windowName}' Not Yet Found... "); } break; } catch (Exception) { engine.ReportProgress($"Window '{windowName}' Not Yet Found... " + (timeToEnd - DateTime.Now).Minutes + "m, " + (timeToEnd - DateTime.Now).Seconds + "s remain"); Thread.Sleep(500); } } if (!User32Functions.GetActiveWindowTitle().Equals(windowName)) { throw new Exception($"Window '{windowName}' Not Found"); } }
public override void RunCommand(object sender) { var engine = (AutomationEngineInstance)sender; string windowName = v_WindowName.ConvertUserVariableToString(engine); User32Functions.ActivateWindow(windowName); }
public static AutomationElement SearchForGUIElement(IAutomationEngineInstance engine, DataTable uiaSearchParams, string variableWindowName) { User32Functions.ActivateWindow(variableWindowName); //create search params var searchParams = from rw in uiaSearchParams.AsEnumerable() where rw.Field <string>("Enabled") == "True" select rw; //create and populate condition list var conditionList = new List <Condition>(); foreach (var param in searchParams) { var parameterName = (string)param["Parameter Name"]; var parameterValue = (string)param["Parameter Value"]; parameterValue = parameterValue.ConvertUserVariableToString(engine); PropertyCondition propCondition; if (bool.TryParse(parameterValue, out bool bValue)) { propCondition = CreatePropertyCondition(parameterName, bValue); } else { propCondition = CreatePropertyCondition(parameterName, parameterValue); } conditionList.Add(propCondition); } //concatenate or take first condition Condition searchConditions; if (conditionList.Count > 1) { searchConditions = new AndCondition(conditionList.ToArray()); } else { searchConditions = conditionList[0]; } //find window var windowElement = AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, variableWindowName)); //if window was not found if (windowElement == null) { throw new Exception("Window named '" + variableWindowName + "' was not found!"); } //find required handle based on specified conditions var element = windowElement.FindFirst(TreeScope.Descendants, searchConditions); return(element); }
public async override Task RunCommand(object sender) { var engine = (IAutomationEngineInstance)sender; var variableWindowName = (string)await v_WindowName.EvaluateCode(engine); if (variableWindowName != "Current Window") { User32Functions.ActivateWindow(variableWindowName); } string textToSend = (string)await v_TextToSend.EvaluateCode(engine); SendKeys.SendWait(Regex.Replace(textToSend, "[+^%~(){}]", "{$0}")); Thread.Sleep(500); }
public override void RunCommand(object sender) { var engine = (IAutomationEngineInstance)sender; var variableWindowName = v_WindowName.ConvertUserVariableToString(engine); if (variableWindowName != "Current Window") { User32Functions.ActivateWindow(variableWindowName); } string textToSend = v_TextToSend.ConvertUserVariableToString(engine); if (v_EncryptionOption == "Encrypted") { textToSend = EncryptionServices.DecryptString(textToSend, "OPENBOTS"); } if (textToSend == "{WIN_KEY}") { User32Functions.KeyDown(Keys.LWin); User32Functions.KeyUp(Keys.LWin); } else if (textToSend.Contains("{WIN_KEY+")) { User32Functions.KeyDown(Keys.LWin); var remainingText = textToSend.Replace("{WIN_KEY+", "").Replace("}", ""); foreach (var c in remainingText) { Keys key = (Keys)Enum.Parse(typeof(Keys), c.ToString()); User32Functions.KeyDown(key); } User32Functions.KeyUp(Keys.LWin); foreach (var c in remainingText) { Keys key = (Keys)Enum.Parse(typeof(Keys), c.ToString()); User32Functions.KeyUp(key); } } else { SendKeys.SendWait(textToSend); } Thread.Sleep(500); }
public async override Task RunCommand(object sender) { var engine = (IAutomationEngineInstance)sender; string windowName = (string)await v_WindowName.EvaluateCode(engine); int timeout = (int)await v_Timeout.EvaluateCode(engine); DateTime timeToEnd = DateTime.Now.AddSeconds(timeout); while (timeToEnd >= DateTime.Now) { try { if (engine.IsCancellationPending) { break; } User32Functions.ActivateWindow(windowName); if (!User32Functions.GetActiveWindowTitle().Equals(windowName)) { throw new Exception($"Window '{windowName}' Not Yet Found... "); } break; } catch (Exception) { engine.ReportProgress($"Window '{windowName}' Not Yet Found... {(timeToEnd - DateTime.Now).Minutes}m, {(timeToEnd - DateTime.Now).Seconds}s remain"); Thread.Sleep(500); } } if (!User32Functions.GetActiveWindowTitle().Equals(windowName)) { throw new Exception($"Window '{windowName}' Not Found"); } }
private void pbRecord_Click(object sender, EventArgs e) { // this.WindowState = FormWindowState.Minimized; if (!_isRecording) { _isRecording = true; SearchParameters = NewSearchParameterDataTable(); //clear all SearchParameters.Rows.Clear(); //get window name and find window WindowName = cboWindowTitle.Text; IntPtr hWnd = User32Functions.FindWindow(WindowName); if (IsRecordingSequence && _isFirstRecordClick) { _isFirstRecordClick = false; _sequenceCommandList = new List <ScriptCommand>(); frmThickAppElementRecorderSettings settingsForm = new frmThickAppElementRecorderSettings(); settingsForm.ShowDialog(); if (settingsForm.DialogResult == DialogResult.OK) { _parameterSettings = settingsForm.ParameterSettingsDT; settingsForm.Dispose(); } else { _isRecording = false; _isFirstRecordClick = true; lblDescription.Text = "Instructions: Select the target window name from the drop-down " + "list and click the record button. Once recording has started, click " + "the element in the target application that you want to capture."; //remove wait for left mouse down event GlobalHook.MouseEvent -= GlobalHook_MouseEvent; GlobalHook.KeyDownEvent -= GlobalHook_KeyDownEvent; GlobalHook.HookStopped -= GlobalHook_HookStopped; settingsForm.Dispose(); return; } dynamic activateWindowCommand = TypeMethods.CreateTypeInstance(_container, "ActivateWindowCommand"); activateWindowCommand.v_WindowName = WindowName; _sequenceCommandList.Add(activateWindowCommand); } //check if window is found if (hWnd != IntPtr.Zero) { //set window state and move to 0,0 User32Functions.ActivateWindow(WindowName); User32Functions.SetWindowPosition(hWnd, 0, 0); //start global hook and wait for left mouse down event GlobalHook.StartEngineCancellationHook(Keys.F2); GlobalHook.HookStopped += GlobalHook_HookStopped; GlobalHook.StartElementCaptureHook(chkStopOnClick.Checked, _container); GlobalHook.MouseEvent += GlobalHook_MouseEvent; GlobalHook.KeyDownEvent += GlobalHook_KeyDownEvent; } if (!chkStopOnClick.Checked) { lblDescription.Text = _recordingMessage; MoveFormToBottomRight(this); } else { WindowState = FormWindowState.Minimized; } } else { _isRecording = false; if (!chkStopOnClick.Checked) { lblDescription.Text = "Recording has stopped. Press F2 to save and close."; } } }
//combobox events for form items #region ComboBox Events private void cboIEWindow_SelectionChangeCommitted(object sender, EventArgs e) { var shellWindows = new ShellWindows(); foreach (IWebBrowser2 shellWindow in shellWindows) { if (shellWindow.Document is HTMLDocument) { if (shellWindow.Document.Title == cboIEWindow.Text) { _ie = shellWindow.Application; var events = (HTMLDocumentEvents2_Event)_ie.Document; events.onclick += (evt) => { _searchParameters = new DataTable(); _searchParameters.Columns.Add("Enabled"); _searchParameters.Columns.Add("Property Name"); _searchParameters.Columns.Add("Property Value"); if (evt.srcElement is IHTMLElement) { IHTMLElement srcInfo = evt.srcElement; var elementProperties = srcInfo.GetType().GetProperties(); foreach (PropertyInfo prp in elementProperties) { var propIsString = prp.PropertyType == typeof(string); var propIsInt = prp.PropertyType == typeof(int); if ((propIsString || propIsInt) && !prp.Name.Contains("IHTML")) { string propName = prp.Name; string propValue = Convert.ToString(prp.GetValue(srcInfo)); _searchParameters.Rows.Add(false, propName, propValue); } } dgvSearchParameters.Invoke(new MethodInvoker(() => { dgvSearchParameters.DataSource = _searchParameters; }) ); } return(false); }; var windowName = cboIEWindow.Text + " - Internet Explorer"; User32Functions.ActivateWindow(windowName); User32Functions.MoveWindow(windowName, "0", "0"); MoveFormToBottomRight(this); TopMost = true; foreach (Form frm in Application.OpenForms) { if (frm.Name != Name) { frm.WindowState = FormWindowState.Minimized; } } } } } }
public static void GetUIElement(object sender, EventArgs e, DataTable nativeSearchParameters, IfrmCommandEditor editor) { ChromeExtensionRegistryManager registryManager = new ChromeExtensionRegistryManager(); bool isChromeNativeMessagingInstalled = registryManager.IsExtensionInstalled(); if (isChromeNativeMessagingInstalled) { try { User32Functions.BringChromeWindowToTop(); string webElementStr; NativeRequest.ProcessRequest("getelement", "", 60, out webElementStr); if (!string.IsNullOrEmpty(webElementStr) && webElementStr != "stopped") { if (!webElementStr.Contains("tagName")) { NativeResponse responseObject = JsonConvert.DeserializeObject <NativeResponse>(webElementStr); if (responseObject.Status == "Failed") { throw new Exception(responseObject.Result); } } WebElement webElement = JsonConvert.DeserializeObject <WebElement>(webElementStr); DataTable SearchParameters = WebElementToDataTable(webElement); if (SearchParameters != null) { nativeSearchParameters.Rows.Clear(); foreach (DataRow rw in SearchParameters.Rows) { nativeSearchParameters.ImportRow(rw); } } } } catch (Exception ex) { // Throw Error in Message Box if (ex.Message.Contains("Pipe hasn't been connected yet.")) { var result = ((Form)editor).Invoke(new Action(() => { editor.ShowMessage("Chrome Native Extension is not installed! \n Please visit: https://chrome.google.com/webstore/detail/openbots-web-automation/kkepankimcahnjamnimeijpplgjpmdpp/related", "MessageBox", DialogType.OkOnly, 10); } )); } else if (ex.Message.ToLower().Contains("arithmetic operation resulted in an overflow")) { var result = ((Form)editor).Invoke(new Action(() => { editor.ShowMessage("Chrome Native Extension stopped responding.", "MessageBox", DialogType.OkOnly, 10); } )); } else { var result = ((Form)editor).Invoke(new Action(() => { editor.ShowMessage(ex.Message, "MessageBox", DialogType.OkOnly, 10); } )); } } finally { Process process = Process.GetCurrentProcess(); User32Functions.ActivateWindow(process.MainWindowTitle); } } else { var result = ((Form)editor).Invoke(new Action(() => { editor.ShowMessage("Chrome Native Extension is not installed! \nPlease open the Extensions Manager to install Chrome Native Extension.", "MessageBox", DialogType.OkOnly, 10); } )); } }
public async override Task RunCommand(object sender) { var engine = (IAutomationEngineInstance)sender; var variableWindowName = (string)await v_WindowName.EvaluateCode(engine); //activate anything except current window if (variableWindowName != "Current Window") { User32Functions.ActivateWindow(variableWindowName); } //track all keys down var keysDown = new List <Keys>(); //run each selected item foreach (DataRow rw in v_KeyActions.Rows) { //get key name var keyName = rw.Field <string>("Key"); //get key action var action = rw.Field <string>("Action"); //parse OEM key name string oemKeyString = keyName.Split('[', ']')[1]; var oemKeyName = (Keys)Enum.Parse(typeof(Keys), oemKeyString); //"Key Press (Down + Up)", "Key Down", "Key Up" switch (action) { case "Key Press (Down + Up)": //simulate press User32Functions.KeyDown(oemKeyName); User32Functions.KeyUp(oemKeyName); //key returned to UP position so remove if we added it to the keys down list if (keysDown.Contains(oemKeyName)) { keysDown.Remove(oemKeyName); } break; case "Key Down": //simulate down User32Functions.KeyDown(oemKeyName); //track via keys down list if (!keysDown.Contains(oemKeyName)) { keysDown.Add(oemKeyName); } break; case "Key Up": //simulate up User32Functions.KeyUp(oemKeyName); //remove from key down if (keysDown.Contains(oemKeyName)) { keysDown.Remove(oemKeyName); } break; default: break; } } //return key to up position if requested if (v_KeyUpDefault == "Yes") { foreach (var key in keysDown) { User32Functions.KeyUp(key); } } }
public override void RunCommand(object sender) { var engine = (AutomationEngineInstance)sender; //create variable window name var variableWindowName = v_WindowName.ConvertUserVariableToString(engine); if (variableWindowName == "Current Window") { variableWindowName = User32Functions.GetActiveWindowTitle(); } User32Functions.ActivateWindow(variableWindowName); dynamic requiredHandle = null; if (v_AutomationType != "Wait For Element To Exist") { requiredHandle = SearchForGUIElement(sender, variableWindowName); } switch (v_AutomationType) { //determine element click type case "Click Element": //if handle was not found if (requiredHandle == null) { throw new Exception("Element was not found in window '" + variableWindowName + "'"); } //create search params var clickType = (from rw in v_UIAActionParameters.AsEnumerable() where rw.Field <string>("Parameter Name") == "Click Type" select rw.Field <string>("Parameter Value")).FirstOrDefault(); //get x adjust var xAdjust = (from rw in v_UIAActionParameters.AsEnumerable() where rw.Field <string>("Parameter Name") == "X Adjustment" select rw.Field <string>("Parameter Value")).FirstOrDefault(); //get y adjust var yAdjust = (from rw in v_UIAActionParameters.AsEnumerable() where rw.Field <string>("Parameter Name") == "Y Adjustment" select rw.Field <string>("Parameter Value")).FirstOrDefault(); //convert potential variable var xAdjustVariable = xAdjust.ConvertUserVariableToString(engine); var yAdjustVariable = yAdjust.ConvertUserVariableToString(engine); int xAdjustInt; int yAdjustInt; //parse to int if (!string.IsNullOrEmpty(xAdjustVariable)) { xAdjustInt = int.Parse(xAdjustVariable); } else { xAdjustInt = 0; } if (!string.IsNullOrEmpty(yAdjustVariable)) { yAdjustInt = int.Parse(yAdjustVariable); } else { yAdjustInt = 0; } //get clickable point var newPoint = requiredHandle.GetClickablePoint(); //send mousemove command User32Functions.SendMouseMove( (newPoint.X + xAdjustInt).ToString(), (newPoint.Y + yAdjustInt).ToString(), clickType); break; case "Set Text": string textToSet = (from rw in v_UIAActionParameters.AsEnumerable() where rw.Field <string>("Parameter Name") == "Text To Set" select rw.Field <string>("Parameter Value")).FirstOrDefault(); string clearElement = (from rw in v_UIAActionParameters.AsEnumerable() where rw.Field <string>("Parameter Name") == "Clear Element Before Setting Text" select rw.Field <string>("Parameter Value")).FirstOrDefault(); string encryptedData = (from rw in v_UIAActionParameters.AsEnumerable() where rw.Field <string>("Parameter Name") == "Encrypted Text" select rw.Field <string>("Parameter Value")).FirstOrDefault(); if (clearElement == null) { clearElement = "No"; } if (encryptedData == "Encrypted") { textToSet = EncryptionServices.DecryptString(textToSet, "OPENBOTS"); } textToSet = textToSet.ConvertUserVariableToString(engine); if (requiredHandle.Current.IsEnabled && requiredHandle.Current.IsKeyboardFocusable) { object valuePattern = null; if (!requiredHandle.TryGetCurrentPattern(ValuePattern.Pattern, out valuePattern)) { //The control does not support ValuePattern Using keyboard input // Set focus for input functionality and begin. requiredHandle.SetFocus(); // Pause before sending keyboard input. Thread.Sleep(100); if (clearElement.ToLower() == "yes") { // Delete existing content in the control and insert new content. SendKeys.SendWait("^{HOME}"); // Move to start of control SendKeys.SendWait("^+{END}"); // Select everything SendKeys.SendWait("{DEL}"); // Delete selection } SendKeys.SendWait(textToSet); } else { if (clearElement.ToLower() == "no") { string currentText; object tPattern = null; if (requiredHandle.TryGetCurrentPattern(TextPattern.Pattern, out tPattern)) { var textPattern = (TextPattern)tPattern; // often there is an extra '\r' hanging off the end. currentText = textPattern.DocumentRange.GetText(-1).TrimEnd('\r').ToString(); } else { currentText = requiredHandle.Current.Name.ToString(); } textToSet = currentText + textToSet; } requiredHandle.SetFocus(); ((ValuePattern)valuePattern).SetValue(textToSet); } } break; case "Set Secure Text": string secureString = (from rw in v_UIAActionParameters.AsEnumerable() where rw.Field <string>("Parameter Name") == "Secure String Variable" select rw.Field <string>("Parameter Value")).FirstOrDefault(); string _clearElement = (from rw in v_UIAActionParameters.AsEnumerable() where rw.Field <string>("Parameter Name") == "Clear Element Before Setting Text" select rw.Field <string>("Parameter Value")).FirstOrDefault(); var secureStrVariable = secureString.ConvertUserVariableToObject(engine); if (secureStrVariable is SecureString) { secureString = ((SecureString)secureStrVariable).ConvertSecureStringToString(); } else { throw new ArgumentException("Provided Argument is not a 'Secure String'"); } if (_clearElement == null) { _clearElement = "No"; } if (requiredHandle.Current.IsEnabled && requiredHandle.Current.IsKeyboardFocusable) { object valuePattern = null; if (!requiredHandle.TryGetCurrentPattern(ValuePattern.Pattern, out valuePattern)) { //The control does not support ValuePattern Using keyboard input // Set focus for input functionality and begin. requiredHandle.SetFocus(); // Pause before sending keyboard input. Thread.Sleep(100); if (_clearElement.ToLower() == "yes") { // Delete existing content in the control and insert new content. SendKeys.SendWait("^{HOME}"); // Move to start of control SendKeys.SendWait("^+{END}"); // Select everything SendKeys.SendWait("{DEL}"); // Delete selection } SendKeys.SendWait(secureString); } else { if (_clearElement.ToLower() == "no") { string currentText; object tPattern = null; if (requiredHandle.TryGetCurrentPattern(TextPattern.Pattern, out tPattern)) { var textPattern = (TextPattern)tPattern; currentText = textPattern.DocumentRange.GetText(-1).TrimEnd('\r').ToString(); // often there is an extra '\r' hanging off the end. } else { currentText = requiredHandle.Current.Name.ToString(); } secureString = currentText + secureString; } requiredHandle.SetFocus(); ((ValuePattern)valuePattern).SetValue(secureString); } } break; case "Clear Element": if (requiredHandle.Current.IsEnabled && requiredHandle.Current.IsKeyboardFocusable) { object valuePattern = null; if (!requiredHandle.TryGetCurrentPattern(ValuePattern.Pattern, out valuePattern)) { //The control does not support ValuePattern Using keyboard input // Set focus for input functionality and begin. requiredHandle.SetFocus(); // Pause before sending keyboard input. Thread.Sleep(100); // Delete existing content in the control and insert new content. SendKeys.SendWait("^{HOME}"); // Move to start of control SendKeys.SendWait("^+{END}"); // Select everything SendKeys.SendWait("{DEL}"); // Delete selection } else { requiredHandle.SetFocus(); ((ValuePattern)valuePattern).SetValue(""); } } break; case "Get Text": //if element exists type case "Check If Element Exists": //Variable Name var applyToVariable = (from rw in v_UIAActionParameters.AsEnumerable() where rw.Field <string>("Parameter Name") == "Variable Name" select rw.Field <string>("Parameter Value")).FirstOrDefault(); //declare search result string searchResult = ""; if (v_AutomationType == "Get Text") { //string currentText; object tPattern = null; if (requiredHandle.TryGetCurrentPattern(TextPattern.Pattern, out tPattern)) { var textPattern = (TextPattern)tPattern; searchResult = textPattern.DocumentRange.GetText(-1).TrimEnd('\r').ToString(); // often there is an extra '\r' hanging off the end. } else { searchResult = requiredHandle.Current.Name.ToString(); } } else if (v_AutomationType == "Check If Element Exists") { //determine search result if (requiredHandle == null) { searchResult = "False"; } else { searchResult = "True"; } } //store data searchResult.StoreInUserVariable(engine, applyToVariable); break; case "Wait For Element To Exist": var timeoutText = (from rw in v_UIAActionParameters.AsEnumerable() where rw.Field <string>("Parameter Name") == "Timeout (Seconds)" select rw.Field <string>("Parameter Value")).FirstOrDefault(); timeoutText = timeoutText.ConvertUserVariableToString(engine); int timeOut = Convert.ToInt32(timeoutText); var timeToEnd = DateTime.Now.AddSeconds(timeOut); while (timeToEnd >= DateTime.Now) { try { requiredHandle = SearchForGUIElement(sender, variableWindowName); break; } catch (Exception) { engine.ReportProgress("Element Not Yet Found... " + (timeToEnd - DateTime.Now).Seconds + "s remain"); Thread.Sleep(1000); } } break; case "Get Value From Element": if (requiredHandle == null) { throw new Exception("Element was not found in window '" + variableWindowName + "'"); } //get value from property var propertyName = (from rw in v_UIAActionParameters.AsEnumerable() where rw.Field <string>("Parameter Name") == "Get Value From" select rw.Field <string>("Parameter Value")).FirstOrDefault(); //Variable Name var applyToVariable2 = (from rw in v_UIAActionParameters.AsEnumerable() where rw.Field <string>("Parameter Name") == "Variable Name" select rw.Field <string>("Parameter Value")).FirstOrDefault(); //get required value var requiredValue = requiredHandle.Current.GetType().GetRuntimeProperty(propertyName)?.GetValue(requiredHandle.Current).ToString(); //store into variable ((object)requiredValue).StoreInUserVariable(engine, applyToVariable2); break; default: throw new NotImplementedException("Automation type '" + v_AutomationType + "' not supported."); } }
public async override Task RunCommand(object sender) { var engine = (IAutomationEngineInstance)sender; string windowName = (string)await v_WindowName.EvaluateCode(engine); int timeout = (int)await v_Timeout.EvaluateCode(engine); var timeToEnd = DateTime.Now.AddSeconds(timeout); bool testMode = TestMode; //user image to bitmap Bitmap userImage = new Bitmap(CommonMethods.Base64ToImage(v_ImageCapture)); double accuracy; try { accuracy = Convert.ToDouble(await v_MatchAccuracy.EvaluateCode(engine)); if (accuracy > 1 || accuracy < 0) { throw new ArgumentOutOfRangeException("Accuracy value is out of range (0-1)"); } } catch (Exception) { throw new InvalidDataException("Accuracy value is invalid"); } //Activate window if specified if (windowName != "None") { while (timeToEnd >= DateTime.Now) { try { if (engine.IsCancellationPending) { break; } User32Functions.ActivateWindow(windowName); if (!User32Functions.GetActiveWindowTitle().Equals(windowName)) { throw new Exception($"Window '{windowName}' Not Yet Found... "); } break; } catch (Exception) { engine.ReportProgress($"Window '{windowName}' Not Yet Found... {(timeToEnd - DateTime.Now).Minutes}m, {(timeToEnd - DateTime.Now).Seconds}s remain"); Thread.Sleep(500); } } if (!User32Functions.GetActiveWindowTitle().Equals(windowName)) { throw new Exception($"Window '{windowName}' Not Found"); } else { Thread.Sleep(500); } } dynamic element = null; while (timeToEnd >= DateTime.Now) { try { if (engine.IsCancellationPending) { break; } element = CommandsHelper.FindImageElement(userImage, accuracy, engine, timeToEnd); if (element == null) { throw new Exception("Specified image was not found in window!"); } else { break; } } catch (Exception) { engine.ReportProgress("Element Not Yet Found... " + (timeToEnd - DateTime.Now).Seconds + "s remain"); Thread.Sleep(1000); } } if (element == null) { FormsHelper.ShowAllForms(engine.EngineContext.IsDebugMode); throw new Exception("Specified image was not found in window!"); } PerformImageElementAction(engine, element); }