Skip to content

Switch to -W versions of registry API #60

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

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions lib/error.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module.exports = {
0x2: 'ERROR_FILE_NOT_FOUND',
0x3: 'ERROR_PATH_NOT_FOUND',
0x5: 'ERROR_ACCESS_DENIED',
0x6: 'ERROR_INVALID_HANDLE'
};
14 changes: 7 additions & 7 deletions lib/native/adv_api.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ var advApi = ffi.Library('Advapi32', {
_Reserved_ LPDWORD lpReserved,
_Out_opt_ LPDWORD lpType,
_Out_opt_ LPBYTE lpData,
_Inout_opt_ LPDWORD lpcbDataRegOpenKeyExA
_Inout_opt_ LPDWORD lpcbData
);
*/
RegQueryValueExA: ['long', [types.HKEY, 'string', 'pointer', types.LPDWORD, types.LPBYTE, types.LPDWORD]],
RegQueryValueExW: ['long', [types.HKEY, types.LPCWSTR, 'pointer', types.LPDWORD, types.LPBYTE, types.LPDWORD]],
/*
LONG WINAPI RegOpenKeyEx(
_In_ HKEY hKey,
Expand All @@ -33,7 +33,7 @@ var advApi = ffi.Library('Advapi32', {
_Out_ PHKEY phkResult
);
*/
RegOpenKeyExA: ['long', ['longlong', 'string', types.DWORD, types.REGSAM, types.PHKEY]],
RegOpenKeyExW: ['long', ['longlong', types.LPCWSTR, types.DWORD, types.REGSAM, types.PHKEY]],
/*
LONG WINAPI RegSetValueEx(
_In_ HKEY hKey,
Expand All @@ -44,7 +44,7 @@ var advApi = ffi.Library('Advapi32', {
_In_ DWORD cbData
);
*/
RegSetValueExA: ['long', [types.HKEY, 'string', 'pointer', types.DWORD, types.LPBYTE, types.DWORD]],
RegSetValueExW: ['long', [types.HKEY, types.LPCWSTR, 'pointer', types.DWORD, types.LPBYTE, types.DWORD]],
/**
* LONG WINAPI RegCreateKeyEx(
_In_ HKEY hKey,
Expand All @@ -58,22 +58,22 @@ var advApi = ffi.Library('Advapi32', {
_Out_opt_ LPDWORD lpdwDisposition
);
*/
RegCreateKeyExA: ['long', [types.HKEY, 'string', 'pointer', 'pointer', types.DWORD, types.REGSAM, 'pointer', types.PHKEY, 'pointer']],
RegCreateKeyExW: ['long', [types.HKEY, types.LPCWSTR, 'pointer', 'pointer', types.DWORD, types.REGSAM, 'pointer', types.PHKEY, 'pointer']],
/*
LONG WINAPI RegDeleteTree(
_In_ HKEY hKey,
_In_opt_ LPCTSTR lpSubKey
);
*/
RegDeleteTreeA: ['long', [types.HKEY, 'string']],
RegDeleteTreeW: ['long', [types.HKEY, types.LPCWSTR]],
/*
LONG WINAPI RegDeleteValue(
_In_ HKEY hKey,
_In_opt_ LPCTSTR lpValueName
);
*/

RegDeleteValueA: ['long', [types.HKEY, 'string']],
RegDeleteValueW: ['long', [types.HKEY, types.LPCWSTR]],
/*
LONG WINAPI RegCloseKey(
_In_ HKEY hKey
Expand Down
2 changes: 1 addition & 1 deletion lib/native/shell32.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ var shell32 = new ffi.Library('Shell32', {
_Inout_ SHELLEXECUTEINFO *pExecInfo
);
*/
ShellExecuteExA: ['bool', [SHELLEXECUTEINFOPtr]]
ShellExecuteExW: ['bool', [SHELLEXECUTEINFOPtr]]
});

module.exports = shell32;
57 changes: 57 additions & 0 deletions lib/ref-LPWSTR.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
'use strict';
var ref = require('ref');

/**
* This is a simple implementation of a zero-terminated UTF-16 string (Windows LPWSTR) for ref.
*
* There is an alternative implementation in the ref-wchar package, ref-wchar.string, but that uses
* iconv for translation between Node's native UTF-8 and UTF-16, which is a large dependency, and does not
* provide a UTF-16-from-string method that we need here either. This version uses Buffer's built-in utf16le
* support.
*/
var LPWSTR = Object.create(ref.types.CString);
LPWSTR.name = 'LPWSTR';

var encoding = 'utf16le';

function isNotNullOrUndefined(input) {
return (input !== null) && (typeof (input) !== 'undefined');
}

LPWSTR.toString = function toString(buffer) {
if (isNotNullOrUndefined(buffer)) {
// Strip trailing nul if present
var length = buffer.length;
if ((length > 2) && (buffer.readInt16LE(length - 2) === 0)) {
length -= 2;
}
return buffer.toString(encoding, 0, length);
}

// Pass null or undefined input through unchanged.
return buffer;
};

LPWSTR.fromString = function fromString(input) {
if (isNotNullOrUndefined(input)) {
// Use 'new Buffer' not 'Buffer.from' for Node v4.1 compatibility
return new Buffer(input + '\0', encoding);
}
return null;
};

LPWSTR.get = function get(buf, offset) {
var _buf = buf.readPointer(offset);
if (_buf.isNull()) {
return null;
}
var stringBuf = _buf.reinterpretUntilZeros(2);
return LPWSTR.toString(stringBuf);
};

LPWSTR.set = function set(buf, offset, val) {
var _buf = Buffer.isBuffer(val) ? val : LPWSTR.fromString(val);
return buf.writePointer(_buf, offset);
};

module.exports = LPWSTR;
37 changes: 16 additions & 21 deletions lib/registry.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ var api = {

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

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

if (result !== 0) {
throw 'Failed to open key error: ' + error[result];
Expand All @@ -44,15 +44,15 @@ var api = {
var pKeyDataLength = ref.alloc(types.LPDWORD, new Buffer(ref.sizeof.pointer)),
pKeyType = ref.alloc(types.LPDWORD, new Buffer(ref.sizeof.pointer));
// QUERY FOR VALUE SIZE & TYPE
var result = advApi.RegQueryValueExA(key.handle.deref(), valueName, null, pKeyType, null, pKeyDataLength);
var result = advApi.RegQueryValueExW(key.handle.deref(), valueName, null, pKeyType, null, pKeyDataLength);
// READ VALUE
var value = new Buffer(pKeyDataLength.readUInt32LE()),
valueType = pKeyType.readUInt32LE();
switch (valueType) {
case windef.REG_VALUE_TYPE.REG_SZ:
case windef.REG_VALUE_TYPE.REG_EXPAND_SZ:
case windef.REG_VALUE_TYPE.REG_LINK:
value.type = types.LPCTSR;
value.type = types.LPCWSTR;
break;
case windef.REG_VALUE_TYPE.REG_BINARY:
value.type = types.PVOID;
Expand All @@ -67,16 +67,14 @@ var api = {
}

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

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

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

return value;
Expand All @@ -93,24 +91,21 @@ var api = {
case windef.REG_VALUE_TYPE.REG_SZ:
case windef.REG_VALUE_TYPE.REG_EXPAND_SZ:
case windef.REG_VALUE_TYPE.REG_LINK:
buffer = new Buffer(value, 'utf8');
buffer = types.LPCWSTR.fromString(value);
byte = ref.alloc(types.LPBYTE, buffer);
debug('content length:' + Buffer.byteLength(value, 'utf8'));
debug(value);
debug(buffer.length);
result = advApi.RegSetValueExA(key.handle.deref(), valueName, null, valueType, byte.deref(), Buffer.byteLength(value, 'utf8'));
result = advApi.RegSetValueExW(key.handle.deref(), valueName, null, valueType, byte.deref(), buffer.length);
break;
case windef.REG_VALUE_TYPE.REG_BINARY:
// we assume that the value is a buffer since it should be binary data
buffer = value;
byte = ref.alloc(types.LPBYTE, buffer);
result = advApi.RegSetValueExA(key.handle.deref(), valueName, null, valueType, byte.deref(), buffer.length);
result = advApi.RegSetValueExW(key.handle.deref(), valueName, null, valueType, byte.deref(), buffer.length);
break;
case windef.REG_VALUE_TYPE.REG_DWORD:
case windef.REG_VALUE_TYPE.REG_DWORD_BIG_ENDIAN:
case windef.REG_VALUE_TYPE.REG_DWORD_LITTLE_ENDIAN:
buffer = new Buffer(4, value);
result = advApi.RegSetValueExA(key.handle.deref(), valueName, null, valueType, byte.deref(), buffer.length);
result = advApi.RegSetValueExW(key.handle.deref(), valueName, null, valueType, byte.deref(), buffer.length);
break;
default:
throw 'The type ' + valueType + ' is currently unsupported';
Expand All @@ -123,21 +118,21 @@ var api = {
createKey: function (key, subKeyName, accessLevel) {
var pHkey = ref.alloc(types.PHKEY, new Buffer(ref.sizeof.pointer));

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

if (result !== 0) {
throw 'Failed to open key error: ' + error[result];
}
},
deleteKey: function (key, subKeyName) {
var result = advApi.RegDeleteTreeA(key.handle.deref(), subKeyName);
var result = advApi.RegDeleteTreeW(key.handle.deref(), subKeyName);

if (result !== 0) {
throw 'Failed to open key error ' + result + ':' + error[result];
throw 'Failed to delete key error ' + result + ':' + error[result];
}
},
deleteValue: function (key, value) {
var result = advApi.RegDeleteValueA(key.handle.deref(), value);
var result = advApi.RegDeleteValueW(key.handle.deref(), value);

if (result !== 0) {
throw 'Failed to delete value error ' + result + ':' + error[result];
Expand Down
7 changes: 5 additions & 2 deletions lib/types.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
var ref = require('ref');
'use strict';
var ref = require('ref'),
LPWSTR = require('./ref-LPWSTR.js');

var types = {
REGSAM: ref.types.ulong,
Expand All @@ -10,7 +12,8 @@ var types = {
PVOID: ref.refType('pointer'),
HANDLE: ref.refType(ref.types.void),
HINSTANCE: ref.refType(ref.types.void),
LPCTSTR: ref.refType(ref.types.CString),
LPCSTR: ref.refType(ref.types.CString),
LPCWSTR: LPWSTR,
STRING: ref.types.CString,
INT: ref.types.int,
LPVOID: ref.refType(ref.types.void)
Expand Down
2 changes: 1 addition & 1 deletion lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ module.exports = {
hProcess: ref.alloc(types.HANDLE)
});

shell32.ShellExecuteExA.async(shellexecuteinfoval.ref(), callback);
shell32.ShellExecuteExW.async(shellexecuteinfoval.ref(), callback);
},
associateExeForFile: function (handlerName, handlerDescription, iconPath, exePath, extensionName) {
var key = registry.openKeyFromPredefined(windef.HKEY.HKEY_CLASSES_ROOT, '', windef.KEY_ACCESS.KEY_ALL_ACCESS);
Expand Down
10 changes: 5 additions & 5 deletions lib/windef.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,14 @@ module.exports = {
cbSize: types.DWORD,
fMask: types.ULONG,
hwnd: types.HWND,
lpVerb: types.STRING,
lpFile: types.STRING,
lpParameters: types.STRING,
lpDirectory: types.STRING,
lpVerb: types.LPCWSTR,
lpFile: types.LPCWSTR,
lpParameters: types.LPCWSTR,
lpDirectory: types.LPCWSTR,
nShow: types.INT,
hInstApp: types.HINSTANCE,
lpIDList: types.LPVOID,
lpClass: types.STRING,
lpClass: types.LPCWSTR,
hkeyClass: types.HKEY,
dwHotKey: types.DWORD,
DUMMYUNIONNAME: DUMMYUNIONNAME,
Expand Down
29 changes: 18 additions & 11 deletions test/mock/adv_api.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,16 @@ var advApi = {
_Inout_opt_ LPDWORD lpcbData
);
*/
RegQueryValueExA: function (hKey, valueName, shouldBeNull, lpType, lpData, lpcbData) {
debug('RegQueryValueExA');
RegQueryValueExW: function (hKey, valueName, shouldBeNull, lpType, lpData, lpcbData) {
debug('RegQueryValueExW');
if (lpData === null) {
debug(keys[hKey.address()].values.test_value_name);
lpType.writeUInt32LE(windef.REG_VALUE_TYPE.REG_SZ, 0);
lpcbData.writeUInt32LE(keys[hKey.address()].values[valueName].length, 0);
return 0;
}

lpData.write(keys[hKey.address()].values[valueName].value, 'utf8');
keys[hKey.address()].values[valueName].valueBuffer.copy(lpData);
lpType.writeUInt16LE(windef.REG_VALUE_TYPE.REG_SZ);
return 0;
},
Expand All @@ -67,9 +67,9 @@ var advApi = {
_Out_ PHKEY phkResult
);
*/
RegOpenKeyExA: function (hKey, subKeyName, shouldBeZero, accessLevel, pHkey) {
RegOpenKeyExW: function (hKey, subKeyName, shouldBeZero, accessLevel, pHkey) {
var accessLevelFound = findValueInHash(accessLevel, windef.KEY_ACCESS);
debug('Mock: RegOpenKeyExA subkey: ' + subKeyName);
debug('Mock: RegOpenKeyExW subkey: ' + subKeyName);
if (hKey.address) {
debug('Mock: hKey address:' + hKey.address());
}
Expand Down Expand Up @@ -123,8 +123,8 @@ var advApi = {
_In_ DWORD cbData
);
*/
RegSetValueExA: function (hKey, valueName, shouldBeNull, valueType, valueBuffer, bufferLength) {
debug('Mock: RegSetValueExA');
RegSetValueExW: function (hKey, valueName, shouldBeNull, valueType, valueBuffer, bufferLength) {
debug('Mock: RegSetValueExW');
// predefined key
if (typeof hKey === 'number') {
assert(findValueInHash(hKey, windef.HKEY), 'Mock: Invalid predefined key specified');
Expand All @@ -136,9 +136,16 @@ var advApi = {
assert(valueBuffer.constructor === Buffer);
assert(typeof bufferLength === 'number');

// Use the passed length, not the length of valueBuffer
var value = ref.reinterpret(valueBuffer, bufferLength, 0);

// Copy the value to a new buffer and store that
var valueCopy = new Buffer(bufferLength);
value.copy(valueCopy, 0, 0, bufferLength);

keys[hKey.address()].values[valueName] = {
valueType: valueType,
value: ref.readCString(valueBuffer),
valueBuffer: valueCopy,
length: bufferLength
};
return 0;
Expand All @@ -156,8 +163,8 @@ var advApi = {
_Out_opt_ LPDWORD lpdwDisposition
);
*/
RegCreateKeyExA: function (hKey, subKeyName, shouldBeNull, shouldBeNull2, securityAttributes, accessLevel, shouldBeNull3, pHkey, shouldBeNull4) {
debug('Mock: RegCreateKeyExA');
RegCreateKeyExW: function (hKey, subKeyName, shouldBeNull, shouldBeNull2, securityAttributes, accessLevel, shouldBeNull3, pHkey, shouldBeNull4) {
debug('Mock: RegCreateKeyExW');
assert(hKey.constructor === Buffer);
assert(typeof subKeyName === 'string');
assert(shouldBeNull === null);
Expand Down Expand Up @@ -192,7 +199,7 @@ var advApi = {
_In_opt_ LPCTSTR lpSubKey
);
*/
RegDeleteTreeA: function (hKey, subKeyName) {
RegDeleteTreeW: function (hKey, subKeyName) {
if (typeof hKey === 'number') {
assert(findValueInHash(hKey, windef.HKEY), 'Mock: Invalid predefined key specified');
} else {
Expand Down
14 changes: 7 additions & 7 deletions test/mock/ffi.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,19 @@ module.exports = {
var lib;
switch (libFile) {
case 'Advapi32':
assert(funcs.RegOpenKeyExA.constructor === Array);
if(funcs.RegOpenKeyExA[1][0].indirection === types.HKEY.indirection &&
funcs.RegOpenKeyExA[1][0].name === types.HKEY.name) {
assert(funcs.RegOpenKeyExW.constructor === Array);
if(funcs.RegOpenKeyExW[1][0].indirection === types.HKEY.indirection &&
funcs.RegOpenKeyExW[1][0].name === types.HKEY.name) {
// this is redefition for the library only specifying
// a different key type
lib = advApi;
break;
}
assert(funcs.RegQueryValueExA.constructor === Array);
assert(funcs.RegCreateKeyExA.constructor === Array);
assert(funcs.RegDeleteTreeA.constructor === Array);
assert(funcs.RegQueryValueExW.constructor === Array);
assert(funcs.RegCreateKeyExW.constructor === Array);
assert(funcs.RegDeleteTreeW.constructor === Array);
assert(funcs.RegCloseKey.constructor === Array);
assert(funcs.RegSetValueExA.constructor === Array);
assert(funcs.RegSetValueExW.constructor === Array);
assert(typeof funcs === 'object');
lib = advApi;
break;
Expand Down
Loading