windows

package
v0.700.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 31, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package windows implements the Windows UI Automation (UIA) accessibility backend for the Pando Desktop Controller (internal/uiauto), Phase 4 of [pando/plans/desktop_controller_uiauto_plan.md]. It talks to UIA over COM using github.com/go-ole/go-ole plus hand-written vtable calls for the UIA-specific interfaces go-ole does not itself bind — no cgo.

Design notes (mirrors the structure of the Phase 2 Linux AT-SPI2 backend, internal/uiauto/platform/linux):

  • Every UIA element is identified, durably, by its RuntimeId (an []int32 UIA hands out per element that is stable across separate COM calls, unlike the IUIAutomationElement COM pointer itself, which is not safe to keep across calls/threads without care). RuntimeId is encoded to a string (runtimeid.go) and stashed in core.Element.Native.Data, mirroring how the Linux backend stashes its (busName, objectPath) accessibleRef.

  • The live COM pointers are NOT stored on core.Element (which must stay a plain, platform-independent value type). Instead the windows-only backend keeps a mutex-guarded handle table (encoded RuntimeId -> live *uiaElement) and resolves an incoming Element back to its COM pointer through that table. A ref whose RuntimeId is not (or no longer) in the table surfaces as STALE_REF/ELEMENT_NOT_FOUND, never a crash — see backend_windows.go's resolveElement.

  • All COM calls happen on a single dedicated OS thread: UIA client objects are apartment-threaded (COINIT_APARTMENTTHREADED) and are not safe to touch from arbitrary goroutines. worker_windows.go runs a goroutine that calls runtime.LockOSThread and CoInitializeEx once, then serves every COM request from a channel for the lifetime of the backend; Close stops the worker and calls CoUninitialize.

  • Traversal batches one cross-process hop per tree level: instead of one round trip per attribute per node (which is what an incremental, per-node AT-SPI-style walk would cost over COM's much higher per-call overhead), each level is fetched with a single IUIAutomationElement::FindAll(TreeScope_Children, TrueCondition, cacheRequest) call using a CacheRequest that pre-fetches Name/ControlType/AutomationId/ClassName/BoundingRectangle/IsEnabled/ IsOffscreen/HasKeyboardFocus for every child in that one call. This is UIA's analogue of the Linux backend's per-object property batching: the traversal is still selector-driven and prunes branches exactly the way findRec does in the Linux backend (see traverse.go, which is platform-independent and shared by both a fake test provider and the real windows one via the nodeProvider interface), but the "one round trip per object" cost of AT-SPI becomes "one round trip per tree level" here, since UIA lets a single call fetch a whole batch of cached children at once.

Files without a `//go:build windows` tag hold everything that does not touch COM (ControlType -> role mapping, RuntimeId encode/decode, the generic selector-driven traversal algorithm over a small nodeProvider interface, and best-effort HRESULT -> core.ErrorCode mapping) so they build and are unit tested on any platform, including this Linux dev machine. Everything that actually calls into COM lives in files tagged `//go:build windows` and is compile-verified only (GOOS=windows go build) — it has never been exercised against a real Windows UI Automation provider.

Index

Constants

View Source
const (
	ControlTypeButton       int32 = 50000
	ControlTypeCalendar     int32 = 50001
	ControlTypeCheckBox     int32 = 50002
	ControlTypeComboBox     int32 = 50003
	ControlTypeEdit         int32 = 50004
	ControlTypeHyperlink    int32 = 50005
	ControlTypeImage        int32 = 50006
	ControlTypeListItem     int32 = 50007
	ControlTypeList         int32 = 50008
	ControlTypeMenu         int32 = 50009
	ControlTypeMenuBar      int32 = 50010
	ControlTypeMenuItem     int32 = 50011
	ControlTypeProgressBar  int32 = 50012
	ControlTypeRadioButton  int32 = 50013
	ControlTypeScrollBar    int32 = 50014
	ControlTypeSlider       int32 = 50015
	ControlTypeSpinner      int32 = 50016
	ControlTypeStatusBar    int32 = 50017
	ControlTypeTab          int32 = 50018
	ControlTypeTabItem      int32 = 50019
	ControlTypeText         int32 = 50020
	ControlTypeToolBar      int32 = 50021
	ControlTypeToolTip      int32 = 50022
	ControlTypeTree         int32 = 50023
	ControlTypeTreeItem     int32 = 50024
	ControlTypeCustom       int32 = 50025
	ControlTypeGroup        int32 = 50026
	ControlTypeThumb        int32 = 50027
	ControlTypeDataGrid     int32 = 50028
	ControlTypeDataItem     int32 = 50029
	ControlTypeDocument     int32 = 50030
	ControlTypeSplitButton  int32 = 50031
	ControlTypeWindow       int32 = 50032
	ControlTypePane         int32 = 50033
	ControlTypeHeader       int32 = 50034
	ControlTypeHeaderItem   int32 = 50035
	ControlTypeTable        int32 = 50036
	ControlTypeTitleBar     int32 = 50037
	ControlTypeSeparator    int32 = 50038
	ControlTypeSemanticZoom int32 = 50039
)

UIA_*ControlTypeId constants, from UIAutomationClient.h / UIAutomationCore.idl. These are the raw numeric ids the CacheRequest/GetCachedPropertyValue call for UIA_ControlTypePropertyId returns.

Variables

This section is empty.

Functions

func ControlTypeName

func ControlTypeName(id int32) string

ControlTypeName returns the friendly, lowercased UIA ControlType name for raw id, or "" when id is not one of the known UIA_*ControlTypeId values.

func DecodeRuntimeID

func DecodeRuntimeID(s string) ([]int32, error)

DecodeRuntimeID parses a string produced by EncodeRuntimeID back into a RuntimeId. It returns an INVALID_ARGS core.DesktopError on malformed input.

func EncodeRuntimeID

func EncodeRuntimeID(id []int32) string

EncodeRuntimeID renders a UIA RuntimeId ([]int32, as returned by IUIAutomationElement::GetRuntimeId) as a stable, comparable string, used both as the durable element identity stashed in core.Element.Native.Data (see element.go's nativeRuntimeIDKey) and as the backend's handle-table key. RuntimeId segments are UIA-internal integers (frequently including a negative "synthetic" leading segment); they are joined by '.', each formatted as a signed decimal so the encoding round-trips exactly.

func RoleForControlType

func RoleForControlType(id int32) core.Role

RoleForControlType normalizes a raw UIA ControlType id to the canonical core.Role vocabulary via core.NormalizeRole("uia", ...), so it stays in sync with the shared per-platform role table in internal/uiauto/core.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL