
Product
Socket Threat Intel Now Shows Which Malware Campaigns Were Blocked or Detected
Socket Threat Intel now shows which malware campaigns were blocked or detected in your organization, with links to scans and Firewall events.
appium-flaui-native-driver
Advanced tools
Appium 3 Windows driver backed by a compiled C# FlaUI sidecar (UIA3/UIA2 + MSAA legacy).
A custom Appium 3 driver for Windows desktop UI automation, built directly on FlaUI (UI Automation / UIA3) through a compiled, self-contained C#/.NET sidecar. It automates Universal Windows Platform (UWP), Windows Forms (WinForms), Windows Presentation Foundation (WPF), and Classic Windows (Win32) apps on Windows 10 and later.
Designed W3C-first and stability-first, it offers:
SendInput mouse/keyboard), not PowerShell string-scraping. This unlocks the full UIA surface and is
faster and more reliable for traditional desktop apps.Value.Value,
Toggle.ToggleState, …), LegacyIAccessible.* (MSAA) with shorthand aliases, Is*PatternAvailable
flags, and a single "all" dump — matching what inspect.exe shows.@*, ControlType aliases, correct positional
semantics, with structural parts pushed down to native UIA conditions.windows: <command>..exe. No .NET install, no
Developer Mode, no separate WinAppDriver server.Status: BETA (
appium-flaui-native-driver@beta; win-x64, win-x86, win-arm64). The command surface below is implemented and verified end-to-end on real Windows machines. Canonical, always-current API/status:docs/03-reference/appium-api.md.
The driver is built for Appium 3. Install it from npm:
appium driver install --source=npm appium-flaui-native-driver@beta
Or from a local checkout/tarball containing prebuilt/<arch>/FlaUiSidecar.exe:
appium driver install --source=local /path/to/appium-flaui-native-driver
appium@^3.0.0), Node ≥ 20.19, npm ≥ 10.[!NOTE] Package size. Each sidecar embeds the .NET runtime, so the prebuilt binary is ~180 MB (x64). This is the deliberate cost of zero end-user setup and offline reliability (see
docs/04-design/decisions.mdADR-013).
The driver supports the following capabilities, grouped by prefix in order — appium: (session & app)
→ ms: (Windows-compat) → flaui: (engine tuning):
| Capability | Description | Default | Example |
|---|---|---|---|
platformName | Must be Windows (case-insensitive). | (Required) | Windows |
appium:automationName | Must be FlaUINative (case-insensitive). | (Required) | FlaUINative |
appium:app | Path to the executable to launch (or Root for the whole desktop). The driver owns a launched app and closes it on session end. | (None) | C:\Windows\System32\notepad.exe |
appium:appTopLevelWindow | Attach to an existing window by hex HWND. | (None) | 0x40344 |
appium:appName | Attach to a running app whose window TITLE matches this regex (case-insensitive). | (None) | .*Notepad |
appium:processName | Attach to a running app by exact exe name (case-insensitive, .exe optional). | (None) | notepad.exe |
appium:createSessionTimeout | Poll budget (ms) to wait for an attach target to appear before failing. | 60000 | 30000 |
appium:appArguments | Arguments passed to the app on launch. | (None) | --debug |
appium:appWorkingDir | Working directory for the launched app. | (None) | C:\Temp |
appium:shouldCloseApp | On session end, close the app started via app (incl. a single-instance app app fell back to attaching). Apps reached via appTopLevelWindow/appName/processName are never closed. | true | false |
appium:newCommandTimeout | Standard Appium cap (provided by base-driver, not redeclared here): seconds the server waits for the next command before auto-ending an idle session. 0 = never. Also drives the default for flaui:idleTimeout (+120s). | 60 | 600 |
appium:prerun | { script } — PowerShell to run before the session starts (requires the power_shell feature). | (None) | {script: '...'} |
appium:postrun | { script } — PowerShell to run on session teardown (requires the power_shell feature). | (None) | {script: '...'} |
appium:typeDelay | Per-character delay (ms) for send_keys / windows: keys typing. | 0 | 100 |
appium:includeContextElementInSearch | Find-from-element searches include the context element itself. | true | false |
appium:convertAbsoluteXPathToRelativeFromElement | When true, a find-from-element XPath starting with // is rewritten to .// (treat a leading // as "from this context element"). | false | true |
ms:waitForAppLaunch | Seconds to wait after launch for the app's window to appear. | 0 | 3 |
ms:forcequit | Kill the app process (instead of a graceful close) on session deletion. | false | true |
flaui:backend | UI Automation API: uia3 = modern COM UIA (full pattern set, recommended); uia2 = managed wrapper — try only if a legacy control misbehaves under UIA3. | uia3 | uia2 |
flaui:operationTimeout | Main per-op watchdog (ms) — every UIA op must finish within this or it's aborted as timeout and the worker recycled (the core anti-hang bound). Raise for legitimately slow ops. | 30000 | 30000 |
flaui:connectionTimeout / flaui:transactionTimeout | Low-level UIA COM timeouts (ms), kept below the watchdog so a stuck COM call bails on its own first. Rarely tuned. | min(20000, opTimeout−5000) | 5000 |
flaui:elementTableMax | Size of the sidecar's RuntimeId→element cache (FIFO). Bump for huge trees / very long sessions. | (sidecar default) | 5000 |
flaui:idleTimeout | Sidecar self-exits after this idle time (orphan guard). Defaults to newCommandTimeout + 120s, so setting newCommandTimeout alone suffices; newCommandTimeout: 0 disables it. | newCommandTimeout + 120000 | 600000 |
flaui:autoRecycle | Opt-in. true = silently recycle the sidecar on a transport failure. false (default) = a dead sidecar fails the session (invalid session id) so you start a fresh one. | false | true |
[!NOTE]
- A target is required — exactly one of
app/appTopLevelWindow/appName/processName. When several are given, the attach target resolvesappTopLevelWindow→processName→appName→app→Root(exact identifiers before fuzzy patterns).- App lifecycle —
applaunches the app and closes it on session end (the driver owns it; unlessshouldCloseApp: false, andms:forcequitkills instead of closing).appTopLevelWindow/appName/processNameattach to a running app and leave it running.- PowerShell timeout is per-call, not a capability:
execute('powershell', [{script\|command, timeout?}])(default60000ms). PowerShell runs out-of-scheduler, soflaui:operationTimeoutdoes not bound it.flaui:caps tune the sidecar — most sessions never set any. The timeout ones form the nested anti-hang chain (UIA < watchdog < RPC < hard-deadline), seedocs/02-architecture/stability.md.- Prefixes:
ms:= WinAppDriver-compatible names ·flaui:= engine tuning ·appium:= standard/session.
from appium import webdriver
from appium.options.common import AppiumOptions
options = AppiumOptions()
options.set_capability('platformName', 'Windows')
options.set_capability('appium:automationName', 'FlaUINative')
options.set_capability('appium:app', 'C:\\Windows\\System32\\notepad.exe')
driver = webdriver.Remote('http://127.0.0.1:4723', options=options)
try:
el = driver.find_element('name', 'Text Editor')
el.send_keys('Hello from FlaUI')
finally:
driver.quit()
const { remote } = require('webdriverio');
const driver = await remote({
hostname: '127.0.0.1', port: 4723,
capabilities: {
platformName: 'Windows',
'appium:automationName': 'FlaUINative',
'appium:app': 'C:\\Windows\\System32\\notepad.exe',
// attach instead: 'appium:appTopLevelWindow': '0x40344'
// whole desktop: 'appium:app': 'Root'
},
});
The driver supports the standard Appium/WinAppDriver location strategies, mapped to UIA attributes as shown
in inspect.exe:
| Strategy | Description | Example |
|---|---|---|
accessibility id | The AutomationId attribute. | CalculatorResults |
class name | The ClassName attribute. | TextBlock |
id | Alias of accessibility id (matches AutomationId). The dotted RuntimeId (e.g. 42.333896.3.1) is what the driver returns as each element's identity. | CalculatorResults |
name | The Name attribute. | Calculator |
tag name | The ControlType short name (e.g. Button, Text). | Text |
xpath | Full XPath 1.0 over any inspect-visible attribute; structural parts run as native UIA conditions. | (//Button)[2] |
Finding from an element (scoped/descendant search) is supported.
Retrieve element details with get_attribute / get_property. Resolution is done natively in FlaUI:
AutomationId, Name, ClassName, RuntimeId, ControlType,
IsEnabled, IsOffscreen, BoundingRectangle, ProviderDescription, IsDialog, and any other direct
UIA property by name.Pattern.Property resolved via FlaUI's pattern objects:
Value.Value, Value.IsReadOnlyToggle.ToggleStateExpandCollapse.ExpandCollapseStateRangeValue.Value / .Minimum / .Maximum / .SmallChangeWindow.CanMaximize / .CanMinimize / .IsModal / .WindowVisualStateSelection.CanSelectMultiple, Grid.RowCount / .ColumnCount, and other patterns.Is<Pattern>PatternAvailable (e.g. IsInvokePatternAvailable,
IsTogglePatternAvailable), derived from FlaUI's pattern library for the current framework.LegacyIAccessible.Name, LegacyIAccessible.Role, LegacyIAccessible.Value,
.State, .Description, .Help, .KeyboardShortcut, .DefaultAction, .ChildId.legacyname, legacyvalue, legacyrole, legacystate, legacydescription,
legacyhelp, legacykeyboardshortcut, legacydefaultaction, legacychildid."all": returns every resolvable attribute as a JSON string (per W3C, single
attributes are returned as strings; the "all" dump is JSON). The same data is available as a native
object via execute('windows: getAttributes', el).Text — a best-effort text value (TextPattern → ValuePattern → Name → LegacyIAccessible).# A single pattern/legacy attribute (returned as a string)
value = element.get_attribute('Value.Value')
legacy_name = element.get_attribute('LegacyIAccessible.Name')
can_invoke = element.get_attribute('IsInvokePatternAvailable')
# Everything at once, as a JSON string
all_attributes = element.get_attribute('all')
[!NOTE] Page source is available via the standard
driver.page_sourceand viaexecute('windows: getPageSource', { elementId })for a subtree.
Unlike in-process drivers, every UIA call runs inside a separate sidecar process on a dedicated, cancellable
STA worker. The timeouts are nested so the gentlest layer fires first
(UIA ≈20s < watchdog 30s < RPC 35s < hard-deadline 40s; see docs/02-architecture/stability.md):
ConnectionTimeout / TransactionTimeout (flaui:connectionTimeout / flaui:transactionTimeout) —
defaulted below the watchdog so a frozen COM call self-aborts before a thread needs poisoning.flaui:operationTimeout).invalid session id (kill the wedged
process, tell the client to create a new session). Opt into silent recycle/re-attach with
flaui:autoRecycle: true.Orphan guard: the sidecar self-exits on parent (Appium) death (stdin-EOF heartbeat) and after
flaui:idleTimeout (default 5 min) with no command — so a forgotten session can't leave a ~180 MB process behind.
The net effect: a frozen target app fails that command in seconds (not a 60 s hang), the session and Appium server keep working, and a truly dead backend ends the session cleanly instead of hanging or silently lying.
Run PowerShell directly from a test. Requires the power_shell insecure feature (see
Security). PowerShell is an opt-in convenience here — it is not the
backend (the backend is FlaUI).
# Execute a command string
driver.execute_script('powershell', {'command': 'Get-Process Notepad'})
# Execute a script string
driver.execute_script('powershell', {'script': '$p = Get-Process Notepad; $p.Kill()'})
# Set the timeout for this call (ms); defaults to 60000 if omitted
driver.execute_script('powershell', {'command': 'Start-Sleep 5; "done"', 'timeout': 10000})
All extensions are invoked via driver.execute_script("windows: <methodName>", args), where args is a
single object of named parameters (this driver uses Appium 3's executeMethodMap).
{ 'elementId': element.id }. As a convenience you may also pass the
WebElement itself as the sole argument (driver.execute_script('windows: invoke', element)) — it
serializes to the W3C element key, which the command accepts.{ 'elementId': element.id, 'value': 'New Value' }. (Unlike
some Windows drivers, a trailing positional value argument is not read — put it in the object.)windows: clickA single (or repeated) mouse click via real SendInput.
| Name | Type | Required | Description | Default |
|---|---|---|---|---|
elementId | string | no | Element to click. With no x/y, clicks the element's clickable point (falls back to rect center, with scrollIntoView). With x/y, treats them as an offset from the element's top-left. | — |
x, y | number | no | Click coordinates. Without elementId, absolute screen coordinates; with elementId, an offset. If both are omitted with no element, uses the current cursor position. | cursor pos |
button | string | no | left, middle, right. | left |
times | number | no | Number of clicks (e.g. 2 for double-click). | 1 |
modifierKeys | string[] | string | no | Keys held during the click: Shift, Ctrl, Alt, Win. | — |
durationMs | number | no | Hold time between press and release. | 0 |
interClickDelayMs | number | no | Pause between clicks when times > 1. | 100 |
driver.execute_script('windows: click', {'elementId': element.id, 'button': 'right', 'times': 2})
driver.execute_script('windows: click', {'x': 500, 'y': 300})
windows: clickAndDragA click-and-drag gesture.
| Name | Type | Required | Description | Default |
|---|---|---|---|---|
startElementId / startX / startY | string / number | no | Drag start (element center, element-relative offset, or absolute / cursor). | cursor pos |
endElementId / endX / endY | string / number | no | Drag end (same semantics as start). | cursor pos |
button | string | no | left, middle, right. | left |
durationMs | number | no | Drag duration; 0 does an instant drag. | 0 |
modifierKeys | string[] | string | no | Keys held during the drag. | — |
driver.execute_script('windows: clickAndDrag', {
'startElementId': src.id, 'endElementId': dst.id, 'durationMs': 2000})
windows: hoverMoves the cursor from a start point to an end point.
| Name | Type | Required | Description | Default |
|---|---|---|---|---|
elementId / x / y | string / number | no | Target point (element center, element-relative offset, or absolute / cursor). | cursor pos |
modifierKeys | string[] | string | no | Keys held during the move. | — |
durationMs | number | no | Move duration; 0 moves instantly. | 0 |
windows: scrollA mouse-wheel scroll gesture.
| Name | Type | Required | Description | Default |
|---|---|---|---|---|
elementId / x / y | string / number | no | Scroll anchor point. | cursor pos |
deltaX | number | no | Horizontal wheel movement (positive = right). | 0 |
deltaY | number | no | Vertical wheel movement (positive = forward/away). | 0 |
amount | number | no | Wheel-notch count. With deltaX/deltaY it multiplies them; on its own (no deltas) it scrolls vertically by that many notches (positive = forward/away). | 1 |
modifierKeys | string[] | string | no | Keys held during the scroll. | — |
driver.execute_script('windows: scroll', {'elementId': element.id, 'deltaY': -120})
windows: keysCustomized keyboard input via real SendInput.
| Name | Type | Required | Description |
|---|---|---|---|
actions | KeyAction[] | KeyAction | yes | One or more key actions. |
KeyAction — { text } to type a string, { virtualKeyCode, down } for explicit key down/up, and
{ pause } to insert a delay (ms) between actions.
driver.execute_script('windows: keys', {'actions': [
{'virtualKeyCode': 0x10, 'down': True}, # Shift down
{'text': 'Hello World'},
{'virtualKeyCode': 0x10, 'down': False}, # Shift up
]})
windows: typeDelaySets the per-character delay (ms) for the session.
| Name | Type | Required | Description |
|---|---|---|---|
delay | number | yes | Delay in milliseconds. |
driver.execute_script('windows: typeDelay', {'delay': 100})
Each targets an element ({ elementId }, or the element passed as the sole argument) and calls the
corresponding UIA pattern in FlaUI.
| Command | UIA Pattern | Description |
|---|---|---|
windows: invoke | Invoke | Activate the element (button press, link, etc.). |
windows: setValue | Value | Replace the element's value: { elementId, value }. Falls back to select-all + delete + keyboard typing for controls without ValuePattern. (The W3C send_keys command instead appends into existing content, matching Selenium/WinAppDriver; use clear() then send_keys to replace from a test.) |
windows: getValue | Value | Read the element's value. |
windows: toggle | Toggle | Toggle a checkbox/toggle control. |
windows: expand | ExpandCollapse | Expand the element. |
windows: collapse | ExpandCollapse | Collapse the element. |
windows: scrollIntoView | ScrollItem | Scroll the element into view. |
windows: setFocus | (UIA SetFocus) | Set keyboard focus to the element. |
windows: getAttributes | — | Return the full attribute set as a native object (object form of get_attribute('all')). |
driver.execute_script('windows: invoke', element) # element as sole arg
driver.execute_script('windows: setValue', {'elementId': element.id, 'value': 'New Value'})
driver.execute_script('windows: toggle', {'elementId': element.id})
For controls supporting the Selection / SelectionItem patterns. Each targets an element ({ elementId }).
| Command | Description |
|---|---|
windows: select | Select the element. |
windows: addToSelection | Add the element to the current selection. |
windows: removeFromSelection | Remove the element from the current selection. |
windows: isMultiple | Whether the container supports multiple selection. |
windows: selectedItem | The currently selected item. |
windows: allSelectedItems | All currently selected items. |
Window-state commands (WindowPattern) target the window element ({ elementId }):
| Command | Description |
|---|---|
windows: maximize | Maximize the window. |
windows: minimize | Minimize the window. |
windows: restore | Restore the window to normal state. |
windows: close | Close the window. |
Bringing a window to the front:
windows: setWindowForegroundBrings a window to the foreground using an escalating strategy — basic foreground first, then stronger
methods (topmost toggle, minimize/restore) if needed. This is the robust counterpart to a plain setFocus.
| Name | Type | Required | Description |
|---|---|---|---|
elementId | string | no | The window element to bring forward. |
windows: clickalready performs a basic bring-on-top before clicking. UsesetWindowForegroundwhen a window needs stronger foregrounding (e.g. it stays behind another window).
windows: setProcessForegroundBrings the main window of a named process to the foreground.
| Name | Type | Required | Description |
|---|---|---|---|
process | string | yes | Process name, e.g. notepad.exe. |
windows: setClipboard / windows: getClipboardGet or set the Windows clipboard as plaintext or a PNG image.
| Name | Type | Required | Description | Default |
|---|---|---|---|---|
b64Content (set only) | string | yes | Base64-encoded content to set. | — |
contentType | string | no | plaintext or image. | plaintext |
driver.execute_script('windows: setClipboard', {'b64Content': 'SGVsbG8=', 'contentType': 'plaintext'})
text = driver.execute_script('windows: getClipboard', {'contentType': 'plaintext'})
windows: launchApp / windows: closeAppLaunch or close the app under test within the current session.
windows: getPageSourceReturns the UIA page source. With { elementId }, returns the subtree rooted at that element.
Standard Appium file commands (insecure features): execute('pushFile', {...}),
execute('pullFile', {...}), execute('pullFolder', {...}).
The standard W3C Actions API (pointer + key) is fully supported via driver.perform_actions(...) /
driver.release_actions().
powershell (incl. appium:prerun), pullFile/pullFolder, and pushFile are Appium insecure
features. This driver targets isolated VM environments and never trades a feature for security
(ADR-015).
Recommended (dev/test, isolated VM) — enable everything with one flag:
appium --relaxed-security
Locked-down alternative — enable only specific features (the --allow-insecure CLI flag does not parse
multiple scoped features reliably, so use a config file):
// appium-config.json
{ "server": { "allow-insecure": [
"flauinative:power_shell", "flauinative:pull_file", "flauinative:push_file"
] } }
appium --config appium-config.json
Trust boundary (no sandbox): once enabled,
power_shellruns arbitrary code and the file commands read/write any path — by design. Enable only when the server and all clients are trusted.
npm install # install dependencies
npm run build # transpile TypeScript -> JS
npm run test:unit # cross-platform unit tests (no Windows needed)
Rebuilding the sidecar requires the .NET 10 SDK (end users do not — the prebuilt exe is self-contained):
dotnet publish sidecar -r win-x64 --self-contained -p:PublishSingleFile=true -p:EnableCompressionInSingleFile=true
Start at docs/README.md — a top-down reading map (overview → architecture → reference).
| Doc | What |
|---|---|
docs/01-overview/ | Introduction & quickstart |
docs/02-architecture/ | Overview (C4), request-flow, stability (timeouts/anti-hang), backend-flaui, sidecar-internals |
docs/03-reference/appium-api.md | Canonical API reference — W3C + windows: commands & status |
docs/03-reference/rpc-protocol.md | The TS↔C# JSON seam (ops, envelope, errors) |
docs/03-reference/capabilities.md · locators-xpath.md | Capabilities; locators & XPath |
docs/04-design/ | ADRs, known-issues, security |
docs/05-operations/clean-reinstall.md | Fast clean wipe-and-reinstall on Windows test boxes |
docs/internal/ | Maintainer notes (changelog, audit, subagents) |
-windows uiautomation raw-condition locator, rawView page source, active-element / getDeviceTime, and
run-verification of the win-arm64 prebuilt on real ARM hardware. Screen recording is out of scope
(ADR-012). See docs/04-design/known-issues.md.
FAQs
Appium 3 Windows driver backed by a compiled C# FlaUI sidecar (UIA3/UIA2 + MSAA legacy).
The npm package appium-flaui-native-driver receives a total of 8 weekly downloads. As such, appium-flaui-native-driver popularity was classified as not popular.
We found that appium-flaui-native-driver demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Product
Socket Threat Intel now shows which malware campaigns were blocked or detected in your organization, with links to scans and Firewall events.

Security News
Fixes for eMudhra and Brotli are out. Getting them everywhere they’re needed means tracking down bundled libraries, runtimes, and certificate stores.

Threat Research
/Security News
Socket uncovered secret-stealing workflows that ran in LiteLLM’s GitHub repository 11 days before the supply chain attack on PyPI and Docker.