Automate Windows desktop applications
Automate Calculator
If you have Windows 8 or higher, download and install the old Windows 7 Calculator and install it to C:/Windows/System32/calc1.exe.
The code example below also works with the Windows 10 Calculator.
View the code
import com.workfusion.odf2.compiler.BotTask;
import com.workfusion.odf2.core.task.AdHocTask;
import com.workfusion.odf2.core.task.TaskInput;
import com.workfusion.odf2.core.task.output.TaskRunnerOutput;
import com.workfusion.odf2.core.webharvest.rpa.RpaDriver;
import com.workfusion.odf2.core.webharvest.rpa.RpaFactory;
import com.workfusion.odf2.core.webharvest.rpa.RpaRunner;
import com.workfusion.rpa.driver.Driver;
import com.workfusion.rpa.helpers.RPA;
import com.workfusion.rpa.helpers.Resource;
import com.workfusion.rpa.helpers.UiElement;
import org.openqa.selenium.Keys;
import org.slf4j.Logger;
import javax.inject.Inject;
import java.util.*;
import static com.workfusion.rpa.helpers.RPA.$;
@BotTask(requireRpa = true)
public class AutomateWindowsDesktopApplications implements AdHocTask {
private final RpaRunner rpaRunner;
private final Logger logger;
@Inject
public AutomateWindowsDesktopApplications(RpaFactory rpaFactory, Logger logger){
this.rpaRunner = rpaFactory
.builder(RpaDriver.UNIVERSAL)
.closeOnCompletion(true)
.build();
this.logger = logger;
}
@Override
public TaskRunnerOutput run(TaskInput taskInput) {
rpaRunner.execute(driver->{
this.automateCalculator(driver);
});
return taskInput.asResult()
.withColumn("example_bot_task_output", "completed_successfully");
}
private void automateCalculator(Driver driver){
RPA.open("calc.exe");
RPA.switchTo().window("[CSS:.ApplicationFrameWindow[title=\"Calculator\"]]");
List<String> calInput = new ArrayList<>(Arrays.asList("2*4*8*16=", "5*5=", "10/2="));
Map<String,String> calOutput = new HashMap<>();
for(String input:calInput){
RPA.sendKeys(Keys.ESCAPE);
RPA.sendKeys(input);
String result = RPA.selectAllTextAndCopy();
calOutput.put(input,result);
logger.debug(" Input " + input + result);
}
RPA.close();
}
}
Automate Notepad
You can automate Notepad with Save as dialog.
View the code
import com.workfusion.odf2.compiler.BotTask;
import com.workfusion.odf2.core.task.AdHocTask;
import com.workfusion.odf2.core.task.TaskInput;
import com.workfusion.odf2.core.task.output.TaskRunnerOutput;
import com.workfusion.odf2.core.webharvest.rpa.RpaDriver;
import com.workfusion.odf2.core.webharvest.rpa.RpaFactory;
import com.workfusion.odf2.core.webharvest.rpa.RpaRunner;
import com.workfusion.rpa.driver.Driver;
import com.workfusion.rpa.helpers.RPA;
import com.workfusion.rpa.helpers.Resource;
import com.workfusion.rpa.helpers.UiElement;
import org.openqa.selenium.Keys;
import org.slf4j.Logger;
import javax.inject.Inject;
import java.util.*;
import static com.workfusion.rpa.helpers.RPA.$;
@BotTask(requireRpa = true)
public class AutomateWindowsDesktopApplications implements AdHocTask {
private final RpaRunner rpaRunner;
private final Logger logger;
@Inject
public AutomateWindowsDesktopApplications(RpaFactory rpaFactory, Logger logger){
this.rpaRunner = rpaFactory
.builder(RpaDriver.UNIVERSAL)
.closeOnCompletion(true)
.build();
this.logger = logger;
}
@Override
public TaskRunnerOutput run(TaskInput taskInput) {
rpaRunner.execute(driver->{
this.automateNotepad(driver);
});
return taskInput.asResult()
.withColumn("example_bot_task_output", "completed_successfully");
}
private void automateNotepad(Driver driver){
String path = "D:\\_temp\\new.txt";
Resource.createFileOverwrite(path);
Resource.append(path, "This is a content of the temporary txt file", "utf-8");
RPA.open("notepad.exe " + path);
RPA.switchTo().window("[CLASS:Notepad]");
RPA.sleep(500);
UiElement editor = $("[CLASS:Edit; INSTANCE:1]");
// Navigate to the document's end.
editor.sendKeys("^{END}");
// Add new content.
editor.sendKeys("\n\nThis is a new content which is added by \"WorkFusion Bot\".");
// Try to close the notepad.
driver.close();
RPA.sleep(500);
// Switch to the "Save changes" dialog.
RPA.switchTo().window("[CLASS:#32770]");
// Click the "Save" button.
$("[CLASS:Button; INSTANCE:1]").click();
RPA.sleep(2000);
// Validate the result.
RPA.open("notepad.exe " + path);
RPA.sleep(2000);
}
}
As the Notepad application was updated in Windows 11, you might not capture selectors of some elements. In this case, it is recommended to perform the image-based or hotkey-based automation for Notepad in Windows 11.
Explore Excel window
Use Inspector to get Excel UI and document controls
In this example, you practice the usage of Inspector, a built-in tool to explore properties and values of objects located in the application, providing functionality for quick element selector search.
Here, you can use Inspector to automate a Windows desktop application on the example of MS Excel. Let's assume you have the following table in an Excel file.
Having Euro as the base currency, scrap five currencies:
- Open the file with 1,000 rows and filter by date = today and the currency code.
- Set the exchange rate to euro.
- Take the following currencies: USD, AUD, INR, GBP, SGD.
- Set the date to 28.11.2018.
Using Inspector, you can get appropriate fields. For example, get the selector of the A1 cell.

You can also navigate through the cells using the Inspector attributes.

Use identified selectors in Bot Task code
You can design the Bot Task source code in the following way:
View the code
import com.j256.ormlite.stmt.query.In;
import com.workfusion.odf2.compiler.BotTask;
import com.workfusion.odf2.core.task.AdHocTask;
import com.workfusion.odf2.core.task.TaskInput;
import com.workfusion.odf2.core.task.output.TaskRunnerOutput;
import com.workfusion.odf2.core.webharvest.rpa.RpaDriver;
import com.workfusion.odf2.core.webharvest.rpa.RpaFactory;
import com.workfusion.odf2.core.webharvest.rpa.RpaRunner;
import com.workfusion.rpa.driver.Driver;
import com.workfusion.rpa.helpers.RPA;
import com.workfusion.rpa.helpers.Resource;
import com.workfusion.rpa.helpers.UiElement;
import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.OutputType;
import org.slf4j.Logger;
import javax.inject.Inject;
import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.List;
import static com.workfusion.rpa.helpers.RPA.*;
import static java.awt.datatransfer.DataFlavor.stringFlavor;
@BotTask(requireRpa = true)
public class AutomateWindowsDesktopApplications implements AdHocTask {
private final RpaRunner rpaRunner;
private final Logger logger;
@Inject
public AutomateWindowsDesktopApplications(RpaFactory rpaFactory, Logger logger){
this.rpaRunner = rpaFactory
.builder(RpaDriver.UNIVERSAL)
.closeOnCompletion(true)
.build();
this.logger = logger;
}
@Override
public TaskRunnerOutput run(TaskInput taskInput) {
rpaRunner.execute(driver->{
try {
this.exploreExcelWindow(driver);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (UnsupportedFlavorException e) {
throw new RuntimeException(e);
}
});
return taskInput.asResult()
.withColumn("example_bot_task_output", "completed_successfully");
}
private void exploreExcelWindow(Driver driver) throws IOException, UnsupportedFlavorException {
String path = downloadFileOnAgent(s3Path); //downloadFileOnAgent with path to Excel file with initial data.
String fileName = path.substring(path.lastIndexOf('\\') + 1, path.length() - 5) ; //Get file name from path.
Map<String,Double> currencyMap = new HashMap<>(); //Create a Map for currencies with a value in EUR.
currencyMap.put("USD", 0.878028); //Adding data to the map.
currencyMap.put("AUD", 0.643376);
currencyMap.put("INR", 0.0125605);
currencyMap.put("GBP", 1.12634);
currencyMap.put("SGD", 0.640738);
RPA.sendKeys(StringTransformations.getHotKeyText(114, 4));
RPA.sendKeys("excel"); // Opening Excel
RPA.pressEnter();
RPA.sleep(2000);
RPA.switchTo().window("[CLASS:XLMAIN; TITLE:Excel]"); //Switch to the Excel window.
$("[CLASS:NetUIRibbonTab; NAME:msotcidPlaceOpen]").click(); //Click the "Open" button in the main menu.
$("[CLASS:NetUISimpleButton; NAME:Browse]").click(); //Click the "Browse" button.
RPA.switchTo().window("[CLASS:#32770; TITLE:Open]"); //Switch to the "Open" window.
$("[CLASS:Edit]").sendKeys(path); //Enter the path in the "Edit" field.
RPA.sendKeys("{ENTER}"); //Press Enter.
RPA.switchTo().window("[CLASS:XLMAIN; TITLE:" + fileName + " - Excel]"); //Switch to the Excel open file window.
$("[CLASS:XLGridRowHeader; NAME:1]").click() ; //Select the first row with headers.
RPA.sendKeys(Keys.chord(Keys.LEFT_CONTROL, "c")); //Press "Ctrl + C" to copy.
String data = (String)Toolkit.getDefaultToolkit().getSystemClipboard().getData(stringFlavor); //Getting data from the clipboard in String format.
String[] headers = data.replaceAll("\n", "").split("\t"); //Delete a blank line and split the data to get a List of headers.
$("[CLASS:XLSpreadsheetCell; NAME:A2]").click(); //Select cell A2.
RPA.sendKeys(Keys.chord(Keys.LEFT_SHIFT,"{RIGHT " + (headers.length - 1) + "}")); //Select all columns with headers starting at cell A2.
RPA.sendKeys(Keys.chord(Keys.LEFT_SHIFT,Keys.LEFT_CONTROL,"{DOWN}")); //Select all data below the previously selected cells.
List<Map<String,String>> table = new ArrayList<Map<String,String>>(); //Create a List to store data from the table.
int rowID = 1; //Initialize a variable with the row number.
RPA.sendKeys(Keys.chord(Keys.LEFT_CONTROL, "c")); //Press "Ctrl + C" to copy.
data = (String) Toolkit.getDefaultToolkit().getSystemClipboard().getData(stringFlavor); //Get data from the clipboard in the String format. //Start parsing data from the Excel table into simple Groovy objects.
for(String item : data.split("\n")){ //Separate the copied data into lines and work with each line separately in the cycle.
Map<String,String> row = new HashMap<>(); //Create a Map to store row data.
rowID++; //Increment the "rowID" variable.
row.put("id", String.valueOf(rowID)); //Put the row number on the Map.
int index = 0; //Create an "index" variable to get the headers from the first to the last one.
for(String rowItem : item.split("\t")){ //Separate data on the row to obtain cell values and work with each separately in the loop.
row.put(headers[index], rowItem); //Put the cell value with the appropriate header on the map.
index++; //Increment the "index" variable.
}
table.add(row) ; //Add a Map with row data to a List for table data.
}
String date = "28.11.2018"; //Initialize the date value for the first data filter.
List<Map<String,String>> result = new ArrayList<Map<String ,String>>(); //Create a List to store data from the table with the applied filters.
for(Map<String,String> item : table){ //Perform a work cycle with each Map from the List with table data.
if(item.get("date").equals(date)){ //Apply filter by date.
for(String currency : currencyMap.keySet()){ //Perform a work cycle with each currency name from the List with all keys from the Map with currency.
if(item.get("currency_code").equals(currency)){ //Apply a filter by currency.
Map<String,String> newValue = new HashMap<>(); //Create a Map to store past data filters.
newValue.put("id", item.get("id")); //Put the row number on the Map.
Double valueOfmultiplication = Double.parseDouble(item.get("value"))*currencyMap.get(currency);
newValue.put("value",valueOfmultiplication.toString() );//Put the calculated value in EUR on the Map.
result.add(newValue); //Add a Map with new data to a List for result data.
break; //Make a cycle break.
}
}
}
}
RPA.sendKeys(Keys.chord(Keys.LEFT_SHIFT,Keys.LEFT_CONTROL,"{UP}")); //Select the first two rows to access cell A1.
$("[CLASS:XLSpreadsheetCell; NAME:A1]").click() ; //Select cell A1, see Fig.1.
RPA.sendKeys("{RIGHT " + headers.length + "}result{ENTER}"); //Switch to the first empty column and add the "result" header.
$("[CLASS:Edit; NAME:Name Box]").click(); //Select the "Name Box" field.
RPA.sendKeys(Keys.chord(Keys.LEFT_CONTROL, "c")); //Press "Ctrl + C" to copy.
RPA.sendKeys("{BACKSPACE}"); //Delete text from the "Name Box" field.
data = (String) Toolkit.getDefaultToolkit().getSystemClipboard().getData(stringFlavor); //Get data from the clipboard in the String format.
String columnName = data.substring(0, 1); //Get the column name "result".
for(Map<String,String> item : result){ //Perform a work cycle with each Map from the List with result data.
$("[CLASS:Edit; NAME:Name Box]").click() ; //Click the "Name Box" field, see the picture below.
RPA.sendKeys(columnName + item.get("id") + "{ENTER}" + item.get("value").toString() + "{ENTER}"); //Enter the cell address and new data.
RPA.sleep(2 * 1000);
}
RPA.sendKeys(Keys.chord(Keys.LEFT_CONTROL, "s")); //Click the "Save As" button in the main menu.
$("[CLASS:NetUIAppFrameHelper; NAME:Close]").click();
byte[] fileContent = downloadFileFromAgent(path); //Download the file from the computer to a variable.
deleteFileOnAgent(path); //Delete the file from the computer.
String file_name = fileName + ".xlsx"; //Define a variable with a filename and format
String pathToSaveFile = "C:\\Users\\sshukla\\"; // Folder where modified file will be saved
Path pathText = Paths.get(pathToSaveFile + file_name);
Files.write(pathText,fileContent); //Saving the modifed file to the desired folder
}
}
The resulting file looks as follows:
