Skip to main content

Introduction

The NVDA Controller Client API allows external applications to communicate with NVDA, enabling them to:
  • Speak text programmatically
  • Display braille messages
  • Cancel speech
  • Check if NVDA is running
  • Speak SSML (Speech Synthesis Markup Language)
The API is implemented as a DLL (Dynamic Link Library) that can be called from any programming language that supports loading and calling functions from DLLs.
The Controller Client API is primarily designed for applications that want to provide their own accessibility layer or need to communicate information directly to NVDA users.

Getting the API

Download the *controllerClient.zip from:

What’s Included

DLL Files

Platform-specific nvdaControllerClient.dll files for x86, x64, and ARM64

Header File

nvdaController.h with C declarations for all functions

Import Libraries

.lib and .exp files for C/C++ linking

Examples

Sample code in Python, C, C#, and Rust

API Versions

Version 2.0 (NVDA 2024.1+)

Added support for:
  • nvdaController_getProcessId - Get NVDA’s process ID
  • nvdaController_speakSsml - Speak SSML with markup support
These functions return error code 1717 (RPC_S_UNKNOWN_IF) on NVDA versions older than 2024.1.

Version 1.0

Core functions available in all NVDA versions:
  • Test if NVDA is running
  • Speak text
  • Braille messages
  • Cancel speech

Security Considerations

NVDA runs on the lock screen and secure screens. Before providing information to users via the Controller Client API, check if Windows is locked or on a secure screen to prevent leaking secure data.
Applications should implement security checks:
import ctypes

# Check if on secure desktop
def is_secure_desktop():
    # Implementation depends on your security requirements
    # Check for locked workstation or secure screen
    pass

# Only speak if not on secure desktop
if not is_secure_desktop():
    clientLib.nvdaController_speakText("Sensitive information")

Core Functions

nvdaController_testIfRunning()

Test if NVDA is running and accessible.
Return Value
int
0 on success, non-zero Windows error code on failure
import ctypes

clientLib = ctypes.windll.LoadLibrary("./nvdaControllerClient.dll")

res = clientLib.nvdaController_testIfRunning()
if res != 0:
    errorMessage = str(ctypes.WinError(res))
    print(f"NVDA not running: {errorMessage}")

nvdaController_speakText(text)

Speak the provided text through NVDA.
text
wchar_t*
required
The text to speak (wide character string)
Return Value
int
0 on success, non-zero error code on failure
# Speak a message
clientLib.nvdaController_speakText("Hello from my application!")

nvdaController_brailleMessage(message)

Display a message on the user’s braille display.
message
wchar_t*
required
The message to display in braille
Return Value
int
0 on success, non-zero error code on failure
# Show a braille message
clientLib.nvdaController_brailleMessage("Status: Connected")

nvdaController_cancelSpeech()

Cancel all currently queued speech.
Return Value
int
0 on success, non-zero error code on failure
# Stop speaking
clientLib.nvdaController_cancelSpeech()

nvdaController_getProcessId()

Get the process ID of the running NVDA instance.
processId
DWORD*
required
Pointer to receive the process ID
Return Value
int
0 on success, 1717 if not supported, other non-zero on failure
Available in NVDA 2024.1 and later
processId = ctypes.c_ulong()
res = clientLib.nvdaController_getProcessId(ctypes.byref(processId))
if res == 0:
    print(f"NVDA Process ID: {processId.value}")

nvdaController_speakSsml()

Speak SSML (Speech Synthesis Markup Language) with support for prosody, marks, and breaks.
ssml
wchar_t*
required
The SSML string to speak
symbolLevel
int
Symbol verbosity level (-1 for user’s setting)
priority
int
Speech priority (0 = normal)
asynchronous
bool
Whether to speak asynchronously
Return Value
int
0 on success, 1717 if not supported, other non-zero on failure
Available in NVDA 2024.1 and later
ssml = """<speak>
    This is one sentence.
    <mark name="test" />
    <prosody pitch="200%">This sentence is pronounced with higher pitch.</prosody>
    <break time="1000ms" />
    This is after a one second pause.
</speak>"""

clientLib.nvdaController_speakSsml(ssml, -1, 0, False)

nvdaController_setOnSsmlMarkReachedCallback()

Set a callback function to be called when SSML marks are reached.
callback
function pointer
Function with signature: int callback(wchar_t* markName)
Return Value
int
0 on success, non-zero error code on failure
@ctypes.WINFUNCTYPE(ctypes.c_ulong, ctypes.c_wchar_p)
def onMarkReached(name: str) -> int:
    print(f"Reached SSML mark: {name}")
    return 0

clientLib.nvdaController_setOnSsmlMarkReachedCallback(onMarkReached)
# Speak SSML with marks
clientLib.nvdaController_speakSsml(ssml, -1, 0, False)
# Clear callback when done
clientLib.nvdaController_setOnSsmlMarkReachedCallback(None)

Complete Examples

Python Example

import ctypes
import time

# Load the library
clientLib = ctypes.windll.LoadLibrary("./nvdaControllerClient.dll")

# Test if NVDA is running
res = clientLib.nvdaController_testIfRunning()
if res != 0:
    errorMessage = str(ctypes.WinError(res))
    print(f"Error: {errorMessage}")
    exit(1)

# Speak and braille messages
clientLib.nvdaController_speakText("Application started successfully")
clientLib.nvdaController_brailleMessage("Ready")

# Cancel speech if needed
time.sleep(2)
clientLib.nvdaController_cancelSpeech()

C Example

example_c.c
#include "nvdaController.h"
#include <windows.h>

int main(int argc, char **argv) {
    // Test if NVDA is running
    long res = nvdaController_testIfRunning();
    if (res != 0) {
        MessageBoxA(NULL, "Error communicating with NVDA", "Error", MB_OK);
        return 1;
    }
    
    // Speak text
    nvdaController_speakText(L"Hello from C application!");
    
    // Display braille message
    nvdaController_brailleMessage(L"C App Running");
    
    // Cancel speech
    Sleep(2000);
    nvdaController_cancelSpeech();
    
    return 0;
}

C# Example

Program.cs
using System;
using System.Runtime.InteropServices;
using System.Threading;

class Program
{
    [DllImport("nvdaControllerClient.dll")]
    static extern int nvdaController_testIfRunning();
    
    [DllImport("nvdaControllerClient.dll")]
    static extern int nvdaController_speakText([MarshalAs(UnmanagedType.LPWStr)] string text);
    
    [DllImport("nvdaControllerClient.dll")]
    static extern int nvdaController_brailleMessage([MarshalAs(UnmanagedType.LPWStr)] string message);
    
    [DllImport("nvdaControllerClient.dll")]
    static extern int nvdaController_cancelSpeech();
    
    static void Main(string[] args)
    {
        // Test if NVDA is running
        int res = nvdaController_testIfRunning();
        if (res != 0)
        {
            Console.WriteLine("NVDA is not running");
            return;
        }
        
        // Speak and braille
        nvdaController_speakText("Hello from C# application!");
        nvdaController_brailleMessage("C# App");
        
        Thread.Sleep(2000);
        nvdaController_cancelSpeech();
    }
}

Error Handling

All functions return 0 on success and a non-zero Windows error code on failure. Common error codes:
Error CodeMeaning
0Success
1717RPC_S_UNKNOWN_IF - Function not supported in this NVDA version
OtherStandard Windows error codes (use WinError or equivalent to decode)
Always check the return value of controller client functions and handle errors gracefully. NVDA might not be running, or the user might have disabled it temporarily.

Best Practices

Test Availability

Always call nvdaController_testIfRunning() before other operations

Security First

Check for secure desktops before speaking sensitive information

Graceful Degradation

Handle errors gracefully - user might not have NVDA installed

Version Detection

Check return codes to detect unsupported functions on older NVDA versions

Use Cases

Progress Notifications

def report_progress(percent):
    clientLib.nvdaController_brailleMessage(f"Progress: {percent}%")
    if percent == 100:
        clientLib.nvdaController_speakText("Operation complete")

Form Validation

def announce_error(field_name, error_message):
    message = f"{field_name}: {error_message}"
    clientLib.nvdaController_speakText(message)
    clientLib.nvdaController_brailleMessage(f"Error: {field_name}")

Status Updates

def announce_connection_status(status):
    ssml = f"""<speak>
        Connection status:
        <mark name="status_start" />
        <prosody rate="slow">{status}</prosody>
        <mark name="status_end" />
    </speak>"""
    clientLib.nvdaController_speakSsml(ssml, -1, 0, False)

License

The NVDA Controller Client API is licensed under the GNU Lesser General Public License (LGPL), version 2.1. This means:
  • ✅ You can use this library in any application (commercial or open source)
  • ✅ You can distribute the DLL with your application
  • ⚠️ If you modify the library, you must contribute changes back under LGPL 2.1

View Full License

Read the complete LGPL 2.1 license text

Additional Resources

Example Code

Complete working examples in multiple languages

Header File

nvdaController.h with full API documentation

Download Client

Download pre-built controller client DLLs

GitHub Issues

Report issues or request features

Build docs developers (and LLMs) love