447 lines
14 KiB
C
447 lines
14 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1992 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
regqmval.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module contains the client side wrappers for the Win32 Registry
|
|||
|
query multiple values APIs:
|
|||
|
- RegQueryMultipleValuesA
|
|||
|
- RegQueryMultipleValuesW
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
John Vert (jvert) 15-Jun-1995
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include <rpc.h>
|
|||
|
#include "regrpc.h"
|
|||
|
#include "client.h"
|
|||
|
|
|||
|
|
|||
|
WINADVAPI
|
|||
|
LONG
|
|||
|
APIENTRY
|
|||
|
RegQueryMultipleValuesA (
|
|||
|
HKEY hKey,
|
|||
|
PVALENTA val_list,
|
|||
|
DWORD num_vals,
|
|||
|
LPSTR lpValueBuf,
|
|||
|
LPDWORD ldwTotsize
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
The RegQueryMultipleValues function retrieves a list of
|
|||
|
data type/data pairs for a list of value names associated
|
|||
|
with an open registry key.
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
hKey
|
|||
|
Identifies a currently open key or any of the pre-defined reserved handle values:
|
|||
|
HKEY_CLASSES_ROOT
|
|||
|
HEY_CURRENT_USER
|
|||
|
HKEY_LOCAL_MACHINE
|
|||
|
HKEY_USERS
|
|||
|
|
|||
|
valList
|
|||
|
Points to an array of structures describing one or more value entries. This
|
|||
|
contains the value names of the values to be queried. Refer to Appendix A for a
|
|||
|
description of VALUE_ENTRY structure.
|
|||
|
|
|||
|
num_vals
|
|||
|
Size of valList in bytes. If valListLength is not a multiple of the sizeof pvalue, the
|
|||
|
fractional extra space pointed to by valList is ignored.
|
|||
|
|
|||
|
lpValueBuf
|
|||
|
The output buffer for returning value information (value names and value data). Data
|
|||
|
is DWORD aligned with pads inserted as necessary.
|
|||
|
|
|||
|
ldwTotsize
|
|||
|
The total size of the output buffer pointed to by lpvalueBuf. On output ldwTotsize
|
|||
|
contains the number of bytes used including pads. If lpValueBuf was too short, then on
|
|||
|
output ldwTotsize will be the size needed, and caller should assume that lpValueBuf was
|
|||
|
filled up to the size specified by ldwTotsize on input.
|
|||
|
|
|||
|
Return value:
|
|||
|
|
|||
|
If the function succeeds, the return value is ERROR_SUCCESS; otherwise it is one
|
|||
|
of the error value which can be returned by RegQueryValueEx. In addition, if
|
|||
|
either valList or lpValueBuf is too small then ERROR_INSUFFICIENT_BUFFER is returned
|
|||
|
If the function is unable to instantiate/access the provider of the
|
|||
|
dynamic key, it will return ERROR_CANTREAD. If the total length of the
|
|||
|
requested data (valListLength + ldwTotSize) is more than the system limit of one
|
|||
|
megabytes, then the function returns ERROR_TRANSFER_TOO_LONG and only the first
|
|||
|
megabyte of data is returned.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
PRVALENT Values;
|
|||
|
PUNICODE_STRING Names;
|
|||
|
LONG Error;
|
|||
|
ULONG i;
|
|||
|
ULONG DataLength;
|
|||
|
ULONG InputLength;
|
|||
|
LPDWORD pTotalSize;
|
|||
|
DWORD TotalSize;
|
|||
|
ANSI_STRING AnsiString;
|
|||
|
LPSTR NewValueBuf = NULL;
|
|||
|
DWORD DataOffset;
|
|||
|
ULONG AnsiLength;
|
|||
|
HKEY TempHandle = NULL;
|
|||
|
|
|||
|
hKey = MapPredefinedHandle(hKey, &TempHandle);
|
|||
|
if (hKey == NULL) {
|
|||
|
Error = ERROR_INVALID_HANDLE;
|
|||
|
goto ExitCleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Allocate an array of RVALENTs to describe the input value names
|
|||
|
//
|
|||
|
Values = RtlAllocateHeap(RtlProcessHeap(),0,num_vals * sizeof(RVALENT));
|
|||
|
if (Values == NULL) {
|
|||
|
Error = ERROR_OUTOFMEMORY;
|
|||
|
goto ExitCleanup;
|
|||
|
}
|
|||
|
ZeroMemory(Values, sizeof(RVALENT)*num_vals);
|
|||
|
|
|||
|
//
|
|||
|
// Allocate an array of UNICODE_STRINGs to contain the input names
|
|||
|
//
|
|||
|
Names = RtlAllocateHeap(RtlProcessHeap(),0,num_vals * sizeof(UNICODE_STRING));
|
|||
|
if (Names == NULL) {
|
|||
|
Error = ERROR_OUTOFMEMORY;
|
|||
|
goto ExitCleanup;
|
|||
|
}
|
|||
|
ZeroMemory(Names, num_vals*sizeof(UNICODE_STRING));
|
|||
|
|
|||
|
//
|
|||
|
// Convert the value names to UNICODE_STRINGs
|
|||
|
//
|
|||
|
for (i=0; i<num_vals; i++) {
|
|||
|
RtlInitAnsiString(&AnsiString, val_list[i].ve_valuename);
|
|||
|
Status = RtlAnsiStringToUnicodeString(&Names[i], &AnsiString, TRUE);
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
Error = RtlNtStatusToDosError( Status );
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Add the terminating NULL to the Length so that RPC transmits
|
|||
|
// it.
|
|||
|
//
|
|||
|
Names[i].Length += sizeof( UNICODE_NULL );
|
|||
|
Values[i].rv_valuename = &Names[i];
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Allocate a data buffer twice the size of the input buffer
|
|||
|
// so that any Unicode value data will fit before it is converted
|
|||
|
// to Ansi.
|
|||
|
//
|
|||
|
|
|||
|
if ((ldwTotsize == NULL) || (*ldwTotsize == 0)) {
|
|||
|
TotalSize = 0;
|
|||
|
} else {
|
|||
|
TotalSize = *ldwTotsize * sizeof(WCHAR);
|
|||
|
NewValueBuf = RtlAllocateHeap(RtlProcessHeap(),0,TotalSize);
|
|||
|
if (NewValueBuf == NULL) {
|
|||
|
Error = ERROR_OUTOFMEMORY;
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
}
|
|||
|
pTotalSize = &TotalSize;
|
|||
|
|
|||
|
//
|
|||
|
// Call the Base API, passing it the supplied parameters and the
|
|||
|
// counted Unicode strings.
|
|||
|
//
|
|||
|
|
|||
|
if (IsLocalHandle(hKey)) {
|
|||
|
Error = (LONG)LocalBaseRegQueryMultipleValues(hKey,
|
|||
|
Values,
|
|||
|
num_vals,
|
|||
|
NewValueBuf,
|
|||
|
pTotalSize);
|
|||
|
} else {
|
|||
|
DWORD dwVersion;
|
|||
|
Error = (LONG)BaseRegQueryMultipleValues(DereferenceRemoteHandle( hKey ),
|
|||
|
Values,
|
|||
|
num_vals,
|
|||
|
NewValueBuf,
|
|||
|
pTotalSize);
|
|||
|
if ((Error == ERROR_SUCCESS) &&
|
|||
|
(IsWin95Server(DereferenceRemoteHandle(hKey),dwVersion))) {
|
|||
|
//
|
|||
|
// Win95's RegQueryMultipleValues doesn't return Unicode
|
|||
|
// value data, so do not try and convert it back to Ansi.
|
|||
|
//
|
|||
|
for (i=0; i<num_vals; i++) {
|
|||
|
val_list[i].ve_valuelen = Values[i].rv_valuelen;
|
|||
|
val_list[i].ve_type = Values[i].rv_type;
|
|||
|
val_list[i].ve_valueptr = (DWORD_PTR)(lpValueBuf + Values[i].rv_valueptr);
|
|||
|
}
|
|||
|
CopyMemory(lpValueBuf,NewValueBuf,TotalSize);
|
|||
|
if (ldwTotsize != NULL) {
|
|||
|
*ldwTotsize = TotalSize;
|
|||
|
}
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
}
|
|||
|
if (Error == ERROR_SUCCESS) {
|
|||
|
//
|
|||
|
// Convert results back.
|
|||
|
//
|
|||
|
DataOffset = 0;
|
|||
|
for (i=0; i < num_vals; i++) {
|
|||
|
val_list[i].ve_valuelen = Values[i].rv_valuelen;
|
|||
|
val_list[i].ve_type = Values[i].rv_type;
|
|||
|
val_list[i].ve_valueptr = (DWORD_PTR)(lpValueBuf + DataOffset);
|
|||
|
if ((val_list[i].ve_type == REG_SZ) ||
|
|||
|
(val_list[i].ve_type == REG_EXPAND_SZ) ||
|
|||
|
(val_list[i].ve_type == REG_MULTI_SZ)) {
|
|||
|
|
|||
|
Status = RtlUnicodeToMultiByteN(lpValueBuf + DataOffset,
|
|||
|
Values[i].rv_valuelen/sizeof(WCHAR),
|
|||
|
&AnsiLength,
|
|||
|
(PWCH)(NewValueBuf + Values[i].rv_valueptr),
|
|||
|
Values[i].rv_valuelen);
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
Error = RtlNtStatusToDosError( Status );
|
|||
|
}
|
|||
|
val_list[i].ve_valuelen = AnsiLength;
|
|||
|
DataOffset += AnsiLength;
|
|||
|
} else {
|
|||
|
CopyMemory(lpValueBuf + DataOffset,
|
|||
|
NewValueBuf + Values[i].rv_valueptr,
|
|||
|
Values[i].rv_valuelen);
|
|||
|
DataOffset += Values[i].rv_valuelen;
|
|||
|
}
|
|||
|
//
|
|||
|
// Round DataOffset up to dword boundary.
|
|||
|
//
|
|||
|
DataOffset = (DataOffset + sizeof(DWORD) - 1) & ~(sizeof(DWORD)-1);
|
|||
|
}
|
|||
|
if (ldwTotsize != NULL) {
|
|||
|
*ldwTotsize = DataOffset;
|
|||
|
}
|
|||
|
} else if (Error == ERROR_MORE_DATA) {
|
|||
|
//
|
|||
|
// We need to thunk the Unicode required bytes back to Ansi. But
|
|||
|
// there is not really any way to do this without having the data
|
|||
|
// available. So just return the required bytes for the Unicode
|
|||
|
// data, as this will always be enough.
|
|||
|
//
|
|||
|
if (ldwTotsize != NULL) {
|
|||
|
*ldwTotsize = *pTotalSize;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Cleanup:
|
|||
|
if (NewValueBuf != NULL) {
|
|||
|
RtlFreeHeap(RtlProcessHeap(),0,NewValueBuf);
|
|||
|
}
|
|||
|
for (i=0; i<num_vals; i++) {
|
|||
|
if (Names[i].Buffer != NULL) {
|
|||
|
RtlFreeUnicodeString(&Names[i]);
|
|||
|
}
|
|||
|
}
|
|||
|
RtlFreeHeap(RtlProcessHeap(),0,Values);
|
|||
|
RtlFreeHeap(RtlProcessHeap(),0,Names);
|
|||
|
|
|||
|
ExitCleanup:
|
|||
|
CLOSE_LOCAL_HANDLE(TempHandle);
|
|||
|
return Error;
|
|||
|
}
|
|||
|
|
|||
|
WINADVAPI
|
|||
|
LONG
|
|||
|
APIENTRY
|
|||
|
RegQueryMultipleValuesW (
|
|||
|
HKEY hKey,
|
|||
|
PVALENTW val_list,
|
|||
|
DWORD num_vals,
|
|||
|
LPWSTR lpValueBuf,
|
|||
|
LPDWORD ldwTotsize
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
The RegQueryMultipleValues function retrieves a list of
|
|||
|
data type/data pairs for a list of value names associated
|
|||
|
with an open registry key.
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
hKey
|
|||
|
Identifies a currently open key or any of the pre-defined reserved handle values:
|
|||
|
HKEY_CLASSES_ROOT
|
|||
|
HEY_CURRENT_USER
|
|||
|
HKEY_LOCAL_MACHINE
|
|||
|
HKEY_USERS
|
|||
|
|
|||
|
valList
|
|||
|
Points to an array of structures describing one or more value entries. This
|
|||
|
contains the value names of the values to be queried. Refer to Appendix A for a
|
|||
|
description of VALUE_ENTRY structure.
|
|||
|
|
|||
|
num_vals
|
|||
|
Size of valList in bytes. If valListLength is not a multiple of the sizeof pvalue, the
|
|||
|
fractional extra space pointed to by valList is ignored.
|
|||
|
|
|||
|
lpValueBuf
|
|||
|
The output buffer for returning value information (value names and value data). Data
|
|||
|
is DWORD aligned with pads inserted as necessary.
|
|||
|
|
|||
|
ldwTotsize
|
|||
|
The total size of the output buffer pointed to by lpValueBuf. On output ldwTotsize
|
|||
|
contains the number of bytes used including pads. If lpValueBuf was too short, then on
|
|||
|
output ldwTotsize will be the size needed, and caller should assume that lpValueBuf was
|
|||
|
filled up to the size specified by ldwTotsize on input.
|
|||
|
|
|||
|
Return value:
|
|||
|
|
|||
|
If the function succeeds, the return value is ERROR_SUCCESS; otherwise it is one
|
|||
|
of the error value which can be returned by RegQueryValueEx. In addition, if
|
|||
|
either valList or lpValueBuf is too small then ERROR_INSUFFICIENT_BUFFER is returned
|
|||
|
If the function is unable to instantiate/access the provider of the
|
|||
|
dynamic key, it will return ERROR_CANTREAD. If the total length of the
|
|||
|
requested data (valListLength + ldwTotSize) is more than the system limit of one
|
|||
|
megabytes, then the function returns ERROR_TRANSFER_TOO_LONG and only the first
|
|||
|
megabyte of data is returned.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
PRVALENT Values;
|
|||
|
PUNICODE_STRING Names;
|
|||
|
LONG Error;
|
|||
|
ULONG i;
|
|||
|
ULONG DataLength;
|
|||
|
ULONG InputLength;
|
|||
|
LPDWORD pTotalSize;
|
|||
|
DWORD TotalSize;
|
|||
|
DWORD StringLength;
|
|||
|
HKEY TempHandle = NULL;
|
|||
|
|
|||
|
hKey = MapPredefinedHandle(hKey, &TempHandle);
|
|||
|
if (hKey == NULL) {
|
|||
|
Error = ERROR_INVALID_HANDLE;
|
|||
|
goto ExitCleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Allocate an array of RVALENTs to describe the input value names
|
|||
|
//
|
|||
|
Values = RtlAllocateHeap(RtlProcessHeap(),0,num_vals * sizeof(RVALENT));
|
|||
|
if (Values == NULL) {
|
|||
|
Error = ERROR_OUTOFMEMORY;
|
|||
|
goto ExitCleanup;
|
|||
|
}
|
|||
|
ZeroMemory(Values, sizeof(RVALENT)*num_vals);
|
|||
|
|
|||
|
//
|
|||
|
// Allocate an array of UNICODE_STRINGs to contain the input names
|
|||
|
//
|
|||
|
Names = RtlAllocateHeap(RtlProcessHeap(),0,num_vals * sizeof(UNICODE_STRING));
|
|||
|
if (Names == NULL) {
|
|||
|
RtlFreeHeap(RtlProcessHeap(), 0, Values);
|
|||
|
Error = ERROR_OUTOFMEMORY;
|
|||
|
goto ExitCleanup;
|
|||
|
}
|
|||
|
ZeroMemory(Names, num_vals*sizeof(UNICODE_STRING));
|
|||
|
|
|||
|
//
|
|||
|
// Copy and convert the value names to UNICODE_STRINGs
|
|||
|
// Note that we have to copy the value names because RPC tromps
|
|||
|
// on them.
|
|||
|
//
|
|||
|
for (i=0; i<num_vals; i++) {
|
|||
|
|
|||
|
StringLength = wcslen(val_list[i].ve_valuename)*sizeof(WCHAR);
|
|||
|
Names[i].Buffer = RtlAllocateHeap(RtlProcessHeap(), 0, StringLength + sizeof(UNICODE_NULL));
|
|||
|
if (Names[i].Buffer == NULL) {
|
|||
|
goto error_exit;
|
|||
|
}
|
|||
|
Names[i].Length = Names[i].MaximumLength = (USHORT)StringLength + sizeof(UNICODE_NULL);
|
|||
|
CopyMemory(Names[i].Buffer, val_list[i].ve_valuename, StringLength + sizeof(UNICODE_NULL));
|
|||
|
|
|||
|
Values[i].rv_valuename = &Names[i];
|
|||
|
}
|
|||
|
|
|||
|
if (ldwTotsize == NULL) {
|
|||
|
TotalSize = 0;
|
|||
|
pTotalSize = &TotalSize;
|
|||
|
} else {
|
|||
|
pTotalSize = ldwTotsize;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Call the Base API, passing it the supplied parameters and the
|
|||
|
// counted Unicode strings.
|
|||
|
//
|
|||
|
|
|||
|
if (IsLocalHandle(hKey)) {
|
|||
|
Error = (LONG)LocalBaseRegQueryMultipleValues(hKey,
|
|||
|
Values,
|
|||
|
num_vals,
|
|||
|
(LPSTR)lpValueBuf,
|
|||
|
pTotalSize);
|
|||
|
} else {
|
|||
|
DWORD dwVersion;
|
|||
|
if (IsWin95Server(DereferenceRemoteHandle(hKey),dwVersion)) {
|
|||
|
//
|
|||
|
// We cannot support RegQueryMultipleValuesW to Win95 servers
|
|||
|
// since they do not return Unicode value data.
|
|||
|
//
|
|||
|
Error = ERROR_CALL_NOT_IMPLEMENTED;
|
|||
|
} else {
|
|||
|
Error = (LONG)BaseRegQueryMultipleValues(DereferenceRemoteHandle( hKey ),
|
|||
|
Values,
|
|||
|
num_vals,
|
|||
|
(LPSTR)lpValueBuf,
|
|||
|
pTotalSize);
|
|||
|
}
|
|||
|
}
|
|||
|
if (Error == ERROR_SUCCESS) {
|
|||
|
//
|
|||
|
// Convert results back.
|
|||
|
//
|
|||
|
for (i=0; i < num_vals; i++) {
|
|||
|
val_list[i].ve_valuelen = Values[i].rv_valuelen;
|
|||
|
val_list[i].ve_valueptr = (DWORD_PTR)((LPCSTR)lpValueBuf + Values[i].rv_valueptr);
|
|||
|
val_list[i].ve_type = Values[i].rv_type;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
error_exit:
|
|||
|
for (i=0; i < num_vals; i++) {
|
|||
|
if (Names[i].Buffer != NULL) {
|
|||
|
RtlFreeHeap(RtlProcessHeap(), 0, Names[i].Buffer);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
RtlFreeHeap(RtlProcessHeap(),0,Values);
|
|||
|
RtlFreeHeap(RtlProcessHeap(),0,Names);
|
|||
|
|
|||
|
ExitCleanup:
|
|||
|
CLOSE_LOCAL_HANDLE(TempHandle);
|
|||
|
return Error;
|
|||
|
}
|