win32-api

repository·main·Indexed 19 days ago

https://github.com/waitingsong/node-win32-api

FFI definitions and high-level wrappers for the Windows win32 API for Node.js, enabling calls to native functions from DLLs such as user32.dll, kernel32.dll, comctl32.dll, gdi32.dll, and spoolss.dll. The library provides dedicated namespaces for system DLLs, a promised API for async/await usage, and the win32-def package for underlying data types, structures, and unions.

Tokens
17.1K
Snippets
59
Records
91
Agent score
64%

What's inside win32-api

  1. Overview of win32-api packages

    main

    The repository is organized into two main packages:

    1. win32-api: The primary package containing FFI definitions and utility wrappers for calling Windows native functions from user32.dll, kernel32.dll, and comctl32.dll via [koffi].
    2. win32-def: Provides the underlying definitions used by the API.
  2. Auto-create structures and unions during library loading

    main

    When defining a library's method definitions, you can use string templates to reference structures or unions (e.g., S.LPPOINT for POINT*). If these structures are referenced in the method definition, they will be automatically created when the library is loaded via load().

    import * as D from '##/index.def.js'
    import * as S from '##/index.struct.js'
    import * as T from '##/index.types.js' 
    
    class DefWin {
      // S.LPPOINT == 'POINT*'
      static ClientToScreen = [D.BOOL, [D.HWND, `_Inout_ ${S.LPPOINT}`]] 
      // S.LPDISPLAY_DEVICEW == 'DISPLAY_DEVICEW*'
      static EnumDisplayDevicesW = [D.BOOL, [D.LPCWSTR, D.DWORD, `_Inout_ ${S.LPDISPLAY_DEVICEW}`, D.DWORD]] 
      static GetCursorPos = [D.BOOL, [`_Out_ ${S.LPPOINT}`]]
    }
  3. Use win32-api DLL wrappers

    main

    The project provides high-level wrappers for common Windows DLLs, such as User32 and Winspool, located in the win32-api/util entry point. These wrappers allow you to call native Windows functions directly in Node.js without manually managing FFI logic for every call.

    Commonly used wrappers include:

    • User32 (e.g., for window management)
    • Winspool (e.g., for printer operations)
    import { 
      FindWindowEx, 
      GetDefaultPrinter,
    } from 'win32-api/util'
    import { spawn } from 'child_process'; 
    
    // Retrieves the printer name of the default printer for the current user on the local computer
    const printerName = await GetDefaultPrinter()
    
    const child = spawn('notepad.exe')
    // Finds the window handle for Notepad
    const hWnd = await FindWindowEx(0, 0, 'Notepad', null)
  4. Migrate win32-def imports to v13

    main

    In v13, win32-def has moved away from namespace exports in favor of pkg.exports. You must now use wildcard imports (import * as ...) and target specific sub-paths for types, structs, and unions.

    Key changes:

    • Use import * as M from 'win32-def' for general definitions.
    • Use import * as W from 'win32-def/common.def' for common data types.
    • Use import * as DS from 'win32-def/struct.def' for structures.
    • Use import * as DU from 'win32-def/union.def' for unions.
    • The _Struct suffix has been removed from struct types (e.g., M.POINT_Struct is now M.POINT).
    • ANSI data types are no longer supported.
    // Before
    import { DModel as M } from 'win32-def';
    import { DTypes as W } from 'win32-def';
    import { DStruct as DS } from 'win32-def';
    import { DUnion } from 'win32-def';
    
    // Current (v13+)
    import * as M from 'win32-def';
    import * as W from 'win32-def/common.def';
    import * as DS from 'win32-def/struct.def';
    import * as DU from 'win32-def/union.def';
  5. Use the promised API in win32-api

    main

    v13 introduces a promised version of the Win32 APIs. Instead of using the standard callback or synchronous patterns, you can import the API namespaces directly from win32-api/promise to use them with async/await.

    import { Comctl32 } from 'win32-api/promise';
    import { Kernel32 } from 'win32-api/promise';
    import { Ntdll } from 'win32-api/promise';
    import { User32 } from 'win32-api/promise';
  6. Define Win32 DLL Function Types

    main

    To provide type safety when working with Win32 DLLs, you can define a DllFuncsType interface. This interface maps function names to their expected signatures. This pattern is used to bridge the gap between raw FFI calls and TypeScript's type system.

    When defining these interfaces, ensure you are mapping the function name to its return type and parameter types as expected by the Win32 API.

    import * as T from 'win32-def';
    
    export interface MyDllFunctions extends T.DllFuncsType {
      // Example: FunctionName(param: Type): ReturnType
      SomeWin32Function(param: T.UINT): T.BOOL;
      AnotherFunction(): T.INT;
    }
  7. Understand the KoffiFunctionLike interface

    main

    The KoffiFunctionLike interface describes the shape of a function generated by the library. It is not just a standard function; it includes metadata about its definition and supports both synchronous and asynchronous execution.

    It provides:

    • A standard callable signature: (...args: any[]): any
    • An async property: async: (...args: any[]) => any
    • An info object containing metadata:
      • name: The function name.
      • fnDefRetType: The return type definition.
      • fnDefCallParams: The parameter call definitions.
    // Example of how a KoffiFunctionLike object might be accessed
    const myFunc: KoffiFunctionLike = getFunctionFromLib();
    
    // Synchronous call
    myFunc(arg1, arg2);
    
    // Asynchronous call
    await myFunc.async(arg1, arg2);
    
    // Accessing metadata
    console.log(myFunc.info.name);
  8. Understand StructFactory and StructFactoryResult

    main

    When working with complex Win32 structures, StructFactory is used to generate new instances of a struct.

    A StructFactory is a function that returns a StructFactoryResult. The StructFactoryResult contains:

    • payload: The actual struct data (used for _Out_ or _Inout_ parameters).
    • name: The name of the struct.
    • pointer: The pointer string representation.
    • CType: The underlying koffi C type.
    • size: The size of the struct in bytes.
    • sizeColumns: Optional property keys representing the columns/fields.
  9. Understand Win32 primitive type mappings in TypeScript

    main

    The win32-def package provides TypeScript type definitions that map Win32 API primitive types to JavaScript/TypeScript types. This ensures type safety when interacting with low-level Windows APIs via the win32-api package.

    Key mapping patterns include:

    • Handles and Addresses: HANDLE, HWND, HMODULE, etc., are typically represented as number | BigIntStr to accommodate both 32-bit and 64-bit address spaces. PTR_Addr (used for INT_PTR, ULONG_PTR, etc.) also follows this pattern.
    • Pointers: Most pointer types (prefixed with P, e.g., PVOID, PBYTE, PLONG) are mapped to _POINTER, which is a Buffer. This allows direct memory manipulation.
    • Strings:
      • WCHAR_String maps to string.
      • PWCHAR_String (pointer to wide character string) maps to _POINTER (Buffer).
      • LPCWSTR and LPCTSTR can be either a string or a PUINT16 (pointer to unsigned 16-bit integer).
    • Integers:
      • 32-bit integers (e.g., DWORD, LONG, UINT) map to number.
      • 64-bit integers (e.g., QWORD, UINT64, LONGLONG) map to BigIntStr to prevent precision loss in JavaScript's number type.
    • Booleans: BOOL is a number, while BOOLEAN is a boolean.