La plupart des opérations automatiques sur l'écran de Windows sont rendues possibles par les méthodes décrites ci-dessous.
** Automatisation de "Akanchan Kawaiitta" par RPA Nine People ** https://qiita.com/mima_ita/items/d4655de865f30bb51c65
En fait, il y a un cas gênant, qui est le cas de la création d'un écran avec Java. Cette fois, examinons si le fonctionnement automatique est possible en utilisant un écran créé en Java à titre d'exemple.
** Environnement d'expérimentation ** Windows10 Home Java 8 Visual Studio 2019 PowerShell 5.1 UiPath 2019.10.0-beta 111
Swing peut être utilisé comme méthode principale pour créer des écrans en Java, ou JavaFx peut être utilisé.
Créez un écran Swing simple en vous référant à la page ci-dessous.
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;
/**
*Liste de choses à faire
*Référence ci-dessous
* 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());
//Générer une liste
toDoListModel = new DefaultListModel<String>();
toDoList = new JList<String>(toDoListModel);
JScrollPane listScrollPane = new JScrollPane(toDoList);
//Générer un champ de texte pour ajouter des tâches
toDoInputField = new JTextField();
//Génération de chaque bouton
JPanel buttonPanel = new JPanel();
addButton = new JButton("ajouter à");
//Définir l'auditeur sur le bouton
addButton.addActionListener(new AddActionHandler());
buttonPanel.add(addButton);
add(listScrollPane, BorderLayout.NORTH);
add(toDoInputField, BorderLayout.CENTER);
add(buttonPanel, BorderLayout.SOUTH);
}
/**
*Gestionnaire pour les actions de bouton supplémentaires
*/
private class AddActionHandler implements ActionListener {
public void actionPerformed(ActionEvent e) {
//Ajouter le contenu du champ de texte au modèle de liste
toDoListModel.addElement
(toDoInputField.getText());
}
}
}
Tout le code est ci-dessous. https://github.com/mima3/testjavagui/tree/master/java/Swing001
JavaFx crée également un écran simple.
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="liste" />
<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="ajouter à" />
<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 talon de méthode généré automatiquement
textBox.setText("veuillez remplir la valeur.");
}
@FXML
public void onAddButtonClicked(ActionEvent event) {
//Définir une chaîne de caractères dans la zone de texte
list.getItems().add(textBox.getText());
textBox.setText("");
}
}
Tout le code est ci-dessous. https://github.com/mima3/testjavagui/tree/master/java/Java8Fx001
JavaFX est séparé du JDK Oracle depuis le JDK 11. Par conséquent, lors de la création d'un écran JavaFx, la procédure suivante est requise.
(1) Téléchargez JavaFX. https://gluonhq.com/products/javafx/
(2) Ajoutez le jar dans lib dans le dossier téléchargé à la bibliothèque de référence du projet.
(3) Au moment de l'exécution ** Lors de l'exécution à partir de la ligne de commande **
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
** Configuration de l'exécution lors de l'exécution avec Eclipse **
Vérifiez si l'écran Java créé peut être exploité via UI Automation en utilisant inspect.exe ..
Vous pouvez voir que les informations de contrôle n'ont pas été acquises dans UI Automation. En d'autres termes, ** Les applications créées avec Swing ne peuvent pas être exploitées via UI Automation **.
Vous pouvez voir que l'élément UIAutomation a été acquis et le ControlType a été défini de manière appropriée. Utilisons en fait PowerShell pour effectuer des opérations automatiques.
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()
{
//Déclaration de struct array
INPUT[] input = new INPUT[2];
//Bouton gauche Bas
input[0].mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
//Bouton gauche Haut
input[1].mi.dwFlags = MOUSEEVENTF_LEFTUP;
//Génération par lots d'événements
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.S'il est égal ou supérieur à 0, il est plus facile de le décrire en utilisant.
$autoElem = [System.Windows.Automation.AutomationElement]
#Répertoriez tous les contrôles qui remplissent les conditions spécifiées sous la fenêtre
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)
}
#Obtenez un contrôle qui remplit les conditions spécifiées sous la fenêtre
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)
}
#Convertir des éléments en modèle de valeur
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]
}
#Entrez du texte dans l'élément
#TxtValuePtn pour Java8.Alternative pour SetValue ne fonctionne pas correctement
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
}
#Traitement principal
$mainForm = [AutomationHelper]::GetMainWindowByTitle("Liste de choses à faire")
if ($mainForm -eq $null) {
Write-Error "Lancez l'écran Java Fx"
exit 1
}
$mainForm.SetFocus()
$editType = [System.Windows.Automation.ControlType]::Edit
$textCtrl = findFirstElement $mainForm $autoElem::ControlTypeProperty $editType
#Dans le cas de Java8, une erreur se produit dans SetValue de ValuePattern.
#$txtValuePtn = convertValuePattern $textCtrl
#$txtGetValue = $txtValuePtn.Current.Value
#Write-Host "Changer avant:$txtGetValue"
#$txtValuePtn.SetValue("Wafuru");
sendTextValue $textCtrl "Gaufre"
$btnCtrl = findFirstElement $mainForm $autoElem::NameProperty "ajouter à"
$btnInvoke = $btnCtrl.GetCurrentPattern([System.Windows.Automation.InvokePattern]::Pattern) -as [System.Windows.Automation.InvokePattern]
$btnInvoke.Invoke()
#2ème caractère
sendTextValue $textCtrl "Chat"
$btnInvoke.Invoke()
#3ème caractère
sendTextValue $textCtrl "chien"
$btnInvoke.Invoke()
#Sélection de liste
$listitemType = [System.Windows.Automation.ControlType]::ListItem
$listitems = findAllElements $mainForm $autoElem::ControlTypeProperty $listitemType
$listPtn = convertSelectionItemPattern $listitems[1]
$listPtn.Select()
** Résultat d'exécution **
Lorsque ceci est exécuté, l'écran utilisant JavaFx de Java11 sera terminé normalement, mais l'écran utilisant JavaFx de Java8 affichera l'erreur suivante.
Lors de la définition d'une valeur avec Value Pattern of Ui Automation for Java Fx créé avec Java 8, l'erreur suivante apparaît.
** Côté PowerShell **
"1"Spécifier le nombre d'arguments"SetValue"Une exception s'est produite lors de l'appel: ""
Lieu de l'événement C:\dev\testjavagui\out\javafx_auto_err.ps1:146 caractères:1
+ $txtValuePtn.SetValue("Wafuru");
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : COMException
** Côté Java **
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)
Cette erreur ne se produit pas pour JavaFx créé avec Java 11.
Utilisez Java Access Bridge Windows pourra utiliser l'interface graphique Java.
Veuillez savoir si le processus qui utilise Java ou Java Access Bridge est 32 bits ou 64 bits.
Tout d'abord, nous expliquerons comment utiliser Access Bridge Explorer, qui est un outil pour explorer les éléments de l'interface graphique à l'aide de Java Access Bridge.
(1) Activer Java Access Bridge
%JRE_HOME%\bin\jabswitch -enable
(2) Confirmez que WindowsAccessBridge-64.dll existe dans% JRE_HOME% \ jre \ bin et ajoutez% JRE_HOME% \ jre \ bin à la variable d'environnement PATH. S'il s'agit d'une version plus ancienne, vous devez la télécharger à partir de ce qui suit. https://www.oracle.com/technetwork/java/javase/tech/index-jsp-136191.html
(3) Téléchargez Access Bridge Explorer à partir de ce qui suit. https://github.com/google/access-bridge-explorer/releases
(4) Démarrez Access Bridge Explorer.
Un exemple de fonctionnement de Java Access Bridge à partir de .NET a été publié ci-dessous. https://github.com/jdog3/JavaAccessBridge.Net-Sample
Sur la base de ce qui précède, l'exemple qui permet une opération de clic et une opération à partir de l'application console est le suivant. 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("Liste de choses à faire", out vmID);
//Paramètres de texte
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("Actions exploitables-------------");
foreach (string a in actionList)
{
Console.WriteLine(a);
}
//Cliquez sur exécuter
JabHelpers.DoAccessibleActions(vmID, button.acPtr, "Cliquez sur");
//
JabApi.setTextContents(vmID, txt.acPtr, "Irohanihohe");
JabHelpers.DoAccessibleActions(vmID, button.acPtr, "Cliquez sur");
//
JabApi.setTextContents(vmID, txt.acPtr, "Poussière visqueuse");
JabHelpers.DoAccessibleActions(vmID, button.acPtr, "Cliquez sur");
//Contenu de la liste
Console.WriteLine("Liste liste-------------");
javaTree = JabHelpers.GetComponentTreeByTitle("Liste de choses à faire", 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, "Cliquez sur");
Console.ReadLine();
}
}
}
Les opérations qui peuvent être effectuées avec DoAccessibleActions sont différentes pour chaque contrôle et vous pouvez découvrir ce que vous pouvez faire avec GetAccessibleActions. Dans JabApi, API Java Access Bridge Les fonctions qui appellent sont implémentées collectivement. Cette fois, il est supposé qu'il fonctionne sur 64 bits, changez donc la ligne suivante dans JabApi.cs si nécessaire.
public static class JabApi
{
public const String WinAccessBridgeDll = @"WindowsAccessBridge-64.dll";
De plus, Windows_run, qui est le traitement initial de Java Access Bridge, nécessite une pompe de messages, et si le message n'est pas traité, le traitement ultérieur ne fonctionnera pas normalement. C'est pourquoi l'original .NET vers Java Access Bridge Operation Sample indique que Windows_run doit être inclus au chargement du formulaire. .. Cette fois, DoEvents est exécuté après Windows_run comme suit pour qu'il fonctionne sur la console.
// Windows_run a besoin d'une pompe de message
// https://stackoverflow.com/questions/50582769/windowsaccessbridge-for-java-automation-using-c-sharp
public static void Init()
{
JabApi.Windows_run();
DoEvents();
}
** Résultat d'exécution **
Vous pouvez écrire un script qui effectue la même opération dans PowerShell basé sur C #. Le JabApi.dll que vous utilisez doit être Download ou Source Code / testjavagui / tree / master / cs ) Veuillez compiler. La DLL répertoriée sur GitHub est 64 bits + .NET 2.0, elle ne peut donc pas être utilisée en fonction de l'environnement.
#Prémisse 64 bits
$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("Liste de choses à faire",[ref]$vmID)
$txt = $javaTree.children[0].children[1].children[0].children[0].children[1]
[JabApiLib.JavaAccessBridge.JabApi]::setTextContents($vmID, $txt.acPtr, "Warosuwarosu")
#Cliquez sur
$button = $javaTree.children[0].children[1].children[0].children[0].children[2].children[0]
[JabApiLib.JavaAccessBridge.JabHelpers]::DoAccessibleActions($vmID, $button.acPtr, "Cliquez sur")
#
[JabApiLib.JavaAccessBridge.JabApi]::setTextContents($vmID, $txt.acPtr, "Ahhhh")
[JabApiLib.JavaAccessBridge.JabHelpers]::DoAccessibleActions($vmID, $button.acPtr, "Cliquez sur")
#
[JabApiLib.JavaAccessBridge.JabApi]::setTextContents($vmID, $txt.acPtr, "Bien")
[JabApiLib.JavaAccessBridge.JabHelpers]::DoAccessibleActions($vmID, $button.acPtr, "Cliquez sur")
#Rechercher des mises à jour
$javaTree = [JabApiLib.JavaAccessBridge.JabHelpers]::GetComponentTreeByTitle("Liste de choses à faire",[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, "Cliquez sur")
Les opérations de l'interface graphique Java sont possibles en installant l'extension Java à partir de l'outil. Lorsque l'extension est installée, UiPathJavaBridgeV8_x64.dll est stocké dans "% JRE_HOME% \ bin ".
Après avoir installé l'extension, vous pourrez créer des écrans comme d'habitude.
** Résultat d'exécution **
Il peut être possible d'effectuer des opérations automatiques à l'aide du framework de test GUI. Puisqu'il était différent de l'objectif de cette époque, je ne l'ai pas étudié en détail.
Automation Automation est un framework qui facilite le test des interfaces graphiques Swing et JavaFx.
Il peut être écrit en Java, mais il peut également être écrit dans un script Groovy comme celui ci-dessous.
clickOn 'text:Some Button'
doubleClickOn 'username-input'
type 'my-username'
clickOn 'text:Login'
TestFX JavaFX est un framework de test simple et propre. https://github.com/TestFX/TestFX
AssertJ Swing AssertJ Swing semble être capable de tester l'interface graphique de Swing. Maintenant, c'est une fourchette de 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