.NET事件用于进程可执行文件启动

19

有没有办法注册一个事件,当特定文件名的可执行文件启动时触发?我知道很容易通过获取进程句柄并注册退出事件来获得进程退出事件。但是如何在进程尚未运行时通知您当一个进程启动时...而不必轮询所有正在运行的进程呢?

3个回答

32
你可以使用以下代码:

You could use the following:

    private ManagementEventWatcher WatchForProcessStart(string processName)
    {
        string queryString =
            "SELECT TargetInstance" +
            "  FROM __InstanceCreationEvent " +
            "WITHIN  10 " +
            " WHERE TargetInstance ISA 'Win32_Process' " +
            "   AND TargetInstance.Name = '" + processName + "'";

        // The dot in the scope means use the current machine
        string scope = @"\\.\root\CIMV2";

        // Create a watcher and listen for events
        ManagementEventWatcher watcher = new ManagementEventWatcher(scope, queryString);
        watcher.EventArrived += ProcessStarted;
        watcher.Start();
        return watcher;
    }

    private ManagementEventWatcher WatchForProcessEnd(string processName)
    {
        string queryString =
            "SELECT TargetInstance" +
            "  FROM __InstanceDeletionEvent " +
            "WITHIN  10 " +
            " WHERE TargetInstance ISA 'Win32_Process' " +
            "   AND TargetInstance.Name = '" + processName + "'";

        // The dot in the scope means use the current machine
        string scope = @"\\.\root\CIMV2";

        // Create a watcher and listen for events
        ManagementEventWatcher watcher = new ManagementEventWatcher(scope, queryString);
        watcher.EventArrived += ProcessEnded;
        watcher.Start();
        return watcher;
    }

    private void ProcessEnded(object sender, EventArrivedEventArgs e)
    {
        ManagementBaseObject targetInstance = (ManagementBaseObject) e.NewEvent.Properties["TargetInstance"].Value;
        string processName = targetInstance.Properties["Name"].Value.ToString();
        Console.WriteLine(String.Format("{0} process ended", processName));
    }

    private void ProcessStarted(object sender, EventArrivedEventArgs e)
    {
        ManagementBaseObject targetInstance = (ManagementBaseObject)e.NewEvent.Properties["TargetInstance"].Value;
        string processName = targetInstance.Properties["Name"].Value.ToString();
        Console.WriteLine(String.Format("{0} process started", processName));
    }

你可以调用WatchForProcessStart和/或WatchForProcessEnd方法,传入进程名称(例如“notepad.exe”)。

两个Watch*方法返回ManagementEventWatcher对象,因为它实现了IDisposable接口,所以当你使用完后应该调用Dispose方法释放资源,以防出现问题。

如果需要在进程启动后更快地触发事件,你还可以更改查询中的轮询值。要执行此操作,请将“WITHIN 10”行更改为小于10的数字。


嘿,我的ManagementBaseObject对象返回的除了“Handle”之外,所有属性都设置为null。 - chaz
我如何能够获取所有新进程的通知,而不仅仅是记事本? - Hossein
1
@Hossein,您应该能够编辑WatchForProcessStart方法。删除processName参数,然后将queryString字符串更改为:"SELECT TargetInstance.Name FROM __InstanceCreationEvent WITHIN 10 WHERE TargetInstance ISA 'Win32_Process' "; - Clive
非常感谢 :-),顺便问一下,如果我将那个10设置为1,这是否意味着它将每1毫秒轮询一次?我注意到使用wmi获取信息时有延迟,不知道是c#/.net的问题还是wmi本身就很慢。 - Hossein
@Hossein 我相信这个值是以秒为单位,而不是毫秒。不确定是否有办法让代码变得超级(亚秒级)响应。 - Clive
显示剩余5条评论

1

这里是代码。

请注意,您需要以管理员身份启动Visual Studio才能执行此代码。

using System;
using System.Management;

namespace AppLaunchDetector
{
    class Program
    {
        static void Main(string[] args)
        {           
            ManagementEventWatcher w = null;
            WqlEventQuery q;
            try
            {
                q = new WqlEventQuery();
                q.EventClassName = "Win32_ProcessStartTrace";
                w = new ManagementEventWatcher(q);
                w.EventArrived += new EventArrivedEventHandler(ProcessStartEventArrived);
                w.Start();
                Console.ReadLine(); // block main thread for test purposes
            }
            catch (Exception ex)
            {

            }
            finally
            {
                w.Stop();
            }
        }

        static void ProcessStartEventArrived(object sender, EventArrivedEventArgs e)
        {
            foreach (PropertyData pd in e.NewEvent.Properties)
            {
                Console.WriteLine("\n============================= =========");
                Console.WriteLine("{0},{1},{2}", pd.Name, pd.Type, pd.Value);
            }
        }
    }
}

1

WMI 可以在进程创建时创建事件。然后,您可以过滤这些事件。


@Adam:我预料到会有这样的评论。不幸的是,我已经很久没有使用WMI事件(几年了),也没有使用.NET,所以我必须自己学习如何做...而且现在没有时间。 - Richard

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接