/*++ Copyright (c) Microsoft Corporation. All rights reserved. Module Name: registry.c Abstract: Registry interface routines for Windows NT Setup API Dll. Author: Ted Miller (tedm) 6-Feb-1995 Revision History: --*/ #include "precomp.h" #pragma hdrstop // // Private function prototypes. // DWORD QueryMultiSzDevRegPropToArray( IN DEVINST DevInst, IN ULONG CmPropertyCode, OUT PTSTR **StringArray, OUT PUINT StringCount ); DWORD SetArrayToMultiSzDevRegProp( IN DEVINST DevInst, IN ULONG CmPropertyCode, IN PTSTR *StringArray, IN UINT StringCount ); #if MEM_DBG DWORD TrackedQueryRegistryValue( IN TRACK_ARG_DECLARE TRACK_ARG_COMMA IN HKEY KeyHandle, IN PCTSTR ValueName, OUT PTSTR *Value, OUT PDWORD DataType, OUT PDWORD DataSizeBytes ) { DWORD d; TRACK_PUSH // defined again below #undef QueryRegistryValue d = QueryRegistryValue ( KeyHandle, ValueName, Value, DataType, DataSizeBytes ); TRACK_POP return d; } #endif DWORD QueryRegistryValue( IN HKEY KeyHandle, IN PCTSTR ValueName, OUT PTSTR *Value, OUT PDWORD DataType, OUT PDWORD DataSizeBytes ) /*++ Routine Description: Return an allocated buffer holding a copy of what's in registry buffer is padded with two extra NULL's so that caller never has to worry about unterminated strings. Caller does have to worry about size of fixed-size data Arguments: KeyHandle - Key to query value in ValueName - name of value to query Value - returned pointer containing value, release with MyFree DataType - type of returned data DataSizeBytes - size of returned data in bytes (not TCHAR's!!!) Return Value: NO_ERROR iff success --*/ { LONG l; DWORD sz; sz = 0; l = RegQueryValueEx(KeyHandle,ValueName,NULL,DataType,NULL,&sz); *DataSizeBytes = sz; if(l != NO_ERROR) { return((DWORD)l); } // // If the size of the value entry is 0 bytes, then return success, but with // Value set to NULL. // if(!sz) { *Value = NULL; return NO_ERROR; } sz += sizeof(TCHAR)*2; // always pad the buffer with extra zero's *Value = MyMalloc(sz); if(*Value == NULL) { return(ERROR_NOT_ENOUGH_MEMORY); } l = RegQueryValueEx(KeyHandle,ValueName,NULL,DataType,(PVOID)*Value,DataSizeBytes); if(l != NO_ERROR) { MyFree(*Value); } else { // // write 2 NULL chars to end of buffer // ZeroMemory(((LPBYTE)*Value)+*DataSizeBytes,sizeof(TCHAR)*2); } return((DWORD)l); } #if MEM_DBG #define QueryRegistryValue(a,b,c,d,e) TrackedQueryRegistryValue(TRACK_ARG_CALL,a,b,c,d,e) #endif DWORD QueryRegistryDwordValue( IN HKEY KeyHandle, IN PCTSTR ValueName, OUT PDWORD Value ) /*++ Routine Description: Return a DWORD value If registry is DWORD, return as IS otherwise convert if data type indicates that's possible Arguments: KeyHandle - Key to query value in ValueName - name of value to query Value - caller allocated, filled with returned DWORD value Return Value: NO_ERROR iff success --*/ { DWORD Err; DWORD DataType; DWORD DataSize; PTSTR Data; Err = QueryRegistryValue(KeyHandle,ValueName,&Data,&DataType,&DataSize); if(Err != NO_ERROR) { *Value = 0; return Err; } switch (DataType) { case REG_DWORD: if(DataSize != sizeof(DWORD)) { MyFree(Data); *Value = 0; return ERROR_INVALID_DATA; } *Value = *(PDWORD)Data; break; case REG_SZ: case REG_EXPAND_SZ: case REG_MULTI_SZ: *Value = (DWORD)_tcstoul(Data,NULL,0); break; default: *Value = 0; break; } MyFree(Data); return NO_ERROR; } #if MEM_DBG DWORD TrackedQueryDeviceRegistryProperty( IN TRACK_ARG_DECLARE TRACK_ARG_COMMA IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData, IN DWORD Property, OUT PTSTR *Value, OUT PDWORD DataType, OUT PDWORD DataSizeBytes ) { DWORD d; TRACK_PUSH // defined again below #undef QueryDeviceRegistryProperty d = QueryDeviceRegistryProperty ( DeviceInfoSet, DeviceInfoData, Property, Value, DataType, DataSizeBytes ); TRACK_POP return d; } #endif DWORD QueryDeviceRegistryProperty( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData, IN DWORD Property, OUT PTSTR *Value, OUT PDWORD DataType, OUT PDWORD DataSizeBytes ) /*++ Routine Description: Return an allocated buffer holding a copy of device registry property Buffer is padded with two extra NULL's so that caller never has to worry about unterminated strings. Caller does have to worry about size of fixed-size data Arguments: DeviceInfoSet/DeviceInfoData/Property passed to SetupDiGetDeviceRegistryProperty Value - returned pointer containing value, release with MyFree DataType - type of returned data DataSizeBytes - size of returned data in bytes (not TCHAR's!!!) Return Value: NO_ERROR iff success --*/ { DWORD Err; DWORD sz; sz = 0; Err = SetupDiGetDeviceRegistryProperty(DeviceInfoSet, DeviceInfoData, Property, DataType, NULL, 0, &sz) ? NO_ERROR : GetLastError(); *DataSizeBytes = sz; if((Err != NO_ERROR) && (Err != ERROR_INSUFFICIENT_BUFFER)) { return Err; } // // If the size of the value entry is 0 bytes, then return success, but with // Value set to NULL. // if(!sz) { *Value = NULL; return NO_ERROR; } sz += sizeof(TCHAR)*2; // always pad the buffer with extra zero's *Value = MyMalloc(sz); if(*Value == NULL) { return(ERROR_NOT_ENOUGH_MEMORY); } Err = SetupDiGetDeviceRegistryProperty(DeviceInfoSet, DeviceInfoData, Property, DataType, (PVOID)*Value, *DataSizeBytes, DataSizeBytes) ? NO_ERROR : GetLastError(); if(Err != NO_ERROR) { MyFree(*Value); } else { // // write 2 NULL chars to end of buffer // ZeroMemory(((LPBYTE)*Value)+*DataSizeBytes,sizeof(TCHAR)*2); } return Err; } #if MEM_DBG #define QueryDeviceRegistryProperty(a,b,c,d,e,f) TrackedQueryDeviceRegistryProperty(TRACK_ARG_CALL,a,b,c,d,e,f) #endif DWORD pSetupQueryMultiSzValueToArray( IN HKEY Root, IN PCTSTR Subkey, IN PCTSTR ValueName, OUT PTSTR **Array, OUT PUINT StringCount, IN BOOL FailIfDoesntExist ) { DWORD d; HKEY hKey; DWORD DataType; DWORD DataSizeBytes; PTSTR Value; DWORD DataSizeChars; INT Count,i; PTSTR *array; PTSTR p; // // Open the subkey // d = RegOpenKeyEx(Root,Subkey,0,KEY_READ,&hKey); if((d != NO_ERROR) && FailIfDoesntExist) { return(d); } if(d != NO_ERROR) { Value = MyMalloc(sizeof(TCHAR)); if(!Value) { return(ERROR_NOT_ENOUGH_MEMORY); } *Value = 0; DataSizeChars = 1; Count = 0; } else { // // Query the value and close the subkey. // If the data is not multisz type, we don't know what to // do with it here. // note that QueryRegistryValue ensures that the string is // always correctly double-NULL terminated // d = QueryRegistryValue(hKey,ValueName,&Value,&DataType,&DataSizeBytes); RegCloseKey(hKey); if(d != NO_ERROR) { if(FailIfDoesntExist) { return(d); } } else if(!DataSizeBytes) { // // Value entry was zero bytes in length--that's OK as long as the // datatype is right. // if(DataType != REG_MULTI_SZ) { return(ERROR_INVALID_DATA); } } if((d != NO_ERROR) || !DataSizeBytes) { Value = MyMalloc(sizeof(TCHAR)); if(!Value) { return(ERROR_NOT_ENOUGH_MEMORY); } *Value = 0; DataSizeChars = 1; Count = 0; } else { if(DataType != REG_MULTI_SZ) { MyFree(Value); return(ERROR_INVALID_DATA); } DataSizeChars = DataSizeBytes/sizeof(TCHAR); for(i=0,p=Value; p[0]; i++,p+=lstrlen(p)+1) { // // this will always be ok as QueryRegistryValue // appends two NULLS onto end of string // MYASSERT((DWORD)(p-Value) < DataSizeChars); } Count = i; } } // // Allocate an array to hold the pointers (never allocate a zero-length array!) // if(!(array = MyMalloc(Count ? (Count * sizeof(PTSTR)) : sizeof(PTSTR)))) { MyFree(Value); return(ERROR_INVALID_DATA); } // // Walk through the multi sz and build the string array. // for(i=0,p=Value; p[0]; i++,p+=lstrlen(p)+1) { MYASSERT(iUserRootKey - supplies handle to open registry key. The key must have KEY_SET_VALUE access. SubKeyName - if specified, supplies the name of a subkey of Key where the value is to be stored. If not specified or if "" then the value is stored in Key. If supplied and the key doesn't exist, the key is created. RegContext->DevInst - Optionally, supplies a DEVINST handle for the device instance corresponding to the hardware storage key specified by 'Key'. If this handle is specified, and if SubKeyName is not specified, then the value name being appended will be checked to see whether it is the name of a device registry property. If so, then CM APIs will be used to modify the the corresponding registry property, since the Key handle represents a separate location under Windows NT. ValueName - supplies the value entry name of the multi_sz. If not specified or "" then the unnamed entry is used. If the value entry does not exist it is created. String - supplies the string to be added in to the multi_sz. Must not be an empty string. AllowDuplicates - if TRUE, then the string is simply appended to the multi_sz. Otherwise the string is only appended if no instance of it currently exists in the multi_sz. RegContext - Passed in from _SetupInstallFromInfSection Flags - Flags that may have been got from the INF and passed to us Return Value: Handle to setup file queue. INVALID_HANDLE_VALUE if insufficient memory to create the queue. --*/ { DWORD d; DWORD Disposition; HKEY hKey; PTSTR *Array; PVOID p; BOOL Append; UINT StringCount; UINT i; BOOL IsDevRegProp = FALSE; BOOL IsClassRegProp = FALSE; UINT_PTR CmPropertyCode; MYASSERT(RegContext); // // Empty strings really mess up a multi_sz. // if(!String || !(*String)) { return(ERROR_INVALID_PARAMETER); } // // Open/create the key. // if(SubKeyName && *SubKeyName) { d = RegCreateKeyEx( RegContext->UserRootKey, SubKeyName, 0, NULL, REG_OPTION_NON_VOLATILE, #ifdef _WIN64 (( Flags & FLG_ADDREG_32BITKEY ) ? KEY_WOW64_32KEY:0) | #else (( Flags & FLG_ADDREG_64BITKEY ) ? KEY_WOW64_64KEY:0) | #endif KEY_SET_VALUE, NULL, &hKey, &Disposition ); if(d != NO_ERROR) { return(d); } } else { // // If DevInst was specified, then determine whether the specified value is a Plug&Play // device registry property. // if (ValueName && *ValueName) { if((RegContext->Flags & INF_PFLAG_CLASSPROP) && (IsClassRegProp = LookUpStringInTable(InfRegValToClassRegProp, ValueName, &CmPropertyCode))) { // // This value is a class registry property. Retrieve the current property's data, and // format it into the same string array as returned by the pSetupQueryMultiSzValueToArray call // below. // //d = QueryMultiSzClassRegPropToArray(RegModContext->ClassGuid, CmPropertyCode, &Array, &StringCount); // // No class properties have MultiSz characteristics, so not implemented // d = ERROR_INVALID_DATA; } else if((RegContext->Flags & INF_PFLAG_DEVPROP) && (IsDevRegProp = LookUpStringInTable(InfRegValToDevRegProp, ValueName, &CmPropertyCode))) { // // This value is a device registry property. Retrieve the current property's data, and // format it into the same string array as returned by the pSetupQueryMultiSzValueToArray call // below. // d = QueryMultiSzDevRegPropToArray(RegContext->DevInst, (ULONG)CmPropertyCode, &Array, &StringCount); } } hKey = RegContext->UserRootKey; } if(!IsDevRegProp && !IsClassRegProp) { // // Query the existing registry value. // d = pSetupQueryMultiSzValueToArray(hKey,NULL,ValueName,&Array,&StringCount,FALSE); } if(d == NO_ERROR) { // // Determine whether to append or replace. // If replacing, we don't need to do anything! // Append = TRUE; if(!AllowDuplicates) { for(i=0; iDevInst, (ULONG)CmPropertyCode, Array, StringCount); } else if(IsClassRegProp) { // // not implemented yet, and should return an error before getting here // MYASSERT(IsClassRegProp == FALSE); } else { d = pSetupSetArrayToMultiSzValue(hKey,NULL,ValueName,Array,StringCount); } } pSetupFreeStringArray(Array,StringCount); } if(hKey != RegContext->UserRootKey) { RegCloseKey(hKey); } return(d); } DWORD _DeleteStringFromMultiSz( IN PCTSTR SubKeyName, OPTIONAL IN PCTSTR ValueName, OPTIONAL IN PCTSTR String, IN UINT Flags, IN PREGMOD_CONTEXT RegContext OPTIONAL ) /*++ Routine Description: Delete a string value from a multi_sz. Arguments: RegContext->UserRootKey - supplies handle to open registry key. The key must have KEY_SET_VALUE access. SubKeyName - if specified, supplies the name of a subkey of Key where the value is to be stored. If not specified or if "" then the value is stored in Key. RegContext->DevInst - Optionally, supplies a DEVINST handle for the device instance corresponding to the hardware storage key specified by 'Key'. If this handle is specified, and if SubKeyName is not specified, then the value name being appended will be checked to see whether it is the name of a device registry property. If so, then CM APIs will be used to modify the the corresponding registry property, since the Key handle represents a separate location under Windows NT. ValueName - supplies the value entry name of the multi_sz. If not specified or "" then the unnamed entry is used. String - supplies the string to be added in to the multi_sz. Must not be an empty string. Flags - indicates what kind of delete operation FLG_DELREG_MULTI_SZ_DELSTRING - delete all occurances of string RegContext - Passed in from _SetupInstallFromInfSection Return Value: Handle to setup file queue. INVALID_HANDLE_VALUE if insufficient memory to create the queue. --*/ { DWORD d; DWORD Disposition; HKEY hKey; PTSTR *Array; PVOID p; UINT StringCount; UINT i; BOOL IsDevRegProp = FALSE; BOOL IsClassRegProp = FALSE; BOOL Modified = FALSE; UINT_PTR CmPropertyCode; MYASSERT(RegContext); // // Can't delete an empty string from multi-sz // if(!String || !(*String)) { return(ERROR_INVALID_PARAMETER); } // // Open the key. // if(SubKeyName && *SubKeyName) { d = RegOpenKeyEx( RegContext->UserRootKey, SubKeyName, 0, #ifdef _WIN64 ((Flags & FLG_DELREG_32BITKEY) ? KEY_WOW64_32KEY:0) | #else ((Flags & FLG_DELREG_64BITKEY) ? KEY_WOW64_64KEY:0) | #endif KEY_SET_VALUE | KEY_QUERY_VALUE, &hKey ); if(d != NO_ERROR) { return(d); } } else { if (ValueName && *ValueName) { // // If DevInst was specified, then determine whether the specified value is a Plug&Play // device registry property. // if((RegContext->Flags & INF_PFLAG_CLASSPROP) && (IsClassRegProp = LookUpStringInTable(InfRegValToClassRegProp, ValueName, &CmPropertyCode))) { // // This value is a class registry property. Retrieve the current property's data, and // format it into the same string array as returned by the pSetupQueryMultiSzValueToArray call // below. // //d = QueryMultiSzClassRegPropToArray(RegModContext->ClassGuid, CmPropertyCode, &Array, &StringCount); // // No class properties have MultiSz characteristics, so not implemented // d = ERROR_INVALID_DATA; } else if((RegContext->Flags & INF_PFLAG_DEVPROP) && (IsDevRegProp = LookUpStringInTable(InfRegValToDevRegProp, ValueName, &CmPropertyCode))) { // // This value is a device registry property. Retrieve the current property's data, and // format it into the same string array as returned by the pSetupQueryMultiSzValueToArray call // below. // fails if not multi-sz // d = QueryMultiSzDevRegPropToArray(RegContext->DevInst, (ULONG)CmPropertyCode, &Array, &StringCount); } } hKey = RegContext->UserRootKey; } if(!IsDevRegProp && !IsClassRegProp) { // // Query the existing registry value. // fails if not multi-sz // d = pSetupQueryMultiSzValueToArray(hKey,NULL,ValueName,&Array,&StringCount,FALSE); } if(d == NO_ERROR) { switch (Flags) { case FLG_DELREG_32BITKEY | FLG_DELREG_MULTI_SZ_DELSTRING: case FLG_DELREG_64BITKEY | FLG_DELREG_MULTI_SZ_DELSTRING: case FLG_DELREG_MULTI_SZ_DELSTRING: for(i=0; iDevInst, (ULONG)CmPropertyCode, Array, StringCount); } else if(IsClassRegProp) { // // not implemented yet, and should return an error before getting here // MYASSERT(IsClassRegProp == FALSE); } else { d = pSetupSetArrayToMultiSzValue(hKey,NULL,ValueName,Array,StringCount); } } pSetupFreeStringArray(Array,StringCount); } if(hKey != RegContext->UserRootKey) { RegCloseKey(hKey); } return(d); } VOID pSetupFreeStringArray( IN PTSTR *Array, IN UINT StringCount ) { UINT i; for(i=0; i