mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2624 lines
65 KiB
2624 lines
65 KiB
/*++
|
|
|
|
Copyright (c) 2000-2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
registry.c
|
|
|
|
Abstract:
|
|
|
|
Domain Name System (DNS) API
|
|
|
|
Registry management routines.
|
|
|
|
Author:
|
|
|
|
Jim Gilroy (jamesg) March, 2000
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#include "local.h"
|
|
#include "registry.h"
|
|
|
|
|
|
//
|
|
// Globals
|
|
//
|
|
// DWORD globals blob
|
|
//
|
|
// g_IsRegReady protects needs init and protects (requires)
|
|
// global init.
|
|
//
|
|
// See registry.h for discussion of how these globals are
|
|
// exposed both internal to the DLL and external.
|
|
//
|
|
|
|
DNS_GLOBALS_BLOB DnsGlobals;
|
|
|
|
BOOL g_IsRegReady = FALSE;
|
|
|
|
PWSTR g_pwsRemoteResolver = NULL;
|
|
|
|
|
|
//
|
|
// Property table
|
|
//
|
|
|
|
//
|
|
// WARNING: table must be in sync with DNS_REGID definitions
|
|
//
|
|
// For simplicity I did not provide a separate index field and
|
|
// a lookup function (or alternatively a function that returned
|
|
// all the properties or a property pointer).
|
|
//
|
|
// The DNS_REGID values ARE the INDEXES!
|
|
// Hence the table MUST be in sync or the whole deal blows up
|
|
// If you make a change to either -- you must change the other!
|
|
//
|
|
|
|
REG_PROPERTY RegPropertyTable[] =
|
|
{
|
|
// Basic
|
|
|
|
HOST_NAME ,
|
|
0 , // Default FALSE
|
|
0 , // No policy
|
|
1 , // Client
|
|
1 , // TcpIp
|
|
0 , // No Cache
|
|
|
|
DOMAIN_NAME ,
|
|
0 , // Default FALSE
|
|
1 , // Policy
|
|
0 , // No Client
|
|
1 , // TcpIp
|
|
0 , // No Cache
|
|
|
|
DHCP_DOMAIN_NAME ,
|
|
0 , // Default FALSE
|
|
1 , // Policy
|
|
0 , // No Client
|
|
1 , // TcpIp
|
|
0 , // No Cache
|
|
|
|
ADAPTER_DOMAIN_NAME ,
|
|
0 , // Default FALSE
|
|
1 , // Policy
|
|
1 , // Client
|
|
0 , // No TcpIp
|
|
0 , // No Cache
|
|
|
|
PRIMARY_DOMAIN_NAME ,
|
|
0 , // Default FALSE
|
|
1 , // Policy
|
|
1 , // Client
|
|
0 , // No TcpIp
|
|
0 , // No Cache
|
|
|
|
PRIMARY_SUFFIX ,
|
|
0 , // Default FALSE
|
|
1 , // Policy
|
|
1 , // Client
|
|
1 , // TcpIp
|
|
0 , // No Cache
|
|
|
|
ALTERNATE_NAMES ,
|
|
0 , // Default NULL
|
|
0 , // No Policy
|
|
0 , // No Client
|
|
0 , // No TcpIp
|
|
1 , // Cache
|
|
|
|
DNS_SERVERS ,
|
|
0 , // Default FALSE
|
|
1 , // Policy
|
|
0 , // Client
|
|
0 , // TcpIp
|
|
0 , // No Cache
|
|
|
|
SEARCH_LIST_KEY ,
|
|
0 , // Default FALSE
|
|
1 , // Policy
|
|
1 , // Client
|
|
1 , // TcpIp
|
|
0 , // No Cache
|
|
|
|
UPDATE_ZONE_EXCLUSIONS ,
|
|
0 , // Default
|
|
1 , // Policy
|
|
1 , // Client
|
|
0 , // No TcpIp
|
|
0 , // No Cache
|
|
|
|
// Query
|
|
|
|
QUERY_ADAPTER_NAME ,
|
|
1 , // Default TRUE
|
|
1 , // Policy
|
|
1 , // Client
|
|
0 , // No TcpIp
|
|
1 , // Cache
|
|
USE_DOMAIN_NAME_DEVOLUTION ,
|
|
1 , // Default TRUE
|
|
1 , // Policy
|
|
1 , // Client
|
|
1 , // TcpIp
|
|
1 , // Cache
|
|
PRIORITIZE_RECORD_DATA ,
|
|
1 , // Default TRUE
|
|
1 , // Policy
|
|
1 , // Client
|
|
1 , // TcpIp
|
|
1 , // Cache
|
|
ALLOW_UNQUALIFIED_QUERY ,
|
|
0 , // Default FALSE
|
|
1 , // Policy
|
|
1 , // Client
|
|
1 , // TcpIp
|
|
1 , // Cache
|
|
APPEND_TO_MULTI_LABEL_NAME ,
|
|
1 , // Default TRUE
|
|
1 , // Policy
|
|
1 , // Client
|
|
0 , // No TcpIp
|
|
1 , // Cache
|
|
SCREEN_BAD_TLDS ,
|
|
DNS_TLD_SCREEN_DEFAULT , // Default
|
|
1 , // Policy
|
|
1 , // Client
|
|
0 , // No TcpIp
|
|
1 , // Cache
|
|
SCREEN_UNREACHABLE_SERVERS ,
|
|
1 , // Default TRUE
|
|
1 , // Policy
|
|
1 , // Client
|
|
0 , // No TcpIp
|
|
1 , // Cache
|
|
FILTER_CLUSTER_IP ,
|
|
0 , // Default FALSE
|
|
1 , // Policy
|
|
1 , // Client
|
|
0 , // No TcpIp
|
|
1 , // Cache
|
|
WAIT_FOR_NAME_ERROR_ON_ALL ,
|
|
1 , // Default TRUE
|
|
1 , // Policy
|
|
1 , // Client
|
|
0 , // No TcpIp
|
|
1 , // Cache
|
|
USE_EDNS ,
|
|
//REG_EDNS_TRY , // Default TRY EDNS
|
|
0 , // Default FALSE
|
|
1 , // Policy
|
|
1 , // Client
|
|
0 , // No TcpIp
|
|
1 , // Cache
|
|
|
|
// Update
|
|
|
|
REGISTRATION_ENABLED ,
|
|
1 , // Default TRUE
|
|
1 , // Policy
|
|
1 , // Client
|
|
0 , // No TcpIp
|
|
1 , // No Cache
|
|
REGISTER_PRIMARY_NAME ,
|
|
1 , // Default TRUE
|
|
1 , // Policy
|
|
1 , // Client
|
|
0 , // No TcpIp
|
|
1 , // No Cache
|
|
REGISTER_ADAPTER_NAME ,
|
|
1 , // Default TRUE
|
|
1 , // Policy
|
|
1 , // Client
|
|
0 , // No TcpIp
|
|
1 , // No Cache
|
|
REGISTER_REVERSE_LOOKUP ,
|
|
1 , // Default TRUE
|
|
1 , // Policy
|
|
1 , // Client
|
|
0 , // No TcpIp
|
|
1 , // No Cache
|
|
REGISTER_WAN_ADAPTERS ,
|
|
1 , // Default TRUE
|
|
1 , // Policy
|
|
1 , // Client
|
|
0 , // No TcpIp
|
|
1 , // No Cache
|
|
REGISTRATION_OVERWRITES_IN_CONFLICT ,
|
|
1 , // Default TRUE
|
|
1 , // Policy
|
|
1 , // Client
|
|
0 , // No TcpIp
|
|
1 , // No Cache
|
|
REGISTRATION_TTL ,
|
|
REGDEF_REGISTRATION_TTL ,
|
|
1 , // Policy
|
|
1 , // Client
|
|
0 , // No TcpIp
|
|
1 , // No Cache
|
|
REGISTRATION_REFRESH_INTERVAL ,
|
|
REGDEF_REGISTRATION_REFRESH_INTERVAL ,
|
|
1 , // Policy
|
|
1 , // Client
|
|
0 , // No TcpIp
|
|
1 , // No Cache
|
|
REGISTRATION_MAX_ADDRESS_COUNT ,
|
|
1 , // Default register 1 address
|
|
1 , // Policy
|
|
1 , // Client
|
|
0 , // No TcpIp
|
|
1 , // No Cache
|
|
UPDATE_SECURITY_LEVEL ,
|
|
DNS_UPDATE_SECURITY_USE_DEFAULT ,
|
|
1 , // Policy
|
|
1 , // Client
|
|
1 , // TcpIp
|
|
1 , // No Cache
|
|
UPDATE_ZONE_EXCLUDE_FILE ,
|
|
1 , // Default ON
|
|
1 , // Policy
|
|
1 , // Client
|
|
0 , // No TcpIp
|
|
1 , // No Cache
|
|
UPDATE_TOP_LEVEL_DOMAINS ,
|
|
0 , // Default OFF
|
|
1 , // Policy
|
|
1 , // Client
|
|
0 , // No TcpIp
|
|
1 , // No Cache
|
|
|
|
//
|
|
// Backcompat
|
|
//
|
|
// DCR: once policy fixed, policy should be OFF on all backcompat
|
|
//
|
|
|
|
DISABLE_ADAPTER_DOMAIN_NAME ,
|
|
0 , // Default FALSE
|
|
0 , // Policy
|
|
0 , // Client
|
|
1 , // TcpIp
|
|
0 , // No Cache
|
|
DISABLE_DYNAMIC_UPDATE ,
|
|
0 , // Default FALSE
|
|
0 , // Policy
|
|
0 , // Client
|
|
1 , // TcpIp
|
|
0 , // No Cache
|
|
ENABLE_ADAPTER_DOMAIN_NAME_REGISTRATION ,
|
|
0 , // Default TRUE
|
|
0 , // Policy
|
|
0 , // Client
|
|
1 , // TcpIp
|
|
0 , // No Cache
|
|
DISABLE_REVERSE_ADDRESS_REGISTRATIONS ,
|
|
0 , // Default FALSE
|
|
0 , // Policy
|
|
0 , // Client
|
|
1 , // TcpIp
|
|
0 , // No Cache
|
|
DISABLE_WAN_DYNAMIC_UPDATE ,
|
|
0 , // Default FALSE
|
|
0 , // Policy OFF
|
|
0 , // Client
|
|
1 , // TcpIp
|
|
0 , // No Cache
|
|
ENABLE_WAN_UPDATE_EVENT_LOG ,
|
|
0 , // Default FALSE
|
|
0 , // Policy OFF
|
|
0 , // Client
|
|
1 , // TcpIp
|
|
0 , // No Cache
|
|
DISABLE_REPLACE_ADDRESSES_IN_CONFLICTS ,
|
|
0 , // Default FALSE
|
|
0 , // Policy
|
|
0 , // Client
|
|
1 , // TcpIp
|
|
0 , // No Cache
|
|
DEFAULT_REGISTRATION_TTL ,
|
|
REGDEF_REGISTRATION_TTL ,
|
|
0 , // Policy OFF
|
|
0 , // Client
|
|
1 , // TcpIp
|
|
0 , // No Cache
|
|
DEFAULT_REGISTRATION_REFRESH_INTERVAL ,
|
|
REGDEF_REGISTRATION_REFRESH_INTERVAL ,
|
|
0 , // Policy
|
|
0 , // Client
|
|
1 , // TcpIp
|
|
0 , // No Cache
|
|
MAX_NUMBER_OF_ADDRESSES_TO_REGISTER ,
|
|
1 , // Default register 1 address
|
|
0 , // Policy
|
|
0 , // Client
|
|
1 , // TcpIp
|
|
0 , // No Cache
|
|
|
|
|
|
// Micellaneous
|
|
|
|
NT_SETUP_MODE ,
|
|
0 , // Default FALSE
|
|
0 , // No policy
|
|
0 , // Client
|
|
0 , // No TcpIp
|
|
0 , // No Cache
|
|
|
|
DNS_TEST_MODE ,
|
|
0 , // Default FALSE
|
|
0 , // No policy
|
|
0 , // No Client
|
|
0 , // No TcpIp
|
|
1 , // In Cache
|
|
|
|
REMOTE_DNS_RESOLVER ,
|
|
0 , // Default FALSE
|
|
1 , // Policy
|
|
1 , // Client
|
|
0 , // No TcpIp
|
|
1 , // In Cache
|
|
|
|
// Resolver
|
|
|
|
MAX_CACHE_SIZE ,
|
|
1000 , // Default 1000 record sets
|
|
1 , // Policy
|
|
1 , // Client
|
|
0 , // No TcpIp
|
|
1 , // Cache
|
|
|
|
MAX_CACHE_TTL ,
|
|
86400 , // Default 1 day
|
|
1 , // Policy
|
|
1 , // Client
|
|
0 , // No TcpIp
|
|
1 , // Cache
|
|
|
|
MAX_NEGATIVE_CACHE_TTL ,
|
|
900 , // Default 15 minutes
|
|
1 , // Policy
|
|
1 , // Client
|
|
0 , // No TcpIp
|
|
1 , // Cache
|
|
|
|
ADAPTER_TIMEOUT_LIMIT ,
|
|
600 , // Default 10 minutes
|
|
1 , // Policy
|
|
1 , // Client
|
|
0 , // No TcpIp
|
|
1 , // Cache
|
|
|
|
SERVER_PRIORITY_TIME_LIMIT ,
|
|
//3600 , // Default 1 hour
|
|
// DCR: change once registry change notify
|
|
// while no change-notify, this is essentially registry
|
|
// check time so use 15 minutes
|
|
900 , // Default 15 minutes
|
|
1 , // Policy
|
|
1 , // Client
|
|
0 , // No TcpIp
|
|
1 , // Cache
|
|
|
|
MAX_CACHED_SOCKETS ,
|
|
10 , // Default 10
|
|
1 , // Policy
|
|
1 , // Client
|
|
0 , // No TcpIp
|
|
1 , // Cache
|
|
|
|
// Multicast
|
|
|
|
USE_MULTICAST ,
|
|
0 , // Default OFF
|
|
//1 , // Default ON
|
|
1 , // Policy
|
|
1 , // Client
|
|
0 , // No TcpIp
|
|
1 , // Cache
|
|
|
|
MULTICAST_ON_NAME_ERROR ,
|
|
0 , // Default OFF
|
|
//1 , // Default ON
|
|
1 , // Policy
|
|
1 , // Client
|
|
0 , // No TcpIp
|
|
1 , // Cache
|
|
|
|
USE_DOT_LOCAL_DOMAIN ,
|
|
0 , // Default OFF
|
|
//1 , // Default ON
|
|
1 , // Policy
|
|
1 , // Client
|
|
0 , // No TcpIp
|
|
1 , // Cache
|
|
|
|
LISTEN_ON_MULTICAST ,
|
|
0 , // Default OFF
|
|
//1 , // Default ON
|
|
1 , // Policy
|
|
1 , // Client
|
|
0 , // No TcpIp
|
|
1 , // Cache
|
|
|
|
// Termination
|
|
|
|
NULL, 0, 0, 0, 0, 0
|
|
};
|
|
|
|
|
|
//
|
|
// Backward compatibility list
|
|
//
|
|
// Maps new reg id to old reg id.
|
|
// Flag fReverse indicates need to reverse (!) the value.
|
|
//
|
|
|
|
#define NO_BACK_VALUE ((DWORD)(0xffffffff))
|
|
|
|
typedef struct _Backpat
|
|
{
|
|
DWORD NewId;
|
|
DWORD OldId;
|
|
BOOL fReverse;
|
|
}
|
|
BACKPAT;
|
|
|
|
BACKPAT BackpatArray[] =
|
|
{
|
|
RegIdQueryAdapterName,
|
|
RegIdDisableAdapterDomainName,
|
|
TRUE,
|
|
|
|
RegIdRegistrationEnabled,
|
|
RegIdDisableDynamicUpdate,
|
|
TRUE,
|
|
|
|
RegIdRegisterAdapterName,
|
|
RegIdEnableAdapterDomainNameRegistration,
|
|
FALSE,
|
|
|
|
RegIdRegisterReverseLookup,
|
|
RegIdDisableReverseAddressRegistrations,
|
|
TRUE,
|
|
|
|
RegIdRegisterWanAdapters,
|
|
RegIdDisableWanDynamicUpdate,
|
|
TRUE,
|
|
|
|
RegIdRegistrationOverwritesInConflict,
|
|
RegIdDisableReplaceAddressesInConflicts,
|
|
TRUE,
|
|
|
|
RegIdRegistrationTtl,
|
|
RegIdDefaultRegistrationTTL,
|
|
FALSE,
|
|
|
|
RegIdRegistrationRefreshInterval,
|
|
RegIdDefaultRegistrationRefreshInterval,
|
|
FALSE,
|
|
|
|
RegIdRegistrationMaxAddressCount,
|
|
RegIdMaxNumberOfAddressesToRegister,
|
|
FALSE,
|
|
|
|
NO_BACK_VALUE, 0, 0
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
VOID
|
|
Reg_Init(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Init DNS registry stuff.
|
|
|
|
Essentially this means get system version info.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Globals:
|
|
|
|
Sets the system info globals above:
|
|
g_IsWin9X
|
|
g_IsWin2000
|
|
g_IsNT4
|
|
g_IsWorkstation
|
|
g_IsServer
|
|
g_IsDomainController
|
|
g_IsRegReady
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
OSVERSIONINFOEX osvi;
|
|
BOOL bversionInfoEx;
|
|
|
|
//
|
|
// do this just once
|
|
//
|
|
|
|
if ( g_IsRegReady )
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// code validity check
|
|
// property table should have entry for every reg value plus an
|
|
// extra one for the terminator
|
|
//
|
|
|
|
#if DBG
|
|
DNS_ASSERT( (RegIdValueCount+1)*sizeof(REG_PROPERTY) ==
|
|
sizeof(RegPropertyTable) );
|
|
#endif
|
|
|
|
//
|
|
// clear globals blob
|
|
//
|
|
// DCR: warning clearing DnsGlobals but don't read them all
|
|
// this is protected by read-once deal but still kind of
|
|
//
|
|
|
|
RtlZeroMemory(
|
|
& DnsGlobals,
|
|
sizeof(DnsGlobals) );
|
|
|
|
//
|
|
// get version info
|
|
// - Win2000 supports OSVERSIONINFOEX
|
|
// try first with it, then fail back to standard
|
|
//
|
|
|
|
ZeroMemory( &osvi, sizeof(OSVERSIONINFOEX) );
|
|
|
|
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
|
|
|
|
bversionInfoEx = GetVersionEx( (OSVERSIONINFO*) &osvi );
|
|
if ( !bversionInfoEx)
|
|
{
|
|
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
|
|
|
if ( ! GetVersionEx( (OSVERSIONINFO *) &osvi ) )
|
|
{
|
|
DNS_ASSERT( FALSE );
|
|
return;
|
|
}
|
|
}
|
|
|
|
#if DNSBUILDOLD
|
|
//
|
|
// suck out system info
|
|
//
|
|
// if Win9x -- that's it done
|
|
//
|
|
|
|
if ( osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS )
|
|
{
|
|
g_IsWin9X = TRUE;
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// assume anything else is NT, this gets around WIN32 tag
|
|
// leaving in Win64 versions
|
|
//
|
|
// DCR: WinCE issue here?
|
|
// DCR: Win64 issue here?
|
|
//
|
|
|
|
DNS_ASSERT( osvi.dwPlatformId == VER_PLATFORM_WIN32_NT );
|
|
|
|
if ( osvi.dwMajorVersion >= 5 )
|
|
{
|
|
g_IsWin2000 = TRUE;
|
|
}
|
|
else
|
|
{
|
|
g_IsNT4 = TRUE;
|
|
}
|
|
#else
|
|
|
|
// set Win2K flag
|
|
// - easier to keep this flag then to #ifdef the remaining uses
|
|
|
|
g_IsWin2000 = TRUE;
|
|
#endif
|
|
|
|
//
|
|
// get system type -- workstation, server, DC
|
|
//
|
|
|
|
if ( bversionInfoEx )
|
|
{
|
|
if ( osvi.wProductType == VER_NT_WORKSTATION )
|
|
{
|
|
g_IsWorkstation = TRUE;
|
|
}
|
|
else if ( osvi.wProductType == VER_NT_SERVER )
|
|
{
|
|
g_IsServer = TRUE;
|
|
}
|
|
else if ( osvi.wProductType == VER_NT_DOMAIN_CONTROLLER )
|
|
{
|
|
g_IsServer = TRUE;
|
|
g_IsDomainController = TRUE;
|
|
}
|
|
ELSE_ASSERT( FALSE );
|
|
}
|
|
|
|
#if DNSNT4
|
|
//
|
|
// no osviEX (NT4), must suck product from registry
|
|
//
|
|
|
|
else
|
|
{
|
|
HKEY hkey = NULL;
|
|
CHAR productType[MAX_PATH] = "0";
|
|
DWORD bufLength = MAX_PATH;
|
|
|
|
RegOpenKeyExW(
|
|
HKEY_LOCAL_MACHINE,
|
|
L"SYSTEM\\CurrentControlSet\\Control\\ProductOptions",
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
& hkey );
|
|
|
|
if ( !hkey )
|
|
{
|
|
DNS_ASSERT( FALSE );
|
|
goto Done;
|
|
}
|
|
|
|
RegQueryValueExA(
|
|
hkey,
|
|
"ProductType",
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE) productType,
|
|
& bufLength );
|
|
|
|
RegCloseKey( hkey );
|
|
|
|
if ( lstrcmpi( "WINNT", productType) == 0 )
|
|
{
|
|
g_IsWorkstation = TRUE;
|
|
}
|
|
else if ( lstrcmpi( "SERVERNT", productType) == 0 )
|
|
{
|
|
g_IsServer = TRUE;
|
|
}
|
|
else if ( lstrcmpi( "LANMANNT", productType) == 0 )
|
|
{
|
|
g_IsDomainController = TRUE;
|
|
}
|
|
}
|
|
|
|
Done:
|
|
#endif
|
|
|
|
g_IsRegReady = TRUE;
|
|
|
|
#if DNSBUILDOLD
|
|
DNSDBG( REGISTRY, (
|
|
"DNS API registry init:\n"
|
|
"\tWin9X = %d\n"
|
|
"\tNT4 = %d\n"
|
|
"\tWin2000 = %d\n"
|
|
"\tWorksta = %d\n"
|
|
"\tServer = %d\n"
|
|
"\tDC = %d\n",
|
|
g_IsWin9X,
|
|
g_IsNT4,
|
|
g_IsWin2000,
|
|
g_IsWorkstation,
|
|
g_IsServer,
|
|
g_IsDomainController ));
|
|
#endif
|
|
DNSDBG( REGISTRY, (
|
|
"DNS registry init:\n"
|
|
"\tWin2000 = %d\n"
|
|
"\tWorksta = %d\n"
|
|
"\tServer = %d\n"
|
|
"\tDC = %d\n",
|
|
g_IsWin2000,
|
|
g_IsWorkstation,
|
|
g_IsServer,
|
|
g_IsDomainController ));
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// Registry table routines
|
|
//
|
|
|
|
PSTR
|
|
regValueNameForId(
|
|
IN DWORD RegId
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Return registry value name for reg ID
|
|
|
|
Arguments:
|
|
|
|
RegId -- ID for value
|
|
|
|
Return Value:
|
|
|
|
Ptr to reg value name.
|
|
NULL on error.
|
|
|
|
--*/
|
|
{
|
|
DNSDBG( REGISTRY, (
|
|
"regValueNameForId( id=%d )\n",
|
|
RegId ));
|
|
|
|
//
|
|
// validate ID
|
|
//
|
|
|
|
if ( RegId > RegIdValueMax )
|
|
{
|
|
return( NULL );
|
|
}
|
|
|
|
//
|
|
// index into table
|
|
//
|
|
|
|
return( (PSTR)REGPROP_NAME(RegId) );
|
|
}
|
|
|
|
|
|
DWORD
|
|
checkBackCompat(
|
|
IN DWORD NewId,
|
|
OUT PBOOL pfReverse
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check if have backward compatible regkey.
|
|
|
|
Arguments:
|
|
|
|
NewId -- id to check for old backward compatible id
|
|
|
|
pfReverse -- addr to receive reverse flag
|
|
|
|
Return Value:
|
|
|
|
Reg Id of old backward compatible value.
|
|
NO_BACK_VALUE if no old value.
|
|
|
|
--*/
|
|
{
|
|
DWORD i = 0;
|
|
DWORD id;
|
|
|
|
//
|
|
// loop through backcompat list looking for value
|
|
//
|
|
|
|
while ( 1 )
|
|
{
|
|
id = BackpatArray[i].NewId;
|
|
|
|
if ( id == NO_BACK_VALUE )
|
|
{
|
|
return( NO_BACK_VALUE );
|
|
}
|
|
if ( id != NewId )
|
|
{
|
|
i++;
|
|
continue;
|
|
}
|
|
|
|
// found value in backcompat array
|
|
|
|
break;
|
|
}
|
|
|
|
*pfReverse = BackpatArray[i].fReverse;
|
|
|
|
return BackpatArray[i].OldId;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Registry session handle
|
|
//
|
|
|
|
DNS_STATUS
|
|
WINAPI
|
|
Reg_OpenSession(
|
|
OUT PREG_SESSION pRegSession,
|
|
IN DWORD Level,
|
|
IN DWORD RegId
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Open registry for DNS client info.
|
|
|
|
Arguments:
|
|
|
|
pRegSession -- ptr to unitialize reg session blob
|
|
|
|
Level -- level of access to get
|
|
|
|
RegId -- ID of value we're interested in
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
DWORD status = NO_ERROR;
|
|
HKEY hkey = NULL;
|
|
DWORD disposition;
|
|
|
|
// auto init
|
|
|
|
Reg_Init();
|
|
|
|
//
|
|
// clear handles
|
|
//
|
|
|
|
RtlZeroMemory(
|
|
pRegSession,
|
|
sizeof( REG_SESSION ) );
|
|
|
|
|
|
//
|
|
// DCR: handle multiple access levels
|
|
//
|
|
// For know assume that if getting access to "standard"
|
|
// section we'll need both policy and regular.
|
|
//
|
|
|
|
//
|
|
// Win95
|
|
// - TCP/IP location slightly different than NT
|
|
//
|
|
// Devnote: we could collapse these using all _A calls
|
|
// (all key names are ANSI) but we gain some perf
|
|
// by calling NT wide; and since we currently have
|
|
// to do that, it seems worth it
|
|
//
|
|
|
|
#if DNSWIN9X
|
|
if ( g_IsWin9X )
|
|
{
|
|
status = RegCreateKeyExA(
|
|
HKEY_LOCAL_MACHINE,
|
|
WIN95_TCPIP_KEY_A,
|
|
0,
|
|
"Class",
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_READ,
|
|
NULL,
|
|
&hkey,
|
|
&disposition );
|
|
}
|
|
else
|
|
#endif
|
|
|
|
//
|
|
// NT
|
|
// - Win2000
|
|
// - open TCPIP
|
|
// note, always open TCPIP as may not be any policy
|
|
// for some or all of our desired reg values, even
|
|
// if policy key is available
|
|
// - open policy (only if standard successful)
|
|
//
|
|
// - NT4
|
|
// - open TCPIP (no policy)
|
|
//
|
|
|
|
{
|
|
status = RegCreateKeyExW(
|
|
HKEY_LOCAL_MACHINE,
|
|
TCPIP_PARAMETERS_KEY,
|
|
0,
|
|
L"Class",
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_READ,
|
|
NULL,
|
|
&hkey,
|
|
&disposition );
|
|
|
|
if ( !g_IsWin2000 || status != ERROR_SUCCESS )
|
|
{
|
|
goto Done;
|
|
}
|
|
|
|
#ifdef DNSCLIENTKEY
|
|
// open DNS client key
|
|
//
|
|
// DCR: currently no DNSClient regkey
|
|
|
|
RegOpenKeyExW(
|
|
HKEY_LOCAL_MACHINE,
|
|
DNS_CLIENT_KEY,
|
|
0,
|
|
KEY_READ,
|
|
& pRegSession->hClient );
|
|
#endif
|
|
|
|
// open DNS cache key
|
|
|
|
RegOpenKeyExW(
|
|
HKEY_LOCAL_MACHINE,
|
|
DNS_CACHE_KEY,
|
|
0,
|
|
KEY_READ,
|
|
& pRegSession->hCache );
|
|
|
|
// open DNS policy key
|
|
|
|
RegOpenKeyExW(
|
|
HKEY_LOCAL_MACHINE,
|
|
DNS_POLICY_KEY,
|
|
0,
|
|
KEY_READ,
|
|
& pRegSession->hPolicy );
|
|
}
|
|
|
|
Done:
|
|
|
|
//
|
|
// all OS versions return TCP/IP key
|
|
//
|
|
|
|
if ( status == ERROR_SUCCESS )
|
|
{
|
|
pRegSession->hTcpip = hkey;
|
|
}
|
|
else
|
|
{
|
|
Reg_CloseSession( pRegSession );
|
|
}
|
|
|
|
DNSDBG( TRACE, (
|
|
"Leave: Reg_OpenSession( s=%d, t=%p, p=%p, c=%p )\n",
|
|
status,
|
|
pRegSession->hTcpip,
|
|
pRegSession->hPolicy,
|
|
pRegSession->hClient ));
|
|
|
|
return( status );
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
WINAPI
|
|
Reg_CloseSession(
|
|
IN OUT PREG_SESSION pRegSession
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Close registry session handle.
|
|
|
|
This means close underlying regkeys.
|
|
|
|
Arguments:
|
|
|
|
pSessionHandle -- ptr to registry session handle
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// allow sloppy cleanup
|
|
//
|
|
|
|
if ( !pRegSession )
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// close any non-NULL handles
|
|
//
|
|
|
|
if ( pRegSession->hPolicy )
|
|
{
|
|
RegCloseKey( pRegSession->hPolicy );
|
|
}
|
|
if ( pRegSession->hTcpip )
|
|
{
|
|
RegCloseKey( pRegSession->hTcpip );
|
|
}
|
|
#ifdef DNSCLIENTKEY
|
|
if ( pRegSession->hClient )
|
|
{
|
|
RegCloseKey( pRegSession->hClient );
|
|
}
|
|
#endif
|
|
if ( pRegSession->hCache )
|
|
{
|
|
RegCloseKey( pRegSession->hCache );
|
|
}
|
|
|
|
//
|
|
// clear handles (just for safety)
|
|
//
|
|
|
|
RtlZeroMemory(
|
|
pRegSession,
|
|
sizeof(REG_SESSION) );
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Registry reading routines
|
|
//
|
|
|
|
DNS_STATUS
|
|
Reg_GetDword(
|
|
IN PREG_SESSION pRegSession, OPTIONAL
|
|
IN HKEY hRegKey, OPTIONAL
|
|
IN PWSTR pwsKeyName, OPTIONAL
|
|
IN DWORD RegId,
|
|
OUT PDWORD pResult
|
|
//OUT PDWORD pfRead
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Read REG_DWORD value from registry.
|
|
|
|
//
|
|
// DCR: do we need to expose location result?
|
|
// (explicit, policy, defaulted)
|
|
//
|
|
|
|
Arguments:
|
|
|
|
pRegSession -- ptr to reg session already opened (OPTIONAL)
|
|
|
|
hRegKey -- explicit regkey
|
|
|
|
pwsKeyName -- key name OR dummy key
|
|
|
|
RegId -- ID for value
|
|
|
|
pResult -- addr of DWORD to recv result
|
|
|
|
pfRead -- addr to recv result of how value read
|
|
0 -- defaulted
|
|
1 -- read
|
|
Currently just use ERROR_SUCCESS to mean read rather
|
|
than defaulted.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS on success.
|
|
ErrorCode on failure -- value is then defaulted.
|
|
|
|
--*/
|
|
{
|
|
DNS_STATUS status;
|
|
REG_SESSION session;
|
|
PREG_SESSION psession = pRegSession;
|
|
PBYTE pname;
|
|
DWORD regType = REG_DWORD;
|
|
DWORD dataLength = sizeof(DWORD);
|
|
HKEY hkey;
|
|
HKEY hlocalKey = NULL;
|
|
|
|
|
|
DNSDBG( REGISTRY, (
|
|
"Reg_GetDword( s=%p, k=%p, a=%p, id=%d )\n",
|
|
pRegSession,
|
|
hRegKey,
|
|
pwsKeyName,
|
|
RegId ));
|
|
|
|
// auto init
|
|
|
|
Reg_Init();
|
|
|
|
//
|
|
// clear result for error case
|
|
//
|
|
|
|
*pResult = 0;
|
|
|
|
//
|
|
// get proper regval name
|
|
// - wide for NT
|
|
// - narrow for 9X
|
|
//
|
|
|
|
pname = regValueNameForId( RegId );
|
|
if ( !pname )
|
|
{
|
|
DNS_ASSERT( FALSE );
|
|
return( ERROR_INVALID_PARAMETER );
|
|
}
|
|
|
|
//
|
|
// DCR: can use function pointers for wide narrow
|
|
//
|
|
|
|
//
|
|
// three paradigms
|
|
//
|
|
// 1) specific key (adapter or something else)
|
|
// => use it
|
|
//
|
|
// 2) specific key name (adapter or dummy key location)
|
|
// => open key
|
|
// => use it
|
|
// => close
|
|
//
|
|
// 3) session -- passed in or created (default)
|
|
// => use pRegSession or open new
|
|
// => try policy first then TCPIP parameters
|
|
// => close if open
|
|
//
|
|
|
|
if ( hRegKey )
|
|
{
|
|
hkey = hRegKey;
|
|
}
|
|
|
|
else if ( pwsKeyName )
|
|
{
|
|
hkey = Reg_CreateKey(
|
|
pwsKeyName,
|
|
FALSE // read access
|
|
);
|
|
if ( !hkey )
|
|
{
|
|
status = GetLastError();
|
|
goto Done;
|
|
}
|
|
hlocalKey = hkey;
|
|
}
|
|
|
|
else
|
|
{
|
|
// open reg handle if not open
|
|
|
|
if ( !psession )
|
|
{
|
|
status = Reg_OpenSession(
|
|
&session,
|
|
0, // standard level
|
|
RegId // target key
|
|
);
|
|
if ( status != ERROR_SUCCESS )
|
|
{
|
|
goto Done;
|
|
}
|
|
psession = &session;
|
|
}
|
|
|
|
// try policy section -- if available
|
|
|
|
hkey = psession->hPolicy;
|
|
|
|
if ( hkey && REGPROP_POLICY(RegId) )
|
|
{
|
|
//status = DnsShimRegQueryValueExW(
|
|
status = RegQueryValueExW(
|
|
hkey,
|
|
(PWSTR) pname,
|
|
0,
|
|
& regType,
|
|
(PBYTE) pResult,
|
|
& dataLength
|
|
);
|
|
if ( status == ERROR_SUCCESS )
|
|
{
|
|
goto DoneSuccess;
|
|
}
|
|
}
|
|
|
|
// unsuccessful -- try DnsClient
|
|
|
|
#ifdef DNSCLIENTKEY
|
|
hkey = psession->hClient;
|
|
if ( hkey && REGPROP_CLIENT(RegId) )
|
|
{
|
|
//status = DnsShimRegQueryValueExW(
|
|
status = RegQueryValueExW(
|
|
hkey,
|
|
(PWSTR) pname,
|
|
0,
|
|
& regType,
|
|
(PBYTE) pResult,
|
|
& dataLength
|
|
);
|
|
if ( status == ERROR_SUCCESS )
|
|
{
|
|
goto DoneSuccess;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// unsuccessful -- try DnsCache
|
|
|
|
hkey = psession->hCache;
|
|
if ( hkey && REGPROP_CACHE(RegId) )
|
|
{
|
|
//status = DnsShimRegQueryValueExW(
|
|
status = RegQueryValueExW(
|
|
hkey,
|
|
(PWSTR) pname,
|
|
0,
|
|
& regType,
|
|
(PBYTE) pResult,
|
|
& dataLength
|
|
);
|
|
if ( status == ERROR_SUCCESS )
|
|
{
|
|
goto DoneSuccess;
|
|
}
|
|
}
|
|
|
|
// unsuccessful -- try TCPIP key
|
|
// - if have open session it MUST include TCPIP key
|
|
|
|
hkey = psession->hTcpip;
|
|
if ( hkey && REGPROP_TCPIP(RegId) )
|
|
{
|
|
//status = DnsShimRegQueryValueExW(
|
|
status = RegQueryValueExW(
|
|
hkey,
|
|
(PWSTR) pname,
|
|
0,
|
|
& regType,
|
|
(PBYTE) pResult,
|
|
& dataLength
|
|
);
|
|
if ( status == ERROR_SUCCESS )
|
|
{
|
|
goto DoneSuccess;
|
|
}
|
|
}
|
|
|
|
status = ERROR_FILE_NOT_FOUND;
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// explict key (passed in or from name)
|
|
//
|
|
|
|
if ( hkey )
|
|
{
|
|
//status = DnsShimRegQueryValueExW(
|
|
status = RegQueryValueExW(
|
|
hkey,
|
|
(PWSTR) pname,
|
|
0,
|
|
& regType,
|
|
(PBYTE) pResult,
|
|
& dataLength
|
|
);
|
|
}
|
|
ELSE_ASSERT_FALSE;
|
|
|
|
Done:
|
|
|
|
//
|
|
// if value not found, check for backward compatibility value
|
|
//
|
|
|
|
if ( status != ERROR_SUCCESS )
|
|
{
|
|
DWORD oldId;
|
|
BOOL freverse;
|
|
|
|
oldId = checkBackCompat( RegId, &freverse );
|
|
|
|
if ( oldId != NO_BACK_VALUE )
|
|
{
|
|
DWORD backValue;
|
|
|
|
status = Reg_GetDword(
|
|
psession,
|
|
( psession ) ? NULL : hkey,
|
|
( psession ) ? NULL : pwsKeyName,
|
|
oldId,
|
|
& backValue );
|
|
|
|
if ( status == ERROR_SUCCESS )
|
|
{
|
|
if ( freverse )
|
|
{
|
|
backValue = !backValue;
|
|
}
|
|
*pResult = backValue;
|
|
}
|
|
}
|
|
}
|
|
|
|
// default the value if read failed
|
|
|
|
if ( status != ERROR_SUCCESS )
|
|
{
|
|
*pResult = REGPROP_DEFAULT( RegId );
|
|
}
|
|
|
|
DoneSuccess:
|
|
|
|
// cleanup any regkey's opened
|
|
|
|
if ( psession == &session )
|
|
{
|
|
Reg_CloseSession( psession );
|
|
}
|
|
|
|
else if ( hlocalKey )
|
|
{
|
|
RegCloseKey( hlocalKey );
|
|
}
|
|
|
|
return( status );
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DCR_CLEANUP: cleanup Reg_ReadValue()
|
|
// this is a slightly altered version of Glenn's
|
|
// routine to "do it all" -- stripped of the
|
|
// unnecessary Win9x flag;
|
|
// it does not do policy or use the RegHandle yet
|
|
//
|
|
// DCR_PERF: it also makes a bunch of unnecessary
|
|
// heap allocations
|
|
//
|
|
|
|
DNS_STATUS
|
|
privateRegReadValue(
|
|
IN HKEY hKey,
|
|
IN DWORD RegId,
|
|
IN DWORD Flag,
|
|
OUT PBYTE * ppBuffer,
|
|
OUT PDWORD pBufferLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
hKey -- handle of the key whose value field is retrieved.
|
|
|
|
RegId -- reg value ID, assumed to be validated (in table)
|
|
|
|
ppBuffer -- ptr to address to receive buffer ptr
|
|
|
|
pBufferLength -- addr to receive buffer length
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
ErrorCode on failure.
|
|
|
|
--*/
|
|
{
|
|
DWORD status;
|
|
PSTR pname;
|
|
DWORD valueType = 0; // prefix
|
|
DWORD valueSize = 0; // prefix
|
|
PBYTE pdataBuffer;
|
|
PBYTE pallocBuffer = NULL;
|
|
|
|
|
|
//
|
|
// query for buffer size
|
|
//
|
|
|
|
pname = (PSTR) REGPROP_NAME( RegId );
|
|
|
|
//status = DnsShimRegQueryValueExW(
|
|
status = RegQueryValueExW(
|
|
hKey,
|
|
(PWSTR) pname,
|
|
0,
|
|
&valueType,
|
|
NULL,
|
|
&valueSize );
|
|
|
|
if ( status != ERROR_SUCCESS )
|
|
{
|
|
return( status );
|
|
}
|
|
|
|
//
|
|
// setup result buffer
|
|
//
|
|
|
|
switch( valueType )
|
|
{
|
|
case REG_DWORD:
|
|
pdataBuffer = (PBYTE) ppBuffer;
|
|
break;
|
|
|
|
case REG_SZ:
|
|
case REG_MULTI_SZ:
|
|
case REG_EXPAND_SZ:
|
|
case REG_BINARY:
|
|
|
|
// if size is zero, still allocate empty string
|
|
// - min alloc DWORD
|
|
// - can't possibly alloc smaller
|
|
// - good clean init to zero includes MULTISZ zero
|
|
// - need at least WCHAR string zero init
|
|
// and much catch small regbinary (1,2,3)
|
|
|
|
if ( valueSize <= sizeof(DWORD) )
|
|
{
|
|
valueSize = sizeof(DWORD);
|
|
}
|
|
|
|
pallocBuffer = pdataBuffer = ALLOCATE_HEAP( valueSize );
|
|
if ( !pdataBuffer )
|
|
{
|
|
return( DNS_ERROR_NO_MEMORY );
|
|
}
|
|
|
|
*(PDWORD)pdataBuffer = 0;
|
|
break;
|
|
|
|
default:
|
|
return( ERROR_INVALID_PARAMETER );
|
|
}
|
|
|
|
//
|
|
// query for data
|
|
//
|
|
|
|
//status = DnsShimRegQueryValueExW(
|
|
status = RegQueryValueExW(
|
|
hKey,
|
|
(PWSTR) pname,
|
|
0,
|
|
&valueType,
|
|
pdataBuffer,
|
|
&valueSize );
|
|
|
|
//
|
|
// setup return buffer
|
|
//
|
|
|
|
switch( valueType )
|
|
{
|
|
case REG_DWORD:
|
|
case REG_BINARY:
|
|
break;
|
|
|
|
case REG_SZ:
|
|
case REG_EXPAND_SZ:
|
|
case REG_MULTI_SZ:
|
|
|
|
//
|
|
// dump empty strings?
|
|
//
|
|
// note: we always allocate at least a DWORD and
|
|
// set it NULL, so rather than a complex test for
|
|
// different reg types and char sets, can just test
|
|
// if that DWORD is still NULL
|
|
//
|
|
// DCR: do we want to screen whitespace-empty strings
|
|
// - example blank string
|
|
//
|
|
|
|
if ( Flag & DNSREG_FLAG_DUMP_EMPTY )
|
|
{
|
|
if ( valueSize==0 ||
|
|
*(PDWORD)pdataBuffer == 0 )
|
|
{
|
|
status = ERROR_INVALID_DATA;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// by default we return strings as UTF8
|
|
//
|
|
// if flagged, return in unicode
|
|
//
|
|
|
|
if ( Flag & DNSREG_FLAG_GET_UNICODE )
|
|
{
|
|
// no-op, keep unicode string
|
|
}
|
|
|
|
//
|
|
// do default convert to UTF8
|
|
//
|
|
|
|
else
|
|
{
|
|
PBYTE putf8Buffer = ALLOCATE_HEAP( valueSize * 2 );
|
|
if ( !putf8Buffer )
|
|
{
|
|
status = DNS_ERROR_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( !Dns_UnicodeToUtf8(
|
|
(PWSTR) pdataBuffer,
|
|
valueSize / sizeof(WCHAR),
|
|
putf8Buffer,
|
|
valueSize * 2 ) )
|
|
{
|
|
FREE_HEAP( putf8Buffer );
|
|
status = ERROR_INVALID_DATA;
|
|
goto Cleanup;
|
|
}
|
|
FREE_HEAP( pallocBuffer );
|
|
pallocBuffer = NULL;
|
|
pdataBuffer = putf8Buffer;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
//
|
|
// set return
|
|
// - REG_DWORD writes DWORD to ppBuffer directly
|
|
// - otherwise ppBuffer set to allocated buffer ptr
|
|
// or cleanup
|
|
// - on failure dump allocated buffer
|
|
//
|
|
|
|
if ( status == ERROR_SUCCESS )
|
|
{
|
|
if ( valueType != REG_DWORD )
|
|
{
|
|
*ppBuffer = pdataBuffer;
|
|
}
|
|
*pBufferLength = valueSize;
|
|
}
|
|
else
|
|
{
|
|
*ppBuffer = NULL;
|
|
*pBufferLength = 0;
|
|
FREE_HEAP( pallocBuffer );
|
|
}
|
|
|
|
return( status );
|
|
}
|
|
|
|
|
|
|
|
DNS_STATUS
|
|
Reg_GetValueEx(
|
|
IN PREG_SESSION pRegSession, OPTIONAL
|
|
IN HKEY hRegKey, OPTIONAL
|
|
IN LPSTR pwsAdapter, OPTIONAL
|
|
IN DWORD RegId,
|
|
IN DWORD ValueType,
|
|
IN DWORD Flag,
|
|
OUT PBYTE * ppBuffer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
pRegSession -- ptr to registry session, OPTIONAL
|
|
|
|
hRegKey -- handle to open regkey OPTIONAL
|
|
|
|
pwsAdapter -- name of adapter to query under OPTIONAL
|
|
|
|
RegId -- value ID
|
|
|
|
ValueType -- reg type of value
|
|
|
|
Flag -- flags with tweaks to lookup
|
|
|
|
ppBuffer -- addr to receive buffer ptr
|
|
|
|
Note, for REG_DWORD, DWORD data is written directly to this
|
|
location instead of a buffer being allocated and it's ptr
|
|
being written.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
Registry error code on failure.
|
|
|
|
--*/
|
|
{
|
|
DNS_STATUS status = ERROR_FILE_NOT_FOUND;
|
|
REG_SESSION session;
|
|
PREG_SESSION psession = pRegSession;
|
|
PBYTE pname;
|
|
DWORD regType = REG_DWORD;
|
|
DWORD dataLength;
|
|
HKEY hkey;
|
|
HKEY hadapterKey = NULL;
|
|
|
|
|
|
DNSDBG( REGISTRY, (
|
|
"Reg_GetValueEx( s=%p, k=%p, id=%d )\n",
|
|
pRegSession,
|
|
hRegKey,
|
|
RegId ));
|
|
|
|
ASSERT( !pwsAdapter );
|
|
|
|
// auto init
|
|
|
|
Reg_Init();
|
|
|
|
//
|
|
// get proper regval name
|
|
// - wide for NT
|
|
// - narrow for 9X
|
|
//
|
|
|
|
pname = regValueNameForId( RegId );
|
|
if ( !pname )
|
|
{
|
|
DNS_ASSERT( FALSE );
|
|
status = ERROR_INVALID_PARAMETER;
|
|
goto FailedDone;
|
|
}
|
|
|
|
//
|
|
// DCR: can use fucntion pointers for wide narrow
|
|
//
|
|
|
|
//
|
|
// two paradigms
|
|
//
|
|
// 1) specific key (adapter or something else)
|
|
// => use it
|
|
// => open adapter subkey if necessary
|
|
//
|
|
// 2) standard
|
|
// => try policy first, then DNSCache, then TCPIP
|
|
// => use pRegSession or open it
|
|
//
|
|
|
|
if ( hRegKey )
|
|
{
|
|
hkey = hRegKey;
|
|
|
|
// need to open adapter subkey
|
|
|
|
if ( pwsAdapter )
|
|
{
|
|
status = RegOpenKeyExA(
|
|
hkey,
|
|
(PCSTR) pwsAdapter,
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
& hadapterKey );
|
|
|
|
if ( status != ERROR_SUCCESS )
|
|
{
|
|
goto FailedDone;
|
|
}
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
// open reg handle if not open
|
|
|
|
if ( !pRegSession )
|
|
{
|
|
status = Reg_OpenSession(
|
|
&session,
|
|
0, // standard level
|
|
RegId // target key
|
|
);
|
|
if ( status != ERROR_SUCCESS )
|
|
{
|
|
goto FailedDone;
|
|
}
|
|
psession = &session;
|
|
}
|
|
|
|
// try policy section -- if available
|
|
|
|
hkey = psession->hPolicy;
|
|
|
|
if ( hkey && REGPROP_POLICY(RegId) )
|
|
{
|
|
status = privateRegReadValue(
|
|
hkey,
|
|
RegId,
|
|
Flag,
|
|
ppBuffer,
|
|
& dataLength
|
|
);
|
|
if ( status == ERROR_SUCCESS )
|
|
{
|
|
goto Done;
|
|
}
|
|
}
|
|
|
|
// try DNS cache -- if available
|
|
|
|
hkey = psession->hCache;
|
|
|
|
if ( hkey && REGPROP_CACHE(RegId) )
|
|
{
|
|
status = privateRegReadValue(
|
|
hkey,
|
|
RegId,
|
|
Flag,
|
|
ppBuffer,
|
|
& dataLength
|
|
);
|
|
if ( status == ERROR_SUCCESS )
|
|
{
|
|
goto Done;
|
|
}
|
|
}
|
|
|
|
// unsuccessful -- use TCPIP key
|
|
|
|
hkey = psession->hTcpip;
|
|
if ( !hkey )
|
|
{
|
|
goto Done;
|
|
}
|
|
}
|
|
|
|
//
|
|
// explict key OR standard key case
|
|
//
|
|
|
|
status = privateRegReadValue(
|
|
hkey,
|
|
RegId,
|
|
Flag,
|
|
ppBuffer,
|
|
& dataLength
|
|
);
|
|
if ( status == ERROR_SUCCESS )
|
|
{
|
|
goto Done;
|
|
}
|
|
|
|
FailedDone:
|
|
|
|
//
|
|
// if failed
|
|
// - for REG_DWORD, default the value
|
|
// - for strings, ensure NULL return buffer
|
|
// this takes care of cases where privateRegReadValue()
|
|
// never got called
|
|
//
|
|
|
|
if ( status != ERROR_SUCCESS )
|
|
{
|
|
if ( ValueType == REG_DWORD )
|
|
{
|
|
*(PDWORD) ppBuffer = REGPROP_DEFAULT( RegId );
|
|
}
|
|
else
|
|
{
|
|
*ppBuffer = NULL;
|
|
}
|
|
}
|
|
|
|
Done:
|
|
|
|
// cleanup any regkey's opened
|
|
|
|
if ( psession == &session )
|
|
{
|
|
Reg_CloseSession( psession );
|
|
}
|
|
|
|
if ( hadapterKey )
|
|
{
|
|
RegCloseKey( hadapterKey );
|
|
}
|
|
|
|
return( status );
|
|
}
|
|
|
|
|
|
|
|
|
|
DNS_STATUS
|
|
Reg_GetIpArray(
|
|
IN PREG_SESSION pRegSession, OPTIONAL
|
|
IN HKEY hRegKey, OPTIONAL
|
|
IN LPSTR pwsAdapter, OPTIONAL
|
|
IN DWORD RegId,
|
|
IN DWORD ValueType,
|
|
OUT PIP_ARRAY * ppIpArray
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
pRegSession -- ptr to registry session, OPTIONAL
|
|
|
|
hRegKey -- handle to open regkey OPTIONAL
|
|
|
|
pwsAdapter -- name of adapter to query under OPTIONAL
|
|
|
|
RegId -- value ID
|
|
|
|
ValueType -- currently ignored, but could later use
|
|
to distinguish REG_SZ from REG_MULTI_SZ
|
|
processing
|
|
|
|
ppIpArray -- addr to receive IP array ptr
|
|
- array is allocated with Dns_Alloc(),
|
|
caller must free with Dns_Free()
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
Registry error code on failure.
|
|
|
|
--*/
|
|
{
|
|
DNS_STATUS status;
|
|
PSTR pstring = NULL;
|
|
|
|
DNSDBG( REGISTRY, (
|
|
"Reg_GetIpArray( s=%p, k=%p, id=%d )\n",
|
|
pRegSession,
|
|
hRegKey,
|
|
RegId ));
|
|
|
|
//
|
|
// make call to get IP array as string
|
|
//
|
|
|
|
status = Reg_GetValueEx(
|
|
pRegSession,
|
|
hRegKey,
|
|
pwsAdapter,
|
|
RegId,
|
|
REG_SZ, // only supported type is REG_SZ
|
|
0, // no flag
|
|
& pstring );
|
|
|
|
if ( status != ERROR_SUCCESS )
|
|
{
|
|
ASSERT( pstring == NULL );
|
|
return( status );
|
|
}
|
|
|
|
//
|
|
// convert from string to IP array
|
|
//
|
|
// note: this call is limited to a parsing limit
|
|
// but it is a large number suitable for stuff
|
|
// like DNS server lists
|
|
//
|
|
// DCR: use IP array builder for local IP address
|
|
// then need Dns_CreateIpArrayFromMultiIpString()
|
|
// to use count\alloc method when buffer overflows
|
|
//
|
|
|
|
status = Dns_CreateIpArrayFromMultiIpString(
|
|
pstring,
|
|
ppIpArray );
|
|
|
|
// cleanup
|
|
|
|
if ( pstring )
|
|
{
|
|
FREE_HEAP( pstring );
|
|
}
|
|
|
|
return( status );
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// Registry writing routines
|
|
//
|
|
|
|
HKEY
|
|
WINAPI
|
|
Reg_CreateKey(
|
|
IN PWSTR pwsKeyName,
|
|
IN BOOL bWrite
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Open registry key.
|
|
|
|
The purpose of this routine is simply to functionalize
|
|
opening with\without an adapter name.
|
|
So caller can pass through adapter name argument instead
|
|
of building key name or doing two opens for adapter
|
|
present\absent.
|
|
|
|
This is NT only.
|
|
|
|
Arguments:
|
|
|
|
pwsKeyName -- key "name"
|
|
this is one of the REGKEY_X from registry.h
|
|
OR
|
|
adapter name
|
|
|
|
bWrite -- TRUE for write access, FALSE for read
|
|
|
|
Return Value:
|
|
|
|
New opened key.
|
|
|
|
--*/
|
|
{
|
|
HKEY hkey = NULL;
|
|
DWORD disposition;
|
|
DWORD status;
|
|
PWSTR pnameKey;
|
|
WCHAR nameBuffer[ MAX_PATH ];
|
|
|
|
//
|
|
// don't bother for Win9x
|
|
//
|
|
// DCR_QUESTION: any need for use with Win9x
|
|
//
|
|
|
|
#if DNSWIN9X
|
|
if ( g_IsWin9X )
|
|
{
|
|
ASSERT( FALSE );
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return( NULL );
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// determine key name
|
|
//
|
|
// this is either DNSKEY_X dummy pointer from registry.h
|
|
// OR
|
|
// is an adapter name;
|
|
//
|
|
// - if adapter given, open under it
|
|
// adapters are under TCPIP\Interfaces
|
|
// - any other specific key
|
|
// - default is TCPIP params key
|
|
//
|
|
// note: if if grows too big, turn into table
|
|
//
|
|
|
|
if ( pwsKeyName <= REGKEY_DNS_MAX )
|
|
{
|
|
if ( pwsKeyName == REGKEY_TCPIP_PARAMETERS )
|
|
{
|
|
pnameKey = TCPIP_PARAMETERS_KEY;
|
|
}
|
|
else if ( pwsKeyName == REGKEY_DNS_CACHE )
|
|
{
|
|
pnameKey = DNS_CACHE_KEY;
|
|
}
|
|
else if ( pwsKeyName == REGKEY_DNS_POLICY )
|
|
{
|
|
pnameKey = DNS_POLICY_KEY;
|
|
}
|
|
else if ( pwsKeyName == REGKEY_SETUP_MODE_LOCATION )
|
|
{
|
|
pnameKey = NT_SETUP_MODE_KEY;
|
|
}
|
|
else
|
|
{
|
|
pnameKey = TCPIP_PARAMETERS_KEY;
|
|
}
|
|
}
|
|
|
|
else // adapter name
|
|
{
|
|
wcscpy( nameBuffer, TCPIP_INTERFACES_KEY );
|
|
wcscat( nameBuffer, pwsKeyName );
|
|
|
|
pnameKey = nameBuffer;
|
|
}
|
|
|
|
//
|
|
// create\open key
|
|
//
|
|
|
|
if ( bWrite )
|
|
{
|
|
status = RegCreateKeyExW(
|
|
HKEY_LOCAL_MACHINE,
|
|
pnameKey,
|
|
0,
|
|
L"Class",
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_WRITE,
|
|
NULL,
|
|
& hkey,
|
|
& disposition );
|
|
}
|
|
else
|
|
{
|
|
status = RegOpenKeyExW(
|
|
HKEY_LOCAL_MACHINE,
|
|
pnameKey,
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
& hkey );
|
|
}
|
|
|
|
if ( status != ERROR_SUCCESS )
|
|
{
|
|
SetLastError( status );
|
|
}
|
|
ELSE_ASSERT( hkey != NULL );
|
|
|
|
return( hkey );
|
|
}
|
|
|
|
|
|
|
|
DNS_STATUS
|
|
WINAPI
|
|
Reg_SetDwordValueByName(
|
|
IN PVOID pReserved,
|
|
IN HKEY hRegKey,
|
|
IN PWSTR pwsNameKey, OPTIONAL
|
|
IN PWSTR pwsNameValue, OPTIONAL
|
|
IN DWORD dwValue
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set DWORD regkey.
|
|
|
|
Arguments:
|
|
|
|
pReserved -- reserved (may become session)
|
|
|
|
hRegKey -- existing key to set under OPTIONAL
|
|
|
|
pwsNameKey -- name of key or adapter to set under
|
|
|
|
pwsNameValue -- name of reg value to set
|
|
|
|
dwValue -- value to set
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
ErrorCode on failure.
|
|
|
|
--*/
|
|
{
|
|
HKEY hkey;
|
|
DNS_STATUS status;
|
|
|
|
//
|
|
// don't bother for Win9x
|
|
//
|
|
#if DNSWIN9X
|
|
if ( g_IsWin9X )
|
|
{
|
|
ASSERT( FALSE );
|
|
return( ERROR_INVALID_PARAMETER );
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// open key, if not provided
|
|
// - if adapter given, open under it
|
|
// - otherwise TCPIP params key
|
|
//
|
|
|
|
hkey = hRegKey;
|
|
|
|
if ( !hkey )
|
|
{
|
|
hkey = Reg_CreateKey(
|
|
pwsNameKey,
|
|
TRUE // open for write
|
|
);
|
|
if ( !hkey )
|
|
{
|
|
return( GetLastError() );
|
|
}
|
|
}
|
|
|
|
//
|
|
// write back value
|
|
//
|
|
|
|
status = RegSetValueExW(
|
|
hkey,
|
|
pwsNameValue,
|
|
0,
|
|
REG_DWORD,
|
|
(LPBYTE) &dwValue,
|
|
sizeof(DWORD) );
|
|
|
|
if ( !hRegKey )
|
|
{
|
|
RegCloseKey( hkey );
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
DNS_STATUS
|
|
WINAPI
|
|
Reg_SetDwordValue(
|
|
IN PVOID pReserved,
|
|
IN HKEY hRegKey,
|
|
IN PWSTR pwsNameKey, OPTIONAL
|
|
IN DWORD RegId,
|
|
IN DWORD dwValue
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set DWORD regkey.
|
|
|
|
Arguments:
|
|
|
|
pReserved -- reserved (may become session)
|
|
|
|
hRegKey -- existing key to set under OPTIONAL
|
|
|
|
pwsNameKey -- name of key or adapter to set under
|
|
|
|
RegId -- id of value to set
|
|
|
|
dwValue -- value to set
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
ErrorCode on failure.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// write back value using name of id
|
|
//
|
|
|
|
return Reg_SetDwordValueByName(
|
|
pReserved,
|
|
hRegKey,
|
|
pwsNameKey,
|
|
REGPROP_NAME( RegId ),
|
|
dwValue );
|
|
}
|
|
|
|
|
|
|
|
|
|
#if DNSWIN9X
|
|
//
|
|
// Removed with Win95 support
|
|
// Further Win95 support is doubtful -- but this works if needed.
|
|
//
|
|
|
|
//
|
|
// Registry shims
|
|
// Win9x in not providing wide reg calls.
|
|
//
|
|
|
|
PCHAR
|
|
standardWideToAnsiConvert(
|
|
IN PWSTR pWide,
|
|
OUT PCHAR pAnsiBuf
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Standard conversion of wide to narron for shimming registry calls.
|
|
|
|
Arguments:
|
|
|
|
pWide -- parameter in wide call
|
|
|
|
pAnsiBuf -- buffer to hold ANSI param; assumed to be max path
|
|
|
|
Return Value:
|
|
|
|
Ptr to converted buffer.
|
|
NULL if pWide NULL or error in conversion.
|
|
|
|
--*/
|
|
{
|
|
DWORD length;
|
|
DWORD result;
|
|
|
|
// no wide param, no ANSI param
|
|
|
|
if ( !pWide )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// copy\convert to ANSI buf
|
|
|
|
length = MAX_PATH;
|
|
|
|
result = Dns_StringCopy(
|
|
pAnsiBuf,
|
|
&length,
|
|
(PSTR) pWide,
|
|
0, // calc length
|
|
DnsCharSetUnicode,
|
|
DnsCharSetAnsi
|
|
);
|
|
|
|
if ( result == 0 )
|
|
{
|
|
return( NULL );
|
|
}
|
|
else
|
|
{
|
|
return( pAnsiBuf );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//WINADVAPI
|
|
LONG
|
|
WINAPI
|
|
DnsShimRegCreateKeyExW(
|
|
IN HKEY hKey,
|
|
IN LPCWSTR lpSubKey,
|
|
IN DWORD Reserved,
|
|
IN LPWSTR lpClass,
|
|
IN DWORD dwOptions,
|
|
IN REGSAM samDesired,
|
|
IN LPSECURITY_ATTRIBUTES lpSecurityAttributes,
|
|
OUT PHKEY phkResult,
|
|
OUT LPDWORD lpdwDisposition
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Shim RegCreateKeyExW()
|
|
|
|
Arguments:
|
|
|
|
See SDK doc.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
Error code on failure.
|
|
|
|
--*/
|
|
{
|
|
// for NT it's a dumb stub
|
|
|
|
if ( !g_IsWin9X )
|
|
{
|
|
return RegCreateKeyExW(
|
|
hKey,
|
|
lpSubKey,
|
|
Reserved,
|
|
lpClass,
|
|
dwOptions,
|
|
samDesired,
|
|
lpSecurityAttributes,
|
|
phkResult,
|
|
lpdwDisposition );
|
|
}
|
|
|
|
// for Win9x, convert to ANSI and call
|
|
|
|
else
|
|
{
|
|
PCHAR pclass;
|
|
PCHAR psubKey;
|
|
CHAR class[ MAX_PATH ];
|
|
CHAR subKey[ MAX_PATH ];
|
|
|
|
psubKey = standardWideToAnsiConvert(
|
|
(PWSTR) lpSubKey,
|
|
subKey );
|
|
if ( !psubKey )
|
|
{
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
pclass = standardWideToAnsiConvert(
|
|
(PWSTR) lpClass,
|
|
class );
|
|
|
|
return RegCreateKeyExA(
|
|
hKey,
|
|
(LPCSTR) psubKey,
|
|
Reserved,
|
|
pclass,
|
|
dwOptions,
|
|
samDesired,
|
|
lpSecurityAttributes,
|
|
phkResult,
|
|
lpdwDisposition );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//WINADVAPI
|
|
LONG
|
|
WINAPI
|
|
DnsShimRegOpenKeyExW(
|
|
IN HKEY hKey,
|
|
IN LPCWSTR lpSubKey,
|
|
IN DWORD dwOptions,
|
|
IN REGSAM samDesired,
|
|
OUT PHKEY phkResult
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Shim RegOpenKeyExW()
|
|
|
|
Arguments:
|
|
|
|
See SDK doc.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
Error code on failure.
|
|
|
|
--*/
|
|
{
|
|
// for NT it's a dumb stub
|
|
|
|
if ( !g_IsWin9X )
|
|
{
|
|
return RegOpenKeyExW(
|
|
hKey,
|
|
lpSubKey,
|
|
dwOptions,
|
|
samDesired,
|
|
phkResult );
|
|
}
|
|
|
|
// for Win9x, convert to ANSI and call
|
|
|
|
else
|
|
{
|
|
PCHAR psubKey;
|
|
CHAR subKey[ MAX_PATH ];
|
|
|
|
psubKey = standardWideToAnsiConvert(
|
|
(PWSTR) lpSubKey,
|
|
subKey );
|
|
|
|
return RegOpenKeyExA(
|
|
hKey,
|
|
(LPCSTR) psubKey,
|
|
dwOptions,
|
|
samDesired,
|
|
phkResult );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
LONG
|
|
WINAPI
|
|
DnsShimRegQueryValueExW(
|
|
IN HKEY hKey,
|
|
IN LPCWSTR lpValueName,
|
|
IN LPDWORD lpReserved,
|
|
IN LPDWORD lpType,
|
|
IN LPBYTE lpData,
|
|
IN LPDWORD lpcbData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Shim RegQueryValueExW()
|
|
|
|
Arguments:
|
|
|
|
See SDK doc.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
Error code on failure.
|
|
|
|
--*/
|
|
{
|
|
// for NT it's a dumb stub
|
|
|
|
if ( !g_IsWin9X )
|
|
{
|
|
return RegQueryValueExW(
|
|
hKey,
|
|
lpValueName,
|
|
lpReserved,
|
|
lpType,
|
|
lpData,
|
|
lpcbData );
|
|
}
|
|
|
|
// for Win9x, convert to ANSI and call
|
|
|
|
else
|
|
{
|
|
LONG status;
|
|
DWORD type = 0;
|
|
DWORD length = 0;
|
|
PDWORD plength = &length;
|
|
PCHAR pvalueName;
|
|
CHAR valueName[ MAX_PATH ];
|
|
|
|
// convert value name
|
|
|
|
pvalueName = standardWideToAnsiConvert(
|
|
(PWSTR) lpValueName,
|
|
valueName );
|
|
|
|
// use start length
|
|
|
|
if ( lpcbData )
|
|
{
|
|
length = *lpcbData;
|
|
plength = NULL;
|
|
}
|
|
|
|
// make ANSI call
|
|
|
|
status = RegQueryValueExA(
|
|
hKey,
|
|
pvalueName,
|
|
lpReserved,
|
|
& type,
|
|
lpData,
|
|
plength );
|
|
|
|
// return type
|
|
|
|
if ( lpType )
|
|
{
|
|
*lpType = type;
|
|
}
|
|
|
|
//
|
|
// setup length\data return
|
|
// - no length call => just status
|
|
// - no data return or non-string data => set length
|
|
// - string data => convert
|
|
//
|
|
|
|
if ( !lpcbData )
|
|
{
|
|
// no-op, this kind of call just gets status
|
|
}
|
|
|
|
//
|
|
// no data conversion -- set length
|
|
// - unicode length required at twice ANSI length
|
|
// - DWORD and binary length unchanged
|
|
|
|
else if ( !lpData ||
|
|
status != ERROR_SUCCESS ||
|
|
type == REG_DWORD ||
|
|
type == REG_BINARY )
|
|
{
|
|
if ( type == REG_DWORD || type == REG_BINARY )
|
|
{
|
|
*lpcbData = length;
|
|
}
|
|
else
|
|
{
|
|
*lpcbData = length * 2;
|
|
}
|
|
}
|
|
|
|
//
|
|
// convert ANSI data to unicode
|
|
//
|
|
|
|
else
|
|
{
|
|
PBYTE pdataUnicode;
|
|
DWORD unicodeLength;
|
|
|
|
pdataUnicode = (PBYTE) ALLOCATE_HEAP( *lpcbData );
|
|
if ( !pdataUnicode )
|
|
{
|
|
return( DNS_ERROR_NO_MEMORY );
|
|
}
|
|
|
|
unicodeLength = Dns_StringCopy(
|
|
pdataUnicode,
|
|
lpcbData,
|
|
lpData, // ANSI data
|
|
length, // ANSI length
|
|
DnsCharSetAnsi,
|
|
DnsCharSetUnicode );
|
|
|
|
if ( unicodeLength == 0 )
|
|
{
|
|
status = GetLastError();
|
|
ASSERT( status != ERROR_SUCCESS );
|
|
unicodeLength = length * 2;
|
|
}
|
|
|
|
*lpcbData = unicodeLength;
|
|
|
|
FREE_HEAP( pdataUnicode );
|
|
}
|
|
|
|
return( status );
|
|
}
|
|
}
|
|
#endif // Win95 registry shims
|
|
|
|
//
|
|
// End registry.c
|
|
//
|