# Windows

> Call Windows interfaces directly from JavaScript with bun:windows

`bun:windows` exposes Windows interfaces as synchronous functions: the registry, services, the event log, the clipboard, known folders, processes, Job Objects, toast notifications and WSL. The module loads on first import, so it costs nothing at startup.

```ts
import windows from "bun:windows";

if (windows.isSupported) {
  console.log(windows.version().productName); // "Windows 11 Pro"
}
```

On other platforms `isSupported` is `false`. Every function exists, and each call throws an error with code `ERR_BUN_WINDOWS_UNSUPPORTED`.

---

## Errors

A failed Windows call throws a `SystemError` with `code` (such as `"EACCES"`), `syscall` (the Win32 function) and `winError`, the raw `GetLastError` or `HRESULT` value. The message includes the system text for the error.

```ts
try {
  windows.services.stop("EventLog");
} catch (error) {
  // Without elevation: error.winError is 5 (ERROR_ACCESS_DENIED)
  console.log(error.code, error.syscall, error.winError);
}
```

Invalid arguments throw `TypeError` or `RangeError` before any Windows call runs.

---

## System

```ts
const v = windows.version();
v.build; // 26100
v.displayVersion; // "24H2"
v.isWindows11; // true

windows.isWindows11(); // false on other platforms instead of throwing
windows.isElevated(); // true in an administrator process

const info = windows.systemInfo();
info.totalMemory; // bytes
info.theme; // "dark" | "light"
```

`version()` reads `RtlGetVersion` and the `CurrentVersion` registry key once. `productName` says Windows 11 on builds 22000 and later, where the registry still says Windows 10.

---

## Registry

Paths start with a root (`HKCR`, `HKCU`, `HKLM`, `HKU`, `HKCC`, or the long `HKEY_*` names) and use `\` or `/`.

```ts
const { registry } = windows;

registry.set("HKCU\\Software\\MyApp", "Theme", "dark"); // REG_SZ
registry.set("HKCU\\Software\\MyApp", "Launches", 3); // REG_DWORD
registry.set("HKCU\\Software\\MyApp", "Id", 2n ** 40n); // REG_QWORD
registry.set("HKCU\\Software\\MyApp", "Paths", ["C:\\a", "C:\\b"]); // REG_MULTI_SZ
registry.set("HKCU\\Software\\MyApp", "Path", "%USERPROFILE%\\x", "REG_EXPAND_SZ");

registry.get("HKCU\\Software\\MyApp", "Theme"); // { type: "REG_SZ", value: "dark" }
registry.get("HKCU\\Software\\MyApp", "Missing"); // null

registry.list("HKCU\\Software\\MyApp"); // { keys: [], values: [{ name, type, value }, ...] }
registry.deleteValue("HKCU\\Software\\MyApp", "Theme"); // true
registry.deleteKey("HKCU\\Software\\MyApp", { recursive: true }); // true
```

`set` creates missing keys. `REG_QWORD` values read as `bigint` and binary values as `Buffer`. Pass `{ view: "64" }` or `{ view: "32" }` to choose the registry view.

---

## Services

```ts
windows.services.list(); // [{ name, displayName, state, type, pid }, ...]
windows.services.list({ drivers: true });

const svc = windows.services.get("Spooler");
svc?.state; // "running"
svc?.startType; // "auto"

windows.services.stop("Spooler"); // needs an elevated process
windows.services.start("Spooler");
```

`get` returns `null` for an unknown service. Starting a running service and stopping a stopped service are not errors.

---

## Event log

```ts
const errors = windows.eventLog.query("System", {
  xpath: "*[System[(Level=2)]]",
  limit: 10,
});
// Array of event XML strings, newest first

windows.eventLog.write("MyApp", "Started", { type: "information", eventId: 1 });
```

`write` adds an event to the Application log. A source that is not registered still writes, and Event Viewer shows the message with a note about the missing source.

---

## Clipboard

```ts
windows.clipboard.writeText("hello");
windows.clipboard.readText(); // "hello"
windows.clipboard.clear();
windows.clipboard.readText(); // null
```

---

## Known folders

```ts
windows.knownFolder("Downloads"); // "C:\\Users\\me\\Downloads"
windows.knownFolder("ProgramData"); // "C:\\ProgramData"
windows.knownFolder("{FDD39AD0-238F-46AF-ADB4-6C85480369C7}"); // Documents
```

`windows.knownFolders` maps the accepted names to their `KNOWNFOLDERID` GUIDs. Any other GUID works too.

---

## ConPTY

```ts
const info = windows.conpty.info();
info.supported; // true on Windows 10 1809+
info.releasePseudoConsole; // true on Windows 11 24H2+
info.closeBlocks; // true when ClosePseudoConsole waits for the output pipe to drain

const terminal = windows.conpty.open({ cols: 80, rows: 24, data(term, chunk) {} });
terminal.resize(100, 30);
terminal.close();
```

`conpty.open()` returns a `Bun.Terminal`. `info()` reads `RtlGetVersion` and checks kernel32 for `ReleasePseudoConsole` once.

---

## Direct3D 12

```ts
const info = windows.gpu.d3d12.info();
info.adapter; // description of DXGI adapter 0
info.featureLevel; // "12_0" or "11_0"

const pixels = windows.gpu.d3d12.clearRenderTarget(4, 4, [0, 1, 0, 1]); // Uint8Array, RGBA8, 64 bytes
const copy = windows.gpu.d3d12.copyBuffer(new Uint8Array([1, 2, 3])); // upload, GPU copy, readback
```

Each call creates a D3D12 device on adapter 0, runs its commands and waits on a fence before it returns. Nothing is kept between calls. Limits: render targets up to 4096 x 4096, buffers up to 64 MiB. Not provided: swap chains, presentation, shaders, pipeline states, D3D11 and Direct2D.

---

## Processes

```ts
windows.processes.list(); // [{ pid, ppid, name, threads }, ...]
windows.processes.path(process.pid); // full path of bun.exe
windows.processes.terminate(1234, 1);
```

A `pid` argument also accepts an object with a `pid` property, such as the `Subprocess` returned by `Bun.spawn`.

---

## Job Objects

A Job Object groups processes under shared limits and accounting. Children of an assigned process join the job.

```ts
using job = new windows.Job({
  killOnClose: true,
  jobMemory: 512 * 1024 * 1024,
  cpuRate: 50,
});

const child = Bun.spawn(["bun", "worker.ts"]);
job.assign(child);

job.info(); // { activeProcesses, totalProcesses, userTime, kernelTime, peakJobMemory, pids, ... }
job.terminate(1);
```

With `killOnClose`, closing the job (or leaving the `using` block) terminates its processes. A job that is garbage collected is closed too.

| Limit             | Meaning                                     |
| ----------------- | ------------------------------------------- |
| `killOnClose`     | Terminate the processes when the job closes |
| `processMemory`   | Committed memory per process, in bytes      |
| `jobMemory`       | Committed memory of the whole job, in bytes |
| `activeProcesses` | Maximum number of live processes            |
| `cpuRate`         | Hard CPU cap, percentage of the machine     |

---

## Notifications

```ts
windows.notify("Build finished", "3 warnings");

windows.toast(`<toast><visual><binding template="ToastGeneric">
  <text>Custom</text>
</binding></visual></toast>`);
```

Notifications are shown under an App User Model ID. The default is the one of Windows PowerShell, which every Windows install has. Pass `{ appId }` to use the id of your own installed app.

---

## WSL

```ts
windows.wsl.distributions(); // [{ id, name, basePath, version, default }, ...]

const { exitCode, stdout } = windows.wsl.run("Ubuntu", ["uname", "-r"]);
```

`distributions` reads the registry and does not start WSL. `run` calls `wsl.exe -d <name> -- <command>` and waits for it.

---

## NTFS

Read-only access to the USN change journal and the Master File Table of an NTFS volume.

```ts
const { ntfs } = windows;
using vol = new ntfs.Volume("C"); // drive letter or "\\\\?\\Volume{guid}\\"

const journal = vol.journalQuery(); // { journalId, nextUsn, maxSize, ... }
vol.journalCreate(); // creates the journal if missing; no-op otherwise

const change = vol.journalRead(journal.nextUsn, { bufferSize: 64 * 1024 });
change.records; // [{ usn, record, parent, fileName, reason, timestamp, ... }, ...]
change.nextUsn; // pass back in to continue reading

const all = vol.enumerateMft(4 << 20); // every record, bounded by one read buffer size
all.find(r => r.fileName === "autoexec.bat");
```

`record` and `parent` are NTFS file reference numbers: the low 48 bits are the stable MFT record number, the high 16 bits are a reuse sequence. `FSCTL_ENUM_USN_DATA` does not return the reserved metadata files (record numbers 0-15, including the volume root); their attributes are still readable through ordinary path APIs. Opening a volume handle needs an elevated process.

---

## PE, Authenticode and catalog signatures

Pure byte-level parsing of PE32/PE32+ images: headers, sections, imports, exports, resources, ApiSet redirection and Authenticode. Nothing here maps or executes the image — no `LoadLibrary`, no WinRT activation, no `DllMain`.

```ts
const { pe } = windows;
const bytes = new Uint8Array(await Bun.file("C:\\Windows\\System32\\kernel32.dll").arrayBuffer());

pe.looksLikePe(bytes); // true: cheap DOS/NT header check before a full parse

const info = pe.parse(bytes, "kernel32.dll");
info.kind; // "exe" | "dll" | "sys" | ...
info.machine; // "x86" | "x64" | "arm64" | "arm64ec"
info.sections; // [{ name, virtualAddress, virtualSize, rawSize, characteristics }, ...]
info.exports; // [{ name, ordinal, rva, forwarder }, ...]
info.imports; // [{ module, names, delayLoad }, ...]
info.authenticode; // { signer, issuer, digest, certificates } when the certificate table embeds a signature, else null
```

`pe.authenticode.parse(bytes)` parses a standalone PKCS#7 `SignedData` blob (the `WIN_CERTIFICATE` payload, or a `.cat` file) without needing a full PE image.

Most Windows system binaries carry no embedded signature and are listed in a system catalog instead:

```ts
const catalog = pe.catalogFile("C:\\Windows\\System32\\kernel32.dll"); // full path of the .cat file, or null
const signature = pe.catalogSignature("C:\\Windows\\System32\\kernel32.dll"); // { catalog, signer, issuer, digest } | null

pe.releaseCatalogContexts(); // releases the cached SHA-256/SHA-1 admin contexts; optional
```

A `null` result means "not listed in any catalog", never "unsigned" — a file can be both embedded-signed and catalog-listed, or signed through neither API and still be trusted by a mechanism this module does not cover. `catalogFile`/`catalogSignature` are slow (a catalog-database query): avoid them in a hot path over many files.

```ts
pe.parseApiSetSchema(bytes); // apisetschema.dll's `.apiset` section: contract -> host DLLs
pe.apiSetKey("api-ms-win-core-file-l1-2-4.dll"); // "api-ms-win-core-file-l1-2"
```

---

## API families

The Bun binary contains the interfaces above. Other Windows API families ship as packages named `@aphrody/bun-windows-<family>`, and `bun:windows` loads them on first use.

```ts
const kernel32 = windows.family("kernel32"); // exports of @aphrody/bun-windows-kernel32
windows.families.user32; // same as windows.family("user32")
"gdi32" in windows.families; // true when the package is installed
```

The package is resolved from the working directory, then from the entry script, and loaded once per process. A family that is not installed throws an error with code `ERR_BUN_WINDOWS_FAMILY_NOT_FOUND`.

`BUN_WINDOWS_FAMILY_PATH` adds directories to search for `bun-windows-<family>` package folders (separated by `;`).

### Win32 functions, structs and COM

The families are generated from [win32metadata](https://github.com/microsoft/win32metadata) by `scripts/aphrody/win32/gen.ts`: one package per DLL (368 families, about 18,000 functions), plus `@aphrody/bun-windows-win32` with the struct, enum, constant and COM interface descriptions they share. A function is bound with `bun:ffi` the first time it is read.

```ts
const kernel32 = windows.family("kernel32");
kernel32.GetCurrentProcessId(); // 1234
kernel32.constants.PROCESS_QUERY_LIMITED_INFORMATION; // 0x1000

const { win32 } = windows;
const mem = win32.struct("MEMORYSTATUSEX"); // dwLength is filled in
kernel32.GlobalMemoryStatusEx(mem);
mem.ullAvailPhys; // bigint
```

Arguments are converted from their declared types:

- Strings are passed to `PWSTR`, `PSTR` and `BSTR` parameters.
- Plain objects are passed to struct pointers.
- Structs of 8 bytes or less are passed by value (`PtInRect(rect, { x, y })`).
- Functions are passed to callback parameters.

Functions that set the last error record it in `win32.lastError()`.

COM classes are created by coclass name or CLSID. Methods are called through the vtable. `[retval]` parameters become the return value. A failing `HRESULT` throws a `win32.Win32Error`:

```ts
const service = win32.com.create("TaskScheduler", "System.TaskScheduler.ITaskService");
service.Connect(undefined, undefined, undefined, undefined);
service.GetFolder("\\").GetTasks(1).get_Count();
service.release();
```

`win32.com.delegate(iid, handler)` returns a COM callback object that any thread may call. Its reference count is atomic and lives in native code. `handler` runs on the JavaScript thread. Use it for completion and event callbacks that Windows invokes from its thread pool.
