场景:在C#窗体应用程序中监控某软件安装更新卸载时注册表的变化, 包括键值数据的创建修改删除重命名等。
关键点:使用RegNotifyChangeKeyValue函数提供通知机制
RegNotifyChangeKeyValue
该API在指定注册表项属性或内容发生更改时通知调用者。
LONG WINAPI RegNotifyChangeKeyValue(
_In_ HKEY hKey,
_In_ BOOL bWatchSubtree,
_In_ DWORD dwNotifyFilter,
_In_opt_ HANDLE hEvent,
_In_ BOOL fAsynchronous
);
返回值
Long,零(ERROR_SUCCESS)表示成功。其他任何值都代表一个错误代码
参数 类型及说明
hKey,要监视的一个项的句柄,或者指定一个标准项名
bWatchSubtree,TRUE(非零)表示监视子项以及指定的项
dwNotifyFilter:
REG_NOTIFY_CHANGE_NAME 侦测名称的变化,以及侦测注册表的创建和删除事件
REG_NOTIFY_CHANGE_ATTRIBUTES 侦测属性的变化
REG_NOTIFY_CHANGE_LAST_SET 侦测上一次修改时间的变化
REG_NOTIFY_CHANGE_SECURITY 侦测对安全特性的改动
hEvent Long,一个事件的句柄。如fAsynchronus为False,则这里的设置会被忽略
fAsynchronus Long,如果为零,那么除非侦测到一个变化,否则函数不会返回。否则这个函数会立即返回,而且在发生变化时触发由hEvent参数指定的一个事件
源码
1 | using Microsoft.Win32; |
Form1类构造函数中初始化:1
InitialMonitor("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Test");
各Form1类中API定义如下1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250// 通过注册表项路径字符串获得RegistryKey
RegistryKey GetRegistryKey(string text)
{
string[] _SubKey = text.Split('\\');
Microsoft.Win32.RegistryKey _Key = Microsoft.Win32.Registry.CurrentUser;
switch (_SubKey[0])
{
case "HKEY_CLASSES_ROOT":
_Key = Microsoft.Win32.Registry.ClassesRoot;
break;
case "HKEY_CURRENT_USER":
_Key = Microsoft.Win32.Registry.CurrentUser;
break;
case "HKEY_LOCAL_MACHINE":
_Key = Microsoft.Win32.Registry.LocalMachine;
break;
case "HKEY_USERS":
_Key = Microsoft.Win32.Registry.Users;
break;
case "HKEY_CURRENT_CONFIG":
_Key = Microsoft.Win32.Registry.CurrentConfig;
break;
default:
break;
}
_Key = _Key.OpenSubKey(String.Join("\\", _SubKey, 1, _SubKey.Length - 1));
return _Key;
}
// 初始化监控某注册表项
public void InitialMonitor(string text)
{
string[] tmpText = text.Split('\\');
if (tmpText.Length <= 1 || tmpText[1] == null || tmpText[1] == "") return; //no path input
// Monitor the upper level.
string upperText = String.Join("\\", tmpText, 0, tmpText.Length - 1);
if (TS.Count > 100)
{
MessageBox.Show("Monitor more than 100 keys!");
return;
}
if (TS[upperText] != null)
{
return;
}
Microsoft.Win32.RegistryKey _Key = GetRegistryKey(upperText);
if (_Key == null)
{
MessageBox.Show(text + " is not vaild path!");
return;
}
MonitorWindowsReg tmpT = new MonitorWindowsReg(_Key);
// 实例化委托, 调用UpdateReg实际将调用T__UpdateReg
tmpT.UpReg += new MonitorWindowsReg.UpdataReg(T__UpdateReg);
tmpT.Star();
TS.Add(upperText, tmpT);
// Monitor the current level recur.
CreateMonitor(text);
}
// 新增监控注册表项
public void CreateMonitor(string text)
{
string[] tmpText = text.Split('\\');
//Trace.Assert(tmpText[1] != null);
if (tmpText.Length <=1|| tmpText[1] == null || tmpText[1] == "") return; //no path input
if (TS.Count > 100)
{
MessageBox.Show("Monitor more than 100 keys!");
return;
}
if (TS[text] != null)
{
return;
}
Microsoft.Win32.RegistryKey _Key = GetRegistryKey(text);
if(_Key == null)
{
//MessageBox.Show(text + " is not vaild path!");
return;
}
MonitorWindowsReg tmpT = new MonitorWindowsReg(_Key);
tmpT.UpReg += new MonitorWindowsReg.UpdataReg(T__UpdateReg);
tmpT.Star();
TS.Add(text, tmpT);
foreach (string key in _Key.GetSubKeyNames())
{
CreateMonitor(text + '\\' + key);
}
}
// 删除监控的注册表项
public void RemoveMonitor(string text)
{
if (TS[text] == null)
return;
Microsoft.Win32.RegistryKey _Key = GetRegistryKey(text);
if(_Key!=null)
foreach (string key in _Key.GetSubKeyNames())
{
RemoveMonitor(text + '\\' + key);
}
try { ((MonitorWindowsReg)TS[text]).Stop(); } catch { }
TS.Remove(text);
}
// 其他非UI线程数据传入UI线程处理
delegate void RegAppendTextCallback(string text);
private void RegAppendText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.richTextBox_Reg.InvokeRequired)
{
RegAppendTextCallback d = new RegAppendTextCallback(RegAppendText);
this.Invoke(d, new object[] { text });
}
else
{
this.richTextBox_Reg.AppendText(DateTime.Now.ToLocalTime().ToString()+':'+text);
}
}
// 递归遍历并输出该项下面key和value的变化
void recurAppendNewKeyValue(string NewKey)
{
Microsoft.Win32.RegistryKey _Key = GetRegistryKey(NewKey);
if (_Key == null)
{
//MessageBox.Show(text + " is not vaild path!");
return;
}
RegAppendText("Create new key: " + NewKey + '\n');
foreach (string value in _Key.GetValueNames())
{
RegAppendText("Create new value: " + value + " with data " + _Key.GetValue(value).ToString() + '\n');
}
foreach (string key in _Key.GetSubKeyNames())
{
recurAppendNewKeyValue(NewKey + '\\' + key);
}
}
//与MonitorWindowsReg.UpdataReg绑定
void T__UpdateReg(string OldText, object OldValue, string NewText, object NewValue)
{
object Old = OldValue;
object New = NewValue;
if (Old == null || Old.ToString() == "") Old = "null";
if (New == null || New.ToString() == "") New = "null";
string[] nameParts = NewText.Split('\\');
if(nameParts.Length>2 && nameParts[nameParts.Length-1] == "")
NewText = String.Join("\\", nameParts, 0, nameParts.Length-1);
//create value
if (OldText == "" && NewText != "")
{
if (NewValue == null)
{
if(TS[NewText] != null)
{
RegAppendText("Delete old key: " + NewText + '\n');
RemoveMonitor(NewText);
}
return;
}
if(NewValue.ToString() == "_key_")
{
recurAppendNewKeyValue(NewText);
CreateMonitor(NewText);
}
else
{
RegAppendText("Create new value: " + NewText + " with data " + New.ToString() + '\n');
}
}
else if (OldText != "" && NewText == "")
{
if (OldValue == null)
return;
if (OldValue.ToString() == "_key_")
{
if(TS[OldText] != null)
{
RegAppendText("Delete old key: " + OldText + '\n');
RemoveMonitor(OldText);
}
}
else
{
RegAppendText("Delete old value: " + OldText + '\n');
}
}
else if (OldText != "" && NewText == OldText)
{
RegAppendText("Update data of "+NewText+ " from "+ Old.ToString() + " to "+ New.ToString() + '\n');
}else
{
RegAppendText("NewText: " + NewText + "OldText: " + OldText + " OldValue: " + Old.ToString() + " NewValue: " + New.ToString() + '\n');
}
}
//停止所有监控
private void button_StopMon(object sender, EventArgs e)
{
if (TS.Count == 0)
{
MessageBox.Show("Register text not been monitoring!");
return;
}
foreach (string Key in new List<object>(TS.Keys.Cast<object>()))
//foreach (string Key in TS.Keys)//error: Collection was modified; enumeration operation may not exec
{
RemoveMonitor(Key);
}
Trace.Assert(TS.Count == 0);
for (int i = 0; i < TextBoxes.Count; i++)
//foreach (TextBox textBox in TextBoxes)
{
ComboBoxes[i].Enabled = true;
TextBoxes[i].Enabled = true;
}
}
// 开启监控,并记录在hashtable TS中
private void button_StartMon(object sender, EventArgs e)
{
if (TS.Count > 0)
{
MessageBox.Show("Register text has already been monitoring!");
return;
}
for (int i = 0; i < TextBoxes.Count; i++)
//foreach (TextBox textBox in TextBoxes)
{
InitialMonitor(ComboBoxes[i].SelectedItem.ToString() + TextBoxes[i].Text);
}
if (TS.Count > 0)
{
for (int i = 0; i < TextBoxes.Count; i++)
{
ComboBoxes[i].Enabled = false;
TextBoxes[i].Enabled = false;
}
}else
{
MessageBox.Show("No vaild path has been monitoring!");
}
}
其他
本文中RegNotifyChangeKeyValue中bWatchSubtree设置为False, 自行递归监控每个子项, 每项建立一个监控。可以把bWatchSubtree设置为True,直接递归监控注册表项下所有子项。