Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v3 #16

Merged
merged 28 commits into from
Jul 19, 2024
Merged

v3 #16

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# .gitattributes snippet to force users to use same line endings for project.
#
# Handle line endings automatically for files detected as text
# and leave all files detected as binary untouched.
* text=auto


# These files are text and should be normalized (Convert crlf => lf)
*.php text
*.css text
*.js text eol=lf
*.json text
*.htm text
*.html text
*.xml text
*.txt text
*.ini text
*.inc text
*.pl text
*.rb text
*.py text
*.scm text
*.sql text
.htaccess text
*.sh text

# These files are binary and should be left untouched
# (binary is a macro for -text -diff)
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.mov binary
*.mp4 binary
*.mp3 binary
*.flv binary
*.fla binary
*.swf binary
*.gz binary
*.zip binary
*.7z binary
*.ttf binary
*.pyc binary
8 changes: 4 additions & 4 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
- name: Setup node
uses: actions/setup-node@v4
with:
node-version: 18
node-version: 20
registry-url: 'https://registry.npmjs.org/'

- name: Install pnpm
Expand All @@ -26,10 +26,10 @@ jobs:
- name: Install dependencies
run: pnpm install

- name: Lint
run: pnpm check

- name: Publish to npm
env:
GH_TOKEN: ${{ github.token }}
GITHUB_TOKEN: ${{ github.token }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npm publish --tag ${{ github.event.release.prerelease && 'next' || 'latest' }}
42 changes: 42 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Tests

Check failure on line 1 in .github/workflows/test.yml

View workflow job for this annotation

GitHub Actions / Build failed

.github/workflows/test.yml#L1

This run timed out after more than 35 days.

Check failure on line 1 in .github/workflows/test.yml

View workflow job for this annotation

GitHub Actions / Build failed

.github/workflows/test.yml#L1

This run timed out after more than 35 days.

on: [push, pull_request]

concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
group: ${{ github.workflow }}-${{ github.ref }}

jobs:
test:
name: Test on node ${{ matrix.node }}
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
node: [18, 20, 22]

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Use Node.js ${{ matrix.node }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}

- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: latest

- name: Install dependencies
run: pnpm install

- name: Lint
run: pnpm check

- name: Build
run: pnpm build

- name: Run tests
run: pnpm coverage
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@
.DS_Store*
.nyc_output
.vscode
coverage
dist
/build
/prebuilds
junit.xml
node_modules
npm-debug.log
yarn-error.log
.pnpm-debug.log
tmp
*.tsbuildinfo
*.tgz
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
# v3.0.0

* BREAKING CHANGE: Require Node.js 18.17 or newer.
* fix: Fixed order of child key events when parent key is deleted.
* fix: Fixed seg fault during cleanup that occurs when using winreglib in a
worker.
* fix: Fixed bug where the Windows Registry worker thread was being spawned
when adding the first listener. The idea was don't spawn the thread until
there was a listener, however thread shutdown is async and not worth the
hassle in a destructor, so now the background thread is always running.

# v2.0.4 (July 2, 2024)

* fix: Fixed crash when app exits (thanks TitusQian)
Expand Down
167 changes: 124 additions & 43 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,37 @@
# Windows Registry Utility Library
<br>
<div align="center">
<img width="640" height="240" src="media/winreglib.webp" alt="winreglib">
</div>
<br>

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) {
Expand All @@ -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)`
Expand All @@ -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.<String>`, or `null` depending on the value.
Returns a `String`, `Number`, `Buffer`, `Array.<String>`, 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);
```
Expand All @@ -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',
Expand All @@ -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');
Expand All @@ -120,44 +153,92 @@ 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 |
| ---------- | ---------------------------------- |
| `add` | The `key` was added. |
| `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.

## License
## Legal

This project is open source under the [Apache Public License v2][1] and is developed by
[Axway, Inc](http://www.axway.com/) and the community. Please read the [`LICENSE`][1] file included
in this distribution for more information.
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
Loading