npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

node-ctypes

v0.1.5

Published

Python ctypes-like FFI for Node.js using libffi

Readme

node-ctypes

npm version npm downloads Build License: MIT

Python ctypes for Node.js - A high-performance FFI (Foreign Function Interface) library with full Python ctypes compatibility, built on libffi and N-API.

Why node-ctypes?

  • Python ctypes API Compatibility - If you know Python ctypes, you already know node-ctypes! Same syntax, same patterns.
  • 🚀 High Performance - Up to 50x faster than ffi-napi, with struct operations matching or exceeding koffi performance.
  • 🔧 Complete FFI Support - Structs, unions, bit fields, nested structures, arrays, callbacks, variadic functions, and more.
  • 🌍 Cross-platform - Works seamlessly on Linux, macOS, and Windows with identical API.

Features

  • 🐍 Full Python ctypes compatibility - Struct definitions, unions, bit fields, anonymous fields
  • 📚 Load shared libraries (.so, .dll, .dylib) with CDLL and WinDLL
  • 🔄 Variadic functions (printf, sprintf) with automatic detection
  • 📞 Callbacks - JavaScript functions callable from C code
  • 🏗️ Complex data structures - Nested structs, unions in structs, arrays in structs
  • High performance - Eager loading for struct properties, optimized FFI wrapper
  • 🔍 Transparent API - Pass struct objects directly to FFI functions
  • 🔧 Extended type support - All ctypes types (int8, uint8, int16, uint16, etc.)
  • 📊 Memory utilities - readValue/writeValue for direct memory access, enhanced sizeof
  • 🏗️ Advanced array support - String initialization, improved proxy behavior

Installation

From npm (recommended)

npm install node-ctypes

Prebuilt binaries are available for:

  • Windows x64, ARM64
  • Linux x64, ARM64
  • macOS ARM64

From source

Prerequisites

  • Node.js >= 16
  • CMake >= 3.15
  • C++ compiler (GCC, Clang, or MSVC)

Ubuntu/Debian

sudo apt install build-essential cmake

macOS

brew install cmake

Windows

  • Install Visual Studio Build Tools
  • Install CMake

Build

npm install
npm run build

Quick Start - Python ctypes Users

If you're familiar with Python ctypes, you'll feel right at home:

Python ctypes:

from ctypes import CDLL, c_int, Structure

libc = CDLL("libc.so.6")
abs_func = libc.abs
abs_func.argtypes = [c_int]
abs_func.restype = c_int
print(abs_func(-42))  # 42

class Point(Structure):
    _fields_ = [("x", c_int), ("y", c_int)]

p = Point(10, 20)
print(p.x, p.y)  # 10 20

node-ctypes (identical patterns!):

import { CDLL, c_int, Structure } from 'node-ctypes';

// Traditional syntax (always available)
const libc = new CDLL("libc.so.6"); // Linux
// const libc = new CDLL('msvcrt.dll');  // Windows
// const libc = new CDLL('libc.dylib');  // macOS

const abs = libc.func("abs", c_int, [c_int]);
console.log(abs(-42));  // 42

// Python ctypes-like syntax
const abs_func = libc.abs;
abs_func.argtypes = [c_int];
abs_func.restype = c_int;
console.log(abs_func(-42));  // 42

class Point extends Structure {
    static _fields_ = [
        ["x", c_int],
        ["y", c_int]
    ];
}

const p = new Point(10, 20);
console.log(p.x, p.y);  // 10 20

Usage

Basic FFI - Calling C Functions

import { CDLL, c_int, c_double, c_char_p, c_size_t } from 'node-ctypes';

// Load libc
const libc = new CDLL('libc.so.6');  // Linux
// const libc = new CDLL('msvcrt.dll');  // Windows
// const libc = new CDLL('libc.dylib');  // macOS

// Traditional syntax
const abs = libc.func('abs', c_int, [c_int]);
console.log(abs(-42));  // 42

// Python ctypes-like syntax (equivalent!)
const abs_func = libc.abs;
abs_func.argtypes = [c_int];
abs_func.restype = c_int;
console.log(abs_func(-42));  // 42

// Call strlen() - string length
const strlen = libc.func('strlen', c_size_t, [c_char_p]);
console.log(strlen('Hello'));  // 5n (BigInt)

// Load libm for math functions
const libm = new CDLL('libm.so.6'); // Linux
// const libb = new CDLL('ucrtbase.dll'); // Windows
// const libm = new CDLL('libm.dylib'); // macOS

const sqrt = libm.func('sqrt', c_double, [c_double]);
console.log(sqrt(16.0));  // 4.0

Structs - Full Python ctypes Compatibility

import { Structure, c_int, c_uint32 } from 'node-ctypes';

// Simple struct - Python-like class syntax
class Point extends Structure {
    static _fields_ = [
        ["x", c_int],
        ["y", c_int]
    ];
}

// Create and initialize - direct property access!
const p = new Point(10, 20);
console.log(p.x, p.y);  // 10 20

// Modify properties directly
p.x = 100;
console.log(p.x);  // 100

// Get struct size
console.log(Point.size);  // 8

// Nested structs
class Rectangle extends Structure {
    static _fields_ = [
        ["topLeft", Point],
        ["bottomRight", Point],
        ["color", c_uint32]
    ];
}

const rect = new Rectangle({
    topLeft: { x: 0, y: 0 },
    bottomRight: { x: 100, y: 200 },
    color: 0xff0000
});

console.log(rect.topLeft.x);      // 0
console.log(rect.bottomRight.x);  // 100
console.log(rect.color);          // 16711680

Unions - Shared Memory Regions

import { Union, c_int, c_float } from 'node-ctypes';

// Union - all fields share the same memory
class IntOrFloat extends Union {
    static _fields_ = [
        ["i", c_int],
        ["f", c_float]
    ];
}

const u = new IntOrFloat();
u.f = 3.14159;
console.log(u.i);  // Bit pattern of float as integer

u.i = 42;
console.log(u.f);  // 42 reinterpreted as float

Bit Fields - Compact Data Structures

import { Structure, bitfield, c_uint32 } from 'node-ctypes';

// Bit fields for flags and compact data
class Flags extends Structure {
    static _fields_ = [
        ["enabled", bitfield(c_uint32, 1)],   // 1 bit
        ["mode", bitfield(c_uint32, 3)],      // 3 bits
        ["priority", bitfield(c_uint32, 4)],  // 4 bits
        ["reserved", bitfield(c_uint32, 24)]  // 24 bits
    ];
}

const flags = new Flags();

flags.enabled = 1;
flags.mode = 5;
flags.priority = 12;

console.log(flags.enabled);   // 1
console.log(flags.mode);      // 5
console.log(flags.priority);  // 12

Arrays - Fixed-size and Dynamic

import { c_int32, c_uint8, array } from 'node-ctypes';

// Fixed-size array
const IntArray = array(c_int32, 5);
const arr = IntArray.create([1, 2, 3, 4, 5]);

// Array access
console.log(arr[0]);  // 1
console.log(arr[4]);  // 5

// Iterate
for (const val of arr) {
    console.log(val);
}

// Arrays in structs
import { Structure, array, c_uint8 } from 'node-ctypes';

class Packet extends Structure {
    static _fields_ = [
        ["header", array(c_uint8, 8)],
        ["data", array(c_uint8, 256)]
    ];
}

const pkt = new Packet({
    header: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
    data: new Array(256).fill(0)
});

console.log(pkt.header.toString());  // [1, 2, 3, 4, 5, 6, 7, 8]

Complex Nested Structures

Real-world example from our test suite:

import { Structure, Union, c_uint8, c_uint16, c_int32, array } from 'node-ctypes';

// RGB color components
class RGB extends Structure {
    static _fields_ = [
        ["r", c_uint8],
        ["g", c_uint8],
        ["b", c_uint8],
        ["a", c_uint8]
    ];
}

// Union for color access (as RGB or as 32-bit value)
class Color extends Union {
    static _fields_ = [
        ["rgb", RGB],
        ["value", c_int32]
    ];
}

// Pixel with position and color
class Pixel extends Structure {
    static _fields_ = [
        ["x", c_uint16],
        ["y", c_uint16],
        ["color", Color]
    ];
}

// Image with array of pixels
class Image extends Structure {
    static _fields_ = [
        ["width", c_uint16],
        ["height", c_uint16],
        ["pixels", array(Pixel, 2)]
    ];
}

// Create and manipulate
const img = new Image({
    width: 640,
    height: 480,
    pixels: [
        { x: 10, y: 20, color: { rgb: { r: 255, g: 0, b: 0, a: 255 } } },
        { x: 30, y: 40, color: { value: 0xFF00FF00 } }
    ]
});

console.log(img.pixels[0].color.rgb.r);  // 255
console.log(img.pixels[1].color.value);  // -16711936 (0xFF00FF00 as signed)

// Union nested in struct - direct property access!
img.pixels[0].color.rgb.g = 128;  // Works correctly!
console.log(img.pixels[0].color.rgb.g);  // 128

Callbacks - JavaScript Functions in C

import { CDLL, callback, c_int32, c_void, c_void_p, c_size_t, readValue, writeValue, create_string_buffer } from 'node-ctypes';

const libc = new CDLL('msvcrt.dll');  // or libc.so.6 on Linux

// Create a comparison callback for qsort
const compare = callback(
    (a, b) => {
        // a and b are pointers to int32 values
        const aVal = readValue(a, c_int32);
        const bVal = readValue(b, c_int32);
        return aVal - bVal;
    },
    c_int32,              // return type
    [c_void_p, c_void_p]  // argument types: two pointers
);

// Sort an array using qsort
const qsort = libc.func('qsort', c_void, [
    c_void_p,  // array pointer
    c_size_t,  // number of elements
    c_size_t,  // element size
    c_void_p   // comparison function
]);

const arr = create_string_buffer(5 * 4);
const values = [5, 2, 8, 1, 9];
values.forEach((v, i) => writeValue(arr, c_int32, v, i * 4));
qsort(arr, 5, 4, compare.pointer);

// Array is now sorted: [1, 2, 5, 8, 9]
console.log(readValue(arr, c_int32, 0));  // 1
console.log(readValue(arr, c_int32, 4));  // 2

// IMPORTANT: Release callback when done
compare.release();

Variadic Functions - printf, sprintf

import { CDLL, create_string_buffer, string_at, c_int, c_void_p, c_char_p } from 'node-ctypes';

const libc = new CDLL('msvcrt.dll');  // Windows
// const libc = new CDLL('libc.so.6');  // Linux

// Define only the fixed parameters - variadic args detected automatically!
const sprintf = libc.func('sprintf', c_int, [c_void_p, c_char_p]);

const buffer = Buffer.alloc(256);

// Pass extra arguments - automatically handled as variadic
sprintf(buffer, 'Hello %s!', 'World');
console.log(string_at(buffer));  // "Hello World!"

sprintf(buffer, 'Number: %d', 42);
console.log(string_at(buffer));  // "Number: 42"

sprintf(buffer, '%s: %d + %d = %d', 'Sum', 10, 20, 30);
console.log(string_at(buffer));  // "Sum: 10 + 20 = 30"

sprintf(buffer, 'Pi ≈ %.2f', 3.14159);
console.log(string_at(buffer));  // "Pi ≈ 3.14"

Automatic variadic detection - When you pass more arguments than specified, node-ctypes:

  • ✅ Detects the extra arguments
  • ✅ Infers their types (string → char*, number → int32/double, Buffer → pointer)
  • ✅ Uses ffi_prep_cif_var for variadic call preparation
  • ✅ Calls the function with correct argument marshalling

This matches Python ctypes behavior exactly!

Windows API - Full Support

import { WinDLL, Structure, c_uint16, c_uint32, c_void_p, c_wchar_p, c_int } from 'node-ctypes';

// WinDLL uses __stdcall convention (default for Windows API)
const kernel32 = new WinDLL('kernel32.dll');

// SYSTEMTIME structure (from tests/windows/test_winapi.js)
class SYSTEMTIME extends Structure {
    static _fields_ = [
        ["wYear", c_uint16],
        ["wMonth", c_uint16],
        ["wDayOfWeek", c_uint16],
        ["wDay", c_uint16],
        ["wHour", c_uint16],
        ["wMinute", c_uint16],
        ["wSecond", c_uint16],
        ["wMilliseconds", c_uint16]
    ];
}

// Get local time
const GetLocalTime = kernel32.func('GetLocalTime', c_void, [c_void_p]);

const st = new SYSTEMTIME();
GetLocalTime(st);  // Pass struct directly - automatic _buffer extraction!

console.log(`${st.wYear}-${st.wMonth}-${st.wDay}`);
console.log(`${st.wHour}:${st.wMinute}:${st.wSecond}`);

// MessageBox (wide string version)
const user32 = new WinDLL('user32.dll');
const MessageBoxW = user32.func('MessageBoxW', c_int, [
    c_void_p,   // hWnd
    c_wchar_p,  // lpText
    c_wchar_p,  // lpCaption
    c_uint32    // uType
]);

// Create UTF-16 buffers for wide strings
const text = Buffer.from('Hello from node-ctypes!\0', 'utf16le');
const caption = Buffer.from('node-ctypes\0', 'utf16le');

MessageBoxW(null, text, caption, 0);

Memory Operations - Low-level Control

import { readValue, writeValue, sizeof, create_string_buffer, string_at, c_int, c_double, c_void_p } from 'node-ctypes';

// Allocate memory
const buf = Buffer.alloc(16);

// Write values at specific offsets
writeValue(buf, c_int, 12345, 0);
writeValue(buf, c_double, 3.14159, 8);

// Read values back
console.log(readValue(buf, c_int, 0));     // 12345
console.log(readValue(buf, c_double, 8));  // 3.14159

// Get type sizes
console.log(sizeof(c_int));      // 4
console.log(sizeof(c_double));   // 8
console.log(sizeof(c_void_p));   // 8 (on 64-bit)

// String handling
const str = create_string_buffer('Hello, World!');
console.log(string_at(str));  // "Hello, World!"

Performance Benchmarks

Benchmarked on Windows with Node.js v24.11.0:

vs koffi (comprehensive 10-benchmark comparison, geometric mean: 3.27x slower):

  • Simple int32 function: 1.74x slower
  • String parameter: 1.95x slower
  • Floating point: 1.83x slower
  • No arguments: 2.11x slower
  • Multiple arguments: 1.40x faster
  • Variadic function: 1.28x slower
  • Struct read/write: 14.91x slower
  • Buffer allocation: 40.5% overhead
  • Raw vs CDLL wrapper: 7.3% overhead
  • Callback creation: 1.51x slower

Key Insights:

  • koffi excels at simple operations and struct access
  • node-ctypes competitive on complex argument handling
  • Struct performance gap: koffi 15x faster due to direct object manipulation
  • Callback overhead: koffi 1.5x faster at callback creation

Transparent API overhead: Only 3.5% for auto ._buffer extraction!

See tests/benchmarks/ for full benchmark suite.

Supported Types

| Type Name | Aliases | Size | |-----------|---------|------| | void | - | 0 | | int8 | c_int8, char | 1 | | uint8 | c_uint8, uchar | 1 | | int16 | c_int16, short | 2 | | uint16 | c_uint16, ushort | 2 | | int32 | c_int32, int, c_int | 4 | | uint32 | c_uint32, uint, c_uint | 4 | | int64 | c_int64, long long | 8 | | uint64 | c_uint64 | 8 | | float | c_float | 4 | | double | c_double | 8 | | pointer | c_void_p, void*, ptr | 8 (64-bit) | | string | c_char_p, char* | pointer | | bool | c_bool | 1 | | size_t | c_size_t | pointer |

API Reference

Classes

CDLL(libPath)

Load a shared library using default (cdecl) calling convention.

WinDLL(libPath)

Load a shared library using stdcall calling convention (Windows).

Library(libPath)

Low-level library wrapper.


Detailed API Reference (from lib/index.js)

This section provides a more complete description of the APIs exported from lib/index.js, with quick examples and usage notes.

Native classes and exports

  • Version - Version information exposed by the native module.
  • Library - Represents a loaded native library and exposes low-level functions for symbols and library management.
  • FFIFunction - Low-level object representing an FFI function (has properties like address and internal methods).
  • Callback - Builds JS callbacks callable from C (main thread).
  • ThreadSafeCallback - Builds thread-safe JS callbacks (can be called from external threads).
  • CType, StructType, ArrayType - Types and helpers exposed by the native layer.

Library loading and wrappers

  • load(libPath)Library : loads a native library; libPath can be null for the current executable.
  • CDLL(libPath) : common-use wrapper for C calls with cdecl convention; maintains a function cache and provides more convenient func().
  • WinDLL(libPath) : like CDLL but with abi: 'stdcall' by default (useful for WinAPI).

Example:

import { CDLL, c_int32 } from './lib/index.js';
const libc = new CDLL(null);

// Traditional syntax
const abs = libc.func('abs', c_int32, [c_int32]);
console.log(abs(-5));

// Python ctypes-like syntax
const abs_func = libc.abs;
abs_func.argtypes = [c_int32];
abs_func.restype = c_int32;
console.log(abs_func(-5));

Detailed CDLL API

  • func(name, returnType, argTypes = [], options = {})Function : gets a callable function. The returned function is optimized and:
    • automatically extracts ._buffer from struct objects passed as arguments;
    • exposes non-enumerable metadata: funcName, address, _ffi;
    • provides the errcheck property as getter/setter to intercept return errors.
  • Python ctypes-like access: libc.functionName returns a wrapper with argtypes/restype/errcheck properties for Python-compatible syntax.
  • symbol(name)BigInt : address of a symbol.
  • close() : closes the library and clears the cache.
  • path (getter) : library path.
  • loaded (getter) : loading status.

Callback

  • callback(fn, returnType, argTypes = []){ pointer, release(), _callback } : fast callback, main thread only.
  • threadSafeCallback(fn, returnType, argTypes = []){ pointer, release(), _callback } : thread-safe callback for external threads.

Note: always call release() when a callback is no longer needed.

Allocation and strings

  • Buffer.alloc(size)Buffer : allocates native memory.
  • create_string_buffer(init)Buffer : creates null-terminated C string (init: size|string|Buffer).
  • create_unicode_buffer(init)Buffer : creates null-terminated wide string (wchar_t).
  • ptrToBuffer(address, size)Buffer : view on native address (use with caution).
  • addressof(ptr)BigInt : get the address as BigInt.

Example string creation and passing to function:

import { create_string_buffer, CDLL, c_int32, c_void_p } from './lib/index.js';
const libc = new CDLL(null);
const puts = libc.func('puts', c_int32, [c_void_p]);
const s = create_string_buffer('hello');
puts(s);

Reading and writing values

  • readValue(ptr, type, offset = 0) : supports fast-path for Buffer + basic types (int8, uint8, int16, int32, int64, float, double, bool).
  • writeValue(ptr, type, value, offset = 0) : writes values with fast-path for Buffer.

Types and helpers

  • sizeof(type)number : size in bytes of a type.
  • POINTER(baseType) : creates a pointer type with helpers create(), fromBuffer(), deref(), set().
  • byref(buffer) : passes a buffer by reference (Python ctypes compatibility).
  • cast(ptr, targetType) : interprets a pointer as another type (returns wrapper for struct).

Struct / Union / Array / Bitfield

  • struct(fields, options) : defines struct with support for nested, bitfields, anonymous fields, packed option. Returns object with create(), get(), set(), toObject(), getNestedBuffer().
  • union(fields) : defines union; provides create(), get(), set(), toObject() and returns plain objects with properties.
  • array(elementType, count) : defines ArrayType; wrap(buffer) returns Proxy with indexing.
  • bitfield(baseType, bits) : bitfield definition.

Struct example:

class Point extends Structure {
    static _fields_ = [
        ["x", c_int32],
        ["y", c_int32]
    ];
}

const p = new Point(1, 2);
console.log(p.x, p.y);  // 1 2

Python-compatible conveniences

  • create_string_buffer(init) : create string buffer from number/string/Buffer.
  • create_unicode_buffer(init) : create wide string buffer.
  • string_at(address, size) / wstring_at(address, size) : read strings from address.

Memory: utilities

  • memmove(dst, src, count) : copy memory.
  • memset(dst, value, count) : set memory.

Error handling and WinAPI helpers

  • get_errno() / set_errno(value) : access to errno (platform-specific implementation).
  • _initWinError() internals; public helpers: GetLastError(), SetLastError(code), FormatError(code), WinError(code).

Type aliases The following aliases are exposed (mapped from native.types): c_int, c_uint, c_int8, c_uint8, c_int16, c_uint16, c_int32, c_uint32, c_int64, c_uint64, c_float, c_double, c_char, c_char_p, c_wchar, c_wchar_p, c_void_p, c_bool, c_size_t, c_long, c_ulong.

Constants

  • POINTER_SIZE - pointer size (from native.POINTER_SIZE).
  • WCHAR_SIZE - wchar size (from native.WCHAR_SIZE).
  • NULL - exported null value.

If you want, I can generate additional Windows or Linux-specific snippets, or integrate examples in the tests/ directory.

Functions

load(libPath)Library

Load a shared library.

callback(fn, returnType, argTypes){pointer, release()}

Create a callback from a JavaScript function.

create_string_buffer(init)Buffer

Create a null-terminated C string buffer (like Python ctypes). init can be a size, a string, or an existing Buffer.

create_unicode_buffer(init)Buffer

Create a wide (wchar_t) null-terminated buffer (UTF-16LE on Windows).

string_at(address, [size])string

Read a C string from an address or buffer.

readValue(ptr, type, [offset])value

Read a value from memory.

writeValue(ptr, type, value, [offset])bytesWritten

Write a value to memory.

sizeof(type)number

Get the size of a type in bytes.

struct(fields)StructDefinition

Create a simple struct definition.

Structure (base class)

Base class for Python-like struct definitions. Subclasses should define static _fields_.

Union (base class)

Base class for Python-like union definitions. Subclasses should define static _fields_.

Python ctypes Compatibility Reference

API Comparison

| Feature | Python ctypes | node-ctypes | |---------|---------------|-------------| | Load library | CDLL("lib.so") | new CDLL("lib.so") | | Define function | lib.func.argtypes = [c_int]lib.func.restype = c_int | lib.func("func", c_int, [c_int])orlib.func.argtypes = [c_int]lib.func.restype = c_int | | Structs | class Point(Structure):  _fields_ = [("x", c_int)] | class Point extends Structure  { static _fields_ = [["x", c_int]] } | | Unions | class U(Union):  _fields_ = [("i", c_int)] | class U extends Union  { static _fields_ = [["i", c_int]] } | | Arrays | c_int * 5 | array(c_int, 5) | | Bit fields | ("flags", c_uint, 3) | bitfield(c_uint32, 3) | | Callbacks | CFUNCTYPE(c_int, c_int) | callback(fn, c_int, [c_int]) | | Strings | c_char_p(b"hello") | create_string_buffer("hello")orc_char_p(b"hello") | | Pointers | POINTER(c_int) | c_void_p | | Variadic | sprintf(buf, b"%d", 42) | sprintf(buf, fmt, 42) (auto) | | Sizeof | sizeof(c_int) | sizeof(c_int) |

What's Supported

Fully Compatible:

  • All basic types (int8-64, uint8-64, float, double, bool, pointer, string)
  • Structs with nested structures
  • Unions (including nested in structs)
  • Bit fields
  • Arrays (fixed-size)
  • Callbacks (with manual release)
  • Variadic functions (automatic detection)
  • Anonymous fields in structs/unions
  • Class-based struct/union definitions (class MyStruct extends Structure)
  • Platform-specific types (c_long, c_ulong, c_size_t)
  • Memory operations (alloc, read, write)
  • Windows API (__stdcall via WinDLL)

⚠️ Differences from Python ctypes:

  • Structs use .toObject() for property access (eager loading for performance)
  • Callbacks must be manually released with .release()
  • Function definition supports both syntaxes: func(name, returnType, argTypes) or func.argtypes = [...]; func.restype = ...
  • No POINTER() type - use c_void_p

Limitations & Known Issues

  • ⚠️ Callbacks must be released manually with .release() to prevent memory leaks
  • ⚠️ No automatic memory management for returned pointers (manual free() required)
  • ⚠️ Struct alignment follows platform defaults (not customizable per-field)
  • ℹ️ Nested union access uses getter caching (slight behavior difference from Python)
  • ℹ️ Struct property access via .toObject() instead of direct field access (performance optimization)

Examples in Test Suite

For complete, working examples, see the test suite:

Run tests:

cd tests
npm install 
npm run test               # All tests
npm run bench:koffi        # Benchmark vs koffi

Examples

For practical GUI application examples using the Windows API:

License

MIT

Credits

Built with: