#region Notice
// http://www.rhyous.com/2011/01/24/how-read-the-64-bit-registry-from-a-32-bit-application-or-vice-versa/
// http://www.pinvoke.net/default.aspx/advapi32.regenumkeyex
#endregion

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

namespace WebWorks.Help.Registry64
{
    public enum RegSAM
    {
        QueryValue = 0x0001,
        SetValue = 0x0002,
        CreateSubKey = 0x0004,
        EnumerateSubKeys = 0x0008,
        Notify = 0x0010,
        CreateLink = 0x0020,
        WOW64_32Key = 0x0200,
        WOW64_64Key = 0x0100,
        WOW64_Res = 0x0300,
        Read = 0x00020019,
        Write = 0x00020006,
        Execute = 0x00020019,
        AllAccess = 0x000f003f
    }

    public static class RegHive
    {
        public static UIntPtr HKEY_CLASSES_ROOT = new UIntPtr(0x80000000u);
        public static UIntPtr HKEY_CURRENT_USER = new UIntPtr(0x80000001u);
        public static UIntPtr HKEY_LOCAL_MACHINE = new UIntPtr(0x80000002u);
    }

    public static class RegistryWOW6432
    {
        #region Member Variables
        #region Read 64bit Reg from 32bit app
        [DllImport("Advapi32.dll")]
        static extern uint RegOpenKeyEx(
            UIntPtr hKey,
            string lpSubKey,
            uint ulOptions,
            int samDesired,
            out uint phkResult);

        [DllImport("Advapi32.dll")]
        static extern uint RegCloseKey(uint hKey);

        [DllImport("advapi32.dll", EntryPoint = "RegQueryValueEx")]
        public static extern int RegQueryValueEx(
            uint hKey, string lpValueName,
            int lpReserved,
            ref uint lpType,
            System.Text.StringBuilder lpData,
            ref uint lpcbData);

        [DllImport("advapi32.dll", EntryPoint = "RegEnumKeyEx")]
        extern private static uint RegEnumKeyEx(
            UIntPtr hkey,
            uint index,
            StringBuilder lpName,
            ref uint lpcbName,
            IntPtr reserved,
            IntPtr lpClass,
            IntPtr lpcbClass,
            out long lpftLastWriteTime);
        #endregion
        #endregion

        #region Functions
        static private bool GetRegKeyExists32Or64(UIntPtr inHive, String inKeyName, RegSAM in32or64key)
        {
            bool result = false;
            uint hkey = 0;

            try
            {
                uint lResult = RegOpenKeyEx(inHive, inKeyName, 0, (int)RegSAM.QueryValue | (int)in32or64key, out hkey);
                result = (lResult == 0);
            }
            finally
            {
                if (0 != hkey) RegCloseKey(hkey);
            }

            return result;
        }

        static public bool GetRegKeyExists64(UIntPtr inHive, String inKeyName)
        {
            return GetRegKeyExists32Or64(inHive, inKeyName, RegSAM.WOW64_64Key);
        }

        static public bool GetRegKeyExists32(UIntPtr inHive, String inKeyName)
        {
            return GetRegKeyExists32Or64(inHive, inKeyName, RegSAM.WOW64_32Key);
        }

        static private string GetRegKeyValue32Or64(UIntPtr inHive, String inKeyName, RegSAM in32or64key, String inPropertyName)
        {
            string result = null;
            uint hkey = 0;

            try
            {
                uint lResult = RegOpenKeyEx(inHive, inKeyName, 0, (int)RegSAM.QueryValue | (int)in32or64key, out hkey);
                if (0 != lResult) return null;
                uint lpType = 0;
                uint lpcbData = 4096;
                StringBuilder buffer = new StringBuilder(4096);
                RegQueryValueEx(hkey, inPropertyName, 0, ref lpType, buffer, ref lpcbData);
                result = buffer.ToString();
            }
            finally
            {
                if (0 != hkey) RegCloseKey(hkey);
            }

            return result;
        }

        static public string GetRegKeyValue64(UIntPtr inHive, String inKeyName, String inPropertyName)
        {
            return GetRegKeyValue32Or64(inHive, inKeyName, RegSAM.WOW64_64Key, inPropertyName);
        }

        static public string GetRegKeyValue32(UIntPtr inHive, String inKeyName, String inPropertyName)
        {
            return GetRegKeyValue32Or64(inHive, inKeyName, RegSAM.WOW64_32Key, inPropertyName);
        }

        static private string[] GetSubKeyNames32Or64(UIntPtr inHive, String inKeyName, RegSAM in32or64key)
        {
            List<string> _subKeyNames = new List<string>();
            uint lResult;
            uint hkey;

            hkey = 0;
            try
            {
                lResult = RegOpenKeyEx(
                    inHive, inKeyName, 0, (int)RegSAM.Read | (int)in32or64key, out hkey
                );
                if (0 == lResult)
                {
                    UIntPtr _hkey = new UIntPtr(hkey);
                    uint _index;
                    uint _maxKeyNameLength;
                    StringBuilder _subKeyName;
                    long _lastWriteTime;

                    _index = 0;
                    _lastWriteTime = 0;
                    while (lResult == 0)
                    {
                        _maxKeyNameLength = 256;
                        _subKeyName = new StringBuilder((int)_maxKeyNameLength);
                        lResult = RegEnumKeyEx(
                            _hkey, _index, _subKeyName, ref _maxKeyNameLength,
                            IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, out _lastWriteTime
                        );
                        if (lResult == 0)
                        {
                            _subKeyNames.Add(_subKeyName.ToString());
                        }

                        _index += 1;
                    }
                }
            }
            finally
            {
                if (0 != hkey) RegCloseKey(hkey);
            }

            return _subKeyNames.ToArray();
        }

        static public string[] GetSubKeyNames64(UIntPtr inHive, String inKeyName)
        {
            return GetSubKeyNames32Or64(inHive, inKeyName, RegSAM.WOW64_64Key);
        }

        static public string[] GetSubKeyNames32(UIntPtr inHive, String inKeyName)
        {
            return GetSubKeyNames32Or64(inHive, inKeyName, RegSAM.WOW64_32Key);
        }
        #endregion

        #region Enums
        #endregion
    }
}
