【问题标题】:C# Monitor launch of an executable and do operation before the user an use itC#监控可执行文件的启动并在用户使用它之前执行操作
【发布时间】:2018-09-21 05:00:19
【问题描述】:

我正在编写一个测试应用程序来监视另一个 Windows 应用程序并在允许用户使用它们之前执行操作。

背景

我们的用户可以访问计算机并启动应用程序。对于其中一些应用程序,我们希望用户填写一个小表格,然后他们将被允许使用该应用程序。同时,我们希望跟踪应用程序的总运行时间(即用户使用应用程序的时间)。 用户可以运行的应用程序并非都是第 3 方应用程序,我们无法控制它们的“质量”。

当前解决方案

使用Code Project article 和 WMI,我创建了一个监控应用程序,用于跟踪应用程序的打开和关闭并显示要填写的表单。

问题

我正在以 Calculator.exe 为例测试监控应用程序。监控正确检测到可执行文件的启动和关闭,如果用户取消弹出的表单,我们可以终止应用程序。我们还可以使用表单中的数据以及开始和结束时间编写日志。 不幸的是,可执行文件没有以任何方式“绑定”到应用程序,我们无法阻止用户简单地忽略监控应用程序表单并使用他们启动的应用程序。

可能的解决方案

  1. 杀死启动的应用程序,如果用户提交了所有信息,则显示表单并重新启动应用程序。 此解决方案可行,但某些应用程序可能不乐意被突然终止。

  2. 使用solution described in this answer暂停已启动应用程序的线程。 我的疑问是关于暂停线程。如上所述,我们不知道 3rd 方应用程序的编写情况如何。有死锁的风险吗? 同样在这种情况下,终止进程可能会给某些 3rd 方应用程序带来问题

  3. 改变策略:不要监视应用程序的启动,而是创建一个启动器和edit the registry key for the application 来启动启动器而不是应用程序。这个策略是我倾向于的,但是如果我更改注册表项,我仍然不知道如何从启动器启动应用程序。

有没有我们没有考虑过的更好的解决方案? 如果不是,这 3 个中的哪一个会是“首选”?

谢谢!

【问题讨论】:

  • 您不能在用户尝试打开应用程序时关闭应用程序,然后显示弹出窗口,如果用户同意则再次启动应用程序?如果用户不同意,则只需关闭弹出窗口。
  • 这就是我上面描述的选项 1。在启动时终止进程是可以的,但某些 3rd 方程序可能不喜欢这种情况。

标签: c# monitoring


【解决方案1】:

您最好的选择是使用 windows hooks
使用钩子,您可以监视系统中某些类型的事件。例如正在执行的应用程序或拦截点击次数更多。
您也可以使用event tracing 来监控Windows 中应用程序的执行和终止。
这是我刚刚给出的链接中的一个示例:

using Diagnostics.Tracing;
using Diagnostics.Tracing.Parsers;
using System;
using System.Diagnostics;
using System.IO;

namespace ProcessMonitor
{

    /// <summary>
    /// The main program monitors processes (and image loads) using ETW.  
    /// </summary>
    class Program
    {
        /// <summary>
        /// This is a demo of using TraceEvent to activate a 'real time' provider that is listening to 
        /// the MyEventSource above.   Normally this event source would be in a differnet process,  but 
        /// it also works if this process generate the evnets and I do that here for simplicity.  
        /// </summary>
        static int Main(string[] args)
        {
            // Today you have to be Admin to turn on ETW events (anyone can write ETW events).   
            if (!(TraceEventSession.IsElevated() ?? false))
            {
                Console.WriteLine("To turn on ETW events you need to be Administrator, please run from an Admin process.");
                return -1;
            }

            // As mentioned below, sessions can outlive the process that created them.  Thus you need a way of 
            // naming the session so that you can 'reconnect' to it from another process.   This is what the name
            // is for.  It can be anything, but it should be descriptive and unique.   If you expect mulitple versions
            // of your program to run simultaneously, you need to generate unique names (e.g. add a process ID suffix) 
            var sessionName = "ProessMonitorSession";
            using (var session = new TraceEventSession(sessionName, null))  // the null second parameter means 'real time session'
            {
                // Note that sessions create a OS object (a session) that lives beyond the lifetime of the process
                // that created it (like Filles), thus you have to be more careful about always cleaning them up. 
                // An importanty way you can do this is to set the 'StopOnDispose' property which will cause the session to 
                // stop (and thus the OS object will die) when the TraceEventSession dies.   Because we used a 'using'
                // statement, this means that any exception in the code below will clean up the OS object.   
                session.StopOnDispose = true;

                // By default, if you hit Ctrl-C your .NET objects may not be disposed, so force it to.  It is OK if dispose is called twice.
                Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { session.Dispose(); };

                // prepare to read from the session, connect the ETWTraceEventSource to the session
                using (var source = new ETWTraceEventSource(sessionName, TraceEventSourceType.Session))
                {
                    Action<TraceEvent> action = delegate(TraceEvent data)
                    {
                        // Console.WriteLine("GOT EVENT: " + data.ToString());
                        var taskName = data.TaskName;
                        if (taskName == "ProcessStart" || taskName == "ProcessStop") 
                        {
                            string exe = (string) data.PayloadByName("ImageName");
                            string exeName = Path.GetFileNameWithoutExtension(exe);

                            int processId = (int) data.PayloadByName("ProcessID");
                            if (taskName == "ProcessStart")
                            {
                                int parentProcessId = (int)data.PayloadByName("ParentProcessID");
                                Console.WriteLine("{0:HH:mm:ss.fff}: {1,-12}: {2} ID: {3} ParentID: {4}", 
                                    data.TimeStamp, taskName, exeName, processId, parentProcessId);
                            }
                            else
                            {
                                int exitCode = (int) data.PayloadByName("ExitCode");
                                long cpuCycles = (long) data.PayloadByName("CPUCycleCount");
                                Console.WriteLine("{0:HH:mm:ss.fff}: {1,-12}: {2} ID: {3} EXIT: {4} CPU Cycles: {5:n0}",
                                    data.TimeStamp, taskName, exeName, processId, exitCode, cpuCycles);
                            }
                        }
                    };

                    // Hook up the parser that knows about Any EventSources regsitered with windows.  (e.g. the OS ones. 
                    var registeredParser = new RegisteredTraceEventParser(source);
                    registeredParser.All += action;

                    // You can also simply use 'logman query providers' to find out the GUID yourself and wire it in. 
                    var processProviderGuid = TraceEventSession.GetProviderByName("Microsoft-Windows-Kernel-Process");
                    if (processProviderGuid == Guid.Empty)
                    {
                        Console.WriteLine("Error could not find Microsoft-Windows-Kernel-Process etw provider.");
                        return -1;
                    }

                    // Using logman query providers Microsoft-Windows-Kernel-Process I get 
                    //     0x0000000000000010  WINEVENT_KEYWORD_PROCESS
                    //     0x0000000000000020  WINEVENT_KEYWORD_THREAD
                    //     0x0000000000000040  WINEVENT_KEYWORD_IMAGE
                    //     0x0000000000000080  WINEVENT_KEYWORD_CPU_PRIORITY
                    //     0x0000000000000100  WINEVENT_KEYWORD_OTHER_PRIORITY
                    //     0x0000000000000200  WINEVENT_KEYWORD_PROCESS_FREEZE
                    //     0x8000000000000000  Microsoft-Windows-Kernel-Process/Analytic
                    // So 0x10 is WINEVENT_KEYWORD_PROCESS
                    session.EnableProvider(processProviderGuid, TraceEventLevel.Informational, 0x10);

                    Console.WriteLine("Starting Listening for events");
                    // go into a loop processing events can calling the callbacks.  Because this is live data (not from a file)
                    // processing never completes by itself, but only because someone called 'source.Close()'.  
                    source.Process();
                    Console.WriteLine();
                    Console.WriteLine("Stopping Listening for events");
                }
            }
            return 0;
        }
    }

}

【讨论】:

  • 反对票是为了什么?想解释一下这里出了什么问题?
  • 您好,感谢您的建议。我发现两个 CodeProject 文章似乎指向正确的方向 codeproject.com/Articles/2018/…codeproject.com/Articles/11985/… 是否有任何其他示例显示更接近我们想要实现的机制?很可能,我可以通过这些文章弄清楚所有内容,但也许还有更好的例子。
  • @Lethi,您可能会发现msdn.microsoft.com/en-us/library/ms644977(v=VS.85).aspx 也很有用,尽管第一篇文章非常好。您可能还想看看blogs.msdn.microsoft.com/vancem/2013/03/09/…。顺便说一句,如果您在rohitab.com/discuss 提出这些问题,您的运气会更好。大约 5 到 6 年前,我正在处理 windows 挂钩和驱动程序开发,唯一对我有帮助的地方是 rohitab。所以试着在那里问这样的问题。
  • 我认为问题已得到解答。 Breze 甚至为我提供了可以使用的初始代码。
【解决方案2】:

我错过了什么吗? 最简单的解决方案似乎是在他们填写您的表单之前不要启动应用程序。

【讨论】:

  • 是的,这就是我们想要实现的。问题是用户可以继续双击快捷方式或直接双击exe。 Breeze 解决方案将允许我们挂钩进程并阻止它启动,直到他们填写表格。有没有比使用 hooks 更好的解决方案?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-22
  • 1970-01-01
相关资源
最近更新 更多