Skip to content

Commit d4eb405

Browse files
committed
Switch to -W versions of registry API
using UTF-16 strings throughout, to fix encoding issues in the key paths and value strings read and written.
1 parent 3ce58cd commit d4eb405

File tree

6 files changed

+78
-31
lines changed

6 files changed

+78
-31
lines changed

lib/error.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
module.exports = {
22
0x2: 'ERROR_FILE_NOT_FOUND',
33
0x3: 'ERROR_PATH_NOT_FOUND',
4+
0x5: 'ERROR_ACCESS_DENIED',
45
0x6: 'ERROR_INVALID_HANDLE'
56
};

lib/key.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class Key {
3232
}
3333

3434
deleteKey () {
35-
this._registry.deleteKey(this);
35+
this._registry.deleteKey(this, null);
3636
}
3737

3838
/**

lib/native/adv_api.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ var advApi = ffi.Library('Advapi32', {
2020
_Reserved_ LPDWORD lpReserved,
2121
_Out_opt_ LPDWORD lpType,
2222
_Out_opt_ LPBYTE lpData,
23-
_Inout_opt_ LPDWORD lpcbDataRegOpenKeyExA
23+
_Inout_opt_ LPDWORD lpcbData
2424
);
2525
*/
26-
RegQueryValueExA: ['long', [types.HKEY, 'string', 'pointer', types.LPDWORD, types.LPBYTE, types.LPDWORD]],
26+
RegQueryValueExW: ['long', [types.HKEY, types.LPCWSTR, 'pointer', types.LPDWORD, types.LPBYTE, types.LPDWORD]],
2727
/*
2828
LONG WINAPI RegOpenKeyEx(
2929
_In_ HKEY hKey,
@@ -33,7 +33,7 @@ var advApi = ffi.Library('Advapi32', {
3333
_Out_ PHKEY phkResult
3434
);
3535
*/
36-
RegOpenKeyExA: ['long', ['longlong', 'string', types.DWORD, types.REGSAM, types.PHKEY]],
36+
RegOpenKeyExW: ['long', ['longlong', types.LPCWSTR, types.DWORD, types.REGSAM, types.PHKEY]],
3737
/*
3838
LONG WINAPI RegSetValueEx(
3939
_In_ HKEY hKey,
@@ -44,7 +44,7 @@ var advApi = ffi.Library('Advapi32', {
4444
_In_ DWORD cbData
4545
);
4646
*/
47-
RegSetValueExA: ['long', [types.HKEY, 'string', 'pointer', types.DWORD, types.LPBYTE, types.DWORD]],
47+
RegSetValueExW: ['long', [types.HKEY, types.LPCWSTR, 'pointer', types.DWORD, types.LPBYTE, types.DWORD]],
4848
/**
4949
* LONG WINAPI RegCreateKeyEx(
5050
_In_ HKEY hKey,
@@ -58,22 +58,22 @@ var advApi = ffi.Library('Advapi32', {
5858
_Out_opt_ LPDWORD lpdwDisposition
5959
);
6060
*/
61-
RegCreateKeyExA: ['long', [types.HKEY, 'string', 'pointer', 'pointer', types.DWORD, types.REGSAM, 'pointer', types.PHKEY, 'pointer']],
61+
RegCreateKeyExW: ['long', [types.HKEY, types.LPCWSTR, 'pointer', 'pointer', types.DWORD, types.REGSAM, 'pointer', types.PHKEY, 'pointer']],
6262
/*
6363
LONG WINAPI RegDeleteTree(
6464
_In_ HKEY hKey,
6565
_In_opt_ LPCTSTR lpSubKey
6666
);
6767
*/
68-
RegDeleteTreeA: ['long', [types.HKEY, 'string']],
68+
RegDeleteTreeW: ['long', [types.HKEY, types.LPCWSTR]],
6969
/*
7070
LONG WINAPI RegDeleteValue(
7171
_In_ HKEY hKey,
7272
_In_opt_ LPCTSTR lpValueName
7373
);
7474
*/
7575

76-
RegDeleteValueA: ['long', [types.HKEY, 'string']],
76+
RegDeleteValueW: ['long', [types.HKEY, types.LPCWSTR]],
7777
/*
7878
LONG WINAPI RegCloseKey(
7979
_In_ HKEY hKey

lib/ref-LPWSTR.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
'use strict';
2+
var ref = require('ref');
3+
4+
/**
5+
* This is a simple implementation of a zero-terminated UTF-16 string (Windows LPWSTR) for ref.
6+
*
7+
* There is an alternative implementation in the ref-wchar package, ref-wchar.string, but that uses
8+
* iconv for translation between Node's native UTF-8 and UTF-16, which is a large dependency, and does not
9+
* providing a UTF-16-from-string method that we need here either. This version uses Buffer's built-in utf16le
10+
* support.
11+
*/
12+
var LPWSTR = Object.create(ref.types.CString);
13+
LPWSTR.name = 'LPWSTR';
14+
15+
var encoding = 'utf16le';
16+
17+
LPWSTR.toString = function toString(buffer) {
18+
// Strip trailing nul if present
19+
var length = buffer.length;
20+
if ((length > 2) && (buffer.readInt16LE(length - 2) === 0)) {
21+
length -= 2;
22+
}
23+
return buffer.toString(encoding, 0, length);
24+
};
25+
26+
LPWSTR.fromString = function fromString(input) {
27+
if ((input !== null) && (typeof (input) !== 'undefined')) {
28+
// Use 'new Buffer' not 'Buffer.from' for Node v4.1 compatibility
29+
return new Buffer(input + '\0', encoding);
30+
}
31+
return null;
32+
};
33+
34+
LPWSTR.get = function get(buf, offset) {
35+
var _buf = buf.readPointer(offset);
36+
if (_buf.isNull()) {
37+
return null;
38+
}
39+
var stringBuf = _buf.reinterpretUntilZeros(2);
40+
return LPWSTR.toString(stringBuf);
41+
};
42+
43+
LPWSTR.set = function set(buf, offset, val) {
44+
var _buf = Buffer.isBuffer(val) ? val : LPWSTR.fromString(val);
45+
return buf.writePointer(_buf, offset);
46+
};
47+
48+
module.exports = LPWSTR;

lib/registry.js

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ var api = {
1717

1818
var pHkey = ref.alloc(types.PHKEY, new Buffer(ref.sizeof.pointer));
1919
debug('PHKEY LENGTH: ' + pHkey.deref().length);
20-
var result = advApi.RegOpenKeyExA(preDefinedKey, subKeyName, 0, accessLevel, pHkey);
20+
var result = advApi.RegOpenKeyExW(preDefinedKey, subKeyName, 0, accessLevel, pHkey);
2121
debug('result:' + result);
2222
if (result !== 0) {
2323
throw 'Failed to open key error: ' + error[result];
@@ -30,9 +30,9 @@ var api = {
3030

3131
// RegOpenKeyEx can also take an HKEY in addition to a predefined value
3232
var advApi2 = ffi.Library('Advapi32', {
33-
RegOpenKeyExA: ['long', [types.HKEY, 'string', types.DWORD, types.REGSAM, types.PHKEY]]
33+
RegOpenKeyExW: ['long', [types.HKEY, types.LPCWSTR, types.DWORD, types.REGSAM, types.PHKEY]]
3434
});
35-
var result = advApi2.RegOpenKeyExA(keyObject.handle.deref(), subKeyName, 0, accessLevel, pHkey);
35+
var result = advApi2.RegOpenKeyExW(keyObject.handle.deref(), subKeyName, 0, accessLevel, pHkey);
3636

3737
if (result !== 0) {
3838
throw 'Failed to open key error: ' + error[result];
@@ -44,15 +44,15 @@ var api = {
4444
var pKeyDataLength = ref.alloc(types.LPDWORD, new Buffer(ref.sizeof.pointer)),
4545
pKeyType = ref.alloc(types.LPDWORD, new Buffer(ref.sizeof.pointer));
4646
// QUERY FOR VALUE SIZE & TYPE
47-
var result = advApi.RegQueryValueExA(key.handle.deref(), valueName, null, pKeyType, null, pKeyDataLength);
47+
var result = advApi.RegQueryValueExW(key.handle.deref(), valueName, null, pKeyType, null, pKeyDataLength);
4848
// READ VALUE
4949
var value = new Buffer(pKeyDataLength.readUInt32LE()),
5050
valueType = pKeyType.readUInt32LE();
5151
switch (valueType) {
5252
case windef.REG_VALUE_TYPE.REG_SZ:
5353
case windef.REG_VALUE_TYPE.REG_EXPAND_SZ:
5454
case windef.REG_VALUE_TYPE.REG_LINK:
55-
value.type = types.LPCTSR;
55+
value.type = types.LPCWSTR;
5656
break;
5757
case windef.REG_VALUE_TYPE.REG_BINARY:
5858
value.type = types.PVOID;
@@ -67,16 +67,14 @@ var api = {
6767
}
6868

6969
// READ VALUE
70-
result = advApi.RegQueryValueExA(key.handle.deref(), valueName, null, pKeyType, value, pKeyDataLength);
70+
result = advApi.RegQueryValueExW(key.handle.deref(), valueName, null, pKeyType, value, pKeyDataLength);
7171

7272
if (result !== 0) {
7373
throw 'Failed to open key error: ' + error[result];
7474
}
7575

76-
if (value.type === types.LPTSR) {
77-
// TODO not sure why buffer's utf8 parsing leaves in the unicode null
78-
// escape sequence. This is a work-around (at least on node 4.1)
79-
value = value.toString().replace('\u0000', '');
76+
if (value.type === types.LPCWSTR) {
77+
value = types.LPCWSTR.toString(value);
8078
}
8179

8280
return value;
@@ -93,24 +91,21 @@ var api = {
9391
case windef.REG_VALUE_TYPE.REG_SZ:
9492
case windef.REG_VALUE_TYPE.REG_EXPAND_SZ:
9593
case windef.REG_VALUE_TYPE.REG_LINK:
96-
buffer = new Buffer(value, 'utf8');
94+
buffer = types.LPCWSTR.fromString(value);
9795
byte = ref.alloc(types.LPBYTE, buffer);
98-
debug('content length:' + Buffer.byteLength(value, 'utf8'));
99-
debug(value);
100-
debug(buffer.length);
101-
result = advApi.RegSetValueExA(key.handle.deref(), valueName, null, valueType, byte.deref(), Buffer.byteLength(value, 'utf8'));
96+
result = advApi.RegSetValueExW(key.handle.deref(), valueName, null, valueType, byte.deref(), buffer.length);
10297
break;
10398
case windef.REG_VALUE_TYPE.REG_BINARY:
10499
// we assume that the value is a buffer since it should be binary data
105100
buffer = value;
106101
byte = ref.alloc(types.LPBYTE, buffer);
107-
result = advApi.RegSetValueExA(key.handle.deref(), valueName, null, valueType, byte.deref(), buffer.length);
102+
result = advApi.RegSetValueExW(key.handle.deref(), valueName, null, valueType, byte.deref(), buffer.length);
108103
break;
109104
case windef.REG_VALUE_TYPE.REG_DWORD:
110105
case windef.REG_VALUE_TYPE.REG_DWORD_BIG_ENDIAN:
111106
case windef.REG_VALUE_TYPE.REG_DWORD_LITTLE_ENDIAN:
112107
buffer = new Buffer(4, value);
113-
result = advApi.RegSetValueExA(key.handle.deref(), valueName, null, valueType, byte.deref(), buffer.length);
108+
result = advApi.RegSetValueExW(key.handle.deref(), valueName, null, valueType, byte.deref(), buffer.length);
114109
break;
115110
default:
116111
throw 'The type ' + valueType + ' is currently unsupported';
@@ -123,21 +118,21 @@ var api = {
123118
createKey: function (key, subKeyName, accessLevel) {
124119
var pHkey = ref.alloc(types.PHKEY, new Buffer(ref.sizeof.pointer));
125120

126-
var result = advApi.RegCreateKeyExA(key.handle.deref(), subKeyName, null, null, windef.REG_OPTION_NON_VOLATILE, accessLevel, null, pHkey, null);
121+
var result = advApi.RegCreateKeyExW(key.handle.deref(), subKeyName, null, null, windef.REG_OPTION_NON_VOLATILE, accessLevel, null, pHkey, null);
127122

128123
if (result !== 0) {
129124
throw 'Failed to open key error: ' + error[result];
130125
}
131126
},
132127
deleteKey: function (key, subKeyName) {
133-
var result = advApi.RegDeleteTreeA(key.handle.deref(), subKeyName);
128+
var result = advApi.RegDeleteTreeW(key.handle.deref(), subKeyName);
134129

135130
if (result !== 0) {
136-
throw 'Failed to open key error ' + result + ':' + error[result];
131+
throw 'Failed to delete key error ' + result + ':' + error[result];
137132
}
138133
},
139134
deleteValue: function (key, value) {
140-
var result = advApi.RegDeleteValueA(key.handle.deref(), value);
135+
var result = advApi.RegDeleteValueW(key.handle.deref(), value);
141136

142137
if (result !== 0) {
143138
throw 'Failed to delete value error ' + result + ':' + error[result];

lib/types.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
var ref = require('ref');
1+
'use strict';
2+
var ref = require('ref'),
3+
LPWSTR = require('./ref-LPWSTR.js');
24

35
var types = {
46
REGSAM: ref.types.ulong,
@@ -10,7 +12,8 @@ var types = {
1012
PVOID: ref.refType('pointer'),
1113
HANDLE: ref.refType(ref.types.void),
1214
HINSTANCE: ref.refType(ref.types.void),
13-
LPCTSTR: ref.refType(ref.types.CString),
15+
LPCSTR: ref.refType(ref.types.CString),
16+
LPCWSTR: LPWSTR,
1417
STRING: ref.types.CString,
1518
INT: ref.types.int,
1619
LPVOID: ref.refType(ref.types.void)

0 commit comments

Comments
 (0)