C# 监视可执行文件的启动并在用户使用它之前执行操作 [英] C# Monitor launch of an executable and do operation before the user an use it

查看:55
本文介绍了C# 监视可执行文件的启动并在用户使用它之前执行操作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am writing a test app to monitor another windows application(s) and perform operations before the user is allowed to use them.

背景

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

We have users that can access computers and launch applications. For some of these applications, we want the user to fill a little form and then they would be allowed to use the application. At the same time, we want to keep track of the total run time of the application (i.e. how long the user has used the application). The application that a user can run is not all 3rd party apps and we have no control over their "quality".

当前的解决方案

使用这篇代码项目文章和 WMI,我创建了一个监控应用程序,用于跟踪应用程序的打开和关闭显示要填写的表单.

Using this Code Project article and WMI, I created a monitoring app that keep track of the opening and closing of an application displays the form to be filled.

问题

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

I am testing the monitoring app using Calculator.exe as an example. The monitoring detects correctly the launch and the close of the executable and we can kill the app if the user cancel the form that pops up. We can also write a log with the data from the form and the start and end time. Unfortunately, the executable is not "bound" in any way to the app and we cannot prevent the user from simply ignore the monitoring app form and use the application they launched.

可能的解决方案

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

  1. kill the launched application, display the form and re-launch the application if the user submit all the info. This solution would work, but some of the applications may not be happy to be abruptly killed.

使用暂停已启动应用程序的线程此答案中描述的解决方案.我的疑问是关于暂停线程.如上所述,我们不知道第 3 方应用程序的编写情况如何.是否存在死锁风险?同样在这种情况下,终止进程可能会给某些 3rd 方应用程序带来问题

Suspend the thread of the launched application using the solution described in this answer. My doubt here is about suspending the thread. As mentioned above, we do not know how well are the 3rd party application written. Is there a risk of deadlocks? Also in this case, killing the process might present an issue with some of the 3rd party applications

改变策略:不是监控应用程序的启动,而是创建一个启动器和编辑应用程序的注册表项 以启动启动器而不是应用程序.我倾向于采用这种策略,但如果更改注册表项,我仍然不知道如何从启动器启动应用程序.

Change tactic: instead of monitoring the launch of an application, make a launcher and edit the registry key for the application to start the launcher instead of the application. This strategy is what I am leaning toward, but I still don't know how to launch the application from the launcher if I change the registry key.

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

Is there a better solution we are not contemplating? If not, which of the 3 would be the "go-to"?

谢谢!

推荐答案

你最好的选择是使用 windows 钩子.
使用钩子可以监视系统的某些类型的事件.例如正在执行的应用程序或拦截更多点击.
您也可以使用 事件跟踪,用于监视 Windows 中应用程序的执行和终止.
这是我刚刚提供的链接中的一个示例:

Your best option is to use windows hooks.
Using a hook you can monitor the system for certain types of events. such as an application being executed or intercepting clicks an a lot more.
You may also use event tracing for monitoring the execution and termination of application in windows.
here's an example taken from the link I just gave:

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;
        }
    }

}

这篇关于C# 监视可执行文件的启动并在用户使用它之前执行操作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆