context: //
  // Microsoft.Win32/Win32RegistryApi.cs: wrapper for win32 registry API
  //
  // Authos:
  //	Erik LeBel (eriklebel@yahoo.ca)
  //      Jackson Harper (jackson@ximian.com)
  //      Miguel de Icaza (miguel@gnome.org)
  //
  // Copyright (C) Erik LeBel 2004
  // (C) 2004, 2005 Novell, Inc (http://www.novell.com)
  // 
  
  //
  // Copyright (C) 2004, 2005 Novell, Inc (http://www.novell.com)
  //
  // 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.
  //
  
  #if !NET_2_1
  
  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.IO;
  using System.Runtime.InteropServices;
  using System.Security;
  using System.Text;
  using Microsoft.Win32.SafeHandles;
  
  namespace Microsoft.Win32
  {
  	/// <summary>
  	///	Function stubs, constants and helper functions for
  	///	the Win32 registry manipulation utilities.
  	/// </summary>
  	internal class Win32RegistryApi : IRegistryApi
  	{
  		// bit masks for registry key open access permissions
  		const int OpenRegKeyRead = 0x00020019; 
  		const int OpenRegKeyWrite = 0x00020006; 
  
  		// FIXME must be a way to determin this dynamically?
  		const int Int32ByteSize = 4;
  		const int Int64ByteSize = 8;
  
  		// FIXME this is hard coded on Mono, can it be determined dynamically? 
  		readonly int NativeBytesPerCharacter = Marshal.SystemDefaultCharSize;
  
  		const int RegOptionsNonVolatile = 0x00000000;
  		const int RegOptionsVolatile = 0x00000001;
  
  		[DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegCreateKeyEx")]
  		static extern int RegCreateKeyEx (IntPtr keyBase, string keyName, int reserved, 
  			IntPtr lpClass, int options, int access, IntPtr securityAttrs,
  			out IntPtr keyHandle, out int disposition);
  	       
  		[DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegCloseKey")]
  		static extern int RegCloseKey (IntPtr keyHandle);
  
  		[DllImport ("advapi32.dll", CharSet=CharSet.Unicode)]
  		static extern int RegConnectRegistry (string machineName, IntPtr hKey,
  				out IntPtr keyHandle);
  
  		[DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegFlushKey")]
  		private static extern int RegFlushKey (IntPtr keyHandle);
  
  		[DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegOpenKeyEx")]
  		private static extern int RegOpenKeyEx (IntPtr keyBase,
  				string keyName, IntPtr reserved, int access,
  				out IntPtr keyHandle);
  
  		[DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegDeleteKey")]
  		private static extern int RegDeleteKey (IntPtr keyHandle, string valueName);
  
  		[DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegDeleteValue")]
  		private static extern int RegDeleteValue (IntPtr keyHandle, string valueName);
  
  		[DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegEnumKey")]
  		private static extern int RegEnumKey (IntPtr keyBase, int index, StringBuilder nameBuffer, int bufferLength);
  
  		[DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegEnumValue")]
  		private static extern int RegEnumValue (IntPtr keyBase, 
  				int index, StringBuilder nameBuffer, 
  				ref int nameLength, IntPtr reserved, 
  				ref RegistryValueKind type, IntPtr data, IntPtr dataLength);
  
  //		[DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
  //		private static extern int RegSetValueEx (IntPtr keyBase, 
  //				string valueName, IntPtr reserved, RegistryValueKind type,
  //				StringBuilder data, int rawDataLength);
  
  		[DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
  		private static extern int RegSetValueEx (IntPtr keyBase, 
  				string valueName, IntPtr reserved, RegistryValueKind type,
  				string data, int rawDataLength);
  
  		[DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
  		private static extern int RegSetValueEx (IntPtr keyBase, 
  				string valueName, IntPtr reserved, RegistryValueKind type,
  				byte[] rawData, int rawDataLength);
  
  		[DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
  		private static extern int RegSetValueEx (IntPtr keyBase, 
  				string valueName, IntPtr reserved, RegistryValueKind type,
  				ref int data, int rawDataLength);
  
  		[DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
  		private static extern int RegSetValueEx (IntPtr keyBase, 
  				string valueName, IntPtr reserved, RegistryValueKind type,
  				ref long data, int rawDataLength);
  
  		[DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueEx")]
  		private static extern int RegQueryValueEx (IntPtr keyBase,
  				string valueName, IntPtr reserved, ref RegistryValueKind type,
  				IntPtr zero, ref int dataSize);
  
  		[DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueEx")]
  		private static extern int RegQueryValueEx (IntPtr keyBase,
  				string valueName, IntPtr reserved, ref RegistryValueKind type,
  				[Out] byte[] data, ref int dataSize);
  
  		[DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueEx")]
  		private static extern int RegQueryValueEx (IntPtr keyBase,
  				string valueName, IntPtr reserved, ref RegistryValueKind type,
  				ref int data, ref int dataSize);
  
  		[DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueEx")]
  		private static extern int RegQueryValueEx (IntPtr keyBase,
  				string valueName, IntPtr reserved, ref RegistryValueKind type,
  				ref long data, ref int dataSize);
  
  		// Returns our handle from the RegistryKey
  		public IntPtr GetHandle (RegistryKey key)
  		{
  			return (IntPtr) key.InternalHandle;
  		}
  
  		static bool IsHandleValid (RegistryKey key)
  		{
  			return key.InternalHandle != null;
  		}
  
  		public RegistryValueKind GetValueKind (RegistryKey rkey, string name)
  		{
  			RegistryValueKind type = 0;
  			int size = 0;
  			IntPtr handle = GetHandle (rkey);
  			int result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, IntPtr.Zero, ref size);
  
  			if (result == Win32ResultCode.FileNotFound || result == Win32ResultCode.MarkedForDeletion) 
  				return RegistryValueKind.Unknown;
  
  			return type;
  		}
  		
  		/// <summary>
  		/// Acctually read a registry value. Requires knowledge of the
  		/// value's type and size.
  		/// </summary>
  		public object GetValue (RegistryKey rkey, string name, object defaultValue, RegistryValueOptions options)
  		{
  			RegistryValueKind type = 0;
  			int size = 0;
  			object obj = null;
  			IntPtr handle = GetHandle (rkey);
  			int result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, IntPtr.Zero, ref size);
  
  			if (result == Win32ResultCode.FileNotFound || result == Win32ResultCode.MarkedForDeletion) {
  				return defaultValue;
  			}
  			
  			if (result != Win32ResultCode.MoreData && result != Win32ResultCode.Success ) {
  				GenerateException (result);
  			}
  			
  			if (type == RegistryValueKind.String) {
  				byte[] data;
  				result = GetBinaryValue (rkey, name, type, out data, size);
  				obj = RegistryKey.DecodeString (data);
  			} else if (type == RegistryValueKind.ExpandString) {
  				byte [] data;
  				result = GetBinaryValue (rkey, name, type, out data, size);
  				obj = RegistryKey.DecodeString (data);
  				if ((options & RegistryValueOptions.DoNotExpandEnvironmentNames) == 0)
  					obj = Environment.ExpandEnvironmentVariables ((string) obj);
  			} else if (type == RegistryValueKind.DWord) {
  				int data = 0;
  				result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, ref data, ref size);
  				obj = data;
  			} else if (type == RegistryValueKind.QWord) {
  				long data = 0;
  				result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, ref data, ref size);
  				obj = data;
  			} else if (type == RegistryValueKind.Binary) {
  				byte[] data;
  				result = GetBinaryValue (rkey, name, type, out data, size);
  				obj = data;
  			} else if (type == RegistryValueKind.MultiString) {
  				obj = null;
  				byte[] data;
  				result = GetBinaryValue (rkey, name, type, out data, size);
  				
  				if (result == Win32ResultCode.Success)
  					obj = RegistryKey.DecodeString (data).Split ('\0');
  			} else {
  				// should never get here
  				throw new SystemException ();
  			}
  
  			// check result codes again:
  			if (result != Win32ResultCode.Success)
  			{
  				GenerateException (result);
  			}
  			
  
  			return obj;
  		}
  
  		//
  		// This version has to do extra checking, make sure that the requested
  		// valueKind matches the type of the value being stored
  		//
  		public void SetValue (RegistryKey rkey, string name, object value, RegistryValueKind valueKind)
  		{
  			Type type = value.GetType ();
  			int result;
  			IntPtr handle = GetHandle (rkey);
  
  			if (valueKind == RegistryValueKind.QWord && type == typeof (long)) {
  				long rawValue = (long)value;
  				result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.QWord, ref rawValue, Int64ByteSize); 
  			} else if (valueKind == RegistryValueKind.DWord && type == typeof (int)) {
  				int rawValue = (int)value;
  				result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.DWord, ref rawValue, Int32ByteSize); 
  			} else if (valueKind == RegistryValueKind.Binary && type == typeof (byte[])) {
  				byte[] rawValue = (byte[]) value;
  				result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.Binary, rawValue, rawValue.Length);
  			} else if (valueKind == RegistryValueKind.MultiString && type == typeof (string[])) {
  				string[] vals = (string[]) value;
  				StringBuilder fullStringValue = new StringBuilder ();
  				foreach (string v in vals)
  				{
  					fullStringValue.Append (v);
  					fullStringValue.Append ('\0');
  				}
  				fullStringValue.Append ('\0');
  
  				byte[] rawValue = Encoding.Unicode.GetBytes (fullStringValue.ToString ());
  			
  				result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.MultiString, rawValue, rawValue.Length); 
  			} else if ((valueKind == RegistryValueKind.String || valueKind == RegistryValueKind.ExpandString) &&
  				   type == typeof (string)){
  				string rawValue = String.Format ("{0}{1}", value, '\0');
  				result = RegSetValueEx (handle, name, IntPtr.Zero, valueKind, rawValue,
  							rawValue.Length * NativeBytesPerCharacter);
  				
  			} else if (type.IsArray) {
  				throw new ArgumentException ("Only string and byte arrays can written as registry values");
  			} else {
  				throw new ArgumentException ("Type does not match the valueKind");
  			}
  
  			// handle the result codes
  			if (result != Win32ResultCode.Success)
  			{
  				GenerateException (result);
  			}
  		}
  	
  		public void SetValue (RegistryKey rkey, string name, object value)
  		{
  			Type type = value.GetType ();
  			int result;
  			IntPtr handle = GetHandle (rkey);
  
  			if (type == typeof (int)) {
  				int rawValue = (int)value;
  				result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.DWord, ref rawValue, Int32ByteSize); 
  			} else if (type == typeof (byte[])) {
  				byte[] rawValue = (byte[]) value;
  				result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.Binary, rawValue, rawValue.Length);
  			} else if (type == typeof (string[])) {
  				string[] vals = (string[]) value;
  				StringBuilder fullStringValue = new StringBuilder ();
  				foreach (string v in vals)
  				{
  					fullStringValue.Append (v);
  					fullStringValue.Append ('\0');
  				}
  				fullStringValue.Append ('\0');
  
  				byte[] rawValue = Encoding.Unicode.GetBytes (fullStringValue.ToString ());
  			
  				result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.MultiString, rawValue, rawValue.Length); 
  			} else if (type.IsArray) {
  				throw new ArgumentException ("Only string and byte arrays can written as registry values");
  			} else {
  				string rawValue = String.Format ("{0}{1}", value, '\0');
  				result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.String, rawValue,
  							rawValue.Length * NativeBytesPerCharacter);
  			}
  
  			if (result == Win32ResultCode.MarkedForDeletion)
  				throw RegistryKey.CreateMarkedForDeletionException ();
  
  			// handle the result codes
  			if (result != Win32ResultCode.Success)
  			{
  				GenerateException (result);
  			}
  		}
  
  		/// <summary>
  		///	Get a binary value.
  		/// </summary>
  		private int GetBinaryValue (RegistryKey rkey, string name, RegistryValueKind type, out byte[] data, int size)
  		{
  			byte[] internalData = new byte [size];
  			IntPtr handle = GetHandle (rkey);
  			int result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, internalData, ref size);
  			data = internalData;
  			return result;
  		}
  
  		
  		// Arbitrary max size for key/values names that can be fetched.
  		// .NET framework SDK docs say that the max name length that can 
  		// be used is 255 characters, we'll allow for a bit more.
  		const int BufferMaxLength = 1024;
  		
  		public int SubKeyCount (RegistryKey rkey)
  		{
  			int index;
  			StringBuilder stringBuffer = new StringBuilder (BufferMaxLength);
  			IntPtr handle = GetHandle (rkey);
  			
  			for (index = 0; true; index ++) {
  				int result = RegEnumKey (handle, index, stringBuffer,
  					stringBuffer.Capacity);
  
  				if (result == Win32ResultCode.MarkedForDeletion)
  					throw RegistryKey.CreateMarkedForDeletionException ();
  
  				if (result == Win32ResultCode.Success)
  					continue;
  				
  				if (result == Win32ResultCode.NoMoreEntries)
  					break;
  				
  				// something is wrong!!
  				GenerateException (result);
  			}
  			return index;
  		}
  
  		public int ValueCount (RegistryKey rkey)
  		{
  			int index, result, bufferCapacity;
  			RegistryValueKind type;
  			StringBuilder buffer = new StringBuilder (BufferMaxLength);
  			
  			IntPtr handle = GetHandle (rkey);
  			for (index = 0; true; index ++) {
  				type = 0;
  				bufferCapacity = buffer.Capacity;
  				result = RegEnumValue (handle, index, 
  						       buffer, ref bufferCapacity,
  						       IntPtr.Zero, ref type, 
  						       IntPtr.Zero, IntPtr.Zero);
  
  				if (result == Win32ResultCode.MarkedForDeletion)
  					throw RegistryKey.CreateMarkedForDeletionException ();
  
  				if (result == Win32ResultCode.Success || result == Win32ResultCode.MoreData)
  					continue;
  				
  				if (result == Win32ResultCode.NoMoreEntries)
  					break;
  				
  				// something is wrong
  				GenerateException (result);
  			}
  			return index;
  		}
  
  		public RegistryKey OpenRemoteBaseKey (RegistryHive hKey, string machineName)
  		{
  			IntPtr handle = new IntPtr ((int) hKey);
  
  			IntPtr keyHandle;
  			int result = RegConnectRegistry (machineName, handle, out keyHandle);
  			if (result != Win32ResultCode.Success)
  				GenerateException (result);
  
  			return new RegistryKey (hKey, keyHandle, true);
  		}
  
  		public RegistryKey OpenSubKey (RegistryKey rkey, string keyName, bool writable)
  		{
  			int access = OpenRegKeyRead;
  			if (writable) access |= OpenRegKeyWrite;
  			IntPtr handle = GetHandle (rkey);
  			
  			IntPtr subKeyHandle;
  			int result = RegOpenKeyEx (handle, keyName, IntPtr.Zero, access, out subKeyHandle);
  
  			if (result == Win32ResultCode.FileNotFound || result == Win32ResultCode.MarkedForDeletion)
  				return null;
  			
  			if (result != Win32ResultCode.Success)
  				GenerateException (result);
  			
  			return new RegistryKey (subKeyHandle, CombineName (rkey, keyName), writable);
  		}
  
  		public void Flush (RegistryKey rkey)
  		{
  			if (!IsHandleValid (rkey))
  				return;
  			IntPtr handle = GetHandle (rkey);
  			RegFlushKey (handle);
  		}
  
  		public void Close (RegistryKey rkey)
  		{
  			if (!IsHandleValid (rkey))
  				return;
  #if NET_4_0
  			SafeRegistryHandle safe_handle = rkey.Handle;
  			if (safe_handle != null) {
  				// closes the unmanaged pointer for us.
  				safe_handle.Close ();
  				return;
  			}
  #endif
  			IntPtr handle = GetHandle (rkey);
  			RegCloseKey (handle);
  		}
  
  #if NET_4_0
  		public RegistryKey FromHandle (SafeRegistryHandle handle)
  		{
  			// At this point we can't tell whether the key is writable
  			// or not (nor the name), so we let the error check code handle it later, as
  			// .Net seems to do.
  			return new RegistryKey (handle.DangerousGetHandle (), String.Empty, true);
  		}
  #endif
  
  		public RegistryKey CreateSubKey (RegistryKey rkey, string keyName)
  		{
  			IntPtr handle = GetHandle (rkey);
  			IntPtr subKeyHandle;
  			int disposition;
  			int result = RegCreateKeyEx (handle , keyName, 0, IntPtr.Zero,
  				RegOptionsNonVolatile,
  				OpenRegKeyRead | OpenRegKeyWrite, IntPtr.Zero, out subKeyHandle, out disposition);
  
  			if (result == Win32ResultCode.MarkedForDeletion)
  				throw RegistryKey.CreateMarkedForDeletionException ();
  
  
gt: 			if (result != Win32ResultCode.Success) {