native-reg
Provides direct access to the Windows Registry native APIs using a node addon (native C++ code).
Contents:
Example
A simple installer example:
const reg = require('native-reg');
const key = reg.openKey(
reg.HKCU,
'Software\\MyCompany\\MySoftware',
reg.Access.ALL_ACCESS);
const version = reg.getValue(key, 'Install', 'Version');
if (isOldVersion(version)) {
reg.deleteTree(key, 'Install');
const installKey = reg.createKey(key, 'Install', reg.Access.ALL_ACCESS);
reg.setValueSZ(installKey, 'Version', newVersion);
// ...
reg.closeKey(installKey);
}
reg.closeKey(key);
Comparison to other packages
There were already several other packages that provide access to the Windows Registry:
winreg
(and it's several forks) is very lightweight, as it simply callsreg.exe
without any build or bundling dependencies, but in exchange:- it exposes you to the system encoding, meaning it's more work to support Unicode,
- it has to handle correctly escaping the command line and parsing the output, something that can commonly cause security issues,
- it apparently may be broken by some Group Policy settings?
- it is still a good and popular option if you want to avoid native code!
regedit
is something of a middle-ground, using the Windows Script Host (cscript.exe
in this case) to access the registry, which can avoid the potential for commandline encoding issues but still requires an external process and adds the scripting files (.wsf
) themselves as something which has to be handled by your bundling setup. Still a decent middle-ground option to avoid native code.registry-js
is most similar to this library, using a native addon with prebuilt binaries, but it only aims to implement the most common subset of behavior rather than attempting to reflect the full API: most notably as of 1.16.0 it does not have support for creating and deleting keys (rather than values). I do have some concern about the GitHub issues, otherwise this seems like a good option to try if you don't need the full API.windows-registry
is one of the earliest packages I could find. It uses theffi
package, so it still requires a build step, but is now unmaintained and - despite the name - has a strange API that attempts to cover several different uses like UAC elevation, and misses basic registry APIs like key and value enumeration. Probably not a good choice for new projects due to the lack of maintenance if nothing else, but perhaps you might find the extra uses helpful.
This package aims to improve on the existing packages by:
- providing the entire Windows Registry API (with the notable exception of the transacted APIs)
- implementing the API using a native addon to provide good performance and correctness.
- directly mapping to the Windows API, providing a useful overview in this readme, and linking to the official documentation for each function.
- providing prebuilt binaries for x86, x64, and ARM64 Windows to avoid the need for a build toolchain
on installation
- As far as I know, these are the only currently supported windows architectures, so there is no fallback to build from source, but feel free to request either another prebuild target or fallback if you find a use I've missed.
- If not running on Windows, the module will not fail to load in order to simplify cross-platform bundling, but it will assert if any of the functions are called to avoid accidental use.
API
Read each API's linked Windows documentation for details on functionality and usage.
Contents:
Errors
The API initially validates the arguments with the standard node assert
library:
try {
// Should not be 'HKLM', but reg.HKLM or reg.HKEY.LOCAL_MACHINE
reg.openKey('HKLM', 'SOFTWARE\\Microsoft\\Windows', reg.Access.READ);
assert.fail();
} catch (e) {
assert(e instanceof require('assert').AssertionError);
}
Except as noted by the individual API documentation, for example "not found" errors,
if the wrapped Windows API returns an error it is thrown as a javascript Error
instance with the generic Windows-provided error message, e.g.: Access is denied.
,
and with additional errno
and syscall
properties as with Node's own errors,
for example: a common error is trying to use Access.ALL_ACCESS
on reg.HKLM
, which
you need UAC elevation for:
try {
// Assuming you are not running as administrator!
reg.openKey(reg.HKLM, 'SOFTWARE\\Microsoft\\Windows', reg.Access.ALL_ACCESS);
assert.fail();
} catch (e) {
assert.strictEqual(e.message, 'Access is denied.');
assert.strictEqual(e.errno, 5);
assert.strictEqual(e.syscall, 'RegOpenKeyExW');
}
Constants
This library uses Typescript enum
s for constants, this generates a
two way name <-> value mapping.
For example: Access.SET_VALUE
is 0x0002
, and Access[2]
is "SET_VALUE"
.
HKEY
enum
Exports the set of predefined HKEY
s.
createKey
, openKey
, loadAppKey
and
openCurrentUser
will return other values for HKEY
s,
which at the moment are objects with a single property native
with the native
HKEY handle as a V8 External
value, for use in other native packages, or null
after it is closed.
For these values you can call closeKey
once you are done to clean up
early, but they will be closed by the garbage collector if you chose not to.
export enum HKEY {
CLASSES_ROOT = 0x80000000,
CURRENT_USER = 0x80000001,
LOCAL_MACHINE = 0x80000002,
USERS = 0x80000003,
PERFORMANCE_DATA = 0x80000004,
PERFORMANCE_TEXT = 0x80000050,
PERFORMANCE_NLSTEXT = 0x80000060,
CURRENT_CONFIG = 0x80000005,
DYN_DATA = 0x80000006,
CURRENT_USER_LOCAL_SETTINGS = 0x80000007,
}
Shorthands
Also exports the standard shorthand HKEY names:
export const HKCR = HKEY.CLASSES_ROOT;
export const HKCU = HKEY.CURRENT_USER;
export const HKLM = HKEY.LOCAL_MACHINE;
export const HKU = HKEY.USERS;
isHKEY
Helper returns if the argument is a valid-looking HKEY
. Most APIs will throw
an assertion error if hkey
that does not return true for this is used.
Valid HKEY values are,
- The values of the
HKEY
enum - Objects returned from
createKey
,openKey
,loadAppKey
andopenCurrentUser
External
values that represent HKEYs (for example, from another node addon)- Non-
0
32-bit values that represent the pointer value of HKEYs (for example, from another node addon) - not that this is unreliable on 64-bit applications, and should be avoided.
export function isHKEY(hkey: any): boolean;
Access
enum
Specifies access checks for opened or created keys. Not always enforced for opened keys:
Certain registry operations perform access checks against the security descriptor of the key, not the access mask specified when the handle to the key was obtained. For example, even if a key is opened with a samDesired of KEY_READ, it can be used to create registry keys if the key's security descriptor permits. In contrast, the RegSetValueEx function specifically requires that the key be opened with the KEY_SET_VALUE access right.
// from https://docs.microsoft.com/en-nz/windows/desktop/SysInfo/registry-key-security-and-access-rights
export enum Access {
// Specific rights
QUERY_VALUE = 0x0001,
SET_VALUE = 0x0002,
CREATE_SUB_KEY = 0x0004,
ENUMERATE_SUB_KEYS = 0x0008,
NOTIFY = 0x0010,
CREATE_LINK = 0x0020,
// WOW64. See https://docs.microsoft.com/en-nz/windows/desktop/WinProg64/accessing-an-alternate-registry-view
WOW64_64KEY = 0x0100,
WOW64_32KEY = 0x0200,
// Generic rights.
READ = 0x2_0019,
WRITE = 0x2_0006,
EXECUTE = READ,
ALL_ACCESS = 0xF_003F,
}
ValueType
enum
Types for registry values. See documentation.
export enum ValueType {
NONE = 0, // No value type
SZ = 1, // Unicode nul terminated string
EXPAND_SZ = 2, // Unicode nul terminated string
// (with environment variable references)
BINARY = 3, // Free form binary
DWORD = 4, // 32-bit number
DWORD_LITTLE_ENDIAN = 4, // 32-bit number (same as REG_DWORD)
DWORD_BIG_ENDIAN = 5, // 32-bit number
LINK = 6, // Symbolic Link (unicode)
MULTI_SZ = 7, // Multiple Unicode strings
RESOURCE_LIST = 8, // Resource list in the resource map
FULL_RESOURCE_DESCRIPTOR = 9, // Resource list in the hardware description
RESOURCE_REQUIREMENTS_LIST = 10,
QWORD = 11, // 64-bit number
QWORD_LITTLE_ENDIAN = 11, // 64-bit number (same as REG_QWORD)
}
Raw APIs
These APIs fairly directly wrap the Windows API linked, only abstracting some of the allocation and general usage style.
The exception is enumKeyNames
and enumValueNames
which iterate to build a list and only return the names, and not other properties.
Value
type
Raw registry values returned from queryValueRaw
and getValueRaw
are simply Node Buffer
s with an additional type
property from ValueType
:
export type Value = Buffer & { type: ValueType };
createKey
Wraps RegCreateKeyExW
Creates the specified registry key. If the key already exists, the function opens it. Note that key names are not case sensitive.
You must call closeKey
on the result to clean up.
export function createKey(
hkey: HKEY,
subKey: string,
access: Access,
options: CreateKeyOptions = 0,
): HKEY;
export enum CreateKeyOptions {
NON_VOLATILE = 0,
VOLATILE = 1,
CREATE_LINK = 2,
BACKUP_RESTORE = 4,
}
openKey
Wraps RegOpenKeyExW
Opens the specified registry key. Note that key names are not case sensitive.
Returns null
if subKey
does not exist under hkey
.
You must call closeKey
on the result to clean up.
export function openKey(
hkey: HKEY,
subKey: string,
access: Access,
options: OpenKeyOptions = 0,
): HKEY | null;
export enum OpenKeyOptions {
OPEN_LINK = 8,
}
loadAppKey
Wraps RegLoadAppKeyW
Loads the specified registry hive as an application hive.
You must call closeKey
on the result to clean up.
export function loadAppKey(
file: string,
access: Access,
): HKEY;
openCurrentUser
Wraps RegOpenCurrentUser
Retrieves a handle to the
HKEY_CURRENT_USER
key for the user the current thread is impersonating.
Only makes sense to use if you have access to Windows user impersonation. Maybe take a look at my currently WIP Windows user account package.
You must call closeKey
on the result to clean up.
export function openCurrentUser(access: Access): HKEY;
enumKeyNames
Wraps RegEnumKeyExW
iterated to get the sub key names for a key.
Enumerates the subkeys of the specified open registry key.
export function enumKeyNames(hkey: HKEY): string[];
enumValueNames
Wraps RegEnumValueW
iterated to get the value names for a key.
Enumerates the values for the specified open registry key.
export function enumValueNames(hkey: HKEY): string[];
queryValueRaw
Wraps RegQueryValueExW
without additional parsing.
Retrieves the type and data for the specified value name associated with an open registry key.
You may want to use queryValue
instead.
Returns null
if valueName
does not exist under hkey
.
export function queryValueRaw(
hkey: HKEY,
valueName: string | null,
): Value | null;
getValueRaw
Wraps RegGetValueW
without additional parsing.
Retrieves the type and data for the specified registry value.
You may want to use getValue
instead.
Returns null
if subKey
or valueName
does not exist under hkey
.
export function getValueRaw(
hkey: HKEY,
subKey: string | null,
valueName: string | null,
flags: GetValueFlags = 0,
): Value | null;
export enum GetValueFlags {
RT_ANY = 0xffff,
RT_REG_NONE = 0x0001,
RT_REG_SZ = 0x0002,
RT_REG_EXPAND_SZ = 0x0004,
RT_REG_BINARY = 0x0008,
RT_REG_DWORD = 0x0010,
RT_REG_MULTI_SZ = 0x0020,
RT_REG_QWORD = 0x0040,
RT_DWORD = RT_REG_DWORD | RT_REG_BINARY,
RT_QWORD = RT_REG_QWORD | RT_REG_BINARY,
NO_EXPAND = 0x10000000,
// ZEROONFAILURE = 0x20000000, // doesn't make sense here
SUBKEY_WOW6464KEY = 0x00010000,
SUBKEY_WOW6432KEY = 0x00020000,
}
setValueRaw
Wraps RegSetValueExW
Sets the data and type of a specified value under a registry key.
export function setValueRaw(
hkey: HKEY,
valueName: string | null,
valueType: ValueType,
data: Buffer,
): void;
renameKey
Wraps RegRenameKey
Changes the name of the specified registry key.
export function renameKey(
hkey: HKEY,
subKey: string | null,
newSubKey: string,
): void;
copyTree
Wraps RegCopyTreeW
Copies the specified registry key, along with its values and subkeys, to the specified destination key.
export function copyTree(
hkeySrc: HKEY,
subKey: string | null,
hkeyDest: HKEY,
): void;
deleteKey
Wraps RegDeleteKeyW
Deletes a subkey and its values. Note that key names are not case sensitive.
Returns true if the key existed before it was deleted.
export function deleteKey(
hkey: HKEY,
subKey: string,
): boolean;
deleteTree
Wraps RegDeleteTreeW
Deletes the subkeys and values of the specified key recursively.
Returns true if the key existed before it was deleted.
export function deleteTree(
hkey: HKEY,
subKey: string | null,
): boolean;
deleteKeyValue
Wraps RegDeleteKeyValueW
Removes the specified value from the specified registry key and subkey.
Returns true if the value existed before it was deleted.
export function deleteKeyValue(
hkey: HKEY,
subKey: string,
valueName: string,
): boolean;
deleteValue
Wraps RegDeleteValueW
Removes a named value from the specified registry key. Note that value names are not case sensitive.
Returns true if the value existed before it was deleted.
export function deleteValue(
hkey: HKEY,
valueName: string | null,
): boolean;
closeKey
Wraps RegCloseKey
Closes a handle to the specified registry key.
For convenience, null
or undefined
values are allowed and ignored.
export function closeKey(hkey: HKEY | null | undefined): void;
Format helpers
parseValue
Returns the JS-native value for common Value
types:
SZ
,EXPAND_SZ
->string
BINARY
->Buffer
DWORD
/DWORD_LITTLE_ENDIAN
->number
DWORD_BIG_ENDIAN
->number
MULTI_SZ
->string[]
QWORD
/QWORD_LITTLE_ENDIAN
->bigint
Throws an assertion error if the type is not one of the above!
For convenience, passes through null
so missing values don't have
to be specially treated.
export type ParsedValue = number | bigint | string | string[] | Buffer;
export function parseValue(value: Value | null): ParsedValue | null;
parseString
Parses SZ
and EXPAND_SZ
(etc.) registry values.
export function parseString(value: Buffer): string;
parseMultiString
Parses MULTI_SZ
registry values.
export function parseMultiString(value: Buffer): string[];
formatString
Formats a string to SZ
, EXPAND_SZ
(etc.) format.
export function formatString(value: string): Buffer;
formatMultiString
Formats an array of string
s to MULTI_SZ
format.
export function formatMultiString(values: string[]): Buffer;
formatDWORD
Formats a number
to DWORD
/ DWORD_LITTLE_ENDIAN
format.
export function formatDWORD(value: number): Buffer;
formatQWORD
Formats a number
or bigint
to QWORD
format.
export function formatQWORD(value: number | bigint): Buffer;
Formatted value APIs
These APIs wrap the raw value APIs with the formatting helpers.
setValue{Type}
A set of wrappers that sets the registry value using setValueRaw
with the appropriate ValueType
and format helper.
For example, setValueSZ
is setValueRaw(hkey, valueName, ValueType.SZ, formatString(value))
.
export function setValueSZ(
hkey: HKEY,
valueName: string | null,
value: string,
): void;
export function setValueEXPAND_SZ(
hkey: HKEY,
valueName: string | null,
value: string,
): void;
export function setValueMULTI_SZ(
hkey: HKEY,
valueName: string | null,
value: string[],
): void;
export function setValueDWORD(
hkey: HKEY,
valueName: string | null,
value: number,
): void;
export function setValueQWORD(
hkey: HKEY,
valueName: string | null,
value: number | bigint,
): void;
getValue
Wraps getValueRaw
in parseValue
.
export function getValue(
hkey: HKEY,
subKey: string | null,
valueName: string | null,
flags: GetValueFlags = 0,
): ParsedValue | null;
queryValue
Wraps queryValueRaw
in parseValue
.
export function queryValue(
hkey: HKEY,
valueName: string | null,
): ParsedValue | null;
Bundling
Using code with native dependencies with bundlers can be complicated. The simplest option is simply to mark this
package as external, for example external: ["native-reg"]
for rollup / vite.
There is an example showing both this approach, and using a manual bundling plugin in the examples/rollup directory, which should at least get you started.
Development
This package uses prebuildify
to create native builds for x64
, x64
, and ARM64
Windows. This wraps node-gyp
to cross-compile the native code for each target, copying the results to the prebuilds
directory. Because of this,
if you are a user of this library you should not need anything installed yourself.
If you want to build this library locally you will need to install the required build tools, check out the node-gyp
documentation for the official instructions.
Then it should simply be yarn build
to build the typescript and native code for each platform, and run a simple test
to ensure the native code loads and supports basic functionality. If you are only testing changes to the TypeScript
code you should only need to run yarn build:ts
and yarn build:{arch}
for your current architecture, for example
you can avoid needing to install the ARM64 build tools.