From 1d126dfa7890a8b9f3b2a07f25d9c8cfba97eb6f Mon Sep 17 00:00:00 2001 From: "Gordon Lam (SH) (from Dev Box)" Date: Fri, 24 Jul 2026 12:09:14 +0800 Subject: [PATCH 01/11] docs(readme): add Win32 + classic COM section (JavaScript/TypeScript) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Broaden the tagline/intro to reflect three metadata-driven surfaces (WinRT, classic COM, flat Win32) and add a dedicated "New: Win32 and classic COM support" section with copy-pasteable, snapshot-verified examples: - Classic COM via CoCreateInstance + vtable (ITaskbarList3) - WinRT interop bridge from HWND (IDataTransferManagerInterop) - Flat Win32 [DllImport] exports (Registry RegOpenKeyExW) Docs-only; no code changes. Documents the capabilities added by the classic-COM (#65) and flat-Win32 (#67) PRs — best merged after those land. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- README.md | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index eba8cf69..6837439b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # dynwinrt -**Call any Windows Runtime (WinRT) API from JavaScript or TypeScript — without writing a native addon.** +**Call Windows APIs — WinRT, classic COM, and flat Win32 (`[DllImport]`) — from JavaScript or TypeScript without writing a native addon.** [![@microsoft/dynwinrt](https://img.shields.io/npm/v/@microsoft/dynwinrt.svg?label=%40microsoft%2Fdynwinrt)](https://www.npmjs.com/package/@microsoft/dynwinrt) [![@microsoft/dynwinrt-codegen](https://img.shields.io/npm/v/@microsoft/dynwinrt-codegen.svg?label=%40microsoft%2Fdynwinrt-codegen)](https://www.npmjs.com/package/@microsoft/dynwinrt-codegen) @@ -14,7 +14,7 @@ If you've ever tried to call a modern Windows API (WinAppSDK, Windows AI, notifi - **Writing a C# addon via `node-api-dotnet`** — needs the .NET SDK, a `csproj` build step, and a hand-maintained wrapper for every API surface you want to expose. - **Waiting for an official projection** — Windows ships `.winmd` metadata months before any JavaScript- or Python-friendly projection appears in a published package. -`dynwinrt` removes all of that. It reads the same `.winmd` metadata your Windows SDK / WinAppSDK NuGet packages already ship and calls the underlying COM vtables **dynamically at runtime via libffi**. The codegen emits typed `.js` + `.d.ts` wrappers; the runtime invokes them through `dynwinrt`'s native binary. No MSBuild step in your app, no `node-gyp`, no per-Windows-version recompile. +`dynwinrt` removes all of that. It reads the same `.winmd` metadata your Windows SDK / WinAppSDK NuGet packages already ship and calls the underlying COM vtables **dynamically at runtime via libffi**. The codegen emits typed `.js` + `.d.ts` wrappers; the runtime invokes them through `dynwinrt`'s native binary. No MSBuild step in your app, no `node-gyp`, no per-Windows-version recompile. The same runtime and generator now cover three metadata-driven surfaces — **WinRT**, **classic COM**, and **flat Win32 (`[DllImport]`)** — so you can reach Windows APIs whether or not they ship as WinRT runtime classes. ```ts import { LanguageModel } from './bindings/winrt'; @@ -87,6 +87,82 @@ set `WINAPPSDK_BOOTSTRAP_DLL_PATH` to the architecture-matched configures its UI thread for Per-Monitor V2 DPI awareness. Packaged processes can omit the bootstrap call. +## New: Win32 and classic COM support (JavaScript/TypeScript) + +`dynwinrt` now reaches beyond WinRT runtime classes. From the same metadata-driven pipeline it can also project **Windows APIs that are not WinRT**: + +- **Classic COM** — `IUnknown`-rooted interfaces created via `CoCreateInstance` and dispatched through the COM vtable (e.g. taskbar, shell). +- **WinRT interop bridges** — the desktop `*Interop` shims a windowed app needs to reach WinRT features from an `HWND` (Share, media controls, file-picker parenting). +- **Flat Win32 `[DllImport]`** — plain DLL exports (e.g. Registry, credentials) called through `LoadLibrary` + `GetProcAddress` + libffi. + +Generate Win32/COM wrappers by pointing the codegen at the Win32 metadata (`Windows.Win32.winmd` from the [win32metadata](https://github.com/microsoft/win32metadata) package): + +```bash +npx dynwinrt-codegen generate \ + --winmd path/to/Windows.Win32.winmd \ + --namespace Windows.Win32.UI.Shell \ + --class-name ITaskbarList3 \ + --output ./generated +``` + +> Win32 and classic-COM generation currently emits JavaScript/TypeScript (`.js` + `.d.ts`). + +### Classic COM: `CoCreateInstance` + vtable interfaces + +```js +import { ITaskbarList3 } from './generated/ITaskbarList3.js'; +import { TBPFLAG } from './generated/TBPFLAG.js'; + +// HWND is generated as `bigint | Buffer`. In Electron, +// BrowserWindow.getNativeWindowHandle() returns a Buffer you can pass directly. +const hwnd = 0x0000000000123456n; // your real HWND + +const taskbar = ITaskbarList3.create(); // CoCreateInstance under the hood +taskbar.hrInit(); +taskbar.setProgressState(hwnd, TBPFLAG.TBPF_NORMAL); +taskbar.setProgressValue(hwnd, 40n, 100n); // 40% +``` + +The generated wrapper exposes natural typed methods while the runtime handles `CoCreateInstance`, interface registration, pointer arguments, `HRESULT` checks, and vtable slot dispatch — no hand-written IIDs, `REFIID`, `void**`, or vtable indices. + +### WinRT interop bridges: from `HWND` to WinRT objects + +```js +import { IDataTransferManagerInterop } from './generated/IDataTransferManagerInterop.js'; + +const hwnd = 0x0000000000123456n; // your real HWND or accepted Buffer handle + +const interop = IDataTransferManagerInterop.create(); +const dtm = interop.getForWindow(hwnd); // → DynWinRtValue bridge to DataTransferManager +interop.showShareUIForWindow(hwnd); +``` + +Interop interfaces are the Windows pattern for features that require an `HWND` but operate on WinRT objects. `create()` activates the WinRT factory and QIs to the interop interface; `getForWindow(hwnd)` adopts the returned COM pointer and hands it back as a `DynWinRtValue` bridge — with no `riid`/`void**` in the signature. + +### Flat Win32 exports: call `[DllImport]` APIs + +```js +import { regOpenKeyExW, regCloseKey } from './generated/Apis.js'; +import { REG_SAM_FLAGS } from './generated/REG_SAM_FLAGS.js'; + +const HKEY_LOCAL_MACHINE = 0x80000002n; + +const open = regOpenKeyExW( + HKEY_LOCAL_MACHINE, + 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion', // note: doubled backslashes in JS + 0, + REG_SAM_FLAGS.KEY_READ, +); +if (open.status !== 0) throw new Error(`RegOpenKeyExW failed: LSTATUS=${open.status}`); +try { + console.log(`hKey = 0x${open.phkResult.toString(16)}`); +} finally { + regCloseKey(open.phkResult); +} +``` + +`[out]` parameters become return fields (`{ status, phkResult }`), caller-allocated buffers and in/out sizes are handled for you, `LPCWSTR` ↔ `string`, and the Win32 `LSTATUS`/error is surfaced. Handles (`HKEY`, `HANDLE`) project as `bigint`. + ## Repository layout ``` From 815cb2adc6a61495dee4dc71b62977fa5dc48c82 Mon Sep 17 00:00:00 2001 From: "Gordon Lam (SH) (from Dev Box)" Date: Fri, 24 Jul 2026 14:35:43 +0800 Subject: [PATCH 02/11] docs(readme): add one-step Windows.Win32.winmd download guide Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 6837439b..3aabf458 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,17 @@ can omit the bootstrap call. Generate Win32/COM wrappers by pointing the codegen at the Win32 metadata (`Windows.Win32.winmd` from the [win32metadata](https://github.com/microsoft/win32metadata) package): +### Getting `Windows.Win32.winmd` + +`Windows.Win32.winmd` is not on a stock Windows machine — it ships in the MIT-licensed NuGet package [`Microsoft.Windows.SDK.Win32Metadata`](https://www.nuget.org/packages/Microsoft.Windows.SDK.Win32Metadata). A `.nupkg` is just a zip, and the `.winmd` sits at its root, so you can grab it with one command (no NuGet client required): + +```powershell +$v = '71.0.14-preview' # latest at time of writing; see nuget.org for newer +Invoke-WebRequest "https://api.nuget.org/v3-flatcontainer/microsoft.windows.sdk.win32metadata/$v/microsoft.windows.sdk.win32metadata.$v.nupkg" -OutFile win32meta.zip +Expand-Archive win32meta.zip -DestinationPath win32meta +# → win32meta\Windows.Win32.winmd (pass this path to --winmd below) +``` + ```bash npx dynwinrt-codegen generate \ --winmd path/to/Windows.Win32.winmd \ From 748ebf15295e2d80d4852c810b89741ddab7f77f Mon Sep 17 00:00:00 2001 From: "Gordon Lam (SH) (from Dev Box)" Date: Fri, 24 Jul 2026 22:35:46 +0800 Subject: [PATCH 03/11] docs(readme): foolproof winmd fetch (auto-latest) + correct Electron HWND guidance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace the hardcoded-version winmd download with a single block that auto-resolves the latest Microsoft.Windows.SDK.Win32Metadata version from nuget.org, downloads, and extracts to ./win32meta — no version to pick, no NuGet client. The generate example now references ./win32meta/Windows.Win32.winmd. Verified end-to-end (resolves 71.0.14-preview, extracts the 23 MB winmd). - Fix now-incorrect handle guidance: classic-COM HWND projects as `bigint | number` (not `bigint | Buffer`). For Electron, read the value out of getNativeWindowHandle() (readBigUInt64LE(0)) — do not pass the Buffer itself. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- README.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 3aabf458..8dc3eb8b 100644 --- a/README.md +++ b/README.md @@ -99,18 +99,19 @@ Generate Win32/COM wrappers by pointing the codegen at the Win32 metadata (`Wind ### Getting `Windows.Win32.winmd` -`Windows.Win32.winmd` is not on a stock Windows machine — it ships in the MIT-licensed NuGet package [`Microsoft.Windows.SDK.Win32Metadata`](https://www.nuget.org/packages/Microsoft.Windows.SDK.Win32Metadata). A `.nupkg` is just a zip, and the `.winmd` sits at its root, so you can grab it with one command (no NuGet client required): +`Windows.Win32.winmd` is not on a stock Windows machine — it ships in the MIT-licensed NuGet package [`Microsoft.Windows.SDK.Win32Metadata`](https://www.nuget.org/packages/Microsoft.Windows.SDK.Win32Metadata). A `.nupkg` is just a zip with the `.winmd` at its root, so this one block fetches the **latest** version and drops it in `./win32meta` — no NuGet client, and no version number to pick: ```powershell -$v = '71.0.14-preview' # latest at time of writing; see nuget.org for newer -Invoke-WebRequest "https://api.nuget.org/v3-flatcontainer/microsoft.windows.sdk.win32metadata/$v/microsoft.windows.sdk.win32metadata.$v.nupkg" -OutFile win32meta.zip -Expand-Archive win32meta.zip -DestinationPath win32meta -# → win32meta\Windows.Win32.winmd (pass this path to --winmd below) +$pkg = 'microsoft.windows.sdk.win32metadata' +$ver = (Invoke-RestMethod "https://api.nuget.org/v3-flatcontainer/$pkg/index.json").versions[-1] +Invoke-WebRequest "https://api.nuget.org/v3-flatcontainer/$pkg/$ver/$pkg.$ver.nupkg" -OutFile "$env:TEMP\win32meta.zip" +Expand-Archive "$env:TEMP\win32meta.zip" -DestinationPath .\win32meta -Force +# → .\win32meta\Windows.Win32.winmd ``` ```bash npx dynwinrt-codegen generate \ - --winmd path/to/Windows.Win32.winmd \ + --winmd ./win32meta/Windows.Win32.winmd \ --namespace Windows.Win32.UI.Shell \ --class-name ITaskbarList3 \ --output ./generated @@ -124,8 +125,9 @@ npx dynwinrt-codegen generate \ import { ITaskbarList3 } from './generated/ITaskbarList3.js'; import { TBPFLAG } from './generated/TBPFLAG.js'; -// HWND is generated as `bigint | Buffer`. In Electron, -// BrowserWindow.getNativeWindowHandle() returns a Buffer you can pass directly. +// HWND is generated as `bigint | number`. In Electron, read the handle value +// out of BrowserWindow.getNativeWindowHandle() (a Buffer) — don't pass the +// Buffer itself: const hwnd = win.getNativeWindowHandle().readBigUInt64LE(0); const hwnd = 0x0000000000123456n; // your real HWND const taskbar = ITaskbarList3.create(); // CoCreateInstance under the hood @@ -141,7 +143,7 @@ The generated wrapper exposes natural typed methods while the runtime handles `C ```js import { IDataTransferManagerInterop } from './generated/IDataTransferManagerInterop.js'; -const hwnd = 0x0000000000123456n; // your real HWND or accepted Buffer handle +const hwnd = 0x0000000000123456n; // your real HWND as a bigint (see the Electron note above) const interop = IDataTransferManagerInterop.create(); const dtm = interop.getForWindow(hwnd); // → DynWinRtValue bridge to DataTransferManager From b27afe97535dcd6b59db45a2cbb6c9278be3d0dc Mon Sep 17 00:00:00 2001 From: "Gordon Lam (SH) (from Dev Box)" Date: Sat, 25 Jul 2026 07:03:57 +0800 Subject: [PATCH 04/11] docs(readme): make Win32 setup a single copy-paste PowerShell block MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fetch and generate steps were two separate blocks, and the generate block used bash `\` line-continuations that break when pasted into PowerShell on Windows. Merge them into one PowerShell block that fetches the latest winmd and runs `npx dynwinrt-codegen generate` in one paste — no editing, no version to pick. Verified the fetch+generate flow end-to-end (produces ITaskbarList3.js/.d.ts). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- README.md | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 8dc3eb8b..89351d52 100644 --- a/README.md +++ b/README.md @@ -99,23 +99,17 @@ Generate Win32/COM wrappers by pointing the codegen at the Win32 metadata (`Wind ### Getting `Windows.Win32.winmd` -`Windows.Win32.winmd` is not on a stock Windows machine — it ships in the MIT-licensed NuGet package [`Microsoft.Windows.SDK.Win32Metadata`](https://www.nuget.org/packages/Microsoft.Windows.SDK.Win32Metadata). A `.nupkg` is just a zip with the `.winmd` at its root, so this one block fetches the **latest** version and drops it in `./win32meta` — no NuGet client, and no version number to pick: +`Windows.Win32.winmd` is not on a stock Windows machine — it ships in the MIT-licensed NuGet package [`Microsoft.Windows.SDK.Win32Metadata`](https://www.nuget.org/packages/Microsoft.Windows.SDK.Win32Metadata). A `.nupkg` is just a zip with the `.winmd` at its root, so **copy-paste this whole block into PowerShell as-is** — it fetches the latest winmd and generates `ITaskbarList3` wrappers into `./generated` (no NuGet client, no version number to pick): ```powershell $pkg = 'microsoft.windows.sdk.win32metadata' $ver = (Invoke-RestMethod "https://api.nuget.org/v3-flatcontainer/$pkg/index.json").versions[-1] Invoke-WebRequest "https://api.nuget.org/v3-flatcontainer/$pkg/$ver/$pkg.$ver.nupkg" -OutFile "$env:TEMP\win32meta.zip" Expand-Archive "$env:TEMP\win32meta.zip" -DestinationPath .\win32meta -Force -# → .\win32meta\Windows.Win32.winmd +npx dynwinrt-codegen generate --winmd .\win32meta\Windows.Win32.winmd --namespace Windows.Win32.UI.Shell --class-name ITaskbarList3 --output ./generated ``` -```bash -npx dynwinrt-codegen generate \ - --winmd ./win32meta/Windows.Win32.winmd \ - --namespace Windows.Win32.UI.Shell \ - --class-name ITaskbarList3 \ - --output ./generated -``` +Swap `--namespace` / `--class-name` for whatever API you need; reuse the same `.\win32meta\Windows.Win32.winmd` for every run. > Win32 and classic-COM generation currently emits JavaScript/TypeScript (`.js` + `.d.ts`). From 763450834f0db7ea0f586ec19cb42c1eca397568 Mon Sep 17 00:00:00 2001 From: "Gordon Lam (SH) (from Dev Box)" Date: Mon, 27 Jul 2026 09:10:07 +0800 Subject: [PATCH 05/11] docs(readme): lead the Win32 generate example with DWM (dark title bar / Mica) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two research sub-agents (npm demand analysis + local generation verification) independently converged: the most compelling Win32 example for Electron devs is DWM window effects — the Windows 11 dark title bar / Mica / Acrylic look, which has NO Electron/JS equivalent and uses the same HWND from getNativeWindowHandle(). Change the generate line to `--namespace Windows.Win32.Graphics.Dwm --class-name Apis` and point out the other high-demand APIs (ITaskbarList3, Credential Manager as a keytar replacement, Registry). Verified the exact block generates `dwmSetWindowAttribute` end-to-end. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 89351d52..ce1c0d79 100644 --- a/README.md +++ b/README.md @@ -99,17 +99,17 @@ Generate Win32/COM wrappers by pointing the codegen at the Win32 metadata (`Wind ### Getting `Windows.Win32.winmd` -`Windows.Win32.winmd` is not on a stock Windows machine — it ships in the MIT-licensed NuGet package [`Microsoft.Windows.SDK.Win32Metadata`](https://www.nuget.org/packages/Microsoft.Windows.SDK.Win32Metadata). A `.nupkg` is just a zip with the `.winmd` at its root, so **copy-paste this whole block into PowerShell as-is** — it fetches the latest winmd and generates `ITaskbarList3` wrappers into `./generated` (no NuGet client, no version number to pick): +`Windows.Win32.winmd` is not on a stock Windows machine — it ships in the MIT-licensed NuGet package [`Microsoft.Windows.SDK.Win32Metadata`](https://www.nuget.org/packages/Microsoft.Windows.SDK.Win32Metadata). A `.nupkg` is just a zip with the `.winmd` at its root, so **copy-paste this whole block into PowerShell as-is** — it fetches the latest winmd and generates the **DWM window-effects** API (Windows 11 **dark title bar**, **Mica / Acrylic** backdrop, rounded corners) into `./generated` (no NuGet client, no version number to pick): ```powershell $pkg = 'microsoft.windows.sdk.win32metadata' $ver = (Invoke-RestMethod "https://api.nuget.org/v3-flatcontainer/$pkg/index.json").versions[-1] Invoke-WebRequest "https://api.nuget.org/v3-flatcontainer/$pkg/$ver/$pkg.$ver.nupkg" -OutFile "$env:TEMP\win32meta.zip" Expand-Archive "$env:TEMP\win32meta.zip" -DestinationPath .\win32meta -Force -npx dynwinrt-codegen generate --winmd .\win32meta\Windows.Win32.winmd --namespace Windows.Win32.UI.Shell --class-name ITaskbarList3 --output ./generated +npx dynwinrt-codegen generate --winmd .\win32meta\Windows.Win32.winmd --namespace Windows.Win32.Graphics.Dwm --class-name Apis --output ./generated ``` -Swap `--namespace` / `--class-name` for whatever API you need; reuse the same `.\win32meta\Windows.Win32.winmd` for every run. +That gives you `dwmSetWindowAttribute` (the Windows 11 dark title bar / Mica look every Electron app wants — no native addon) against the same `HWND` Electron already hands you via `BrowserWindow.getNativeWindowHandle()`. Swap `--namespace` / `--class-name` for any other API — e.g. `Windows.Win32.UI.Shell` / `ITaskbarList3` (taskbar progress + overlay icons), `Windows.Win32.Security.Credentials` / `Apis` (Credential Manager — a drop-in for the deprecated `keytar`), or `Windows.Win32.System.Registry` / `Apis`. Reuse the same `.\win32meta\Windows.Win32.winmd` for every run. > Win32 and classic-COM generation currently emits JavaScript/TypeScript (`.js` + `.d.ts`). From 20068f0178eaf0bcd04ea83d632ad3eaa60bd02a Mon Sep 17 00:00:00 2001 From: "Gordon Lam (SH) (from Dev Box)" Date: Tue, 28 Jul 2026 09:04:59 +0800 Subject: [PATCH 06/11] docs(readme): make the flat-Win32 usage example DWM dark title bar (matches generate line) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The generate line now showcases Windows.Win32.Graphics.Dwm, so align the flat usage example with it: show `dwmSetWindowAttribute` enabling the Windows 11 dark title bar (the "wow"), instead of the registry read. Keep the registry `{ status, phkResult }` out-param pattern described in the prose so the flat return-object contract is still taught. Verified the exact call at runtime — dwmSetWindowAttribute(hwnd, 20, <4-byte BOOL Buffer>, 4) returns { status: 0x0 }. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- README.md | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index ce1c0d79..ef8af43a 100644 --- a/README.md +++ b/README.md @@ -148,27 +148,23 @@ Interop interfaces are the Windows pattern for features that require an `HWND` b ### Flat Win32 exports: call `[DllImport]` APIs +Give an Electron window the Windows 11 dark title bar — no native addon: + ```js -import { regOpenKeyExW, regCloseKey } from './generated/Apis.js'; -import { REG_SAM_FLAGS } from './generated/REG_SAM_FLAGS.js'; - -const HKEY_LOCAL_MACHINE = 0x80000002n; - -const open = regOpenKeyExW( - HKEY_LOCAL_MACHINE, - 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion', // note: doubled backslashes in JS - 0, - REG_SAM_FLAGS.KEY_READ, -); -if (open.status !== 0) throw new Error(`RegOpenKeyExW failed: LSTATUS=${open.status}`); -try { - console.log(`hKey = 0x${open.phkResult.toString(16)}`); -} finally { - regCloseKey(open.phkResult); -} +import { dwmSetWindowAttribute } from './generated/Apis.js'; + +// Electron hands you the native window handle as a Buffer — read the HWND value. +const hwnd = mainWindow.getNativeWindowHandle().readBigUInt64LE(0); + +// DWMWA_USE_IMMERSIVE_DARK_MODE = 20. Attribute values are passed as a +// pointer-sized Buffer — here a 4-byte BOOL set to TRUE. +const enable = Buffer.alloc(4); +enable.writeInt32LE(1, 0); +const { status } = dwmSetWindowAttribute(hwnd, 20, enable, enable.length); +if (status !== 0) throw new Error(`DwmSetWindowAttribute failed: 0x${(status >>> 0).toString(16)}`); ``` -`[out]` parameters become return fields (`{ status, phkResult }`), caller-allocated buffers and in/out sizes are handled for you, `LPCWSTR` ↔ `string`, and the Win32 `LSTATUS`/error is surfaced. Handles (`HKEY`, `HANDLE`) project as `bigint`. +Flat exports return an object with `status` (the Win32/`HRESULT` result) plus a field for each `[out]`/in-out parameter — e.g. `regOpenKeyExW(...)` returns `{ status, phkResult }`. Caller-allocated buffers and in/out sizes are handled for you, `LPCWSTR` ↔ `string`, and handles (`HWND`, `HKEY`, `HANDLE`) project as `bigint | number`. ## Repository layout From 7db8269330e4c394b187558518857d0a2bcd73e7 Mon Sep 17 00:00:00 2001 From: "Gordon Lam (SH) (from Dev Box)" Date: Tue, 28 Jul 2026 09:17:32 +0800 Subject: [PATCH 07/11] docs(readme): revert Win32 example to ITaskbarList3 (coherent flow) + honest swap note MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reverts the DWM experiment. Two problems with DWM as the headline example: (a) the generate line generated DWM but the first usage section is ITaskbarList3 — incoherent; (b) most Electron apps are frameless (frame:false / custom title bar), so DwmSetWindowAttribute(dark title bar) does nothing for them — it isn't "what Electron devs need". Also, Electron already provides the flashy taskbar features (setProgressBar/setOverlayIcon/setThumbarButtons), powerSaveBlocker, flashFrame, globalShortcut, setJumpList. Restore the coherent original: generate ITaskbarList3 (used in the very next section) and keep the clean Registry flat example. The swap note now points at the genuine gaps Electron does NOT cover — Windows Credential Manager (a keytar replacement safeStorage doesn't provide) and arbitrary registry access. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- README.md | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index ef8af43a..8b40e8e7 100644 --- a/README.md +++ b/README.md @@ -99,17 +99,17 @@ Generate Win32/COM wrappers by pointing the codegen at the Win32 metadata (`Wind ### Getting `Windows.Win32.winmd` -`Windows.Win32.winmd` is not on a stock Windows machine — it ships in the MIT-licensed NuGet package [`Microsoft.Windows.SDK.Win32Metadata`](https://www.nuget.org/packages/Microsoft.Windows.SDK.Win32Metadata). A `.nupkg` is just a zip with the `.winmd` at its root, so **copy-paste this whole block into PowerShell as-is** — it fetches the latest winmd and generates the **DWM window-effects** API (Windows 11 **dark title bar**, **Mica / Acrylic** backdrop, rounded corners) into `./generated` (no NuGet client, no version number to pick): +`Windows.Win32.winmd` is not on a stock Windows machine — it ships in the MIT-licensed NuGet package [`Microsoft.Windows.SDK.Win32Metadata`](https://www.nuget.org/packages/Microsoft.Windows.SDK.Win32Metadata). A `.nupkg` is just a zip with the `.winmd` at its root, so **copy-paste this whole block into PowerShell as-is** — it fetches the latest winmd and generates the `ITaskbarList3` wrappers used just below, into `./generated` (no NuGet client, no version number to pick): ```powershell $pkg = 'microsoft.windows.sdk.win32metadata' $ver = (Invoke-RestMethod "https://api.nuget.org/v3-flatcontainer/$pkg/index.json").versions[-1] Invoke-WebRequest "https://api.nuget.org/v3-flatcontainer/$pkg/$ver/$pkg.$ver.nupkg" -OutFile "$env:TEMP\win32meta.zip" Expand-Archive "$env:TEMP\win32meta.zip" -DestinationPath .\win32meta -Force -npx dynwinrt-codegen generate --winmd .\win32meta\Windows.Win32.winmd --namespace Windows.Win32.Graphics.Dwm --class-name Apis --output ./generated +npx dynwinrt-codegen generate --winmd .\win32meta\Windows.Win32.winmd --namespace Windows.Win32.UI.Shell --class-name ITaskbarList3 --output ./generated ``` -That gives you `dwmSetWindowAttribute` (the Windows 11 dark title bar / Mica look every Electron app wants — no native addon) against the same `HWND` Electron already hands you via `BrowserWindow.getNativeWindowHandle()`. Swap `--namespace` / `--class-name` for any other API — e.g. `Windows.Win32.UI.Shell` / `ITaskbarList3` (taskbar progress + overlay icons), `Windows.Win32.Security.Credentials` / `Apis` (Credential Manager — a drop-in for the deprecated `keytar`), or `Windows.Win32.System.Registry` / `Apis`. Reuse the same `.\win32meta\Windows.Win32.winmd` for every run. +Swap `--namespace` / `--class-name` for any other API. Good targets are the things Electron *doesn't* give you: `Windows.Win32.Security.Credentials` / `Apis` (Windows Credential Manager — a replacement for the deprecated `keytar` that `safeStorage` doesn't cover), or `Windows.Win32.System.Registry` / `Apis` (arbitrary registry reads/writes). Reuse the same `.\win32meta\Windows.Win32.winmd` for every run. > Win32 and classic-COM generation currently emits JavaScript/TypeScript (`.js` + `.d.ts`). @@ -148,23 +148,27 @@ Interop interfaces are the Windows pattern for features that require an `HWND` b ### Flat Win32 exports: call `[DllImport]` APIs -Give an Electron window the Windows 11 dark title bar — no native addon: - ```js -import { dwmSetWindowAttribute } from './generated/Apis.js'; - -// Electron hands you the native window handle as a Buffer — read the HWND value. -const hwnd = mainWindow.getNativeWindowHandle().readBigUInt64LE(0); - -// DWMWA_USE_IMMERSIVE_DARK_MODE = 20. Attribute values are passed as a -// pointer-sized Buffer — here a 4-byte BOOL set to TRUE. -const enable = Buffer.alloc(4); -enable.writeInt32LE(1, 0); -const { status } = dwmSetWindowAttribute(hwnd, 20, enable, enable.length); -if (status !== 0) throw new Error(`DwmSetWindowAttribute failed: 0x${(status >>> 0).toString(16)}`); +import { regOpenKeyExW, regCloseKey } from './generated/Apis.js'; +import { REG_SAM_FLAGS } from './generated/REG_SAM_FLAGS.js'; + +const HKEY_LOCAL_MACHINE = 0x80000002n; + +const open = regOpenKeyExW( + HKEY_LOCAL_MACHINE, + 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion', // note: doubled backslashes in JS + 0, + REG_SAM_FLAGS.KEY_READ, +); +if (open.status !== 0) throw new Error(`RegOpenKeyExW failed: LSTATUS=${open.status}`); +try { + console.log(`hKey = 0x${open.phkResult.toString(16)}`); +} finally { + regCloseKey(open.phkResult); +} ``` -Flat exports return an object with `status` (the Win32/`HRESULT` result) plus a field for each `[out]`/in-out parameter — e.g. `regOpenKeyExW(...)` returns `{ status, phkResult }`. Caller-allocated buffers and in/out sizes are handled for you, `LPCWSTR` ↔ `string`, and handles (`HWND`, `HKEY`, `HANDLE`) project as `bigint | number`. +`[out]` parameters become return fields (`{ status, phkResult }`), caller-allocated buffers and in/out sizes are handled for you, `LPCWSTR` ↔ `string`, and the Win32 `LSTATUS`/error is surfaced. Handles (`HKEY`, `HANDLE`) project as `bigint | number`. ## Repository layout From 4a0838909f8314e12eb5198725099176b5b862a9 Mon Sep 17 00:00:00 2001 From: "Gordon Lam (SH) (from Dev Box)" Date: Tue, 28 Jul 2026 10:03:52 +0800 Subject: [PATCH 08/11] docs(readme): generate a whole namespace, not one class (flat Registry Apis) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per feedback that generating one class at a time is the uncommon case: the generate example now generates the whole Windows.Win32.System.Registry flat API (--class-name Apis = the namespace's [DllImport] bundle), which is what the Flat Win32 example below actually imports. Prose explains Apis (namespace bundle) vs naming a specific classic-COM interface (ITaskbarList3). Verified: namespace-only (no --class-name) does NOT emit flat functions — --class-name Apis is required. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8b40e8e7..f62e1096 100644 --- a/README.md +++ b/README.md @@ -99,17 +99,17 @@ Generate Win32/COM wrappers by pointing the codegen at the Win32 metadata (`Wind ### Getting `Windows.Win32.winmd` -`Windows.Win32.winmd` is not on a stock Windows machine — it ships in the MIT-licensed NuGet package [`Microsoft.Windows.SDK.Win32Metadata`](https://www.nuget.org/packages/Microsoft.Windows.SDK.Win32Metadata). A `.nupkg` is just a zip with the `.winmd` at its root, so **copy-paste this whole block into PowerShell as-is** — it fetches the latest winmd and generates the `ITaskbarList3` wrappers used just below, into `./generated` (no NuGet client, no version number to pick): +`Windows.Win32.winmd` is not on a stock Windows machine — it ships in the MIT-licensed NuGet package [`Microsoft.Windows.SDK.Win32Metadata`](https://www.nuget.org/packages/Microsoft.Windows.SDK.Win32Metadata). A `.nupkg` is just a zip with the `.winmd` at its root, so **copy-paste this whole block into PowerShell as-is** — it fetches the latest winmd and generates the whole `Windows.Win32.System.Registry` flat API (every `Reg*` function — the wrappers used in the Flat Win32 example below) into `./generated` (no NuGet client, no version number to pick): ```powershell $pkg = 'microsoft.windows.sdk.win32metadata' $ver = (Invoke-RestMethod "https://api.nuget.org/v3-flatcontainer/$pkg/index.json").versions[-1] Invoke-WebRequest "https://api.nuget.org/v3-flatcontainer/$pkg/$ver/$pkg.$ver.nupkg" -OutFile "$env:TEMP\win32meta.zip" Expand-Archive "$env:TEMP\win32meta.zip" -DestinationPath .\win32meta -Force -npx dynwinrt-codegen generate --winmd .\win32meta\Windows.Win32.winmd --namespace Windows.Win32.UI.Shell --class-name ITaskbarList3 --output ./generated +npx dynwinrt-codegen generate --winmd .\win32meta\Windows.Win32.winmd --namespace Windows.Win32.System.Registry --class-name Apis --output ./generated ``` -Swap `--namespace` / `--class-name` for any other API. Good targets are the things Electron *doesn't* give you: `Windows.Win32.Security.Credentials` / `Apis` (Windows Credential Manager — a replacement for the deprecated `keytar` that `safeStorage` doesn't cover), or `Windows.Win32.System.Registry` / `Apis` (arbitrary registry reads/writes). Reuse the same `.\win32meta\Windows.Win32.winmd` for every run. +`Apis` is the bundle of a namespace's flat `[DllImport]` exports — one command generates them all (this is the usual case). For a classic-COM interface you name it directly instead, e.g. `--namespace Windows.Win32.UI.Shell --class-name ITaskbarList3` (the Classic COM example below). Swap in any other namespace — for something Electron doesn't give you, try `Windows.Win32.Security.Credentials` / `Apis` (Credential Manager — a replacement for the deprecated `keytar`). Reuse the same `.\win32meta\Windows.Win32.winmd` for every run. > Win32 and classic-COM generation currently emits JavaScript/TypeScript (`.js` + `.d.ts`). From a1ba5a0d3c87d86e0f6952fd6e28a98650116893 Mon Sep 17 00:00:00 2001 From: "Gordon Lam (SH) (from Dev Box)" Date: Tue, 28 Jul 2026 11:16:31 +0800 Subject: [PATCH 09/11] docs(readme): align winmd generate example with example order + clarify interop vs classic COM Reading-flow fixes: - The headline "Getting Windows.Win32.winmd" copy-paste block now generates ITaskbarList3 (the FIRST usage example), instead of the Registry flat API that only appears third. Verified the command emits ITaskbarList3.js + TBPFLAG.js. - The Flat Win32 (Registry) example now re-shows its own generate command inline (--class-name Apis, namespace-level flat). Verified it emits Apis.js (regOpenKeyExW/regCloseKey) + REG_SAM_FLAGS.js. - Reworded the --class-name explainer to cover both cases (specific interface vs the synthetic Apis namespace bundle). Clarity fix (Q1): the Classic COM and WinRT-interop examples looked identical from the code. Added a one-line contrast: ITaskbarList3 is a pure classic-COM object (calls stay on its vtable), whereas an interop interface is a one-way bridge that hands back a live WinRT object (DataTransferManager) from an HWND. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- README.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f62e1096..cc16fc44 100644 --- a/README.md +++ b/README.md @@ -99,17 +99,17 @@ Generate Win32/COM wrappers by pointing the codegen at the Win32 metadata (`Wind ### Getting `Windows.Win32.winmd` -`Windows.Win32.winmd` is not on a stock Windows machine — it ships in the MIT-licensed NuGet package [`Microsoft.Windows.SDK.Win32Metadata`](https://www.nuget.org/packages/Microsoft.Windows.SDK.Win32Metadata). A `.nupkg` is just a zip with the `.winmd` at its root, so **copy-paste this whole block into PowerShell as-is** — it fetches the latest winmd and generates the whole `Windows.Win32.System.Registry` flat API (every `Reg*` function — the wrappers used in the Flat Win32 example below) into `./generated` (no NuGet client, no version number to pick): +`Windows.Win32.winmd` is not on a stock Windows machine — it ships in the MIT-licensed NuGet package [`Microsoft.Windows.SDK.Win32Metadata`](https://www.nuget.org/packages/Microsoft.Windows.SDK.Win32Metadata). A `.nupkg` is just a zip with the `.winmd` at its root, so **copy-paste this whole block into PowerShell as-is** — it fetches the latest winmd and generates the `ITaskbarList3` classic-COM wrapper (used in the first example below) into `./generated` (no NuGet client, no version number to pick): ```powershell $pkg = 'microsoft.windows.sdk.win32metadata' $ver = (Invoke-RestMethod "https://api.nuget.org/v3-flatcontainer/$pkg/index.json").versions[-1] Invoke-WebRequest "https://api.nuget.org/v3-flatcontainer/$pkg/$ver/$pkg.$ver.nupkg" -OutFile "$env:TEMP\win32meta.zip" Expand-Archive "$env:TEMP\win32meta.zip" -DestinationPath .\win32meta -Force -npx dynwinrt-codegen generate --winmd .\win32meta\Windows.Win32.winmd --namespace Windows.Win32.System.Registry --class-name Apis --output ./generated +npx dynwinrt-codegen generate --winmd .\win32meta\Windows.Win32.winmd --namespace Windows.Win32.UI.Shell --class-name ITaskbarList3 --output ./generated ``` -`Apis` is the bundle of a namespace's flat `[DllImport]` exports — one command generates them all (this is the usual case). For a classic-COM interface you name it directly instead, e.g. `--namespace Windows.Win32.UI.Shell --class-name ITaskbarList3` (the Classic COM example below). Swap in any other namespace — for something Electron doesn't give you, try `Windows.Win32.Security.Credentials` / `Apis` (Credential Manager — a replacement for the deprecated `keytar`). Reuse the same `.\win32meta\Windows.Win32.winmd` for every run. +`--class-name` names what to generate — either a specific classic-COM/WinRT interface like `ITaskbarList3` (above), or `Apis`, the synthetic bundle of a whole namespace's flat `[DllImport]` exports (shown in the Flat Win32 example below). Dependencies are pulled in automatically. Swap in any other namespace — for something Electron doesn't give you, try `Windows.Win32.Security.Credentials` / `Apis` (Credential Manager — a replacement for the deprecated `keytar`). Reuse the same `.\win32meta\Windows.Win32.winmd` for every run. > Win32 and classic-COM generation currently emits JavaScript/TypeScript (`.js` + `.d.ts`). @@ -144,10 +144,16 @@ const dtm = interop.getForWindow(hwnd); // → DynWinRtValue bridge to DataTrans interop.showShareUIForWindow(hwnd); ``` -Interop interfaces are the Windows pattern for features that require an `HWND` but operate on WinRT objects. `create()` activates the WinRT factory and QIs to the interop interface; `getForWindow(hwnd)` adopts the returned COM pointer and hands it back as a `DynWinRtValue` bridge — with no `riid`/`void**` in the signature. +Where `ITaskbarList3` above is a *pure* classic-COM object — every call stays on its own vtable — an **interop** interface is a one-way bridge from Win32 windowing into WinRT: you hand it an `HWND` and `getForWindow(hwnd)` hands back a live **WinRT** object (here a `DataTransferManager`) that you then drive with the rest of your generated WinRT bindings. `create()` activates the WinRT factory and QIs to the interop interface; the returned COM pointer is adopted and surfaced as a `DynWinRtValue` bridge — with no `riid`/`void**` in the signature. ### Flat Win32 exports: call `[DllImport]` APIs +Flat exports live in a namespace's synthetic `Apis` bundle. Generate a whole namespace at once — here every `Reg*` function — with `--class-name Apis` (reusing the winmd fetched above): + +```powershell +npx dynwinrt-codegen generate --winmd .\win32meta\Windows.Win32.winmd --namespace Windows.Win32.System.Registry --class-name Apis --output ./generated +``` + ```js import { regOpenKeyExW, regCloseKey } from './generated/Apis.js'; import { REG_SAM_FLAGS } from './generated/REG_SAM_FLAGS.js'; From 2ad5b599ede27d7d056023b761a360f8b3eeab4e Mon Sep 17 00:00:00 2001 From: "Gordon Lam (SH) (from Dev Box)" Date: Tue, 28 Jul 2026 12:00:45 +0800 Subject: [PATCH 10/11] docs(readme): frame Win32/COM examples as real Electron main.js snippets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The examples read like C++/Win32 with an HWND bolted on; the audience is Electron developers. Both HWND examples are now grounded in the Electron object model: app.whenReady() → new BrowserWindow → win.getNativeWindowHandle().readBigUInt64LE(0), so where the handle comes from is obvious and the snippet is copy-into-main.js ready. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- README.md | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index cc16fc44..9ca32e2d 100644 --- a/README.md +++ b/README.md @@ -116,18 +116,23 @@ npx dynwinrt-codegen generate --winmd .\win32meta\Windows.Win32.winmd --namespac ### Classic COM: `CoCreateInstance` + vtable interfaces ```js +// main.js (Electron main process) +import { app, BrowserWindow } from 'electron'; import { ITaskbarList3 } from './generated/ITaskbarList3.js'; import { TBPFLAG } from './generated/TBPFLAG.js'; -// HWND is generated as `bigint | number`. In Electron, read the handle value -// out of BrowserWindow.getNativeWindowHandle() (a Buffer) — don't pass the -// Buffer itself: const hwnd = win.getNativeWindowHandle().readBigUInt64LE(0); -const hwnd = 0x0000000000123456n; // your real HWND +app.whenReady().then(() => { + const win = new BrowserWindow({ width: 900, height: 600 }); -const taskbar = ITaskbarList3.create(); // CoCreateInstance under the hood -taskbar.hrInit(); -taskbar.setProgressState(hwnd, TBPFLAG.TBPF_NORMAL); -taskbar.setProgressValue(hwnd, 40n, 100n); // 40% + // Your BrowserWindow's HWND: getNativeWindowHandle() returns a Node Buffer; + // read it as a 64-bit little-endian bigint. + const hwnd = win.getNativeWindowHandle().readBigUInt64LE(0); + + const taskbar = ITaskbarList3.create(); // CoCreateInstance under the hood + taskbar.hrInit(); + taskbar.setProgressState(hwnd, TBPFLAG.TBPF_NORMAL); + taskbar.setProgressValue(hwnd, 40n, 100n); // paint 40% on the taskbar icon +}); ``` The generated wrapper exposes natural typed methods while the runtime handles `CoCreateInstance`, interface registration, pointer arguments, `HRESULT` checks, and vtable slot dispatch — no hand-written IIDs, `REFIID`, `void**`, or vtable indices. @@ -135,13 +140,18 @@ The generated wrapper exposes natural typed methods while the runtime handles `C ### WinRT interop bridges: from `HWND` to WinRT objects ```js +// main.js (Electron main process) +import { app, BrowserWindow } from 'electron'; import { IDataTransferManagerInterop } from './generated/IDataTransferManagerInterop.js'; -const hwnd = 0x0000000000123456n; // your real HWND as a bigint (see the Electron note above) +app.whenReady().then(() => { + const win = new BrowserWindow({ width: 900, height: 600 }); + const hwnd = win.getNativeWindowHandle().readBigUInt64LE(0); // same as above -const interop = IDataTransferManagerInterop.create(); -const dtm = interop.getForWindow(hwnd); // → DynWinRtValue bridge to DataTransferManager -interop.showShareUIForWindow(hwnd); + const interop = IDataTransferManagerInterop.create(); + const dtm = interop.getForWindow(hwnd); // → DynWinRtValue bridge to DataTransferManager + interop.showShareUIForWindow(hwnd); // pops the Windows Share sheet over your window +}); ``` Where `ITaskbarList3` above is a *pure* classic-COM object — every call stays on its own vtable — an **interop** interface is a one-way bridge from Win32 windowing into WinRT: you hand it an `HWND` and `getForWindow(hwnd)` hands back a live **WinRT** object (here a `DataTransferManager`) that you then drive with the rest of your generated WinRT bindings. `create()` activates the WinRT factory and QIs to the interop interface; the returned COM pointer is adopted and surfaced as a `DynWinRtValue` bridge — with no `riid`/`void**` in the signature. From 48f9e6afd4df500e091c8482b9c158daaf8a7899 Mon Sep 17 00:00:00 2001 From: "Gordon Lam (SH) (from Dev Box)" Date: Tue, 28 Jul 2026 12:16:57 +0800 Subject: [PATCH 11/11] docs(readme): use existing mainWindow, not a freshly-constructed one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Constructing `new BrowserWindow({ width: 900, height: 600 })` in the snippet wrongly implied the window size mattered and that you make a new window just to get its handle. Real apps already have their window — read the HWND off the existing mainWindow (any size). Removes the confusion. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- README.md | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 9ca32e2d..22df5302 100644 --- a/README.md +++ b/README.md @@ -116,23 +116,18 @@ npx dynwinrt-codegen generate --winmd .\win32meta\Windows.Win32.winmd --namespac ### Classic COM: `CoCreateInstance` + vtable interfaces ```js -// main.js (Electron main process) -import { app, BrowserWindow } from 'electron'; +// main.js (Electron main process) — mainWindow is your existing BrowserWindow import { ITaskbarList3 } from './generated/ITaskbarList3.js'; import { TBPFLAG } from './generated/TBPFLAG.js'; -app.whenReady().then(() => { - const win = new BrowserWindow({ width: 900, height: 600 }); - - // Your BrowserWindow's HWND: getNativeWindowHandle() returns a Node Buffer; - // read it as a 64-bit little-endian bigint. - const hwnd = win.getNativeWindowHandle().readBigUInt64LE(0); +// Your window's HWND — any window size works. getNativeWindowHandle() returns a +// Node Buffer; read it as a 64-bit little-endian bigint. +const hwnd = mainWindow.getNativeWindowHandle().readBigUInt64LE(0); - const taskbar = ITaskbarList3.create(); // CoCreateInstance under the hood - taskbar.hrInit(); - taskbar.setProgressState(hwnd, TBPFLAG.TBPF_NORMAL); - taskbar.setProgressValue(hwnd, 40n, 100n); // paint 40% on the taskbar icon -}); +const taskbar = ITaskbarList3.create(); // CoCreateInstance under the hood +taskbar.hrInit(); +taskbar.setProgressState(hwnd, TBPFLAG.TBPF_NORMAL); +taskbar.setProgressValue(hwnd, 40n, 100n); // paint 40% on the taskbar icon ``` The generated wrapper exposes natural typed methods while the runtime handles `CoCreateInstance`, interface registration, pointer arguments, `HRESULT` checks, and vtable slot dispatch — no hand-written IIDs, `REFIID`, `void**`, or vtable indices. @@ -141,17 +136,13 @@ The generated wrapper exposes natural typed methods while the runtime handles `C ```js // main.js (Electron main process) -import { app, BrowserWindow } from 'electron'; import { IDataTransferManagerInterop } from './generated/IDataTransferManagerInterop.js'; -app.whenReady().then(() => { - const win = new BrowserWindow({ width: 900, height: 600 }); - const hwnd = win.getNativeWindowHandle().readBigUInt64LE(0); // same as above +const hwnd = mainWindow.getNativeWindowHandle().readBigUInt64LE(0); // your existing window - const interop = IDataTransferManagerInterop.create(); - const dtm = interop.getForWindow(hwnd); // → DynWinRtValue bridge to DataTransferManager - interop.showShareUIForWindow(hwnd); // pops the Windows Share sheet over your window -}); +const interop = IDataTransferManagerInterop.create(); +const dtm = interop.getForWindow(hwnd); // → DynWinRtValue bridge to DataTransferManager +interop.showShareUIForWindow(hwnd); // pops the Windows Share sheet over your window ``` Where `ITaskbarList3` above is a *pure* classic-COM object — every call stays on its own vtable — an **interop** interface is a one-way bridge from Win32 windowing into WinRT: you hand it an `HWND` and `getForWindow(hwnd)` hands back a live **WinRT** object (here a `DataTransferManager`) that you then drive with the rest of your generated WinRT bindings. `create()` activates the WinRT factory and QIs to the interop interface; the returned COM pointer is adopted and surfaced as a `DynWinRtValue` bridge — with no `riid`/`void**` in the signature.