Skip to the content.

Troubleshooting Guide

Common issues and solutions discovered during real-world Windows desktop automation with WinJavaDriver.

VB6 / MSAA Controls

Text Input on Thunder* Controls

VB6 controls (ThunderRT6TextBox, ThunderRT6ComboBox, etc.) don’t respond to standard UIA input methods. WinJavaDriver detects Thunder* class names automatically and uses Win32 messages instead.

Key behavior: Win32 text input replaces the entire text content. If you need to append text, read the current value first.

// This works — server handles Thunder* routing internally
element.sendKeys("new value");

Keyboard Navigation: Use Actions API, Not element.sendKeys()

element.sendKeys(Keys.DOWN) is silently ignored by some controls (MSFlexGrid, certain VB6 controls). Use the Selenium Actions API instead:

// WRONG — silently ignored on some controls
grid.sendKeys(Keys.DOWN);

// CORRECT — works reliably
new Actions(driver).sendKeys(Keys.DOWN).perform();

// Multiple keys
new Actions(driver)
    .sendKeys(Keys.DOWN, Keys.DOWN, Keys.DOWN, Keys.ENTER)
    .perform();

isDisplayed() Returns False for Visible Elements

Some MSAA-bridged elements (Thunder* controls, MSFlexGrid child elements) report isDisplayed() = false even when visible on screen.

Workaround: Use presenceOfElementLocated() instead of visibilityOfElementLocated():

// WRONG — may time out for VB6 elements
wait.until(ExpectedConditions.visibilityOfElementLocated(locator));

// CORRECT — works for all element types
wait.until(ExpectedConditions.presenceOfElementLocated(locator));

Bounding Rectangles May Be Incorrect

Some MSAA elements return bounding rectangles equal to the entire window size instead of their actual bounds. This affects position-based clicking and element screenshots.

Workaround: For position-critical operations, use known coordinates or navigate by keyboard instead.

MSFlexGrid Automation

Grid Structure

MSFlexGrid is exposed as a single element — individual cells are not accessible as child elements. The server provides custom grid endpoints for cell-level access.

Use the Grid Endpoints, Not Coordinates

Address cells by row and column index. The server moves the grid’s selection by logical row/column steps, so cells outside the visible area are reached correctly and results do not depend on screen resolution or display scaling:

WinJavaDriver driver = ...;
WebElement grid = driver.findElement(WinBy.className("MSFlexGridWndClass"));

// Read a cell (0-based row/col, excluding the header row)
String value = driver.getGridCellValue(grid, 32, 1);

// Write a cell
driver.setGridCellValue(grid, 32, 1, "new value");

Off-screen rows need no scrolling on your part — the grid scrolls its own selection into view.

Do not compute cell positions and click them. A grid’s row height, header height and column widths vary per application, and the driver is per-monitor DPI aware while VB6 hosts are DPI unaware — so a coordinate that works on one machine selects a different cell on another.

Only the Editable Column Can Be Read or Written

Cell text is read from and written through the grid’s editor (the floating edit field, control ID 22 in most applications). That editor only ever holds the editable column’s value, so other columns — for example a field-name label in column 0 — cannot be read or written this way.

Requesting a different column fails with a clear error rather than silently returning the editable column’s value. If your grid’s editable column is not column 1, declare it:

// This grid's editor edits column 2
String value = driver.getGridCellValue(grid, 5, 2, 22, 2);
driver.setGridCellValue(grid, 5, 2, "new value", 22, 2);

Grid Information

win_grid_info (or the /grid/{id}/info endpoint) reports rowCountHint, derived from the grid’s scroll range. It is null when the grid has no scrollbar because every row fits. columnCount is always null — a self-drawing grid exposes no column count to an external process.

Status Bar Shows Current Cell Value

The status bar (AutomationId "23") displays the currently selected cell’s value — useful for verification:

WebElement statusBar = driver.findElement(WinBy.accessibilityId("23"));
String cellValue = statusBar.getText();

Field Numbers Skip

VB6 grid field numbers are non-sequential (e.g., F21, F27, F29 — gaps exist). Row number does not equal field number. Use the grid endpoints with 0-based row/col indices instead.

File Dialogs

Finding the Filename Field

The filename field in Windows file dialogs is a ComboBox. Type into its Edit child:

// Find the filename ComboBox
WebElement fileNameBox = driver.findElement(WinBy.accessibilityId("1148"));
// Find the Edit child within it
WebElement editField = fileNameBox.findElement(WinBy.className("Edit"));
editField.clear();
editField.sendKeys("C:\\path\\to\\file.txt");

Button Names Include Ampersand

Windows buttons often include & for keyboard accelerators (e.g., "&Open", "&Save"):

// Include the ampersand in the name
driver.findElement(WinBy.name("&Open")).click();

Wait for Dialog Before Interacting

File dialogs appear asynchronously. Wait for the new window handle:

String mainWindow = driver.getWindowHandle();

// Trigger dialog open
menuItem.click();

// Wait for new window
wait.until(d -> d.getWindowHandles().size() > 1);

// Switch to dialog
for (String handle : driver.getWindowHandles()) {
    if (!handle.equals(mainWindow)) {
        driver.switchTo().window(handle);
        break;
    }
}

// ... interact with dialog ...

// Switch back to main window
driver.switchTo().window(mainWindow);

Window Management

Detecting New Windows/Dialogs

int initialCount = driver.getWindowHandles().size();

// Action that opens a new window
button.click();

// Wait for new window
wait.until(d -> d.getWindowHandles().size() > initialCount);

Switching by Window Title

for (String handle : driver.getWindowHandles()) {
    driver.switchTo().window(handle);
    if (driver.getTitle().contains("Expected Title")) {
        break;
    }
}

Always Switch Back After Dialog

After closing a dialog, the driver’s window context may be invalid. Always switch back:

dialogCloseButton.click();
driver.switchTo().window(mainWindowHandle);

Elements Without Stable Identifiers

Position-Based Fallback Locators

Some VB6 controls have no AutomationId or Name. Use position-based lookup as a last resort:

// Position-based lookup (no unique identifier available)
WebElement target = driver.findElements(WinBy.className("ThunderRT6UserControlDC")).stream()
    .filter(e -> Math.abs(e.getRect().getX() - 441) < 20 && Math.abs(e.getRect().getY() - 283) < 20)
    .findFirst().orElseThrow(() -> new RuntimeException("Element not found at position (441, 283)"));
target.click();

Limitations: Position-based locators are fragile — they break if the window is resized or moved.

Locator Quality in Inspector

The Inspector shows a locator quality indicator for each element:

MCP Tools

Keyboard Modifiers with win_send_keys

Modifier keys (CONTROL, SHIFT, ALT) are held down while subsequent keys are pressed:

win_send_keys("CONTROL a")   → Ctrl+A (select all)
win_send_keys("CONTROL c")   → Ctrl+C (copy)
win_send_keys("SHIFT END")   → Shift+End (select to end)

Fullscreen Screenshots

When dialogs open, win_screenshot only captures the attached window. Use fullscreen: true to capture the entire screen.

VB6 Apps and win_explore

VB6 controls (Thunder* classes) often lack Name and AutomationId. win_explore shows their ClassName and position instead:

[3] Button "" [ThunderRT6CommandButton] @(441,283) ⚠no-id

Use class name strategy with win_find_elements for these controls.

New Window Detection

After clicking a button that opens a dialog, call win_observe — it automatically detects new windows and warns you to switch.

Use Smart Tools for Efficiency

Prefer compound smart tools over individual calls:

Instead of Use
win_page_source + manual parsing win_explore
win_screenshot + win_explore win_observe
win_find_element + win_click win_interact
Multiple find+click sequences win_batch
Multiple find+getText calls win_read_all