appium-desktop-driver
v2.1.0
Published
Appium driver for Windows
Maintainers
Readme
Appium Desktop Driver
Appium Desktop Driver is a Windows UI automation driver for Appium 3. It automates UWP, WinForms, WPF, Win32, Java Swing, and Internet Explorer applications.
Key advantages over WinAppDriver:
- Faster XPath evaluation against the live UIA tree
- RawView element support (elements hidden from ContentView/ControlView)
- Reliable text input independent of the active keyboard layout
- Java Swing / AWT automation via injected JVM agent (no JAB required)
- WebView2, Chrome, and Edge embedded content via CDP
- Internet Explorer 11 automation via built-in IE DOM Bridge (no IEDriverServer required)
- Built-in screen recording, clipboard API, and vision-based finding
- MCP server for direct use with AI coding agents
Installation
appium driver install --source=npm appium-desktop-driverRequires Appium 3 and Windows 10 or later.
Capabilities
All capabilities use the appium: prefix in W3C format
(except platformName).
| Capability | Type | Description |
| --- | --- | --- |
| platformName | string | Required. Must be Windows |
| appium:automationName | string | Required. Must be DesktopDriver |
| appium:app | string | Exe path, UWP AUMID, or Root for the desktop |
| appium:appTopLevelWindow | string | Hex/decimal handle to attach to existing window |
| appium:appArguments | string | CLI arguments for the launched process |
| appium:appWorkingDir | string | Working directory for the process |
| appium:appEnvironment | object | Env vars injected for the session lifetime |
| appium:ms:waitForAppLaunch | number | Seconds (<=120) or ms (>120) to wait for window |
| appium:shouldCloseApp | boolean | Close the window at session end. Default: true |
| appium:prerun | object | { script } or { command } — PowerShell before start |
| appium:postrun | object | { script } or { command } — PowerShell after end |
| appium:isolatedScriptExecution | boolean | Isolated PS for pre/postrun. Default: false |
| appium:smoothPointerMove | string | CSS easing for mouse (e.g. ease-in) |
| appium:delayBeforeClick | number | Milliseconds before each click |
| appium:delayAfterClick | number | Milliseconds after each click |
| appium:javaSwing | boolean | Enable JVM agent for Java Swing/AWT apps |
| appium:jdkPath | string | Path to JDK root (e.g. C:\Program Files\Java\jdk1.8.0_xxx). Overrides JAVA_HOME for agent injection. Required only for Path B/C. |
| appium:webviewEnabled | boolean | Enable WebView2 / Chrome / Edge CDP |
| appium:webviewDevtoolsPort | number | CDP port (auto-selected when omitted) |
| appium:chromedriverExecutablePath | string | Local Chromedriver binary |
| appium:edgedriverExecutablePath | string | Local msedgedriver binary |
| appium:chromedriverCdnUrl | string | Custom CDN for Chromedriver downloads |
| appium:edgedriverCdnUrl | string | Custom CDN for EdgeDriver downloads |
| appium:ffmpegExecutablePath | string | Local ffmpeg binary for screen recording |
| appium:ms:forcequit | boolean | Force-kill process on session close |
| appium:ms:experimental-webdriver | boolean | Experimental WebDriver features |
| appium:logFile | string | Path to write session logs |
| appium:ieDriverServerPath | string | Deprecated. No longer used; IE is automated via the built-in DOM Bridge. |
Examples
Java Swing via JVM agent
The driver automates Java Swing/AWT applications by injecting a lightweight
JVM agent — no jabswitch, no JAB DLL required.
Three injection paths are available:
- Path A — driver launches the JVM (
appium:app+appium:javaSwing: true). NoJAVA_HOMEneeded. - Path B — attach to an already-running JVM at session time (
appium:appTopLevelWindow+appium:javaSwing: true). RequiresJAVA_HOMEorappium:jdkPathpointing to a JDK. - Path C — inject agent mid-session via
windows: attachJavaSwing. Start any session, switch to the Java window, then call the command. RequiresJAVA_HOMEorappium:jdkPath(or passjdkPathas a script argument).
See API.md — Java Swing Automation for full examples, JAVA_HOME setup, and supported XPath attributes.
Desktop root: click an icon and switch windows
import { remote } from 'webdriverio';
const driver = await remote({
hostname: '127.0.0.1',
port: 4723,
path: '/',
capabilities: {
platformName: 'Windows',
'appium:automationName': 'DesktopDriver',
'appium:app': 'Root', // attach to the Windows desktop shell
},
});
// Double-click a desktop shortcut by its accessible name
const icon = await driver.$('~Notepad');
await driver.executeScript('windows: click', [{
elementId: icon.elementId,
times: 2,
interClickDelayMs: 100,
}]);
// Give the app time to open, then switch to the new window by handle...
await driver.pause(2000);
const allHandles = await driver.getWindowHandles();
await driver.switchToWindow(allHandles[allHandles.length - 1]);
// ...or switch by title (partial, case-insensitive)
await driver.executeScript('windows: switchToWindowByTitle', [{ title: 'Notepad' }]);
// To include untitled windows (getWindowHandles omits them), use getWindows instead.
// It returns { handle, title, className } for every visible top-level window.
const windows = await driver.executeScript('windows: getWindows', []);
const popup = windows.find(w => w.className === 'SunAwtDialog' && w.title === '');
if (popup) await driver.switchToWindow(popup.handle);
const titleBar = await driver.$('//TitleBar');
console.log(await titleBar.getText());
// Switch back to the desktop root at any time
await driver.switchToWindow('root');
await driver.deleteSession();Windows Calculator: invoke pattern
windows: invoke fires the UIA InvokePattern. Prefer it over .click()
for controls that do not respond reliably to pointer events.
import { remote } from 'webdriverio';
const driver = await remote({
hostname: '127.0.0.1',
port: 4723,
path: '/',
capabilities: {
platformName: 'Windows',
'appium:automationName': 'DesktopDriver',
'appium:app': 'Microsoft.WindowsCalculator_8wekyb3d8bbwe!App',
},
});
async function invoke(el) {
await driver.executeScript('windows: invoke', [el]);
}
// 5 + 3 =
await invoke(await driver.$('~num5Button'));
await invoke(await driver.$('~plusButton'));
await invoke(await driver.$('~num3Button'));
await invoke(await driver.$('~equalButton'));
const display = await driver.$('~CalculatorResults');
console.log(await display.getText()); // "Display is 8"
await driver.deleteSession();Ctrl+drag via W3C Actions
Actions are processed tick by tick. All actions at the same tick index
across input sources run simultaneously. Ticks advance sequentially.
Use pause entries to keep sources aligned when mixing keyboard and
pointer inputs.
import { remote } from 'webdriverio';
const driver = await remote({
hostname: '127.0.0.1',
port: 4723,
path: '/',
capabilities: {
platformName: 'Windows',
'appium:automationName': 'DesktopDriver',
'appium:app': 'C:\\MyApp\\app.exe',
},
});
const source = await driver.$('~dragSource');
const target = await driver.$('~dropTarget');
await driver.performActions([
{
type: 'key',
id: 'keyboard',
actions: [
{ type: 'pause' }, // tick 0 — wait for mouse move
{ type: 'keyDown', value: '\uE009' }, // tick 1 — Ctrl key (W3C key value)
{ type: 'pause' }, // tick 2 — hold
{ type: 'pause' }, // tick 3 — hold during drag
{ type: 'keyUp', value: '\uE009' }, // tick 4 — Ctrl up
],
},
{
type: 'pointer',
id: 'mouse',
parameters: { pointerType: 'mouse' },
actions: [
{ type: 'pointerMove', duration: 0, origin: source }, // tick 0
{ type: 'pointerDown', button: 0 }, // tick 1
{ type: 'pause', duration: 300 }, // tick 2
{ type: 'pointerMove', duration: 600, origin: target }, // tick 3
{ type: 'pointerUp', button: 0 }, // tick 4
],
},
]);
await driver.releaseActions();
await driver.deleteSession();Internet Explorer (IE DOM Bridge)
The driver includes a built-in IE DOM Bridge — a 32-bit C# process
(IEBridge.exe) that attaches to a running IE 11 window via the
WM_HTML_GETOBJECT message, retrieves IHTMLDocument2 through COM, and
routes all element commands over stdio JSON. No IEDriverServer and no
WebDriver protocol proxy are involved.
IE mode activates automatically whenever a session window has the
IEFrame window class. This happens when:
appium:appis set to the IE executable and the driver launches it, orappium:appTopLevelWindowpoints to an existing IE window handle, orwindows: switchToWindow/windows: switchToWindowByTitletargets an IE window.
Prerequisites:
- Internet Explorer 11 at
C:\Program Files\Internet Explorer\iexplore.exe - Internet Options → Security: Protected Mode disabled on all four zones
- Internet Options → Advanced: Enhanced Protected Mode disabled
- View → Zoom: set to exactly 100%
Supported locator strategies: css selector, id, xpath
Supported XPath patterns: //tag, //tag[@attr="val"],
//tag[contains(text(),"...")], //tag[contains(@attr,"...")], //tag[@attr]
Command support:
| Command | Status | Notes |
|---|---|---|
| findElement / findElements | ✅ | css selector, id, xpath |
| getTitle | ✅ | |
| getUrl | ✅ | |
| getPageSource | ✅ | |
| url() | ✅ | navigate to URL |
| getWindowHandle | ✅ | returns HWND hex string |
| getText(el) | ✅ | |
| getAttribute(el, name) | ✅ | |
| clear(el) | ✅ | input / textarea only |
| setValue(el, value) | ✅ | input / textarea only |
| click(el) | ✅ / ⚠️ | works for buttons, links; broken for checkboxes and radios |
| isDisplayed(el) | ✅ | |
| isEnabled(el) | ⚠️ | unreliable for elements without an id attribute |
| isSelected(el) | ❌ | always returns false |
| executeScript(script, args) | ✅ | runs JS in the IE tab |
| switchToFrame(id) | ✅ | by index, element, or null for default content |
| switchToParentFrame() | ✅ | returns to default content (single-level only) |
| getElementRect(el) | ❌ | not implemented |
| getElementScreenshot(el) | ❌ | not implemented |
| windows: extension commands | ❌ | target the UIA tree, not the HTML DOM |
Launch IE and automate it
import { remote } from 'webdriverio';
const driver = await remote({
hostname: '127.0.0.1',
port: 4723,
path: '/',
capabilities: {
platformName: 'Windows',
'appium:automationName': 'DesktopDriver',
'appium:app': 'C:\\Program Files\\Internet Explorer\\iexplore.exe',
'appium:appArguments': 'https://example.com',
'appium:shouldCloseApp': true,
},
});
await driver.pause(3000); // wait for IE to load the page
await driver.setTimeout({ implicit: 5000 });
const title = await driver.getTitle();
console.log(title); // "Example Domain"
const h1 = await driver.$('h1');
console.log(await h1.getText());
await driver.deleteSession();Attach to an already-open IE window
import { remote } from 'webdriverio';
// hwnd is the decimal or hex HWND string of the IEFrame window
const driver = await remote({
hostname: '127.0.0.1',
port: 4723,
path: '/',
capabilities: {
platformName: 'Windows',
'appium:automationName': 'DesktopDriver',
'appium:appTopLevelWindow': '0x001A0B2C',
'appium:shouldCloseApp': false,
},
});
const url = await driver.getUrl();
console.log(url);
await driver.deleteSession();Switch into an iframe
// By 0-based index
await driver.switchToFrame(0);
// By element
const frameEl = await driver.$('iframe#content');
await driver.switchToFrame(frameEl);
// Element finds are now scoped to the frame's document
const h1 = await driver.$('h1');
console.log(await h1.getText());
// Back to the top-level document
await driver.switchToFrame(null);Only one level of nesting is tracked — switching into a frame while
already inside one replaces the current frame context rather than
stacking. switchToParentFrame() always returns to default content.
Switch between IE and UIA within the same session
Start a Root session and navigate between windows freely. The driver
switches between IE Bridge mode and UIA mode automatically based on whether
the target window is an IEFrame.
import { remote } from 'webdriverio';
const driver = await remote({
hostname: '127.0.0.1',
port: 4723,
path: '/',
capabilities: {
platformName: 'Windows',
'appium:automationName': 'DesktopDriver',
'appium:app': 'Root',
},
});
// Focus the IE window — IE Bridge activates automatically
await driver.executeScript('windows: switchToWindowByTitle', [{ title: 'Internet Explorer' }]);
const h1 = await driver.$('h1');
console.log(await h1.getText());
// Switch to a non-IE window — UIA resumes automatically
await driver.executeScript('windows: switchToWindowByTitle', [{ title: 'Notepad' }]);
await driver.deleteSession();API Reference
Full reference for all locator strategies, windows: extension
commands, WebView/CDP automation, screen recording, and vision-based
finding: API.md.
Development
npm install
npm run lint
npm run build
npm run testUses Comment Tagged Templates for PowerShell/C syntax highlighting in VS Code.
