diff --git a/README.md b/README.md index 3141872..b2bf989 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,37 @@ -# Windows Registry Utility Library +
+
+ winreglib +
+
-A library for querying and watching the Windows Registry. +`winreglib` is a native C++ addon for Node.js for querying and watching Windows +Registry keys. It provides a read-only, synchronous API for accessing the +Windows Registry. -## Prerequisites +## Features -`winreglib` requires N-API version 3 and the following Node.js versions: + - Get, list, and watch registry keys _without_ spawning `reg.exe` + - Written in TypeScript + - Packaged as an ESM module + - Supports x64 (64-bit) and ia32 (32-bit) CPU architectures -* v10.2.0 or newer +## Requirements -## Installation - - npm install winreglib - -## Introduction - -`winreglib` is a native Node.js addon for querying the Windows Registry. The API is synchronous. - -Currently `winreglib` only supports read operations. It can support write operations someday if -need and time exists. +`winreglib` requires Node.js >=18.17.0 (Node-API 8) and ships with pre-built +binaries for x64 and ia32 architectures. ARM-based architectures are not +officially supported, but should technically work as long as you have the +build tools installed. ## Example -```js +```javascript import winreglib from 'winreglib'; const key = 'HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion'; const results = winreglib.list(key); -console.log(`${results.root}\\${results.path}`); +console.log(`Resolved ${results.resolvedRoot}`); +console.log(`${results.key} has ${results.subkeys.length} subkeys`); console.log(' Subkeys:'); for (const subkey of results.subkeys) { @@ -40,6 +44,20 @@ for (const valueName of results.values) { } ``` +## Supported Root Keys + +| Name | Abbreviation | +| ---------------------------------- | ------------ | +| `HKEY_CLASSES_ROOT` | `HKCR` | +| `HKEY_CURRENT_CONFIG` | `HKCC` | +| `HKEY_CURRENT_USER` | `HKCU` | +| `HKEY_CURRENT_USER_LOCAL_SETTINGS` | | +| `HKEY_LOCAL_MACHINE` | `HKLM` | +| `HKEY_PERFORMANCE_DATA` | | +| `HKEY_PERFORMANCE_NLSTEXT` | | +| `HKEY_PERFORMANCE_TEXT` | | +| `HKEY_USERS` | `HKU` | + ## API ### `get(key, valueName)` @@ -51,12 +69,16 @@ Get a value for the given key and value name. | `key` | String | The key beginning with the root. | | `valueName` | String | The name of the value to get. | -Returns a `String`, `Number`, `Buffer`, `Array.`, or `null` depending on the value. +Returns a `String`, `Number`, `Buffer`, `Array.`, or `null` +depending on the value. If `key` or `valueName` is not found, an `Error` is thrown. ```js -const value = winreglib.get('HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion', 'ProgramFilesDir'); +const value = winreglib.get( + 'HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion', + 'ProgramFilesDir' +); console.log(value); ``` @@ -73,20 +95,31 @@ Retreives all subkeys and value names for a give key. | -------- | ------ | -------------------------------- | | `key` | String | The key beginning with the root. | -Returns an `Object` with the resolved `resolvedRoot` (String), `key` (String), `subkeys` -(Array[String]), and `values` (Array[String]). +Returns an `RegistryKey` object: + +``` +type RegistryKey = { + resolvedRoot: string; + key: string; + subkeys: string[]; + values: unknown[]; +}; +``` If `key` is not found, an `Error` is thrown. ```js -const result = winreglib.list('HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup'); +const result = winreglib.list( + 'HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup +); console.log(result); ``` ```js { resolvedRoot: 'HKEY_LOCAL_MACHINE', - key: 'HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup', + key: + 'HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup', subkeys: [ 'DPI', 'ImageServicingData', @@ -109,8 +142,8 @@ Watches a key for changes in subkeys or values. | -------- | ------ | -------------------------------- | | `key` | String | The key beginning with the root. | -Returns a handle (`EventEmitter`) that emits `"change"` events. Call `handle.stop()` to stop watching -the key. +Returns a handle (`WinRegLibWatchHandle` which extends `EventEmitter`) that +emits `"change"` events. Call `handle.stop()` to stop watching the key. ```js const handle = winreglib.watch('HKLM\\SOFTWARE'); @@ -120,7 +153,8 @@ handle.on('change', evt => { }); ``` -The `"change"` event object contains a change `"type"` and the affected `"key"`. +The `"change"` event object contains a change `"type"` and the affected +`"key"`. | Event Type | Description | | ---------- | ---------------------------------- | @@ -128,34 +162,83 @@ The `"change"` event object contains a change `"type"` and the affected `"key"`. | `change` | A subkey or value was added, changed, deleted, or permissions modified, but we don't know exactly what. | | `delete` | The `key` was deleted. | -`watch()` can track keys that do not exist and when they are created, a change event will be -emitted. You can watch the same key multiple times, however each returned handle is unique and you -must call `handle.stop()` for each. +`watch()` can track keys that do not exist and when they are created, a +change event will be emitted. You can watch the same key multiple times, +however each returned handle is unique and you must call `handle.stop()` for +each. -Due to limitations of the Win32 API, `watch()` is unable to determine what actually changed during -a `change` event type. You will need to call `list()` and cache the subkeys and values, then call -`list()` again when a change is emitted and compare the before and after. +Due to limitations of the Win32 API, `watch()` is unable to determine what +actually changed during a `change` event type. You will need to call `list()` +and cache the subkeys and values, then call `list()` again when a change is +emitted and compare the before and after. -Note that `watch()` does not support recursively watching for changes. +Note that `watch()` does not support recursively watching for changes. The +Win32 API does support recursive watching, so this could be added in the +future. ## Advanced ### Debug Logging -`winreglib` exposes an event emitter that emits debug log messages. This is intended to help debug -issues under the hood. The average user will never need to use this, however it would be handy when -filing a bug. +`winreglib` exposes an event emitter that emits debug log messages. This is +intended to help debug issues under the hood. The average user will never +need to use this, however it would be handy when filing a bug. ```js winreglib.on('log', msg => console.log(msg)); ``` -Alternatively, `winreglib` uses the amazing [`snooplogg`][2] debug logger where you simply set the -`SNOOPLOGG` environment variable to `winreglib` (or `*`) and it will print the debug log to stdout. +Alternatively, `winreglib` uses the amazing [`snooplogg`][2] debug logger +where you simply set the `SNOOPLOGG` environment variable to `winreglib` (or +`*`) and it will print the debug log to `stderr`. + +```bash +$ SNOOPLOGG=winreglib node myapp.js + 6.150s winreglib:Watchman Initializing async work (thread 16764576586047274673) + 6.150s winreglib:Watchman::run Initializing run loop (thread 12502165600786624632) + 6.151s winreglib:Watchman::run Populating active copy (count=0) + 6.151s winreglib:Watchman::run Waiting on 2 objects... + 6.152s winreglib:list key="HKLM" subkey="SOFTWARE\Microsoft\Windows\CurrentVersion" + 6.152s winreglib:list 170 keys (max 30), 11 values (max 24) +``` + +### Building `winreglib` + +`winreglib` has two components: the public interface written in TypeScript and +the native addon written in C++. + +To compile the C++ code, you will need the Microsoft Visual Studio (not VSCode) +or the Microsoft Build Tools. You may also need Python 3 installed. + +| Command | Description | +| -------------------- | ----------- | +| `pnpm build` | Compiles the TypeScript and the Node.js native C++ addon for the current architecture | +| `pnpm build:bundle` | Compiles only the TypeScript code | +| `pnpm build:local` | Compiles only the Node.js native C++ addon | +| `pnpm rebuild:local` | Cleans and re-compiles only the Node.js native C++ addon | + +When publishing, the native C++ addon is prebuilt for x64 and ia32 +architectures. Generally you shouldn't need be concerned with the prebuilt +binaries, however the following commands will compile the prebuilds: + +| Command | Description | +| --------------------- | ------------------------------ | +| `pnpm build:prebuild` | Prebuild for x64 and ia32 | +| `pnpm prebuild-arm` | Prebuild for arm (32-bit) | +| `pnpm prebuild-arm64` | Prebuild for arm64 | +| `pnpm prebuild-ia32` | Prebuild for ia32 (x86 32-bit) | +| `pnpm prebuild-x64` | Prebuild for x64 | + +### Architecture + +When `winreglib` is imported, it immediately spawns a background thread in the +event the app is going to watch a key. If a key is added/changed/deleted, the +background thread sends a message to the main thread which emits the change +event. ## Legal Titanium is a registered trademark of TiDev Inc. All Titanium trademark and patent rights were transferred and assigned to TiDev Inc. on 4/7/2022. Please see the LEGAL information about using our trademarks, privacy policy, terms of usage and other legal information at https://tidev.io/legal. -[1]: https://github.com/appcelerator/winreglib/blob/master/LICENSE +[1]: https://github.com/tidev/winreglib/blob/master/LICENSE [2]: https://www.npmjs.com/package/snooplogg diff --git a/media/winreglib.webp b/media/winreglib.webp new file mode 100644 index 0000000..d12a26d Binary files /dev/null and b/media/winreglib.webp differ diff --git a/package.json b/package.json index 9b81021..2180441 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "build:bundle": "rimraf dist && rollup -c rollup.config.ts --configPlugin typescript && pnpm build:types", "build:local": "node-gyp -j 20 build", "build:local-debug": "node-gyp -j 20 build --debug", - "build:prebuild": "pnpm prebuild-arm && pnpm prebuild-arm64 && pnpm prebuild-ia32 && pnpm prebuild-x64", + "build:prebuild": "pnpm pnpm prebuild-ia32 && pnpm prebuild-x64", "build:types": "pnpm build:types:temp && pnpm build:types:roll && pnpm build:types:check", "build:types:temp": "tsc --declaration --emitDeclarationOnly --outDir temp --project tsconfig.build.json", "build:types:roll": "rollup --config rollup.dts.config.ts --configPlugin typescript && rimraf temp", diff --git a/src/index.ts b/src/index.ts index a924d46..528b9e2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -63,19 +63,19 @@ class WinRegLib extends EventEmitter { * Gets the value for a specific key value. * * @param {String} key - The key. - * @param {String} value - The name of the value to get. + * @param {String} valueName - The name of the value to get. * @returns {*} The value reflects the data type from the registry. */ - get(key: string, value: string): unknown { + get(key: string, valueName: string): unknown { if (!key || typeof key !== 'string') { throw new TypeError('Expected key to be a non-empty string'); } - if (!value || typeof value !== 'string') { + if (!valueName || typeof valueName !== 'string') { throw new TypeError('Expected value name to be a non-empty string'); } - return binding.get(key, value); + return binding.get(key, valueName); } /**