Windows Experiment 2 - C#Call DLL Operation Registry

1 Experimental content:

Call DLL with C#to implement operation on registry (advapi32.dll)
This experiment is the first attempt to use windows DLL content. I use Runtime Dynamic Link (using DllImpot). The biggest problem encountered during the experiment is the function parameters (parameter type, number, role of each parameter).

Introduction of 2 dll function

1. Registry Reflection:
Registry redirector Separate 32-bit and 64-bit applications by providing separate logical views of some parts of the registry on WOW64. However, in 32-bit and 64-bit views, some registry keys must have the same value.
Registry reflection copies registry keys and values between two registry views to keep them synchronized. Each view has a separate physical copy of each reflected registry key, one for 32-bit registry views and the other for 64-bit registry views.
Close registry reflection before making changes, and then open it after making changes.

2. Open handle RegOpenKeyEx
The traversal order is operation handle (root key can be used directly), subpath (string type), ulOptions (uint set to 0), samDesired key (int type, set to all available), and phkResult (output handle). The function is called to get the handle before performing a view or modify operation.

3.RegEnumKey:Enumerate subkeys
Variable one, handle variable: IntPtr hKey. Handle variable, points to the item to open. Must be the value returned by RegCreateKeyEx, RegCreateKeyTransacted, RegOpenKeyEx, or can be one of the five root keys.
Variable 2, index: dwIndwx. The first call should be 0, and each subsequent call increments. And since the subkeys are out of order, the function can return subkeys in any order (output in a loop until the return value ret is not zero).
Variable 3, Buffer: ipData, which can be set to the System.Text.StringBuilder type to accept the name of the returned subitem.
Variable 4, Buffer length: int IpcbName, for which the buffer length is customized. However, for many other functions, the buffer length outputs the actual bit data length.

Other functions can refer to the links and settings in the code given below.

3 Ideas

The first is about dll function calls, using dllImport in System.Runtime.InteropServices, as shown in the code.
These functions operate on handles, and to operate on a registry key, whether read or write, you first need to get a handle to the key. You can use RegOpenKeyEx to get a handle to the subkey by specifying the handle and path to the root key.
For registry content acquisition, there are four operations: loading subkeys, fallback, enumerating key values, enumerating subkeys. Back and enter subkeys change the path directly.
The biggest problem encountered when enumerating keys and subkeys is the parameter problem, and many of the examples found do not have good reference value. The solution ideas are as follows: enumerate subkeys with RegEnumKey with fewer parameters; enumerate key values, get the name and data type of key values with RegEnumValue, and get key value content with RegQueryValueEx because you really don't know what to useThe data type now accepts the key content and length.

Error codes: 234-Typically the cache is not large enough or the cache length is set too small; 239-No more data (end of traversal, no subkeys, only default); 87-Parameter error (most encountered).

4. Problems and Errors

*Low-level errors were made, first, typing uint into a unit and checking why C#shows that unit is undefined for half a day; second, when enumerating key values, hKey is the root key to generate a handle to the current item, pHKey is the key to the current item, a mistake was made when calling RegEnumValue, resulting in a return code of 239 all the time. When testing, it was also found that the handle to the enumeration function summary calls the handle to other functionsIt's different, but I don't realize it, I wasted a lot of time.

using System;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Win32;
using System.Text;namespace WindowsExp2
{

    public static class RegUtil
    {
        static readonly IntPtr HKEY_CLASSES_ROOT = new IntPtr(unchecked((int)0x80000000));
        static readonly IntPtr HKEY_CURRENT_USER = new IntPtr(unchecked((int)0x80000001));
        static readonly IntPtr HKEY_LOCAL_MACHINE = new IntPtr(unchecked((int)0x80000002));
        static readonly IntPtr HKEY_USERS = new IntPtr(unchecked((int)0x80000003));
        static readonly IntPtr HKEY_PERFORMANCE_DATA = new IntPtr(unchecked((int)0x80000004));
        static readonly IntPtr HKEY_CURRENT_CONFIG = new IntPtr(unchecked((int)0x80000005));
        static readonly IntPtr HKEY_DYN_DATA = new IntPtr(unchecked((int)0x80000006));
        static int STANDARD_RIGHTS_ALL = (0x001F0000);
        static int KEY_QUERY_VALUE = (0x0001);
        static int KEY_SET_VALUE = (0x0002);
        static int KEY_CREATE_SUB_KEY = (0x0004);
        static int KEY_ENUMERATE_SUB_KEYS = (0x0008);
        static int KEY_NOTIFY = (0x0010);
        static int KEY_CREATE_LINK = (0x0020);
        static int SYNCHRONIZE = (0x00100000);
        static int KEY_WOW64_64KEY = (0x0100);
        static int REG_OPTION_NON_VOLATILE = (0x00000000);
        static int KEY_ALL_ACCESS = (STANDARD_RIGHTS_ALL | KEY_QUERY_VALUE | KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_ENUMERATE_SUB_KEYS
                                | KEY_NOTIFY | KEY_CREATE_LINK) & (~SYNCHRONIZE);


        /// <summary>
        ///Get handle to operation Key value (open existing item)
        /// </summary>
        /// <param name="hKey" has an open handle or standard name ></param>
        /// <param name="lpSubKey" wants to open registry name ></param>
        /// <param name="ulOptions" is not set to 0></param>
        /// <param name="samDesired" constant with prefix KEY_XX></param>
        /// <param name="phkResult" loads name handle of open item ></param>
        [DllImport("Advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int RegOpenKeyEx(IntPtr hKey, string lpSubKey, uint ulOptions, int samDesired, out IntPtr phkResult);

        //Create or open a Key value: 32 bits
        [DllImport("Advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int RegCreateKeyEx(IntPtr hKey, string lpSubKey, int reserved, string type, int dwOptions, int REGSAM, IntPtr lpSecurityAttributes, out IntPtr phkResult,
                                                    out int lpdwDisposition);

        //Turn off registry turns (disable registry reflection for specific items)
        [DllImport("Advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int RegDisableReflectionKey(IntPtr hKey);

        //Open Registry Turn (Open Registry Reflection for a Specific Key)
        [DllImport("Advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int RegEnableReflectionKey(IntPtr hKey);

        //Gets the Key value (that is, the value of the Key object marked by the Key value handle) lpData as a buffer for loading content
        [DllImport("Advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int RegQueryValueEx(IntPtr hKey, string lpValueName, int lpReserved, ref RegistryValueKind lpType, System.Text.StringBuilder lpData, ref uint lpcbData);

        //The value of the enumeration key (that is, the value of the Key object marked by the Key value handle) IpValueName: Buffer IpType holding the value name: Numeric type IpData: Numeric value
        [DllImport("Advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int RegEnumValue(IntPtr hKey,int dwIndex, StringBuilder IpValueName,ref uint IpcbValueName, IntPtr IpReserved,ref RegistryValueKind lpType,IntPtr IpData,IntPtr lpcbData);

        //Set Key Value
        [DllImport("Advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int RegSetValueEx(IntPtr hKey, string lpValueName, uint unReserved, uint unType, byte[] lpData, uint dataCount);

        //Close Key Value
        [DllImport("Advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int RegCloseKey(IntPtr hKey);

        //Get subkey content
        [DllImport("Advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int RegEnumKey(IntPtr hKey, int dwIndex, System.Text.StringBuilder lpData,  int IpcbName);

        //Used to convert follow-key strings to 32-bit handles
        public static IntPtr TransferKeyName(string keyName)
        {
            IntPtr ret = IntPtr.Zero;
            switch (keyName)
            {
                case "HKEY_CLASSES_ROOT":
                    ret = HKEY_CLASSES_ROOT;
                    break;
                case "HKEY_CURRENT_USER":
                    ret = HKEY_CURRENT_USER;
                    break;
                case "HKEY_LOCAL_MACHINE":
                    ret = HKEY_LOCAL_MACHINE;
                    break;
                case "HKEY_USERS":
                    ret = HKEY_USERS;
                    break;
                case "HKEY_PERFORMANCE_DATA":
                    ret = HKEY_PERFORMANCE_DATA;
                    break;
                case "HKEY_CURRENT_CONFIG":
                    ret = HKEY_CURRENT_CONFIG;
                    break;
                case "HKEY_DYN_DATA":
                    ret = HKEY_DYN_DATA;
                    break;
                default:
                    ret = HKEY_LOCAL_MACHINE;
                    break;
            }
            return ret;
        }

        /// <summary>
        ///Set 64-bit registry
        /// </summary>
        /// <param name="key"></param>
        /// <param name="subKey"></param>
        /// <param name="name"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static int SetRegistryKey(string key, string subKey, string name, string value)
        {

            int ret = 0;
            try
            {
                //Convert the primary key name of the Windows registry to an unsigned shaping handle (depending on whether the platform is 32 or 64 bits)
                IntPtr hKey = TransferKeyName(key);

                //Handle that declares that the Key value will be obtained 
                IntPtr pHKey = IntPtr.Zero;

                //Handle to get the operation Key value (point to the item if there is one, create if there is none)
                int lpdwDisposition = 0;
                ret = RegCreateKeyEx(hKey, subKey, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS | KEY_WOW64_64KEY, IntPtr.Zero, out pHKey, out lpdwDisposition);
                if (ret != 0)
                {
                    Console.WriteLine("Unable to create key {0} - {1}: {2}!", key, subKey, ret);
                    return ret;
                }

                //Turn off registry turns (disable registry reflection for specific items)
                RegDisableReflectionKey(pHKey);

                //Set Key Value for Access
                uint REG_SZ = 1;
                byte[] data = Encoding.Unicode.GetBytes(value);

                RegSetValueEx(pHKey, name, 0, REG_SZ, data, (uint)data.Length);

                //Open Registry Turn (turn on registry reflection for a specific key)
                RegEnableReflectionKey(pHKey);
                //Close Handle
                RegCloseKey(pHKey);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
                return -1;
            }

            return ret;
        }



        //Enumerate subkeys
        public static void getEnumKey(string key, string subKey)
        {
            int ret = 0;
            int index = 0;
            int len = 200;
            StringBuilder b = new StringBuilder(200);
            while (ret == 0)
            {
                try
                {
                    IntPtr hKey = TransferKeyName(key);
                    IntPtr pHKey = IntPtr.Zero;
                    ret = RegUtil.RegOpenKeyEx(hKey, subKey, 0, KEY_ALL_ACCESS, out pHKey);
                    if (ret != 0)
                    {
                        Console.WriteLine("Failed to open Reg {0}\\{1}\\{2},return {3}", key, subKey, ret);
                    }

                    ret = RegUtil.RegEnumKey(pHKey, index, b,len);
                    if (ret != 0)
                    {
                        Console.WriteLine("Failed to List EnumKey{0}\\{1}\\{2},return {3}", key, subKey, ret);
                    }
                    Console.WriteLine(b);
                    index++;
                }
                catch
                {

                }
            }
        }
        //enum
        public static void getEnumValue(string key, string subKey)
        {
            int ret = 0;
            int index = 0;
            StringBuilder valueName = new StringBuilder(50);
            Byte[] buffer = new Byte[1024];
            RegistryValueKind valuetype = 0;

            StringBuilder value = new StringBuilder(1024);

            while (ret == 0)
            {
                try
                {
                    IntPtr hKey = TransferKeyName(key);
                    IntPtr pHKey = IntPtr.Zero;
                    ret = RegUtil.RegOpenKeyEx(hKey, subKey, 0, KEY_ALL_ACCESS, out pHKey);
                    if (ret != 0)
                    {
                        Console.WriteLine("Failed to open Reg {0}\\{1}\\{2},return {3}", key, subKey, ret);
                        return;
                    }
                    ;

                    uint valueNameLen = 50;

                    ret = RegUtil.RegEnumValue(pHKey,index,valueName,ref valueNameLen,IntPtr.Zero,ref valuetype,IntPtr.Zero, IntPtr.Zero);
                    if (ret != 0)
                    {
                        Console.WriteLine("Failed to List EnumKey{0}\\{1},return {2}", key, subKey, ret);
                        return;
                    }
                    uint len = 1024;
                    int ret2 = RegUtil.RegQueryValueEx(pHKey, valueName.ToString(), 0,ref valuetype , value,ref len);

                    Console.WriteLine("{0}—{1}—{2}",valueName.ToString(),valuetype.ToString(),value);
                    index++;
                }
                catch
                {

                }
            }
        }
    }

    class Program
    {

        static string Winrarpath = @"SOFTWARE\Microsoft\Windows\CurrentVersion";
        static string key = "DevicePath";

        static void Main(string[] args)
        {
            Console.WriteLine("Please enter opcode 0:Exit 1: View the current item's value 2: Modify and create values    3: Back 4: Turn to Subitem 5: View Subitems");
            while(true)
            {
                char op = Console.ReadLine()[0];
                string name="";
                string value="";
                switch (op) 
                {
                    case '0':
                        return;

                    case '1':
                        //value = RegUtil.GetRegistryValue(Winrarpath, key);
                        //Console.WriteLine("Registry key {0}\r\n value {1} is: {2}",Winrarpath,key,value);
                        RegUtil.getEnumValue("HKEY_LOCAL_MACHINE",Winrarpath);
                        break;

                    case '2':
                        //Modify the item's data, which is automatically created if no data with that name exists, or created if no item exists
                        Console.WriteLine("current location{0}", Winrarpath);
                        Console.WriteLine("Enter subkey name (create subkeys if you enter, otherwise do not create them)");
                        string subkey = @"\"+Console.ReadLine();
                        Console.WriteLine("Please enter a data name:");
                        name = Console.ReadLine();
                        Console.WriteLine("Please enter a numeric value:");
                        value = Console.ReadLine();
                        RegUtil.SetRegistryKey("HKEY_LOCAL_MACHINE",Winrarpath+subkey,name,value);
                        break;

                    case '3':
                        Winrarpath = Winrarpath.Substring(0, Winrarpath.LastIndexOf(@"\"));
                        Console.WriteLine("The current path is{0}", Winrarpath);
                        break;

                    case '4':
                        Console.WriteLine("Enter subitem name(Create if not)");
                        string subkey4 = @"\"+Console.ReadLine();
                        Winrarpath += subkey4;
                        RegUtil.SetRegistryKey("HKEY_LOCAL_MACHINE", Winrarpath, "", "");
                        Console.WriteLine("current location{0}", Winrarpath);
                        break;

                    case '5':
                        RegUtil.getEnumKey("HKEY_LOCAL_MACHINE", Winrarpath);
                        break;

                    default:
                        Console.WriteLine("Undefined Action");
                        break;
                }
                Console.WriteLine("**************************************************");
            }
        }

    }

}

5. Reference documents and tools

List of error codes: System Error Codes (0-499) (WinError.h) - Win32 apps | Microsoft Docs
Introduction to advapi32.dll function: RegEnumValueA function (winreg.h) - Win32 apps | Microsoft Docs
The dll function viewer: Dynamic Link Library Function Viewer supports 64-bit dll download on Downyi.com
Code section reference: C#Operations Registry-Style Cycling-Blog Park (cnblogs.com)


        
        

Keywords: C# Windows dll

Added by localhost on Tue, 05 Oct 2021 20:23:28 +0300