autoitx
AutoItX's API, in Rust, on Windows and macOS.
Drive other applications' user interfaces: keystrokes, mouse, clipboard, windows, processes. The API is modeled on AutoItX, so existing AutoIt automation ports over almost mechanically โ but unlike AutoItX, it also runs natively on macOS.
[]
= "0.1"
๐ฆ crates.io ยท ๐ API documentation ยท โ Buy me a coffee
Quickstart
use ;
use Duration;
let ai = new?;
let orders = from;
// Wait for the window, bring it forward, and fill the screen.
open_and_focus?;
ai.maximize?;
// Type data. `Keys::text` escapes, so a customer name containing `{`
// is typed rather than executed as a key command.
ai.send?;
ai.send?;
// Read a field back, waiting on the OS clipboard counter rather than
// on a sentinel that a real value could collide with.
let total = read_screen_text?;
That compiles unchanged on macOS. What differs โ a Win32 class has no macOS
counterpart โ is handled by SelectorSet, not by
#[cfg] in your logic.
Platform support
| Windows | macOS | Linux | |
|---|---|---|---|
| Backend | AutoItX3_x64.dll, loaded at runtime |
native | planned |
| Keystrokes, mouse, clipboard | โ | โ | โ |
| Windows by title / class / regex | โ | โ (Accessibility) | โ |
Processes, run |
โ | โ | โ |
| Pixel colour / search | โ | โ (needs Screen Recording) | โ |
| Window text and class list | โ | โ (the AX tree, and its roles) | โ |
Controls by ClassNameNN |
โ (by HWND) | โ no HWND to address | โ |
Cursor shape (mouse_get_cursor) |
โ | โ no public API โ use recipes::wait_until_idle |
โ |
Mapped network drives, status bars, WinSetTrans, tooltips |
โ | โ | โ |
Capabilities that exist on only one platform live in ext::windows /
ext::macos. Using one from the wrong platform is a compile error, not a
runtime surprise โ a robot that discovers mid-run it cannot read the cursor has
already half-completed a transaction in someone's ERP.
One thing to know before porting a shortcut
{CTRLDOWN}c{CTRLUP} means Copy on Windows and Control-C on macOS, where
Copy is Command-C. The default (KeyMap::AsWritten) takes the names literally,
so a Windows shortcut does something else โ loudly, rather than silently:
use ;
let ai = builder
.options
.build?;
Swap it only if every one of your CTRL sequences is an editing shortcut. If
any of them means Control literally โ a terminal's Ctrl-C โ leave the default
and translate those call sites by hand.
For operations that both platforms can do by different means, recipes gives
one portable call. wait_until_idle polls the cursor shape on Windows and
probes the Accessibility message timeout on macOS; your code says
wait_until_idle.
Two things this fixes about hand-written AutoIt code
Keystroke injection. Send interprets {}!+^#, so interpolating user or
database data straight into a send string lets that data execute as key
commands โ a password containing { is a live bug, not a theoretical one.
Here, Keys::text() escapes by default, and the raw form has to be asked for
by name.
auto.send_text?; // always literal, whatever is in it
auto.send?; // validated at compile time
Reading the screen through the clipboard. The usual idiom โ put a sentinel
value on the clipboard, send Ctrl+C, then check whether it changed โ races with
anything else touching the clipboard. recipes::read_screen_text waits on the
OS clipboard sequence number instead, which cannot race.
Setup
Windows
autoitx does not ship the AutoItX DLL โ see NOTICE. Download AutoIt
from autoitscript.com and
point the library at AutoItX3_x64.dll, which is searched for in this order:
AutoItBuilder::dll_path(..)$AUTOITX_DLL(full path to the file)$AUTOITX_DIR(directory containing it)- next to your executable
- the current working directory
- the registry (
HKLM\SOFTWARE\AutoIt v3\AutoIt) - whatever
LoadLibraryWfinds onPATH
If none hit, the error lists every path tried. 64-bit only: the DLL is x64, so 32-bit targets are unsupported.
Windows on ARM works โ build an x86-64 binary and let Windows emulate it.
Confirmed on an ARM64 Windows 11 VM running a full automation flow: the
emulated process is x64, so the x64 DLL loads into it normally. What does not
work is building for aarch64-pc-windows-msvc, since a native ARM64 process
cannot load an x64 DLL โ Au3::load reports that specifically rather than as a
generic "not found".
macOS
Two privacy permissions, requested only when first needed:
- Accessibility โ all window and control operations.
- Screen Recording โ pixel and capture operations only.
Grants are keyed to a binary's path and code signature. cargo build rewrites
the binary, and every cargo test run produces a fresh hash-suffixed one, so
macOS will re-prompt constantly during development. Grant the permission to your
terminal or IDE (children inherit it), or ad-hoc sign with
codesign -s - --force.
Developing on a Mac, shipping to Windows
The DLL is loaded at runtime, so there is no link-time Windows dependency, and
cargo check/clippy never invoke a linker. The whole Windows backend is
therefore type-checked, linted, and unit-tested from macOS:
A real .exe needs brew install mingw-w64 (or cargo-xwin for the MSVC ABI).
A Windows machine is needed only to observe real behaviour โ never to compile.
Examples
Eight, in autoitx/examples. Run any with
cargo run --example <name>.
diagnose |
Run this first when something is wrong. The DLL search order with a mark against each candidate; on macOS, which privacy grants this exact binary holds. Also published as a prebuilt binary on each release. |
list_windows |
What is on screen, so you can write a selector that matches it |
type_safely |
The four ways to build a key sequence, and when each is right |
read_field |
Reading a field through the clipboard without the race |
anchored_click |
Clicking without pinning the screen resolution |
wait_until_ready |
One intent, two mechanisms, one call |
portable_selectors |
One selector table for both platforms |
port_from_csharp |
The same flow in AutoItX.Dotnet and here, side by side |
Status
0.1.0 โ Windows complete, macOS complete for everything with a public API.
The platform matrix is the honest statement of what works
where; nothing in it is aspirational.
Verified against reality rather than only against tests: the Windows backend
was audited by calling every function against a live desktop and recording what
it actually returns on failure (that table is at the top of backend/dll.rs,
because the information exists nowhere else), and a full automation flow was
run on a Windows VM. The macOS backend has a live suite that drives real
applications, which is where four bugs a mock could never have caught turned
up โ see the 0.1.0 release notes.
Next: a native Windows backend behind the same API, which drops the DLL dependency entirely. Then Linux (X11 and AT-SPI), then capture and OCR.
FAQ
Do I need AutoIt installed?
On Windows, you need AutoItX3_x64.dll โ it ships with AutoIt and with the
standalone AutoItX download. It is not redistributed here: AutoIt is freeware
under a EULA, not an open-source licence, so shipping it inside a crate would
be a licensing problem rather than a convenience. On macOS nothing is needed;
the backend is native.
Is this affiliated with AutoIt?
No. AutoIt and AutoItX are products of AutoIt Consulting Ltd. This project is
independent and unendorsed โ see NOTICE.
Why is my macOS build not finding any windows?
Almost always the Accessibility grant. Without it, every accessibility call
fails in a way indistinguishable from "no such window". Run
cargo run --example diagnose.
Why does macOS keep re-asking for permission?
Grants are keyed to a binary's path and code signature, and every
cargo build writes a new binary. Grant the permission to your terminal or
IDE, which children inherit, or ad-hoc sign with codesign -s - --force.
Can I run this on Windows ARM?
Yes, as an x86-64 binary under emulation โ confirmed with a full flow on an
ARM64 Windows 11 VM. A native aarch64-pc-windows-msvc build cannot work,
because an ARM64 process cannot load an x64 DLL, and Au3::load says so
specifically.
Does it work on Linux?
Not yet. X11 via x11rb and AT-SPI via zbus are the plan.
Why Keys::text instead of just passing a string?
Because Send interprets {}!+^#. A price, a name, or a password containing
one of those becomes a key command. Keys::text escapes; keys! validates at
compile time; Keys::raw_unchecked exists but has to be named.
Is it thread-safe?
AutoIt is Send + Sync + Clone, and every call takes a lock. That is not
enough on its own โ two flows alternating activate-then-send still fight over
focus โ so ai.session() holds the lock across a run of calls and forwards the
whole API by Deref.
Support
If this saves you time, you can buy me a coffee. โ
License
MIT or Apache-2.0, at your option.
AutoIt and AutoItX are products of AutoIt Consulting Ltd. This project is not affiliated with, endorsed by, or sponsored by them, and the AutoItX3 DLL is not distributed with it. See NOTICE.