Contents

如果我们想在某个进程被执行时做一些事情,该怎么做呢?最简单粗暴的办法是来个死循环,不停地检查这个进程存不存在。代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private static void monitorProcessRuning(string name)
{
while (true)
{
if (Process.GetProcessesByName(name).Length > 0)
{
Console.WriteLine("Process [{0}] is running", name);
}
Thread.Sleep(1000);
}
}

static void Main(string[] args)
{
monitorProcessRuning("notepad");
}

这个看起来技术含量有点低,那就试试别的办法。我们可以求助于__InstanceCreationEvent。当有一个新的实例生成时会触发这个事件。我们可以通过WMI - Windows Management Instrumentation来查询。如下是MSDN上给出的一个示例,当记事本程序运行时,会得到通知,使用了WMI中的Win32_Process

1
SELECT * FROM __InstanceCreationEvent WITHIN PollingInterval WHERE TargetInstance ISA 'Win32_Process' and TargetInstance.Name = 'notepad.exe' 

下面来写我们的C#程序吧,需要用到ManagementEventWatcher

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private static void monitorProcessRuning(string name)
{
WqlEventQuery processQuery = new WqlEventQuery("__InstanceCreationEvent", new TimeSpan(0, 0, 1),
"TargetInstance isa 'Win32_Process' AND TargetInstance.Name = '" + name + "'");

using (var watcher = new ManagementEventWatcher(processQuery))
{
watcher.EventArrived += (sender, eventArgs) =>
{
Console.WriteLine("Process [{0}] is running", name);
};
watcher.Start();

Console.ReadLine();
watcher.Stop();
}
}

static void Main(string[] args)
{
monitorProcessRuning("notepad.exe");
}

我们看看能不能用这个方法来解决之前的博文Windows下如何检测用户修改了系统时间并且把系统时间改回来,在那篇文章中是用户修改过系统时间后会得到通知。我们可以看看那个修改时间的进程是什么,然后就可以在那个进程启动时得到通知了。

首先通过Process Explorer看出启动的进程是rundll32.exe,命令行为"C:\WINDOWS\system32\rundll32.exe" Shell32.dll,Control_RunDLL "C:\WINDOWS\system32\timedate.cpl",。我们可以用下面的代码来监测这个进程启动,我们甚至可以通过监测到它启动就杀掉这个进程来阻止用户修改系统时间。

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
private static void monitorUserChangeTime(bool kill)
{
var target = "rundll32.exe";
string arg = "timedate.cpl";
WqlEventQuery processQuery = new WqlEventQuery("__InstanceCreationEvent", new TimeSpan(0, 0, 1),
"TargetInstance isa 'Win32_Process' AND TargetInstance.Name = '" + target + "'");

using (var watcher = new ManagementEventWatcher(processQuery))
{
watcher.EventArrived += (sender, eventArgs) =>
{
var plist = Process.GetProcessesByName(Path.GetFileNameWithoutExtension(target));
foreach (var p in plist)
{
using (var searcher = new ManagementObjectSearcher("SELECT CommandLine FROM Win32_Process WHERE ProcessId = " + p.Id))
{
var commandLine = new StringBuilder();
foreach (var @object in searcher.Get())
{
commandLine.Append(@object["CommandLine"]);
commandLine.Append(" ");
}
if (commandLine.ToString().Contains(arg))
{
if (kill)
{
Console.WriteLine("Killing the process!");
p.Kill();
}
else
{
Console.WriteLine("User is changing the time!");
}

}
}
}
};
watcher.Start();

Console.ReadLine();
watcher.Stop();
}
}

注意上面的代码中获取进程命令行又是通过WMI实现的,使用了ManagementObjectSearcher.aspx)从Win32_Process中获取命令行。

不能简单的通过ProcessStartInfo.Arguments来获取命令行。ProcessStartInfo只有在用Process.Start时才有用,在用GetProcesses拿到的进程中,StartInfo不包含进程的文件名和命令行参数。

If you did not use the Start method to start a process, the StartInfo property does not reflect the parameters used to start the process. For example, if you use GetProcesses to get an array of processes running on the computer, the StartInfo property of each Process does not contain the original file name or arguments used to start the process.

Contents