Die meisten automatischen Operationen auf dem Windows-Bildschirm werden durch die unten beschriebenen Methoden ermöglicht.
** Automatisierung von "Akanchan Kawaiitta" durch RPA Nine People ** https://qiita.com/mima_ita/items/d4655de865f30bb51c65
Tatsächlich gibt es einen problematischen Fall, bei dem ein Bildschirm mit Java erstellt wird. Lassen Sie uns diesmal überlegen, ob ein automatischer Betrieb am Beispiel eines in Java erstellten Bildschirms möglich ist.
** Experimentierumgebung ** Windows10 Home Java 8 Visual Studio 2019 PowerShell 5.1 UiPath 2019.10.0-beta 111
Swing kann als Hauptmethode zum Erstellen von Bildschirmen in Java verwendet werden, oder JavaFx kann verwendet werden.
Erstellen Sie einen einfachen Swing-Bildschirm, indem Sie auf die folgende Seite verweisen.
ToDoListPane.java
package SwingSample;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
/**
*Aufgabenliste
*Referenz unten
* https://www.atmarkit.co.jp/ait/articles/0609/23/news027.html
*/
public class ToDoListPane extends JPanel {
private JList<String> toDoList;
private DefaultListModel<String> toDoListModel;
private JTextField toDoInputField;
private JButton addButton;
public ToDoListPane() {
super(new BorderLayout());
//Liste erstellen
toDoListModel = new DefaultListModel<String>();
toDoList = new JList<String>(toDoListModel);
JScrollPane listScrollPane = new JScrollPane(toDoList);
//Generieren Sie ein Textfeld zum Hinzufügen von Aufgaben
toDoInputField = new JTextField();
//Generierung jeder Taste
JPanel buttonPanel = new JPanel();
addButton = new JButton("hinzufügen");
//Stellen Sie den Listener auf die Schaltfläche
addButton.addActionListener(new AddActionHandler());
buttonPanel.add(addButton);
add(listScrollPane, BorderLayout.NORTH);
add(toDoInputField, BorderLayout.CENTER);
add(buttonPanel, BorderLayout.SOUTH);
}
/**
*Handler für zusätzliche Schaltflächenaktionen
*/
private class AddActionHandler implements ActionListener {
public void actionPerformed(ActionEvent e) {
//Hinzufügen von Textfeldinhalten zum Listenmodell
toDoListModel.addElement
(toDoInputField.getText());
}
}
}
Der gesamte Code ist unten. https://github.com/mima3/testjavagui/tree/master/java/Swing001
JavaFx erstellt auch einen einfachen Bildschirm.
Main.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="ctrl.Controller">
<!-- TODO Add Nodes -->
<children>
<Pane layoutX="0.0" layoutY="-14.0" prefHeight="297.0" prefWidth="345.0">
<children>
<Label layoutX="14.0" layoutY="14.0" text="aufführen" />
<ListView id="" fx:id="list" layoutX="14.0" layoutY="30.0" prefHeight="198.0" prefWidth="317.0" />
<Button id="" fx:id="btnAdd" layoutX="14.0" layoutY="262.0" mnemonicParsing="false" onAction="#onAddButtonClicked" text="hinzufügen" />
<TextField id="" fx:id="textBox" layoutX="14.0" layoutY="228.0" prefHeight="15.9609375" prefWidth="317.0" />
</children>
</Pane>
</children>
</AnchorPane>
Controler.java
package ctrl;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
public class Controller implements Initializable {
@FXML
private TextField textBox;
@FXML
private Button btnAdd;
@FXML
private ListView<String> list;
@Override
public void initialize(URL location, ResourceBundle resources) {
//TODO automatisch generierter Methodenstub
textBox.setText("Bitte geben Sie den Wert ein.");
}
@FXML
public void onAddButtonClicked(ActionEvent event) {
//Legen Sie eine Zeichenfolge in das Textfeld ein
list.getItems().add(textBox.getText());
textBox.setText("");
}
}
Der gesamte Code ist unten. https://github.com/mima3/testjavagui/tree/master/java/Java8Fx001
JavaFX ist seit JDK 11 vom Oracle JDK getrennt. Daher ist beim Erstellen eines JavaFx-Bildschirms das folgende Verfahren erforderlich.
(1) Laden Sie JavaFX herunter. https://gluonhq.com/products/javafx/
(2) Fügen Sie das jar in lib im heruntergeladenen Ordner zur Referenzbibliothek des Projekts hinzu.
(3) Zur Laufzeit ** Bei Ausführung über die Befehlszeile **
C:\pleiades201904\java\11\bin\java --module-path=C:\tool\lib\javafx-sdk-11.0.2\lib\ --add-modules=javafx.controls --add-modules=javafx.swing --add-modules=javafx.base --add-modules=javafx.fxml --add-modules=javafx.media --add-modules=javafx.web -jar Java11Fx.jar
** Ausführungskonfiguration bei Ausführung mit Eclipse **
Überprüfen Sie mithilfe von inspect.exe, ob der erstellte Java-Bildschirm über UI Automation bedient werden kann. ..
Sie können sehen, dass die Steuerinformationen in UI Automation nicht erfasst wurden. Mit anderen Worten, ** Mit Swing erstellte Anwendungen können nicht über UI Automation ** betrieben werden.
Sie können sehen, dass das UIAutomation-Element erfasst und der ControlType entsprechend festgelegt wurde. Verwenden wir PowerShell, um automatische Vorgänge auszuführen.
Add-Type -AssemblyName UIAutomationClient
Add-Type -AssemblyName UIAutomationTypes
Add-type -AssemblyName System.Windows.Forms
$source = @"
using System;
using System.Windows.Automation;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Drawing;
public class AutomationHelper
{
// https://culage.hatenablog.com/entry/20130611/1370876400
[DllImport("user32.dll")]
extern static uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
[StructLayout(LayoutKind.Sequential)]
struct INPUT
{
public int type;
public MOUSEINPUT mi;
}
[StructLayout(LayoutKind.Sequential)]
struct MOUSEINPUT
{
public int dx;
public int dy;
public int mouseData;
public int dwFlags;
public int time;
public IntPtr dwExtraInfo;
}
const int MOUSEEVENTF_LEFTDOWN = 0x0002;
const int MOUSEEVENTF_LEFTUP = 0x0004;
static public void Click()
{
//Deklaration des Strukturarrays
INPUT[] input = new INPUT[2];
//Linke Taste nach unten
input[0].mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
//Linke Taste nach oben
input[1].mi.dwFlags = MOUSEEVENTF_LEFTUP;
//Stapelgenerierung von Ereignissen
SendInput(2, input, Marshal.SizeOf(input[0]));
}
static public void MouseMove(int x, int y)
{
var pt = new System.Drawing.Point(x, y);
System.Windows.Forms.Cursor.Position = pt;
}
static public void SendKeys(string key)
{
System.Windows.Forms.SendKeys.SendWait(key);
}
public static AutomationElement RootElement
{
get
{
return AutomationElement.RootElement;
}
}
public static AutomationElement GetMainWindowByTitle(string title) {
PropertyCondition cond = new PropertyCondition(AutomationElement.NameProperty, title);
return RootElement.FindFirst(TreeScope.Children, cond);
}
public static AutomationElement ChildWindowByTitle(AutomationElement parent , string title) {
try {
PropertyCondition cond = new PropertyCondition(AutomationElement.NameProperty, title);
return parent.FindFirst(TreeScope.Children, cond);
} catch {
return null;
}
}
public static AutomationElement WaitChildWindowByTitle(AutomationElement parent, string title, int timeout = 10) {
DateTime start = DateTime.Now;
while (true) {
AutomationElement ret = ChildWindowByTitle(parent, title);
if (ret != null) {
return ret;
}
TimeSpan ts = DateTime.Now - start;
if (ts.TotalSeconds > timeout) {
return null;
}
System.Threading.Thread.Sleep(100);
}
}
}
"@
Add-Type -TypeDefinition $source -ReferencedAssemblies("UIAutomationClient", "UIAutomationTypes", "System.Windows.Forms", "System.Drawing")
# 5.Wenn es 0 oder höher ist, ist es einfacher, es mit using zu beschreiben.
$autoElem = [System.Windows.Automation.AutomationElement]
#Listen Sie alle Steuerelemente, die die angegebenen Bedingungen erfüllen, unter dem Fenster auf
function findAllElements($form, $condProp, $condValue) {
$cond = New-Object -TypeName System.Windows.Automation.PropertyCondition($condProp, $condValue)
return $form.FindAll([System.Windows.Automation.TreeScope]::Element -bor [System.Windows.Automation.TreeScope]::Descendants, $cond)
}
#Holen Sie sich ein Steuerelement, das die angegebenen Bedingungen unter dem Fenster erfüllt
function findFirstElement($form, $condProp, $condValue) {
$cond = New-Object -TypeName System.Windows.Automation.PropertyCondition($condProp, $condValue)
return $form.FindFirst([System.Windows.Automation.TreeScope]::Element -bor [System.Windows.Automation.TreeScope]::Descendants, $cond)
}
#Elemente in Wertemuster konvertieren
function convertValuePattern($elem) {
return $elem.GetCurrentPattern([System.Windows.Automation.ValuePattern]::Pattern) -as [System.Windows.Automation.ValuePattern]
}
function convertSelectionItemPattern($elem) {
return $elem.GetCurrentPattern([System.Windows.Automation.SelectionItemPattern]::Pattern) -as [System.Windows.Automation.SelectionItemPattern]
}
#Geben Sie Text in das Element ein
#TxtValuePtn für Java8.Alternative für SetValue funktioniert nicht richtig
function sendTextValue($textCtrl, $message) {
[AutomationHelper]::MouseMove($textCtrl.Current.BoundingRectangle.X + 5, $textCtrl.Current.BoundingRectangle.Y + 5)
[AutomationHelper]::Click()
[AutomationHelper]::SendKeys("^(a)")
[AutomationHelper]::SendKeys("{DEL}")
[AutomationHelper]::SendKeys($message)
Start-Sleep 1
}
#Hauptverarbeitung
$mainForm = [AutomationHelper]::GetMainWindowByTitle("Aufgabenliste")
if ($mainForm -eq $null) {
Write-Error "Starten Sie den Java Fx-Bildschirm"
exit 1
}
$mainForm.SetFocus()
$editType = [System.Windows.Automation.ControlType]::Edit
$textCtrl = findFirstElement $mainForm $autoElem::ControlTypeProperty $editType
#Im Fall von Java8 tritt ein Fehler in SetValue von ValuePattern auf.
#$txtValuePtn = convertValuePattern $textCtrl
#$txtGetValue = $txtValuePtn.Current.Value
#Write-Host "Vorher ändern:$txtGetValue"
#$txtValuePtn.SetValue("Wafuru");
sendTextValue $textCtrl "Waffel"
$btnCtrl = findFirstElement $mainForm $autoElem::NameProperty "hinzufügen"
$btnInvoke = $btnCtrl.GetCurrentPattern([System.Windows.Automation.InvokePattern]::Pattern) -as [System.Windows.Automation.InvokePattern]
$btnInvoke.Invoke()
#2. Zeichen
sendTextValue $textCtrl "Katze"
$btnInvoke.Invoke()
#3. Zeichen
sendTextValue $textCtrl "Hund"
$btnInvoke.Invoke()
#Listenauswahl
$listitemType = [System.Windows.Automation.ControlType]::ListItem
$listitems = findAllElements $mainForm $autoElem::ControlTypeProperty $listitemType
$listPtn = convertSelectionItemPattern $listitems[1]
$listPtn.Select()
** Ausführungsergebnis **
Wenn dies ausgeführt wird, wird der Bildschirm mit JavaFx von Java11 normal ausgefüllt, aber der Bildschirm mit JavaFx von Java8 gibt den folgenden Fehler aus.
Wenn ich einen Wert mit dem mit Java 8 erstellten Wertemuster von Ui Automation für Java Fx festlege, wird der folgende Fehler angezeigt.
** PowerShell-Seite **
"1"Angabe der Anzahl der Argumente"SetValue"Beim Aufruf ist eine Ausnahme aufgetreten: ""
Vorkommensort C.:\dev\testjavagui\out\javafx_auto_err.ps1:146 Zeichen:1
+ $txtValuePtn.SetValue("Wafuru");
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : COMException
** Java-Seite **
Exception in thread "JavaFX Application Thread" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
at javafx.scene.control.TextInputControl.executeAccessibleAction(TextInputControl.java:1590)
at javafx.scene.Node$19.executeAction(Node.java:9649)
at com.sun.glass.ui.Accessible$ExecuteAction.run(Accessible.java:177)
at com.sun.glass.ui.Accessible$ExecuteAction.run(Accessible.java:173)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.glass.ui.Accessible.lambda$executeAction$5(Accessible.java:190)
at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
at com.sun.glass.ui.Accessible.executeAction(Accessible.java:187)
at com.sun.glass.ui.win.WinAccessible.SetValueString(WinAccessible.java:1262)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$152(WinApplication.java:177)
at java.lang.Thread.run(Thread.java:748)
Dieser Fehler tritt nicht bei JavaFx auf, das mit Java 11 erstellt wurde.
Verwenden Sie Java Access Bridge Windows kann die Java-GUI bedienen.
Bitte beachten Sie, ob der Prozess, der Java oder Java Access Bridge verwendet, 32-Bit oder 64-Bit ist.
Zunächst werde ich die Verwendung von Access Bridge Explorer erläutern, einem Tool zum Erkunden von GUI-Elementen mit Java Access Bridge.
(1) Aktivieren Sie die Java Access Bridge
%JRE_HOME%\bin\jabswitch -enable
(2) Vergewissern Sie sich, dass WindowsAccessBridge-64.dll in% JRE_HOME% \ jre \ bin vorhanden ist, und fügen Sie der Umgebungsvariablen PATH% JRE_HOME% \ jre \ bin hinzu. Wenn es sich um eine ältere Version handelt, müssen Sie sie wie folgt herunterladen. https://www.oracle.com/technetwork/java/javase/tech/index-jsp-136191.html
(3) Laden Sie Access Bridge Explorer wie folgt herunter. https://github.com/google/access-bridge-explorer/releases
(4) Starten Sie Access Bridge Explorer.
Ein Beispiel für den Betrieb von Java Access Bridge aus .NET wurde unten veröffentlicht. https://github.com/jdog3/JavaAccessBridge.Net-Sample
Basierend auf dem oben Gesagten ist das folgende Beispiel, das die Klickoperation und die Operation über die Konsolenanwendung ermöglicht, wie folgt. https://github.com/mima3/testjavagui/tree/master/cs
using JabApiLib.JavaAccessBridge;
using System;
using System.Collections.Generic;
using System.Text;
namespace JabApiCsharpSample
{
class Program
{
static void Main(string[] args)
{
//JabApi.Windows_run();
JabHelpers.Init();
int vmID = 0;
JabHelpers.AccessibleTreeItem javaTree = null;
javaTree = JabHelpers.GetComponentTreeByTitle("Aufgabenliste", out vmID);
//Texteinstellungen
JabHelpers.AccessibleTreeItem txt = javaTree.children[0].children[1].children[0].children[0].children[1];
JabApi.setTextContents(vmID, txt.acPtr, "Warosuwarosu");
JabHelpers.AccessibleTreeItem button = javaTree.children[0].children[1].children[0].children[0].children[2].children[0];
List<string> actionList = JabHelpers.GetAccessibleActionsList(vmID, button.acPtr);
Console.WriteLine("Bedienbare Aktionen-------------");
foreach (string a in actionList)
{
Console.WriteLine(a);
}
//Klicken Sie auf Ausführen
JabHelpers.DoAccessibleActions(vmID, button.acPtr, "klicken");
//
JabApi.setTextContents(vmID, txt.acPtr, "Irohanihohe");
JabHelpers.DoAccessibleActions(vmID, button.acPtr, "klicken");
//
JabApi.setTextContents(vmID, txt.acPtr, "Staub schleimig");
JabHelpers.DoAccessibleActions(vmID, button.acPtr, "klicken");
//Inhalt auflisten
Console.WriteLine("Liste Liste-------------");
javaTree = JabHelpers.GetComponentTreeByTitle("Aufgabenliste", out vmID);
JabHelpers.AccessibleTreeItem list = javaTree.children[0].children[1].children[0].children[0].children[0].children[0].children[0];
foreach (JabHelpers.AccessibleTreeItem listitem in list.children)
{
Console.WriteLine(listitem.name );
}
JabHelpers.DoAccessibleActions(vmID, list.children[1].acPtr, "klicken");
Console.ReadLine();
}
}
}
Die Operationen, die mit DoAccessibleActions ausgeführt werden können, sind für jedes Steuerelement unterschiedlich, und Sie können herausfinden, was Sie mit GetAccessibleActions tun können. In JabApi Java Access Bridge API Die aufrufenden Funktionen werden gemeinsam implementiert. Dieses Mal wird davon ausgegangen, dass es mit 64 Bit ausgeführt wird. Ändern Sie daher bei Bedarf die folgende Zeile in JabApi.cs.
public static class JabApi
{
public const String WinAccessBridgeDll = @"WindowsAccessBridge-64.dll";
Darüber hinaus erfordert Windows_run, bei dem es sich um die Erstverarbeitung von Java Access Bridge handelt, eine Nachrichtenpumpe. Wenn die Nachricht nicht verarbeitet wird, funktioniert die nachfolgende Verarbeitung nicht normal. Aus diesem Grund besagt das ursprüngliche Beispiel für den Betrieb von .NET zu Java Access Bridge-Operationen, dass Windows_run beim Laden des Formulars enthalten sein muss. .. Dieses Mal wird DoEvents nach Windows_run wie folgt ausgeführt, damit es auf der Konsole funktioniert.
// Windows_run benötigt eine Nachrichtenpumpe
// https://stackoverflow.com/questions/50582769/windowsaccessbridge-for-java-automation-using-c-sharp
public static void Init()
{
JabApi.Windows_run();
DoEvents();
}
** Ausführungsergebnis **
Sie können ein Skript schreiben, das denselben Vorgang in PowerShell basierend auf C # ausführt. Die von Ihnen verwendete JabApi.dll sollte Download oder Quellcode sein. / testjavagui / tree / master / cs ) Bitte kompilieren. Die auf GitHub aufgeführte DLL ist 64bit + .NET 2.0 und kann daher je nach Umgebung nicht verwendet werden.
#64-Bit-Prämisse
$dllPath = Split-Path $MyInvocation.MyCommand.Path
Set-Item Env:Path "$Env:Path;$dllPath"
Add-Type -Path "$dllPath\JabApi.dll"
[JabApiLib.JavaAccessBridge.JabHelpers]::init()
$vmID = 0
$javaTree = [JabApiLib.JavaAccessBridge.JabHelpers]::GetComponentTreeByTitle("Aufgabenliste",[ref]$vmID)
$txt = $javaTree.children[0].children[1].children[0].children[0].children[1]
[JabApiLib.JavaAccessBridge.JabApi]::setTextContents($vmID, $txt.acPtr, "Warosuwarosu")
#klicken
$button = $javaTree.children[0].children[1].children[0].children[0].children[2].children[0]
[JabApiLib.JavaAccessBridge.JabHelpers]::DoAccessibleActions($vmID, $button.acPtr, "klicken")
#
[JabApiLib.JavaAccessBridge.JabApi]::setTextContents($vmID, $txt.acPtr, "Ahhhh")
[JabApiLib.JavaAccessBridge.JabHelpers]::DoAccessibleActions($vmID, $button.acPtr, "klicken")
#
[JabApiLib.JavaAccessBridge.JabApi]::setTextContents($vmID, $txt.acPtr, "Gut")
[JabApiLib.JavaAccessBridge.JabHelpers]::DoAccessibleActions($vmID, $button.acPtr, "klicken")
#Auf Updates prüfen
$javaTree = [JabApiLib.JavaAccessBridge.JabHelpers]::GetComponentTreeByTitle("Aufgabenliste",[ref]$vmID)
$list = $javaTree.children[0].children[1].children[0].children[0].children[0].children[0].children[0]
foreach($item in $list.children) {
Write-Host $item.name
}
[JabApiLib.JavaAccessBridge.JabHelpers]::DoAccessibleActions($vmID, $list.children[1].acPtr, "klicken")
Java-GUI-Operationen sind möglich, indem Sie die Java-Erweiterung aus dem Tool installieren. Wenn die Erweiterung installiert ist, wird UiPathJavaBridgeV8_x64.dll in "% JRE_HOME% \ bin " gespeichert.
Nach der Installation der Erweiterung können Sie wie gewohnt Bildschirme erstellen.
** Ausführungsergebnis **
Es kann möglich sein, automatische Operationen unter Verwendung des GUI-Testframeworks auszuführen. Da es sich von dem Zweck dieser Zeit unterschied, habe ich es nicht im Detail untersucht.
Automation Automation ist ein Framework, mit dem Swing- und JavaFx-GUIs einfach getestet werden können.
Es kann in Java geschrieben werden, aber es kann auch in einem Groovy-Skript wie dem folgenden geschrieben werden.
clickOn 'text:Some Button'
doubleClickOn 'username-input'
type 'my-username'
clickOn 'text:Login'
TestFX JavaFX ist ein einfaches und sauberes Testframework. https://github.com/TestFX/TestFX
AssertJ Swing AssertJ Swing scheint in der Lage zu sein, die Benutzeroberfläche von Swing zu testen. Jetzt ist es eine Gabelung von Fest Swing.
Java Swing UI test driver replacement for Fest [closed] https://stackoverflow.com/questions/31168990/java-swing-ui-test-driver-replacement-for-fest
Recommended Posts