/*++ Copyright (c) 1992 Microsoft Corporation Module Name: Regeval.c Abstract: This module contains the client side wrappers for the Win32 Registry enumerate value APIs. That is: - RegEnumValueExA - RegEnumValueExW Author: David J. Gilman (davegi) 18-Mar-1992 Notes: See the notes in server\regeval.c. --*/ #include #include "regrpc.h" #include "client.h" LONG RegEnumValueA ( HKEY hKey, DWORD dwIndex, LPSTR lpValueName, LPDWORD lpcbValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData ) /*++ Routine Description: Win32 ANSI RPC wrapper for enumerating values. --*/ { PUNICODE_STRING Name; ANSI_STRING AnsiString; NTSTATUS Status; LONG Error; DWORD ValueType; DWORD ValueLength; DWORD InputLength; PWSTR UnicodeValueBuffer; ULONG UnicodeValueLength; PSTR AnsiValueBuffer; ULONG AnsiValueLength; ULONG Index; BOOLEAN Win95Server = FALSE; #if DBG if ( BreakPointOnEntry ) { DbgBreakPoint(); } #endif // // Validate dependency between lpData and lpcbData parameters. // if( ARGUMENT_PRESENT( lpReserved ) || (ARGUMENT_PRESENT( lpData ) && ( ! ARGUMENT_PRESENT( lpcbData )))) { return ERROR_INVALID_PARAMETER; } hKey = MapPredefinedHandle( hKey ); if( hKey == NULL ) { return ERROR_INVALID_HANDLE; } // // Use the static Unicode string in the TEB as a temporary for the // value's name. // Name = &NtCurrentTeb( )->StaticUnicodeString; ASSERT( Name != NULL ); Name->Length = 0; // // Call the Base API passing it a pointer to the counted Unicode // strings for the value name. Note that zero bytes are transmitted (i.e. // InputLength = 0) for the data. // if (ARGUMENT_PRESENT( lpcbData )) { ValueLength = *lpcbData; } else { ValueLength = 0; } InputLength = 0; if( IsLocalHandle( hKey )) { Error = (LONG)LocalBaseRegEnumValue ( hKey, dwIndex, Name, &ValueType, lpData, &ValueLength, &InputLength ); ASSERT( (&NtCurrentTeb( )->StaticUnicodeString)->Buffer ); } else { DWORD dwVersion; // // Check for a downlevel Win95 server, which requires // us to work around their BaseRegEnumValue bugs. // The returned ValueLength is one WCHAR too large AND // they trash two bytes beyond the end of the buffer // for REG_SZ, REG_MULTI_SZ, and REG_EXPAND_SZ // Win95Server = IsWin95Server(DereferenceRemoteHandle(hKey),dwVersion); if (Win95Server) { LPBYTE lpWin95Data; // // This is a Win95 server. // Allocate a new buffer that is two bytes larger than // the old one so they can trash the last two bytes. // lpWin95Data = RtlAllocateHeap(RtlProcessHeap(), 0, ValueLength+sizeof(WCHAR)); if (lpWin95Data == NULL) { Error = ERROR_NOT_ENOUGH_MEMORY; } else { Error = (LONG)BaseRegEnumValue (DereferenceRemoteHandle( hKey ), dwIndex, Name, &ValueType, lpWin95Data, &ValueLength, &InputLength); if (Error == ERROR_SUCCESS) { if ((ValueType == REG_SZ) || (ValueType == REG_MULTI_SZ) || (ValueType == REG_EXPAND_SZ)) { // // The returned length is one WCHAR too large // and the last two bytes of the buffer are trashed. // ValueLength -= sizeof(WCHAR); } CopyMemory(lpData, lpWin95Data, ValueLength); } RtlFreeHeap(RtlProcessHeap(),0,lpWin95Data); } } else { Error = (LONG)BaseRegEnumValue (DereferenceRemoteHandle( hKey ), dwIndex, Name, &ValueType, lpData, &ValueLength, &InputLength); } } // // If no error or callers buffer too small, and type is one of the null // terminated string types, then do the UNICODE to ANSI translation. // We handle the buffer too small case, because the callers buffer may // be big enough for the ANSI representation, but not the UNICODE one. // In this case, we need to allocate a buffer big enough, do the query // again and then the translation into the callers buffer. We only do // this if the caller actually wants the value data (lpData != NULL) // if ((Error == ERROR_SUCCESS || Error == ERROR_MORE_DATA) && ARGUMENT_PRESENT( lpcbData ) && (ValueType == REG_SZ || ValueType == REG_EXPAND_SZ || ValueType == REG_MULTI_SZ) ) { if( ARGUMENT_PRESENT( lpData ) ) { UnicodeValueBuffer = (PWSTR)lpData; UnicodeValueLength = ValueLength; AnsiValueBuffer = lpData; AnsiValueLength = ARGUMENT_PRESENT( lpcbData )? *lpcbData : 0; if ( ( Error == ERROR_MORE_DATA ) && ((ValueLength / sizeof( WCHAR )) <= *lpcbData) ) { // // Here if the callers buffer is big enough for the ANSI // representation, but not the UNICODE one. Allocate a // buffer for the UNICODE value and reissue the query. // UnicodeValueBuffer = RtlAllocateHeap( RtlProcessHeap(), 0, UnicodeValueLength ); if (UnicodeValueBuffer == NULL) { Error = ERROR_NOT_ENOUGH_MEMORY; } else { InputLength = 0; if( IsLocalHandle( hKey )) { Error = (LONG)LocalBaseRegEnumValue ( hKey, dwIndex, Name, &ValueType, (LPBYTE)UnicodeValueBuffer, &ValueLength, &InputLength ); // // Make sure that the local side didn't destroy the // Buffer in the StaticUnicodeString // ASSERT((&NtCurrentTeb()->StaticUnicodeString)->Buffer); } else { if (Win95Server) { LPBYTE lpWin95Data; // // This is a Win95 server. // Allocate a new buffer that is two bytes larger than // the old one so they can trash the last two bytes. // lpWin95Data = RtlAllocateHeap(RtlProcessHeap(), 0, ValueLength+sizeof(WCHAR)); if (lpWin95Data == NULL) { Error = ERROR_NOT_ENOUGH_MEMORY; } else { Error = (LONG)BaseRegEnumValue (DereferenceRemoteHandle( hKey ), dwIndex, Name, &ValueType, lpWin95Data, &ValueLength, &InputLength); if (Error == ERROR_SUCCESS) { if ((ValueType == REG_SZ) || (ValueType == REG_MULTI_SZ) || (ValueType == REG_EXPAND_SZ)) { // // The returned length is one WCHAR too large // and the last two bytes of the buffer are trashed. // ValueLength -= sizeof(WCHAR); } CopyMemory(UnicodeValueBuffer, lpWin95Data, ValueLength); } RtlFreeHeap(RtlProcessHeap(),0,lpWin95Data); } } else { Error = (LONG)BaseRegEnumValue (DereferenceRemoteHandle( hKey ), dwIndex, Name, &ValueType, (LPBYTE)UnicodeValueBuffer, &ValueLength, &InputLength); } } } } if( ( Error == ERROR_SUCCESS ) && ( UnicodeValueBuffer == ( LPWSTR )lpData ) && ( ( ( DWORD )lpData & 0x1 ) != 0 ) ) { // // Here if the caller's buffer was big enough for the UNICODE // representation of the data in the registry. // In this case we have to allocate another buffer, and copy the // contents of lpData to this new buffer. // This is necessary since the caller's buffer is a PBYTE // (not necessarily aligned), and RtlUnicodeToMultiByteN() // expects a PWSTR as source buffer (an aligned buffer). // If we don't allocate this new buffer, we may have alignment // problems on MIPS or Alpha. // UnicodeValueBuffer = RtlAllocateHeap( RtlProcessHeap(), 0, UnicodeValueLength ); if (UnicodeValueBuffer == NULL) { Error = ERROR_NOT_ENOUGH_MEMORY; } else { RtlMoveMemory( UnicodeValueBuffer, lpData, UnicodeValueLength ); } } if (Error == ERROR_SUCCESS) { // // We have a UNICODE value, so translate it to ANSI in the callers // buffer. In the case where the caller's buffer was big enough // for the UNICODE version, we do the conversion in place, which // works since the ANSI version is smaller than the UNICODE version. // Index = 0; Status = RtlUnicodeToMultiByteN( AnsiValueBuffer, AnsiValueLength, &Index, UnicodeValueBuffer, UnicodeValueLength ); if (!NT_SUCCESS( Status )) { Error = RtlNtStatusToDosError( Status ); } } if (UnicodeValueBuffer != (PWSTR)lpData) { // // Here if we had to allocate a buffer for the UNICODE Value, // so free it. // RtlFreeHeap( RtlProcessHeap(), 0, UnicodeValueBuffer ); } } // // Return the length of the ANSI version to the caller. // ValueLength /= sizeof( WCHAR ); // // Special hack to help out all the idiots who // believe the length of a NULL terminated string is // strlen(foo) instead of strlen(foo) + 1. // If the last character of the buffer is not a NULL // and there is enough space left in the caller's buffer, // slap a NULL in there to prevent him from going nuts // trying to do a strlen(). // if (ARGUMENT_PRESENT( lpData ) && (*lpcbData > ValueLength) && (lpData[ValueLength-1] != '\0')) { lpData[ValueLength] = '\0'; } } // // Return the value type and data length if requested and we have it. // if (Error == ERROR_SUCCESS || Error == ERROR_MORE_DATA) { if (lpcbData != NULL) { *lpcbData = ValueLength; } if (lpType != NULL) { *lpType = ValueType; } } // // If the information was not succesfully queried return the error. // if( Error != ERROR_SUCCESS ) { return Error; } // // Subtract the NULL from the Length. This was added by the server // so that RPC would transmit it. // if ( Name->Length > 0 ) { Name->Length -= sizeof( UNICODE_NULL ); } // // Convert the name to ANSI. // AnsiString.MaximumLength = ( USHORT ) *lpcbValueName; AnsiString.Buffer = lpValueName; Status = RtlUnicodeStringToAnsiString( &AnsiString, Name, FALSE ); // // If the name conversion failed, map and return the error. // if( ! NT_SUCCESS( Status )) { return RtlNtStatusToDosError( Status ); } // // Update the name length return parameter. // *lpcbValueName = AnsiString.Length; return ERROR_SUCCESS; } LONG RegEnumValueW ( HKEY hKey, DWORD dwIndex, LPWSTR lpValueName, LPDWORD lpcbValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData ) /*++ Routine Description: Win32 Unicode RPC wrapper for enumerating values. --*/ { UNICODE_STRING Name; LONG Error; DWORD InputLength; DWORD ValueLength; DWORD ValueType; #if DBG if ( BreakPointOnEntry ) { DbgBreakPoint(); } #endif // // Validate dependency between lpData and lpcbData parameters. // if( ARGUMENT_PRESENT( lpReserved ) || (ARGUMENT_PRESENT( lpData ) && ( ! ARGUMENT_PRESENT( lpcbData )))) { return ERROR_INVALID_PARAMETER; } hKey = MapPredefinedHandle( hKey ); if( hKey == NULL ) { return ERROR_INVALID_HANDLE; } Name.Length = 0; Name.MaximumLength = ( USHORT )( *lpcbValueName << 1 ); Name.Buffer = lpValueName; // // Call the Base API passing it a pointer to the counted Unicode // string for the name and return the results. Note that zero bytes // are transmitted (i.e.InputLength = 0) for the data. // InputLength = 0; ValueLength = ( ARGUMENT_PRESENT( lpcbData ) )? *lpcbData : 0; if( IsLocalHandle( hKey )) { Error = (LONG)LocalBaseRegEnumValue ( hKey, dwIndex, &Name, &ValueType, lpData, &ValueLength, &InputLength ); } else { DWORD dwVersion; if (IsWin95Server(DereferenceRemoteHandle(hKey),dwVersion)) { LPBYTE lpWin95Data; // // This is a Win95 server. // Allocate a new buffer that is two bytes larger than // the old one so they can trash the last two bytes. // lpWin95Data = RtlAllocateHeap(RtlProcessHeap(), 0, ValueLength+sizeof(WCHAR)); if (lpWin95Data == NULL) { Error = ERROR_NOT_ENOUGH_MEMORY; } else { Error = (LONG)BaseRegEnumValue (DereferenceRemoteHandle( hKey ), dwIndex, &Name, &ValueType, lpWin95Data, &ValueLength, &InputLength); if (Error == ERROR_SUCCESS) { if ((ValueType == REG_SZ) || (ValueType == REG_MULTI_SZ) || (ValueType == REG_EXPAND_SZ)) { // // The returned length is one WCHAR too large // and the last two bytes of the buffer are trashed. // ValueLength -= sizeof(WCHAR); } CopyMemory(lpData, lpWin95Data, ValueLength); } RtlFreeHeap(RtlProcessHeap(),0,lpWin95Data); } } else { Error = (LONG)BaseRegEnumValue (DereferenceRemoteHandle( hKey ), dwIndex, &Name, &ValueType, lpData, &ValueLength, &InputLength); } } // // Special hack to help out all the idiots who // believe the length of a NULL terminated string is // strlen(foo) instead of strlen(foo) + 1. // If the last character of the buffer is not a NULL // and there is enough space left in the caller's buffer, // slap a NULL in there to prevent him from going nuts // trying to do a strlen(). // if ( (Error == ERROR_SUCCESS) && ARGUMENT_PRESENT( lpData ) && ( (ValueType == REG_SZ) || (ValueType == REG_EXPAND_SZ) || (ValueType == REG_MULTI_SZ)) && ( ValueLength > sizeof(WCHAR))) { UNALIGNED WCHAR *String = (UNALIGNED WCHAR *)lpData; DWORD Length = ValueLength/sizeof(WCHAR); if ((String[Length-1] != UNICODE_NULL) && (ValueLength+sizeof(WCHAR) <= *lpcbData)) { String[Length] = UNICODE_NULL; } } // // Don't count the NUL. // if( Name.Length != 0 ) { *lpcbValueName = ( Name.Length >> 1 ) - 1; } if( ARGUMENT_PRESENT( lpcbData ) ) { *lpcbData = ValueLength; } if ( ARGUMENT_PRESENT( lpType )) { *lpType = ValueType; } return Error; }