1 实验内容:
用C# 调用dll 实现对注册表的操作 (advapi32.dll)
本次实验是第一次尝试使用windows动态链接库的内容,我采用的是运行时动态链接(使用DllImpot),在实验过程中遇到的最大问题就是函数参数的问题(参数类型、数量、各个参数的作用)
2 dll函数介绍
1.注册表反射:
注册表 重定向程序 通过提供 WOW64 上注册表某些部分的单独逻辑视图来隔离 32 位和 64 位应用程序。 但是,在 32 位和 64 位视图中,某些注册表项的值必须相同。
注册表反射 过程在 两个注册表视图之间复制注册表项和值,使它们保持同步。 每个视图都有每个反映的注册表项的单独物理副本,一个针对 32 位注册表视图,另一个针对 64 位注册表视图。
在修改之前要关闭注册表反射,修改后再打开。
2.打开句柄RegOpenKeyEx
遍历依次为操作句柄(可以直接用根键),子路径(string类型),ulOptions(uint 设为0),samDesired密匙(int类型,设置为全部可用),phkResult(输出的句柄)。在执行查看、修改操作之前都要调用该函数获取句柄。
3.RegEnumKey:枚举子键
变量一,句柄变量:IntPtr hKey。句柄变量,指向要打开的项。必须是RegCreateKeyEx,RegCreateKeyTransacted, RegOpenKeyEx 返回的值,也可以以是五个根键之一。
变量二,索引:dwIndwx。第一次调用应为0,后续每次调用递增。且由于子键没有顺序,函数可以按任意顺序返回子项(用一个循环进行输出,一直到返回值ret不为0)。
变量三,缓冲区:ipData,可以设置为System.Text.StringBuilder类型,接受返回的子项的名称。
变量四,缓冲区长度:int IpcbName,对于该函数,缓冲区长度为定制。然而很多其他函数,缓冲区长度会输出位实际的数据长度。
其他一些函数可以参考下文给出的链接和代码中的设置。
3 思路
首先是关于dll函数调用,用System.Runtime.InteropServices中的dllImport,具体格式见代码。
这些函数的操作是基于句柄的,要对注册表的某个表项进行操作(无论读、还是写)都首先要获得一个指向该项的句柄。可以用RegOpenKeyEx获得,指定根键的句柄和路径,获取子项的句柄。
对于注册表内容获取,可由四个操作组成:装入子键、回退、列举键值、列举子键。回退和进入子键,直接更改路径即可。
在列举键值和子键时,遇到的最大问题是参数问题,查到的很多例子都没有好的参考价值。解决思路如下:用参数量较少的RegEnumKey枚举子项;枚举键值,用RegEnumValue获取键值的名称和数据类型,再用RegQueryValueEx获取键值内容,因为实在是不知道用什么数据类型来接受键值内容和键值长度了。
补充(删除键、删除值):
删除键,使用RegDeleteKey,但该函数要求删除的键的子键必须为空,所以要先递归删除所有子键再删除当前键。这里产生了一个问题,按理说枚举子键需要将index不断增加以获取下一个子键,但递归调用时将index直接设置为0也可以获取到所有子键,还不知道问什么。
错误码:234-一般是缓存区不够或缓存区长度设置过小;239-没有更多数据(遍历结束、没有子项、只有默认值);87-参数错误(遇到最多的)。
4.问题与错误
犯了低级错误,一是将uint错打成了unit,还查了半天C#为何显示unit未定义;二是在枚举键值时,hKey为根键用以生成当前项的句柄,pHKey才为当前项的键,调用RegEnumValue时输错了,导致返回码一直为239,测试的时候还发现枚举函数汇总的句柄调用其他函数的时候句柄不相同,但没有意识到,浪费了很多时间。
5. BUG
运行时可能会产生一下错误:
未能在Microsoft.Win32中找到RegisteryValueKind。这可能与系统或框架版本有关,这种情况下,如何引用RegistryValueKind我也不清楚,但有一下两种方法可以解决:
1. 将函数形参中和后续代码中的RegistryValueKind都改成int,程序可以运行,但是不同数据类型输出的是不同的整数,无法知道具体的数据类型。
2. 受方法1的启发,只要能将整型转化表示数据类型的字符串即可,查阅微软官方文档可知,RegisteryValueKind其实就是一个枚举类型,将using Microsoft.Win32注释掉,加入以下枚举类型的定义即可。
enum RegistryValueKind
{
String = 1,Unknown = 0,QWord = 11,None = -1,MultiString = 7,ExpandString = 2,DWord = 4, Binary = 3
}
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>
/// 获取操作Key值句柄(打开现有项)
/// </summary>
/// <param name="hKey" 已打开句柄,或标准名 ></param>
/// <param name="lpSubKey" 欲打开注册表名字></param>
/// <param name="ulOptions" 未用设为0></param>
/// <param name="samDesired" 带有前缀KEY_XX的常数></param>
/// <param name="phkResult" 装载打开项的名字 句柄></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);
//创建或打开Key值:32位
[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);
//关闭注册表转向(禁用特定项的注册表反射)
[DllImport("Advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int RegDisableReflectionKey(IntPtr hKey);
//开启注册表转向(开启特定项的注册表反射)
[DllImport("Advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int RegEnableReflectionKey(IntPtr hKey);
//获取Key值(即:Key值句柄所标志的Key对象的值) lpData为装载内容的缓冲区
[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);
//枚举键的值(即:Key值句柄所标志的Key对象的值) IpValueName:存放值名的缓冲区 IpType:数值类型 IpData:数值
[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);
//设置Key值
[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);
//关闭Key值
[DllImport("Advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int RegCloseKey(IntPtr hKey);
//获取子键内容
[DllImport("Advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int RegEnumKey(IntPtr hKey, int dwIndex, System.Text.StringBuilder lpData, int IpcbName);
//删除键及其子键
[DllImport("Advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int RegDeleteKeyEx(IntPtr hKey, string lpSubKey, int samDesired,int Reserved);
//删除值
[DllImport("Advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int RegDeleteValue(IntPtr hKey, string lpValueName);
//用于将跟键字符串转化为32位句柄
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>
/// 设置64位注册表
/// </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
{
//将Windows注册表主键名转化成为不带正负号的整形句柄(与平台是32或者64位有关)
IntPtr hKey = TransferKeyName(key);
//声明将要获取Key值的句柄
IntPtr pHKey = IntPtr.Zero;
//获得操作Key值的句柄(有就指向该项,没有则创建)
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;
}
//关闭注册表转向(禁止特定项的注册表反射)
RegDisableReflectionKey(pHKey);
//设置访问的Key值
uint REG_SZ = 1;
byte[] data = Encoding.Unicode.GetBytes(value);
RegSetValueEx(pHKey, name, 0, REG_SZ, data, (uint)data.Length);
//打开注册表转向(开启特定项的注册表反射)
RegEnableReflectionKey(pHKey);
//关闭句柄
RegCloseKey(pHKey);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
return -1;
}
return ret;
}
//枚举所有子键
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},return {2}", key, subKey, ret);
}
ret = RegUtil.RegEnumKey(pHKey, index, b,len);
if (ret != 0)
{
if (ret == 259)
Console.WriteLine("All data hase been listed or no data available in {0}\\{1}", key, subKey);
else
Console.WriteLine("Failed to List EnumKey{0}\\{1}\\{2},return {3}", key, subKey, ret);
}
Console.WriteLine(b);
index++;
}
catch
{
}
}
}
//枚举所有值
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)
{
if (ret == 259)
Console.WriteLine("All data hase been listed or no data available in {0}\\{1}",key,subKey);
else
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
{
}
}
}
//删除键: 先递归的删除其子键
public static void deleteTree(string Hkey,string subKey)
{
int ret = 0;
int index = 0;
int len = 200;
StringBuilder b = new StringBuilder(200);
while (ret == 0)
{
try
{
IntPtr hKey = TransferKeyName(Hkey);
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},return {2}",Hkey,subKey,ret);
}
ret = RegUtil.RegEnumKey(pHKey, 0, b, len); //为什么不需要index?
if (ret != 0)
{
break;
}
deleteTree(Hkey, subKey+@"\"+ b.ToString());
//index++;
}
catch
{
}
}
try
{
//将主键转化为句柄,进行后续操作
IntPtr hKey = TransferKeyName(Hkey);
int k = 0x0200;
ret = RegUtil.RegDeleteKeyEx(hKey,subKey,0,0);
if (ret != 0)
{
Console.WriteLine("Failed to Delete Tree{0}\\{1},return {2}", Hkey, subKey, ret);
return;
}
Console.WriteLine("{0}has been deleted",subKey);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
return;
}
}
//删除值
public static void deleteValue(string Hkey, string subKey, string valueName)
{
int ret = 0;
try
{
//将主键转化为句柄,进行后续操作
IntPtr hKey = TransferKeyName(Hkey);
IntPtr pHKey = IntPtr.Zero;
//转入子键
ret = RegUtil.RegOpenKeyEx(hKey, subKey, 0, KEY_ALL_ACCESS, out pHKey);
if (ret != 0)
{
Console.WriteLine("Failed to open Register {0}\\{1}\\{2},return {3}", Hkey, subKey, ret);
}
ret = RegUtil.RegDeleteValue(pHKey, valueName);
if (ret != 0)
{
Console.WriteLine("Failed to Delete value {0}\\{1}——>{2},return {3}", Hkey, subKey,valueName, ret);
return;
}
Console.WriteLine("{0}\\{1}——>{2} has been deleted",Hkey ,subKey ,valueName);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
return;
}
}
}
class Program
{
static string Winrarpath = @"SOFTWARE\Microsoft\Windows\CurrentVersion";
static string RootKey = "HKEY_LOCAL_MACHINE";
static void Main(string[] args)
{
Console.WriteLine("请输入操作码 0:退出 1:回退 2:转向子项 3:查看当前项的值 4:修改与创建数值 5:查看全部子项 6:删除项 7:删除值 8:切换根键");
while(true)
{
char op = Console.ReadLine()[0];
string name="";
string value="";
switch (op)
{
case '0':
return;
case '1':
Winrarpath = Winrarpath.Substring(0, Winrarpath.LastIndexOf(@"\"));
Console.WriteLine("当前路径为{0}", Winrarpath);
break;
case '2':
Console.WriteLine("输入子项名称(没有则创建)");
string subkey4 = @"\" + Console.ReadLine();
Winrarpath += subkey4;
RegUtil.SetRegistryKey(RootKey, Winrarpath, "", "");
Console.WriteLine("当前位置{0}", Winrarpath);
break;
case '3':
RegUtil.getEnumValue(RootKey, Winrarpath);
break;
case '4':
//修改项的数据,若不存在该名称的数据则自动创建,若不存在该项也会创建
Console.WriteLine("当前位置{0}", Winrarpath);
Console.WriteLine("输入子项名(输入则创建子项,否则不创建)");
string subkey = @"\"+Console.ReadLine();
Console.WriteLine("请输入数据名称:");
name = Console.ReadLine();
Console.WriteLine("请输入数值:");
value = Console.ReadLine();
RegUtil.SetRegistryKey(RootKey, Winrarpath+subkey,name,value);
break;
case '5':
RegUtil.getEnumKey(RootKey, Winrarpath);
break;
case '6':
Console.WriteLine("输入要删除的键名");
string subkey6 = @"\" + Console.ReadLine();
RegUtil.deleteTree("HKEY_LOCAL_MACHINE", Winrarpath+subkey6);
break;
case '7':
Console.WriteLine("输入要删除的值名");
string valueName = Console.ReadLine();
RegUtil.deleteValue(RootKey, Winrarpath,valueName);
break;
case '8':
break;
default:
Console.WriteLine("未定义的操作");
break;
}
Console.WriteLine("**************************************************");
}
}
}
}
5.参考文档和工具
错误码列表:System Error Codes (0-499) (WinError.h) - Win32 apps | Microsoft Docs
advapi32.dll函数介绍:RegEnumValueA function (winreg.h) - Win32 apps | Microsoft Docs
dll函数查看器:动态链接库函数查看器支持64位dll下载当易网 (downyi.com)
代码部分参考:C#操作注册表 - 风情单车 - 博客园 (cnblogs.com)