> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/nvaccess/nvda/llms.txt
> Use this file to discover all available pages before exploring further.

# App Modules

> Create application-specific support for NVDA

## Overview

App Modules allow you to improve NVDA's support for specific applications. When an application is running, NVDA automatically loads the corresponding app module and unloads it when the application closes.

<Note>
  App modules are perfect for adding application-specific functionality, customizing how NVDA presents information from an application, or fixing accessibility issues in specific programs.
</Note>

## Basic App Module Structure

App modules must:

1. Be placed in the `appModules` directory
2. Be named after the application's executable (e.g., `notepad.py` for notepad.exe)
3. Define a class called `AppModule` that inherits from `appModuleHandler.AppModule`

<CodeGroup>
  ```python Basic App Module theme={null}
  # notepad.py
  # App Module for Notepad

  import appModuleHandler
  from scriptHandler import script
  import ui
  import tones

  class AppModule(appModuleHandler.AppModule):

  	def event_gainFocus(self, obj, nextHandler):
  		"""Called when an object gains focus."""
  		tones.beep(550, 50)
  		nextHandler()

  	@script(
  		description="Announces the application version",
  		gesture="kb:NVDA+shift+v"
  	)
  	def script_announceVersion(self, gesture):
  		"""Announce the application version."""
  		ui.message(f"Application: {self.appName}")
  ```

  ```python appModuleHandler.py theme={null}
  # From NVDA source
  class AppModule(baseObject.ScriptableObject):
  	"""Base app module.
  	App modules provide specific support for a single application.
  	"""
  	
  	#: Whether NVDA should sleep while in this application
  	sleepMode = False
  	
  	def __init__(self, processID, appName=None):
  		super(AppModule, self).__init__()
  		self.processID = processID
  		self.appName = appName
  		self.processHandle = getProcessHandleFromProcessId(processID)
  ```
</CodeGroup>

## Naming App Modules

### Standard Naming

For most applications, name your app module file after the executable:

* `notepad.exe` → `notepad.py`
* `explorer.exe` → `explorer.py`
* `firefox.exe` → `firefox.py`

<Warning>
  File names containing dots (other than `.py`) are converted to underscores. For example, `my.app.exe` would use `my_app_exe.py`.
</Warning>

### Custom Mapping

For executables with names that conflict with Python naming rules or when you want to map multiple executables to one app module, use a global plugin to register the mapping:

```python registrationPlugin.py theme={null}
import appModuleHandler
import globalPluginHandler

class GlobalPlugin(globalPluginHandler.GlobalPlugin):

	def __init__(self, *args, **kwargs):
		super().__init__(*args, **kwargs)
		# Map "time.exe" to "time_app_mod" module
		appModuleHandler.registerExecutableWithAppModule("time", "time_app_mod")

	def terminate(self, *args, **kwargs):
		super().terminate(*args, **kwargs)
		appModuleHandler.unregisterExecutable("time")
```

## Handling Events

App modules can respond to events that occur in their application. Events are methods named `event_eventName`.

<CodeGroup>
  ```python Focus Events theme={null}
  import appModuleHandler
  import ui

  class AppModule(appModuleHandler.AppModule):

  	def event_gainFocus(self, obj, nextHandler):
  		"""Called when an object gains focus."""
  		ui.message(f"Focus on {obj.name}")
  		nextHandler()  # Always call nextHandler to propagate the event

  	def event_focusEntered(self, obj, nextHandler):
  		"""Called when focus moves inside a container."""
  		if obj.role == controlTypes.Role.DIALOG:
  			ui.message(f"Entered dialog: {obj.name}")
  		nextHandler()
  ```

  ```python Object Events theme={null}
  import appModuleHandler

  class AppModule(appModuleHandler.AppModule):

  	def event_nameChange(self, obj, nextHandler):
  		"""Called when an object's name changes."""
  		# Handle name changes
  		nextHandler()

  	def event_valueChange(self, obj, nextHandler):
  		"""Called when an object's value changes."""
  		# Handle value changes
  		nextHandler()

  	def event_stateChange(self, obj, nextHandler):
  		"""Called when an object's state changes."""
  		# Handle state changes
  		nextHandler()
  ```

  ```python Application Events theme={null}
  import appModuleHandler

  class AppModule(appModuleHandler.AppModule):

  	def event_appModule_gainFocus(self):
  		"""Called when this application gains focus."""
  		# Set up application-specific configuration
  		pass

  	def event_appModule_loseFocus(self):
  		"""Called when this application loses focus."""
  		# Clean up application-specific resources
  		pass
  ```
</CodeGroup>

### Common Events

<ParamField path="event_gainFocus" type="method">
  Object gained keyboard focus
</ParamField>

<ParamField path="event_loseFocus" type="method">
  Object lost keyboard focus
</ParamField>

<ParamField path="event_focusEntered" type="method">
  Focus moved inside a container object (the object is an ancestor of the focus)
</ParamField>

<ParamField path="event_foreground" type="method">
  Object became the new foreground/active top-level object
</ParamField>

<ParamField path="event_nameChange" type="method">
  Object's name changed
</ParamField>

<ParamField path="event_valueChange" type="method">
  Object's value changed
</ParamField>

<ParamField path="event_stateChange" type="method">
  Object's state changed
</ParamField>

<ParamField path="event_caret" type="method">
  Caret (insertion point) moved within the object
</ParamField>

## Creating Scripts

Scripts are commands that users can execute via keyboard shortcuts. Use the `@script` decorator to define scripts:

```python scripts.py theme={null}
import appModuleHandler
from scriptHandler import script
import ui
import api

class AppModule(appModuleHandler.AppModule):

	@script(
		description="Announces the current object's class name",
		category="My Application",
		gesture="kb:NVDA+shift+c"
	)
	def script_announceClassName(self, gesture):
		"""Announce the window class name."""
		focusObj = api.getFocusObject()
		ui.message(f"Class: {focusObj.windowClassName}")

	@script(
		description="Announces window control ID",
		gestures=["kb:NVDA+shift+i", "kb:NVDA+alt+i"]
	)
	def script_announceControlID(self, gesture):
		"""Announce the control ID."""
		focusObj = api.getFocusObject()
		ui.message(f"Control ID: {focusObj.windowControlID}")
```

### Script Decorator Parameters

<ParamField path="description" type="str" required>
  User-visible description shown in Input Gestures dialog
</ParamField>

<ParamField path="category" type="str">
  Category to group similar scripts (defaults to "Miscellaneous")
</ParamField>

<ParamField path="gesture" type="str">
  Single gesture string (e.g., `"kb:NVDA+shift+v"`)
</ParamField>

<ParamField path="gestures" type="list[str]">
  Multiple gesture strings
</ParamField>

<ParamField path="canPropagate" type="bool" default="False">
  Whether script applies to focus ancestor objects
</ParamField>

<ParamField path="bypassInputHelp" type="bool" default="False">
  Run script even when input help is active
</ParamField>

<ParamField path="allowInSleepMode" type="bool" default="False">
  Allow script to run when sleep mode is active
</ParamField>

## App Module Properties

### Essential Properties

```python properties.py theme={null}
class AppModule(appModuleHandler.AppModule):
	
	# The process ID (read-only, set in __init__)
	processID: int
	
	# The application name (read-only, set in __init__)
	appName: str
	
	# Handle to the process
	processHandle: int
	
	# Whether NVDA should sleep in this application
	sleepMode: bool = False
	
	# Product name from executable version info
	productName: str
	
	# Product version from executable version info
	productVersion: str
	
	# Whether the process is 64-bit
	is64BitProcess: bool
	
	# Whether this is a Windows Store app
	isWindowsStoreApp: bool
```

### Sleep Mode

Sleep mode disables NVDA in an application, useful for self-voicing applications:

```python sleepMode.py theme={null}
import appModuleHandler

class AppModule(appModuleHandler.AppModule):
	"""App module for a self-voicing application."""
	
	sleepMode = True
```

Users can toggle sleep mode with `NVDA+Shift+S`.

## Customizing NVDA Objects

App modules can customize how NVDA Objects behave for specific controls:

```python customObjects.py theme={null}
import appModuleHandler
from NVDAObjects.IAccessible import IAccessible
import controlTypes

class AppModule(appModuleHandler.AppModule):

	def chooseNVDAObjectOverlayClasses(self, obj, clsList):
		"""Choose overlay classes for objects in this application."""
		if obj.windowClassName == "CustomButton":
			# Insert custom class at the beginning
			clsList.insert(0, CustomButton)

class CustomButton(IAccessible):
	"""Custom handling for custom buttons."""
	
	def _get_name(self):
		"""Provide better name for custom buttons."""
		name = super().name
		return f"Custom: {name}"
	
	def _get_role(self):
		"""Override role."""
		return controlTypes.Role.BUTTON
```

## Hosted Apps

Some executables host multiple apps (e.g., `javaw.exe`, `wwahost.exe`). For these, you need to:

1. Name the app module after the hosted app's internal name
2. For `wwahost.exe`, inherit from the `wwahost` app module

<CodeGroup>
  ```python Java App theme={null}
  # test.py - for Java app named "test" running in javaw.exe

  import appModuleHandler

  class AppModule(appModuleHandler.AppModule):
  	pass
  ```

  ```python WWAHost App theme={null}
  # myapp.py - for app hosted in wwahost.exe

  from nvdaBuiltin.appModules.wwahost import *

  class AppModule(AppModule):
  	# Subclass the wwahost AppModule
  	pass
  ```

  ```python WebView2 App theme={null}
  # olk.py - for modern Outlook using Edge WebView2

  import appModuleHandler

  class AppModule(appModuleHandler.AppModule):
  	# Disable browse mode by default
  	disableBrowseModeByDefault: bool = True
  ```
</CodeGroup>

## UIA Handling

Control whether to use UI Automation for specific windows:

```python uiaControl.py theme={null}
import appModuleHandler

class AppModule(appModuleHandler.AppModule):

	def isGoodUIAWindow(self, hwnd):
		"""Return True if UIA should be used for this window."""
		# Check window class name and force UIA
		return False

	def isBadUIAWindow(self, hwnd):
		"""Return True if UIA should be avoided for this window."""
		# Detect broken UIA implementations
		return False

	def shouldProcessUIAPropertyChangedEvent(self, sender, propertyId):
		"""Control whether to process UIA property changes."""
		# Filter out noisy events
		return True
```

## Practical Example

Here's a complete app module for a custom application:

```python complete_example.py theme={null}
# myapp.py
# App Module for My Application

import appModuleHandler
from scriptHandler import script
import api
import ui
import controlTypes
from NVDAObjects.IAccessible import IAccessible

class AppModule(appModuleHandler.AppModule):

	def __init__(self, *args, **kwargs):
		super().__init__(*args, **kwargs)
		self.customData = {}

	def event_appModule_gainFocus(self):
		"""Application gained focus."""
		ui.message("My Application")

	def event_gainFocus(self, obj, nextHandler):
		"""Handle focus events."""
		if obj.role == controlTypes.Role.LISTITEM:
			# Custom handling for list items
			self._announceListItem(obj)
		nextHandler()

	def _announceListItem(self, obj):
		"""Custom announcement for list items."""
		position = obj.positionInfo
		if position:
			ui.message(
				f"{obj.name}, item {position['indexInGroup']} of {position['similarItemsInGroup']}"
			)

	@script(
		description="Shows custom information",
		category="My Application",
		gesture="kb:NVDA+shift+i"
	)
	def script_showInfo(self, gesture):
		"""Display custom information dialog."""
		focusObj = api.getFocusObject()
		ui.message(f"Object: {focusObj.name}, Role: {focusObj.role}")

	def chooseNVDAObjectOverlayClasses(self, obj, clsList):
		"""Customize NVDA objects."""
		if obj.windowClassName == "MyCustomControl":
			clsList.insert(0, MyCustomControl)

class MyCustomControl(IAccessible):
	"""Custom NVDA object for MyCustomControl."""
	
	def _get_name(self):
		"""Provide better name."""
		# Custom logic to get name
		return super().name or "Unnamed custom control"
	
	def _get_description(self):
		"""Provide helpful description."""
		return "This is a custom control"
```

## Testing

<Steps>
  <Step title="Enable scratchpad">
    Go to NVDA Settings > Advanced and enable the scratchpad directory.
  </Step>

  <Step title="Create app module">
    Place your app module in `%APPDATA%\nvda\scratchpad\appModules\`.
  </Step>

  <Step title="Launch application">
    Start the target application.
  </Step>

  <Step title="Reload plugins">
    Press `NVDA+Control+F3` to reload without restarting NVDA.
  </Step>

  <Step title="Test functionality">
    Test your events, scripts, and customizations.
  </Step>
</Steps>

## Best Practices

<Warning>
  * Always call `nextHandler()` in event methods to propagate events
  * Don't perform long-running operations in events
  * Use the `@script` decorator instead of `__gestures` dictionary
  * Provide clear descriptions for all scripts
  * Test with different NVDA configurations
</Warning>

<Note>
  * Use `ui.message()` for user feedback
  * Use `log.debug()`, `log.info()`, `log.warning()` for debugging
  * Check `obj.role` and `obj.windowClassName` for object identification
  * Cache expensive computations in instance variables
</Note>

## Related Resources

* [Global Plugins →](/development/addons/global-plugins)
* [Custom NVDA Objects →](/development/addons/custom-nvda-objects)
* [API Reference →](/development/addons/api-reference)
