- For an installed copy:
- Ctrl+Alt+N (Desktop shortcut)
- test:
startupShutdownNVDA.Starts from desktop shortcut
- test:
- Automatically via Ease of Access on the Windows sign-in screen (at boot or signing out of a previous session)
- Automatically via Ease of Access on User Account Control (UAC) screens
- Automatically by Ease of Access after signing in to Windows
- Ctrl+Alt+N (Desktop shortcut)
- For an installed copy, portable copy, installer:
- An exiting instance of NVDA starting a new process (see shutting down procedures)
- By running the exe.
- This can be triggered by a user or external process such as an existing NVDA instance
- test:
startupShutdownNVDA.Starts
- For source: eg runnvda.bat
- UI within NVDA, with and without an ExitDialog prompt (uses
triggerNVDAExit
):- NVDA+q
- test:
startupShutdownNVDA.Quits from keyboard, Restarts
- test:
- An input gesture to restart
- After changing some settings (eg installed add-ons or UI language), user prompted on dialog exit.
- Via the NVDA menu -> Exit
- test:
startupShutdownNVDA.Quits from menu
- test:
- NVDA+q
- A process sending
WM_QUIT
, eg a new NVDA process starting - A handled crash (directly causes a new process to start, terminates unsafely)
- test:
startupShutdownNVDA.Restarts on crash, Restarts on braille crash
- test:
- An unhandled crash (terminates unsafely)
- requires manual testing/confirmation
- An external command which kills the process (terminates unsafely)
- Windows shutting down (terminates unsafely) (uses
wx.EVT_END_SESSION
)
Check the manual test guide.
These notes are aimed at developers, wishing to understand technical aspects of the NVDA start and exit.
- No more than one NVDA process instance should be running at the same time. Interactions with itself could cause severe issues, some (non-exhaustive list) examples of sub-systems where this would be a problem:
- NVDA config files
- Global (OS level) keyboard hook
- Changed / incompatible in-process code
- As such, we want to be able to detect running instances, cause them to exit, and confirm they have exited.
There are 3 ways that NVDA receives a request to exit:
- From internally calling triggerNVDAExit
- Receiving WM_QUIT Windows message
- Receiving wx.EVT_END_SESSION due to Windows session ending
- Called from within NVDA.
- A function in the core module
- Only executes the code once, uses a lock and flag to ensure this
- Uses a queue on the main thread to queue a safe shutdown
- Once the queued shutdown starts:
- the updateCheck is terminated
- watchdog is terminated
- globalPlugins and the brailleViewer are terminated, so we can close all windows safely
- All wx windows are closed
- Now that windows are closed, a new NVDA instance is started if requested
- A Windows Message received from an external process, such as another NVDA process.
- NVDA accepts
WM_QUIT
messages from other processes and creates a named window that can be discovered. WM_QUIT
is handled bywx
, which force closes all wx windows (other UI features like the systray icon are not windows, and remain) and then exits the main loop.triggerNVDAExit
is a more expansive check than how wxWidgets handlesWM_QUIT
- We subsequently run
triggerNVDAExit
to ensure that clean up code isn't missed, and pump the queue to execute it. - Using a custom message has been considered:
- Would allow custom handling (eg just
triggerNVDAExit
) - Unfortunately, older NVDA versions will only be aware of
WM_QUIT
, so we'd need to sendWM_QUIT
to these versions. - Sending the custom message, waiting for a timeout, then sending
WM_QUIT
adds a significant wait time - Identifying the running version (to selectively send the message) requires maintaining 2 message windows in NVDA (one for legacy behaviour) and adds complexity
- Would allow custom handling (eg just
- This is a wxCloseEvent triggered by a Windows session ending.
- On
wx.EVT_END_SESSION
, we save the config and play the exit sound. - Other actions are not performed as we have limited time to perform an action for this event.
- NVDA is expected to run as long as possible during the sign out process.
- This is achieved through the Windows API, by setting the shutdown priority to the lowest reserved value for non-system applications,
0x100
. - SHUTDOWN_NORETRY ensures that NVDA does not show up in the blocked shutdown list dialog. If it were, the user would have no way of reading the dialog and fixing the issue.
With the requirement to only allow a single instance of NVDA, a new NVDA process must be able to replace an existing NVDA process.
NVDA will exit correctly in response to a WM_QUIT
Windows message, but the process must first be detected / identified in order to send the message.
For new NVDA process to detect an existing NVDA process, a named message window is used.
A new NVDA process searches for an existing NVDA window, and if it is detected, sends WM_QUIT
.
The message window is created late during the start up, and destroyed early in exit and is not perfectly indicative of whether or not an NVDA process is running.
As such, we have a MutEx that ensures a newly started process blocks until any previous NVDA has finished exiting.
To confirm that another NVDA process is not running, a MutEx is owned by the NVDA process. NVDA will be blocked from starting until it can acquire the MutEx. If it can not acquire the MutEx within a timeout, startup is aborted. This is acquired as soon as possible and released by NVDA as late as possible. When the NVDA process exits abnormally, Windows will release the MutEx.
Called in the event of a crash. Exiting NVDA safely in the event of a crash could be improved, but it is limited as we cannot rely on other threads running or the state of NVDA.