private object InternalGetValueCore(string name, object defaultValue, bool doNotExpand)
{
object data = defaultValue;
int type = 0;
int datasize = 0;
int ret = Interop.Advapi32.RegQueryValueEx(_hkey, name, null, ref type, (byte[])null, ref datasize);
if (ret != 0)
{
if (IsPerfDataKey())
{
int size = 65000;
int sizeInput = size;
int r;
byte[] blob = new byte[size];
while (Interop.Errors.ERROR_MORE_DATA == (r = Interop.Advapi32.RegQueryValueEx(_hkey, name, null, ref type, blob, ref sizeInput)))
{
if (size == Int32.MaxValue)
{
// ERROR_MORE_DATA was returned however we cannot increase the buffer size beyond Int32.MaxValue
Win32Error(r, name);
}
else if (size > (Int32.MaxValue / 2))
{
// at this point in the loop "size * 2" would cause an overflow
size = Int32.MaxValue;
}
else
{
size *= 2;
}
sizeInput = size;
blob = new byte[size];
}
if (r != 0)
{
Win32Error(r, name);
}
return blob;
}
else
{
// For stuff like ERROR_FILE_NOT_FOUND, we want to return null (data).
// Some OS's returned ERROR_MORE_DATA even in success cases, so we
// want to continue on through the function.
if (ret != Interop.Errors.ERROR_MORE_DATA)
{
return data;
}
}
}
if (datasize < 0)
{
// unexpected code path
Debug.Fail("[InternalGetValue] RegQueryValue returned ERROR_SUCCESS but gave a negative datasize");
datasize = 0;
}
switch (type)
{
case Interop.Advapi32.RegistryValues.REG_NONE:
case Interop.Advapi32.RegistryValues.REG_DWORD_BIG_ENDIAN:
case Interop.Advapi32.RegistryValues.REG_BINARY:
{
byte[] blob = new byte[datasize];
ret = Interop.Advapi32.RegQueryValueEx(_hkey, name, null, ref type, blob, ref datasize);
data = blob;
}
break;
case Interop.Advapi32.RegistryValues.REG_QWORD:
{ // also REG_QWORD_LITTLE_ENDIAN
if (datasize > 8)
{
// prevent an AV in the edge case that datasize is larger than sizeof(long)
goto case Interop.Advapi32.RegistryValues.REG_BINARY;
}
long blob = 0;
Debug.Assert(datasize == 8, "datasize==8");
// Here, datasize must be 8 when calling this
ret = Interop.Advapi32.RegQueryValueEx(_hkey, name, null, ref type, ref blob, ref datasize);
data = blob;
}
break;
case Interop.Advapi32.RegistryValues.REG_DWORD:
{ // also REG_DWORD_LITTLE_ENDIAN
if (datasize > 4)
{
// prevent an AV in the edge case that datasize is larger than sizeof(int)
goto case Interop.Advapi32.RegistryValues.REG_QWORD;
}
int blob = 0;
Debug.Assert(datasize == 4, "datasize==4");
// Here, datasize must be four when calling this
ret = Interop.Advapi32.RegQueryValueEx(_hkey, name, null, ref type, ref blob, ref datasize);
data = blob;
}
break;
case Interop.Advapi32.RegistryValues.REG_SZ:
{
if (datasize % 2 == 1)
{
// handle the case where the registry contains an odd-byte length (corrupt data?)
try
{
datasize = checked(datasize + 1);
}
catch (OverflowException e)
{
throw new IOException(SR.Arg_RegGetOverflowBug, e);
}
}
char[] blob = new char[datasize / 2];
ret = Interop.Advapi32.RegQueryValueEx(_hkey, name, null, ref type, blob, ref datasize);
if (blob.Length > 0 && blob[blob.Length - 1] == (char)0)
{
data = new string(blob, 0, blob.Length - 1);
}
else
{
// in the very unlikely case the data is missing null termination,
// pass in the whole char[] to prevent truncating a character
data = new string(blob);
}
}
break;
case Interop.Advapi32.RegistryValues.REG_EXPAND_SZ:
{
if (datasize % 2 == 1)
{
// handle the case where the registry contains an odd-byte length (corrupt data?)
try
{
datasize = checked(datasize + 1);
}
catch (OverflowException e)
{
throw new IOException(SR.Arg_RegGetOverflowBug, e);
}
}
char[] blob = new char[datasize / 2];
ret = Interop.Advapi32.RegQueryValueEx(_hkey, name, null, ref type, blob, ref datasize);
if (blob.Length > 0 && blob[blob.Length - 1] == (char)0)
{
data = new string(blob, 0, blob.Length - 1);
}
else
{
// in the very unlikely case the data is missing null termination,
// pass in the whole char[] to prevent truncating a character
data = new string(blob);
}
if (!doNotExpand)
{
data = Environment.ExpandEnvironmentVariables((string)data);
}
}
break;
case Interop.Advapi32.RegistryValues.REG_MULTI_SZ:
{
if (datasize % 2 == 1)
{
// handle the case where the registry contains an odd-byte length (corrupt data?)
try
{
datasize = checked(datasize + 1);
}
catch (OverflowException e)
{
throw new IOException(SR.Arg_RegGetOverflowBug, e);
}
}
char[] blob = new char[datasize / 2];
ret = Interop.Advapi32.RegQueryValueEx(_hkey, name, null, ref type, blob, ref datasize);
// make sure the string is null terminated before processing the data
if (blob.Length > 0 && blob[blob.Length - 1] != (char)0)
{
Array.Resize(ref blob, blob.Length + 1);
}
string[] strings = Array.Empty<string>();
int stringsCount = 0;
int cur = 0;
int len = blob.Length;
while (ret == 0 && cur < len)
{
int nextNull = cur;
while (nextNull < len && blob[nextNull] != (char)0)
{
nextNull++;
}
string toAdd = null;
if (nextNull < len)
{
Debug.Assert(blob[nextNull] == (char)0, "blob[nextNull] should be 0");
if (nextNull - cur > 0)
{
toAdd = new string(blob, cur, nextNull - cur);
}
else
{
// we found an empty string. But if we're at the end of the data,
// it's just the extra null terminator.
if (nextNull != len - 1)
{
toAdd = string.Empty;
}
}
}
else
{
toAdd = new string(blob, cur, len - cur);
}
cur = nextNull + 1;
if (toAdd != null)
{
if (strings.Length == stringsCount)
{
Array.Resize(ref strings, stringsCount > 0 ? stringsCount * 2 : 4);
}
strings[stringsCount++] = toAdd;
}
}
Array.Resize(ref strings, stringsCount);
data = strings;
}
break;
case Interop.Advapi32.RegistryValues.REG_LINK:
default:
break;
}
return data;
}