Skip to content

Make winKernel / winUser wrapper functions type safe and more reliable #11125

@LeonarddeR

Description

@LeonarddeR

Is your feature request related to a problem? Please describe.

NVDA is using many windows API functions that are c function and therefore require certain types as input / output parameters. Most of them can be found in the winKernel and winUser modules.
However, the functions we're using are merely wrappers of these functions, without checking what types we're passing at them and getting from them.

By default, ctypes assumes a return type of ctypes.c_long for all library functions loaded with the WinDLL library loader. However, many of these functions return other types, such as DWORD (unsigned long) and HANDLE. Furthermore, when throwing parameters at these functions, ctypes by default only knows how many bytes the function expects as its input. This is all in depth explained in the ctypes docs

Example:

>>> winKernel.closeHandle()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "winKernel.pyc", line 134, in closeHandle
ValueError: Procedure probably called with not enough arguments (4 bytes missing)
>>> winKernel.closeHandle("a")
0

As you can see, you can throw an empty string at this function. ctypes will convert the string into a c_wchar_p which is 4 bytes in size, the same size as a 32 bit handle. The function fails, but the function shouldn't have been executed at all.

Describe the solution you'd like

I propose two things:

  1. Define a standard for new wrappers added to winKernel and winUser
  2. Update the current wrappers according to this new standard.

As a standard, I'd propose to no longer wrap these functions with python def style functions, but using ctypes.WINFUNCTYPE. This allows us to:

  • Remove a wrapping layer, as ctypes.WINFUNCTYPE is a way to annotate the function pointers
  • Provide expected types, names and default values for arguments
  • Automatic Windows error handling/raising (GetLastError)
  • Automatic input parameters for functions like user32.GetClientRect. You set RECT as an input parameter and ctypes automatically passes an empty structure to the function

A downside of this approach might be that we don't have type annotations.

Metadata

Metadata

Assignees

No one assigned

    Labels

    audience/nvda-devPR or issue is relevant to NVDA / Add-on developerscomponent/ctypesInteraction with external libraries using ctypesepic/64bit-migrationmaintenance/dependencyUpdateMaintenance of NVDA, specifically dependency updates.p5https://github.com/nvaccess/nvda/blob/master/projectDocs/issues/triage.md#prioritytriagedHas been triaged, issue is waiting for implementation.

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions