Windows注册表的常用字段数据类型

介绍一下windows注册表的数据类型,顺便提供C#访问REG_EXPAND_SZ类型的原始值(不使用环境变量值替换)的方法

Windows注册表的字段类型

常用主要有6种

  • 二进制(REG_BINARY)
    在注册表中,二进制是没有长度限制的,可以是任意个字节的长度。

  • DWORD值(REG_DWORD)
    DWORD值是一个32位(4个字节,即双字)长度的整数。在注册表编辑器中,系统以十六进制的方式显示DWORD值。

  • 字符串值(REG_SZ)
    在注册表中,字符串值一般用来表示文件的描述、硬件的标识等,通常它是以空字符(\0)结尾的字符串。

  • QWORD值(REG_QWORD)
    DWORD值是一个64位(8个字节,即四字)长度的数值。在注册表编辑器中,系统以十六进制的方式显示QWORD值。

  • 多字符串值(REG_MULTI_SZ)
    由两个空字符终止的空终止字符串数组。

  • 可扩充字符串值(REG_EXPAND_SZ)
    包含对环境变量的未扩展引用的空终止字符串(例如,“%PATH%”)。

C#针对可扩充字符串值获取原始值

C#一般的方法(Microsoft.Win32下的注册表相关类)获取可扩充字符串(REG_EXPAND_SZ)的值,都会自动将环境变量填充实际的值,如何获取可扩充字符串的原始值而不自动扩展环境变量呢?
这个只能通过调用 Win32 API 来实现。

C#实现方式

// 读取注册表值
[DllImport("advapi32.dll", SetLastError = true)]
static extern uint RegQueryValueEx(
    UIntPtr hKey,
    string lpValueName,
    int lpReserved,
    ref RegistryValueKind lpType,
    IntPtr lpData,
    ref int lpcbData
);

// 打开注册表键
[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public static extern int RegOpenKeyEx(
    UIntPtr hKey,
    string subKey,
    int ulOptions,
    int samDesired,
    out UIntPtr hkResult
);
// 关闭注册表键 因为是非托管内存,GC不会自动回收,一定需要手动释放掉注打开册表键的句柄
[DllImport("advapi32.dll", SetLastError = true)]
public static extern int RegCloseKey(UIntPtr hKey);

private static readonly UIntPtr HKEY_LOCAL_MACHINE = new UIntPtr(0x80000002u);
private static readonly int READ_FLAG_MASK = 0x20019;

// 返回regPath下项名为valName的原始值
public string GetLMNamedValue(string valName, string regPath)
{
    UIntPtr hKey = UIntPtr.Zero;
    IntPtr pResult = IntPtr.Zero;
    try
    {
        if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, regPath, 0, READ_FLAG_MASK, out hKey) == 0)
        {
            int size = 0;
            RegistryValueKind type = RegistryValueKind.Unknown;
            // 获取需要的缓冲区大小
            uint retVal = RegQueryValueEx(hKey, valName, 0, ref type, IntPtr.Zero, ref size);
            if (size == 0)
            {
                return null;
            }
            // Marshal 提供了一个方法集合,这些方法用于分配非托管内存、复制非托管内存块、将托管类型转换为非托管类型,此外还提供了在与非托管代码交互时使用的其他杂项方法。
            pResult = Marshal.AllocHGlobal(size);
            retVal = RegQueryValueEx(hKey, valName, 0, ref type, pResult, ref size);
            if (retVal != 0)
            {
                throw new ApplicationException($"查询错误 '{regPath}\\{valName}: 0x{Marshal.GetLastWin32Error().ToString("x2")}, 返回: {retVal}");
            }
            else
            {
                switch (type)
                {
                    case RegistryValueKind.String:
                        return Marshal.PtrToStringAnsi(pResult);
                    case RegistryValueKind.DWord:
                        return Marshal.ReadInt32(pResult).ToString();
                    case RegistryValueKind.QWord:
                        return Marshal.ReadInt64(pResult).ToString();
                    case RegistryValueKind.ExpandString:
                        // 直接输出原始内容
                        return Marshal.PtrToStringAnsi(pResult);
                }
            }
        }
        else
        {
            throw new ApplicationException($"打开注册表键错误 HKLM\\{regPath}: {Marshal.GetLastWin32Error().ToString("1:x")}");
        }
    }
    finally
    {
        if (hKey != UIntPtr.Zero)
        {
            RegCloseKey(hKey);
        }

        if (pResult != IntPtr.Zero)
        {
            Marshal.FreeHGlobal(pResult);
        }
    }
    return null;
}
posted @ 2022-11-20 23:08  popsicle  阅读(1381)  评论(0编辑  收藏  举报