// // Copyright (c) 2008-2011, Kenneth Bell // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // namespace DiscUtils.Registry { using System; using System.Globalization; using System.Text; /// /// A registry value. /// internal sealed class RegistryValue { private RegistryHive _hive; private ValueCell _cell; internal RegistryValue(RegistryHive hive, ValueCell cell) { _hive = hive; _cell = cell; } /// /// Gets the name of the value, or empty string if unnamed. /// public string Name { get { return _cell.Name ?? string.Empty; } } /// /// Gets the type of the value. /// public RegistryValueType DataType { get { return _cell.DataType; } } /// /// Gets the value data mapped to a .net object. /// /// The mapping from registry type of .NET type is as follows: /// /// /// Value Type /// .NET type /// /// /// String /// string /// /// /// ExpandString /// string /// /// /// Link /// string /// /// /// DWord /// uint /// /// /// DWordBigEndian /// uint /// /// /// MultiString /// string[] /// /// /// QWord /// ulong /// /// /// public object Value { get { return ConvertToObject(GetData(), DataType); } } /// /// The raw value data as a byte array. /// /// The value as a raw byte array. public byte[] GetData() { if (_cell.DataLength < 0) { int len = _cell.DataLength & 0x7FFFFFFF; byte[] buffer = new byte[4]; Utilities.WriteBytesLittleEndian(_cell.DataIndex, buffer, 0); byte[] result = new byte[len]; Array.Copy(buffer, result, len); return result; } return _hive.RawCellData(_cell.DataIndex, _cell.DataLength); } /// /// Sets the value as raw bytes, with no validation that enough data is specified for the given value type. /// /// The data to store. /// The offset within data of the first byte to store. /// The number of bytes to store. /// The type of the data. public void SetData(byte[] data, int offset, int count, RegistryValueType valueType) { // If we can place the data in the DataIndex field, do that to save space / allocation if ((valueType == RegistryValueType.Dword || valueType == RegistryValueType.DwordBigEndian) && count <= 4) { if (_cell.DataLength >= 0) { _hive.FreeCell(_cell.DataIndex); } _cell.DataLength = (int)((uint)count | 0x80000000); _cell.DataIndex = Utilities.ToInt32LittleEndian(data, offset); _cell.DataType = valueType; } else { if (_cell.DataIndex == -1 || _cell.DataLength < 0) { _cell.DataIndex = _hive.AllocateRawCell(count); } if (!_hive.WriteRawCellData(_cell.DataIndex, data, offset, count)) { int newDataIndex = _hive.AllocateRawCell(count); _hive.WriteRawCellData(newDataIndex, data, offset, count); _hive.FreeCell(_cell.DataIndex); _cell.DataIndex = newDataIndex; } _cell.DataLength = count; _cell.DataType = valueType; } _hive.UpdateCell(_cell, false); } /// /// Sets the value stored. /// /// The value to store. /// The registry type of the data. public void SetValue(object value, RegistryValueType valueType) { if (valueType == RegistryValueType.None) { if (value is int) { valueType = RegistryValueType.Dword; } else if (value is byte[]) { valueType = RegistryValueType.Binary; } else if (value is string[]) { valueType = RegistryValueType.MultiString; } else { valueType = RegistryValueType.String; } } byte[] data = ConvertToData(value, valueType); SetData(data, 0, data.Length, valueType); } /// /// Gets a string representation of the registry value. /// /// The registry value as a string. public override string ToString() { return Name + ":" + DataType + ":" + DataAsString(); } private static object ConvertToObject(byte[] data, RegistryValueType type) { switch (type) { case RegistryValueType.String: case RegistryValueType.ExpandString: case RegistryValueType.Link: return Encoding.Unicode.GetString(data).Trim('\0'); case RegistryValueType.Dword: return Utilities.ToInt32LittleEndian(data, 0); case RegistryValueType.DwordBigEndian: return Utilities.ToInt32BigEndian(data, 0); case RegistryValueType.MultiString: string multiString = Encoding.Unicode.GetString(data).Trim('\0'); return multiString.Split('\0'); case RegistryValueType.QWord: return string.Empty + Utilities.ToUInt64LittleEndian(data, 0); default: return data; } } private static byte[] ConvertToData(object value, RegistryValueType valueType) { if (valueType == RegistryValueType.None) { throw new ArgumentException("Specific registry value type must be specified", "valueType"); } byte[] data; switch (valueType) { case RegistryValueType.String: case RegistryValueType.ExpandString: string strValue = value.ToString(); data = new byte[(strValue.Length * 2) + 2]; Encoding.Unicode.GetBytes(strValue, 0, strValue.Length, data, 0); break; case RegistryValueType.Dword: data = new byte[4]; Utilities.WriteBytesLittleEndian((int)value, data, 0); break; case RegistryValueType.DwordBigEndian: data = new byte[4]; Utilities.WriteBytesBigEndian((int)value, data, 0); break; case RegistryValueType.MultiString: string multiStrValue = string.Join("\0", (string[])value) + "\0"; data = new byte[(multiStrValue.Length * 2) + 2]; Encoding.Unicode.GetBytes(multiStrValue, 0, multiStrValue.Length, data, 0); break; default: data = (byte[])value; break; } return data; } private string DataAsString() { switch (DataType) { case RegistryValueType.String: case RegistryValueType.ExpandString: case RegistryValueType.Link: case RegistryValueType.Dword: case RegistryValueType.DwordBigEndian: case RegistryValueType.QWord: return ConvertToObject(GetData(), DataType).ToString(); case RegistryValueType.MultiString: return string.Join(",", (string[])ConvertToObject(GetData(), DataType)); default: byte[] data = GetData(); string result = string.Empty; for (int i = 0; i < Math.Min(data.Length, 8); ++i) { result += string.Format(CultureInfo.InvariantCulture, "{0:X2} ", (int)data[i]); } return result + string.Format(CultureInfo.InvariantCulture, " ({0} bytes)", data.Length); } } } }