.NET Process Monitor - c#

Is there a way to determine when the last time a specific machine last ran a process?
I can use the following to determine if a process is running, but the application cannot grab the process if it has since stopped.
Process[] process = Process.GetProcessesByName(processName, serverName);

WMI provides a way to track processes starting and terminating with the Win32_ProcessTrace classes. Best shown with an example. Start a new Console application, Project + Add Reference, select System.Management. Paste this code:
using System;
using System.Management;
class Process {
public static void Main() {
ManagementEventWatcher startWatch = new ManagementEventWatcher(
new WqlEventQuery("SELECT * FROM Win32_ProcessStartTrace"));
startWatch.EventArrived += new EventArrivedEventHandler(startWatch_EventArrived);
startWatch.Start();
ManagementEventWatcher stopWatch = new ManagementEventWatcher(
new WqlEventQuery("SELECT * FROM Win32_ProcessStopTrace"));
stopWatch.EventArrived += new EventArrivedEventHandler(stopWatch_EventArrived);
stopWatch.Start();
Console.WriteLine("Press any key to exit");
while (!Console.KeyAvailable) System.Threading.Thread.Sleep(50);
startWatch.Stop();
stopWatch.Stop();
}
static void stopWatch_EventArrived(object sender, EventArrivedEventArgs e) {
Console.WriteLine("Process stopped: {0}", e.NewEvent.Properties["ProcessName"].Value);
}
static void startWatch_EventArrived(object sender, EventArrivedEventArgs e) {
Console.WriteLine("Process started: {0}", e.NewEvent.Properties["ProcessName"].Value);
}
}
Edit the manifest so this program runs elevated. Then simply start some programs to see it at work. Beware that it is not especially quick.

You won't be able to do this using the Process class. However, it should be possible to figure out when an application was last run by configuring audit process tracking in Windows. The following links might get you started:
Audit process tracking
How can I track what programs come and go on my machine?
The process tracking will create entries in the Windows event log which you can then access using C#.

Related

Redirecting standard output

I have a C# application A which calls another C# application B, which calls multiple instances of application C.
I would like to redirect the output of application C to the output of application A.
I already have a working redirection of the output of application B into the output of application A.
Now, from within application B's code, I'm redirecting every process's output, and I'm printing the redirected output to the console. Unfortunately, for some reason, nothing is printed.
(I'm currently testing it without using application A - I'm only running application B).
Here is the code:
private void runSingleFile (string execFile, string commandArgs)
{
Process processToRun = new Process();
processToRun .StartInfo.FileName = execFile;
processToRun .StartInfo.Arguments = commandArgs;
processToRun .StartInfo.UseShellExecute = false;
processToRun .StartInfo.RedirectStandardOutput = true;
processToRun .OutputDataReceived += outputRedirection;
processToRun.Start();
Console.WriteLine("");
Thread.Sleep(100);
processToRun.BeginOutputReadLine();
}
private void outputRedirection(object sendingProcess, DataReceivedEventArgs outLine)
{
try
{
if (outLine.Data != null)
Console.WriteLine(outLine.Data);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return;
}
}
Any ideas?
The .net Process object doesn't make it easy for one to "do the right thing" with respect to correctly handling the IO.
These pages list some of the concerns that need to be dealt with:
MSDN's ProcessStartInfo.RedirectStandardOutput Property page
Raymond's blog Be careful when redirecting both a process's stdin and stdout to pipes...
C# test.net's How to use System.Diagnostics.Process correctly
From your code sample, here are some of the items that you will want to look at:
You will want to capture both stderr and stdin (the latter should be immediately closed if not used).
You also need to be aware that you can still receive events on your outputRedirection callback after the child process has exited.
The code you have posted works. Given test program
// ConsoleApplication2.exe
static void Main(string[] args)
{
Console.WriteLine("Test1...");
Console.WriteLine("Test2...");
System.Threading.Thread.Sleep(100);
Console.WriteLine("Test3...");
}
called as :
static void Main(string[] args)
{
runSingleFile("ConsoleApplication2.exe", "");
runSingleFile("ConsoleApplication2.exe", "");
runSingleFile("ConsoleApplication2.exe", "");
Console.ReadLine();
}
produces output :
Clearly something else is not working in another part of your code (Something you have not shown us).
The most likely explanation, if application C is working correctly, is that application B is terminating before all instances of C have completed. You may have to add some code that makes B wait for all instances of C to return.
Note that :
static void Main(string[] args)
{
runSingleFile("ConsoleApplication2.exe", "");
runSingleFile("ConsoleApplication2.exe", "");
runSingleFile("ConsoleApplication2.exe", "");
//Console.ReadLine(); // ** Don't wait!
}
completes immediately and fails to return some or all of the data (especially if you remove the Sleep call in runSingleFile.
Consider :
static long processCount = 0; //ADD
static void runSingleFile(string execFile, string commandArgs)
{
Interlocked.Increment(ref processCount); //ADD
Process processToRun = new Process();
processToRun.StartInfo.FileName = execFile;
processToRun.StartInfo.Arguments = commandArgs;
processToRun.StartInfo.UseShellExecute = false;
processToRun.StartInfo.RedirectStandardOutput = true;
processToRun.OutputDataReceived += outputRedirection;
processToRun.EnableRaisingEvents = true; //ADD
processToRun.Exited += processExited; //ADD
processToRun.Start();
Console.WriteLine("");
processToRun.BeginOutputReadLine();
}
static void processExited(object sender, EventArgs e)
{
Interlocked.Decrement(ref processCount);
}
with
static void Main(string[] args)
{
runSingleFile("ConsoleApplication2.exe", "");
runSingleFile("ConsoleApplication2.exe", "");
runSingleFile("ConsoleApplication2.exe", "");
while (Interlocked.Read(ref processCount) > 0)
{
System.Threading.Thread.Sleep(100);
}
}
The above then makes application B wait until all spawned processes have returned. The example is simplistic and obviously can be improved upon, but it demonstrates the problem, I think, and offers a method for the solution.
You might be tempted to use something more elegant like WaitHandle.WaitAll(), but this does introduce the problem that josh noted where your output events may not fire until after the process terminates - the process handle will signal that it has terminated but its posted messages may still be in the queue. Waiting on the Exited event tidies up that race condition since that event will always be the last message in the queue (AFAIK).
Also, note the use of Interlocked functions here - Console.WriteLine is threadsafe but other variable access is not. Since there is no synchronization context in a console application the events raised by spawned processes are handled by threads in the threadpool (not the main thread of the console application). This introduces all of the issues associated with multithreading which must be managed appropriately.

How to wait for process that will be started?

My application need to wait until specific process will be started. I am doing it this way
while (Process.GetProcessesByName("someProcess").Length == 0)
{
Thread.Sleep(100);
}
Is there any other way(more elegant) how to accomplish this, with functionality similar to WaitForExit()? Thanks for answers.
Take a look at the ManagementEventWatcher class.
Specifically, the code example at the bottom of the link shows you how to setup a ManagementEventWatcher to be notified when a new process is created.
Code copied from MSDN code example (could stand a little cleanup):
using System;
using System.Management;
// This example shows synchronous consumption of events.
// The client is blocked while waiting for events.
public class EventWatcherPolling
{
public static int Main(string[] args)
{
// Create event query to be notified within 1 second of
// a change in a service
WqlEventQuery query =
new WqlEventQuery("__InstanceCreationEvent",
new TimeSpan(0,0,1),
"TargetInstance isa \"Win32_Process\"");
// Initialize an event watcher and subscribe to events
// that match this query
ManagementEventWatcher watcher =
new ManagementEventWatcher();
watcher.Query = query;
// times out watcher.WaitForNextEvent in 5 seconds
watcher.Options.Timeout = new TimeSpan(0,0,5);
// Block until the next event occurs
// Note: this can be done in a loop if waiting for
// more than one occurrence
Console.WriteLine(
"Open an application (notepad.exe) to trigger an event.");
ManagementBaseObject e = watcher.WaitForNextEvent();
//Display information from the event
Console.WriteLine(
"Process {0} has been created, path is: {1}",
((ManagementBaseObject)e
["TargetInstance"])["Name"],
((ManagementBaseObject)e
["TargetInstance"])["ExecutablePath"]);
//Cancel the subscription
watcher.Stop();
return 0;
}
}
Edit
Simplifed example with TargetInstance.Name = 'someProcess' filter added.
var query = new WqlEventQuery(
"__InstanceCreationEvent",
new TimeSpan(0, 0, 1),
"TargetInstance isa \"Win32_Process\" and TargetInstance.Name = 'someProcess'"
);
using(var watcher = new ManagementEventWatcher(query))
{
ManagementBaseObject e = watcher.WaitForNextEvent();
//someProcess created.
watcher.Stop();
}
As far as I know, there is nothing on the Process class that will make it simple.
If you don't have control over the source code in the sub-process, then you should probably go with the WMI solution that Calgary Coder provided.
If you do have control of the code in the sub-process, then there are a few additional ways you can solve this problem. I have used WCF (using an IPC binding), .Net Remoting, and Mutex.
The advantage of these solutions is that the sub process has to opt into it. The sub process is free to wait until it has completed its startup initialization routines before letting the parent app know that it is "ready".
There are samples at each of those links that should give you a start on solving this problem. If you are interested in going with a specific one, and have problems, let me know and I'll post some sample code for that particular solution.

C# Determine when an external app is run or exited [duplicate]

Is there a way to determine when the last time a specific machine last ran a process?
I can use the following to determine if a process is running, but the application cannot grab the process if it has since stopped.
Process[] process = Process.GetProcessesByName(processName, serverName);
WMI provides a way to track processes starting and terminating with the Win32_ProcessTrace classes. Best shown with an example. Start a new Console application, Project + Add Reference, select System.Management. Paste this code:
using System;
using System.Management;
class Process {
public static void Main() {
ManagementEventWatcher startWatch = new ManagementEventWatcher(
new WqlEventQuery("SELECT * FROM Win32_ProcessStartTrace"));
startWatch.EventArrived += new EventArrivedEventHandler(startWatch_EventArrived);
startWatch.Start();
ManagementEventWatcher stopWatch = new ManagementEventWatcher(
new WqlEventQuery("SELECT * FROM Win32_ProcessStopTrace"));
stopWatch.EventArrived += new EventArrivedEventHandler(stopWatch_EventArrived);
stopWatch.Start();
Console.WriteLine("Press any key to exit");
while (!Console.KeyAvailable) System.Threading.Thread.Sleep(50);
startWatch.Stop();
stopWatch.Stop();
}
static void stopWatch_EventArrived(object sender, EventArrivedEventArgs e) {
Console.WriteLine("Process stopped: {0}", e.NewEvent.Properties["ProcessName"].Value);
}
static void startWatch_EventArrived(object sender, EventArrivedEventArgs e) {
Console.WriteLine("Process started: {0}", e.NewEvent.Properties["ProcessName"].Value);
}
}
Edit the manifest so this program runs elevated. Then simply start some programs to see it at work. Beware that it is not especially quick.
You won't be able to do this using the Process class. However, it should be possible to figure out when an application was last run by configuring audit process tracking in Windows. The following links might get you started:
Audit process tracking
How can I track what programs come and go on my machine?
The process tracking will create entries in the Windows event log which you can then access using C#.

Killing Java Process from C# Console App

I posted about this a little while ago, but I resolved the other issue and ran into one more. I am about to deploy this program to 28 hosting machines so I want to make sure this is working before I do so.
I wrote a little c# NET application that is basically a wrapper for a Java application, when my app starts, the Java app starts, when my app closes, it closes, and so on.
Everything works properly except that when I close my application, the Java application continues to run. When I create the process, I store the Process var in a variable outside of the methods, and then use that when my application goes to shutdown. For whatever reason though it is not terminating the Java application.
class Program
{
private static Process minecraftProcess;
public static void LaunchMinecraft(String file, String memoryValue)
{
String memParams = "-Xmx" + memoryValue + "M" + " -Xms" + memoryValue + "M ";
String args = memParams + "-jar " + file + " nogui";
ProcessStartInfo processInfo = new ProcessStartInfo("java.exe", args);
processInfo.CreateNoWindow = true;
processInfo.UseShellExecute = false;
try
{
//using (Process minecraftProcess = Process.Start(processInfo))
using (minecraftProcess = Process.Start(processInfo))
{
minecraftProcess.WaitForExit();
}
}
catch
{
// Log Error
}
}
static void Main(string[] args)
{
Arguments CommandLine = new Arguments(args);
// Hook ProcessExit Event
AppDomain.CurrentDomain.ProcessExit += new EventHandler(Current_ProcessExit);
if (CommandLine["file"] != null && CommandLine["memory"] != null)
{
// Launch the Application (Command Line Parameters)
LaunchMinecraft(CommandLine["file"], CommandLine["memory"]);
}
else
{
// Launch the Application (Default Parameters)
LaunchMinecraft("minecraft_server.jar", "1024");
}
}
static void Current_ProcessExit(object sender, EventArgs e)
{
System.Threading.Thread.Sleep(10000);
// If we have an active Minecraft Service, Shut it down
if (minecraftProcess != null)
{
minecraftProcess.Kill();
}
}
}
You can't Sleep in a ProcessExit handler.
The documentation states:
The total execution time of all
ProcessExit event handlers is limited,
just as the total execution time of
all finalizers is limited at process
shutdown. The default is two seconds.
An unmanaged host can change this
execution time by calling the
ICLRPolicyManager::SetTimeout method
with the OPR_ProcessExit enumeration
value.
Nevermind, I just realized the minecraftProcess variable is static.
Don't know if you did not solve this issue by yourself but:
You should be aware that there are Start methods for instances (returning bool) and static (returning a object).
You should not use using with something other than using-local variables!
Just this should work fine:
minecraftProcess = Process.Start(processInfo)
minecraftProcess.WaitForExit();

How to pass arguments to a console application if it is already running?

I use a Console Application in Windows Mobile to handle incoming message interception. In the same console application i accept parameters (string args[]) which based on the parameters, register the message interceptor.
InterceptorType is a enum
static void Main(string[] args)
{
if (args[0] == "Location")
{
addInterception(InterceptorType.Location, args[1],args[2]);
}
}
private static void addInterception(InterceptorType type, string Location, string Number )
{
if (type == InterceptorType.Location)
{
using (MessageInterceptor interceptor = new MessageInterceptor(InterceptionAction.NotifyAndDelete, false))
{
interceptor.MessageCondition = new MessageCondition(MessageProperty.Sender, MessagePropertyComparisonType.Contains, Number, false);
string myAppPath = Assembly.GetExecutingAssembly().GetName().CodeBase;
interceptor.EnableApplicationLauncher("Location", myAppPath);
interceptor.MessageReceived += new MessageInterceptorEventHandler(interceptor_MessageReceived);
}
}
}
static void interceptor_MessageReceived(object sender, MessageInterceptorEventArgs e)
{
//Do something
}
I made this a console application because i want it keep running in the background and intercept incoming messages.
This works fine for the first time. But the problem is that I have to keep calling the addInterception method to add subsequent interception rules. This makes the console application start again and again for each time i add a rule. How do i make this run only once and add more message interceptor rules?
Since you already have a method in place to call the command prompt once, update your logic with some simple looping so you can pass N commands.
EDIT: I wrote it a fully compileable example to show you exactly what I am talking about. Note how the child process can be called any number of times without re-launching. This is not just a simple command line launch with arguments being passed because that idea will lead to X processes which is exactly what you do not want.
PARENT PROCESS: (The one with System.Diagnostics.Process)
/// <summary>
/// This is the calling application. The one where u currently have System.Diagnostics.Process
/// </summary>
class Program
{
static void Main(string[] args)
{
System.Diagnostics.Process p = new Process();
p.StartInfo.CreateNoWindow = false;
p.StartInfo.UseShellExecute = false;
p.StartInfo.FileName = #"C:\AppfolderThing\ConsoleApplication1.exe";
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardOutput = true;
p.Start();
p.OutputDataReceived += delegate(object sender, DataReceivedEventArgs e)
{
Console.WriteLine("Output received from application: {0}", e.Data);
};
p.ErrorDataReceived += delegate(object sender, DataReceivedEventArgs e)
{
Console.WriteLine("Output received from application: {0}", e.Data);
};
p.BeginErrorReadLine();
p.BeginOutputReadLine();
StreamWriter inputStream = p.StandardInput;
inputStream.WriteLine(1);
inputStream.WriteLine(2);
inputStream.WriteLine(-1);//tell it to exit
p.WaitForExit();
}
}
CHILD PROCESS:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication3
{
enum InterceptorType
{
foo,
bar,
zee,
brah
}
/// <summary>
/// This is the child process called by System.Diagnostics.Process
/// </summary>
class Program
{
public static void Main()
{
while (true)
{
int command = int.Parse(Console.ReadLine());
if (command == -1)
Environment.Exit(0);
else
addInterception((InterceptorType)command, "some location", "0");
}
}
private static void addInterception(InterceptorType type, string Location, string Number)
{
switch (type)
{
case InterceptorType.foo: Console.WriteLine("bind foo"); break;
case InterceptorType.bar: Console.WriteLine("bind bar"); break;
default: Console.WriteLine("default bind zee"); break;
}
}
static void interceptor_MessageReceived(object sender, EventArgs e)
{
//Do something
}
}
}
Note that codeplex has a managed service library.
EDIT
It seems that people are misunterstanding your question (or I am) so here's some clarification on how I'm seeing the problem.
You have an console app that takes in command-line parameters. These parameters are used for something (the what is irrelevant actually). You want to be able to add parameters after the app is already running by calling the app with new command line args.
What is happening is that when you call the app any time after teh first, a new instance of the process starts up instead of the command-line arguments going to the existing, already running application.
END EDIT
The solution is fairly straightforward and requires two pieces.
You need a named mutex. For whatever (poor) reason, the CF doesn't expose a version of a mutex that takes a name, so you have to P/Invoke CreateMutex or use a library (like the SDF) that already has it. Your app needs to create the mutex at startup and check to see if it already exists. if it doesn't you're the first running instance and run as normal. If the mutex exists, you need to pass your command line args to the one that is already running via a P2P queue then simply exits.
After checking the mutex, the first instance spawns a worker thread. This thread listens on a P2P queue for messages. When they come in, you handle them.

Categories