C# Monitor launch of an executable and do operation before the user an use it

江枫思渺然 提交于 2021-01-29 10:55:38

问题


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

Background

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".

Current solution

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.

The problem

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.

Possible solutions

  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.

  2. 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

  3. 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.

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

Thanks!


回答1:


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

}



回答2:


Have I missed something? The simplest solution seems to be not to launch the application until they have filled in your form.



来源:https://stackoverflow.com/questions/52436873/c-sharp-monitor-launch-of-an-executable-and-do-operation-before-the-user-an-use

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!