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.
Related
I am developing a .net application, where I am using FileSystemWatcher class and attached its Created event on a folder. I have to do action on this event (i.e. copy file to some other location). When I am putting a large size into the attached watch folder the event raised immediately even the file copy process still not completed. I don’t want to check this by file.open method.
Is there any way get notify that my file copy process into the watch folder has been completed and then my event get fire.
It is indeed a bummer that FileSystemWatcher (and the underlying ReadDirectoryChangesW API) provide no way to get notified when a new file has been fully created.
The best and safest way around this that I've come across so far (and that doesn't rely on timers) goes like this:
Upon receiving the Created event, start a thread that, in a loop, checks whether the file is still locked (using an appropriate retry interval and maximum retry count). The only way to check if a file is locked is by trying to open it with exclusive access: If it succeeds (not throwing an IOException), then the File is done copying, and your thread can raise an appropriate event (e.g. FileCopyCompleted).
I have had the exact same problem, and solved it this way:
Set FileSystemWatcher to notify when files are created and when they are modified.
When a notification comes in:
a. If there is no timer set for this filename (see below), set a timer to expire in a suitable interval (I commonly use 1 second).
b. If there is a timer set for this filename, cancel the timer and set a new one to expire in the same interval.
When a timer expires, you know that the associated file has been created or modified and has been untouched for the time interval. This means that the copy/modify is probably done and you can now process it.
You could listen for the modified event, and start a timer. If the modified event is raised again, reset the timer. When the timer has reached a certain value without the modify event being raised you can try to perform the copy.
I subscribe to the Changed- and Renamed-event and try to rename the file on every Changed-event catching the IOExceptions. If the rename succeeds, the copy has finished and the Rename-event is fired only once.
Three issues with FileSystemWatcher, the first is that it can send out duplicate creation events so you check for that with something like:
this.watcher.Created += (s, e) =>
{
if (!this.seen.ContainsKey(e.FullPath)
|| (DateTime.Now - this.seen[e.FullPath]) > this.seenInterval)
{
this.seen[e.FullPath] = DateTime.Now;
ThreadPool.QueueUserWorkItem(
this.WaitForCreatingProcessToCloseFileThenDoStuff, e.FullPath);
}
};
where this.seen is a Dictionary<string, DateTime> and this.seenInterval is a TimeSpan.
Next, you have to wait around for the file creator to finish writing it (the issue raised in the question). And, third, you must be careful because sometimes the file creation event gets thrown before the file can be opened without giving you a FileNotFoundException but it can also be removed before you can get a hold of it which also gives a FileNotFoundException.
private void WaitForCreatingProcessToCloseFileThenDoStuff(object threadContext)
{
// Make sure the just-found file is done being
// written by repeatedly attempting to open it
// for exclusive access.
var path = (string)threadContext;
DateTime started = DateTime.Now;
DateTime lastLengthChange = DateTime.Now;
long lastLength = 0;
var noGrowthLimit = new TimeSpan(0, 5, 0);
var notFoundLimit = new TimeSpan(0, 0, 1);
for (int tries = 0;; ++tries)
{
try
{
using (var fileStream = new FileStream(
path, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
{
// Do Stuff
}
break;
}
catch (FileNotFoundException)
{
// Sometimes the file appears before it is there.
if (DateTime.Now - started > notFoundLimit)
{
// Should be there by now
break;
}
}
catch (IOException ex)
{
// mask in severity, customer, and code
var hr = (int)(ex.HResult & 0xA000FFFF);
if (hr != 0x80000020 && hr != 0x80000021)
{
// not a share violation or a lock violation
throw;
}
}
try
{
var fi = new FileInfo(path);
if (fi.Length > lastLength)
{
lastLength = fi.Length;
lastLengthChange = DateTime.Now;
}
}
catch (Exception ex)
{
}
// still locked
if (DateTime.Now - lastLengthChange > noGrowthLimit)
{
// 5 minutes, still locked, no growth.
break;
}
Thread.Sleep(111);
}
You can, of course, set your own timeouts. This code leaves enough time for a 5 minute hang. Real code would also have a flag to exit the thread if requested.
This answer is a bit late, but if possible I'd get the source process to copy a small marker file after the large file or files and use the FileWatcher on that.
Try to set filters
myWatcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite;
I have a clickonce application, and I have set up several file handlers for this application (for the sake of this example, I want to handle files with either the .aaa or .bbb extensions).
If I select a single file with one of these extensions, my application starts up as expected, everything is good. But if I select multiple files and open them (either by hitting Enter or by right clicking and selecting Open), then multiple instances of my aopplication are started up - one instance per file that was selected.
This is not the behavior I expected, I want just one instance to start with multiple file entries in the AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData. Can this be achieved, or is my expectation incorrect?
Edit:
Just to elaborate: we have followed the single instance approach as mentioned by #Matthias, the first instance to start up creates a named server pipe. Subsequent instances then start up, detect that they are secondary, communicate their command line arguments (filename) through to the main instance via the named pipe, then quit. The main instance receives the filename via the named pipe, and does its thing (starts up a file import wizard).
The issue comes when a user selects several files (i.e. 5 files), then selects to open those files in the application. Instead of getting one secondary instance starting with 5 file names supplied on the command line, I'm getting 5 secondary instances of the application starting, each with a single filename on the command line. Each of these then creates a client named pipe and communicates that filename to the main instance - so the server named pipe receives 5 separate messages.
Follow up thoughts:
after chatting about this it occurs to me that maybe this is just the way registered file handlers work, maybe it is not related to clickonce. Maybe the solution is for the server named pipe to pause after receiving each message and to attempt to queue messages before actioning them?
You can achieve this by implementing a single instance application. If the application is already running (second call), you can use named pipes to inform the application (first call) of a file open event.
EDIT
Found a code snippet from an earlier project. I want to underline that the code definitely needs improvements, but it should be a good point where you can start from.
In your static main:
const string pipeName = "auDeo.Server";
var ownCmd = string.Join(" ", args);
try
{
using (var ipc = new IPC(pipeName))
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var form = new ServerForm();
ipc.MessageReceived += m =>
{
var remoteCmd = Encoding.UTF8.GetString(m);
form.Invoke(remoteCmd);
};
if (!string.IsNullOrEmpty(ownCmd))
form.Invoke(ownCmd);
Application.Run(form);
}
}
catch (Exception)
{
//MessageBox.Show(e.ToString());
if (string.IsNullOrEmpty(ownCmd))
return;
var msg = Encoding.UTF8.GetBytes(ownCmd);
IPC.SendMessage(pipeName, msg);
}
The IPC class:
public class IPC : IDisposable
{
public IPC(string pipeName)
{
Stream = new NamedPipeServerStream(pipeName,
PipeDirection.InOut,
1,
PipeTransmissionMode.Byte,
PipeOptions.Asynchronous);
AsyncCallback callback = null;
callback = delegate(IAsyncResult ar)
{
try
{
Stream.EndWaitForConnection(ar);
}
catch (ObjectDisposedException)
{
return;
}
var buffer = new byte[2000];
var length = Stream.Read(buffer, 0, buffer.Length);
var message = new byte[length];
Array.Copy(buffer, message, length);
if (MessageReceived != null)
MessageReceived(message);
Stream.Disconnect();
// ReSharper disable AccessToModifiedClosure
Stream.BeginWaitForConnection(callback, null);
// ReSharper restore AccessToModifiedClosure
};
Stream.BeginWaitForConnection(callback, null);
}
private NamedPipeServerStream Stream
{
get;
set;
}
#region IDisposable Members
public void Dispose()
{
if (Stream != null)
Stream.Dispose();
}
#endregion
public static void SendMessage(string pipeName, byte[] message)
{
using (var client = new NamedPipeClientStream(".", pipeName))
{
client.Connect();
client.Write(message, 0, message.Length);
client.Close();
}
}
~IPC()
{
Dispose();
}
public event MessageHandler MessageReceived;
}
The answer to the problem was to have a small delay at the server end of the pipe. In summary:
the first started instance of the app is the owner of the server end of the pipe, subsequent instances of the app are a client
When receiving a message from a client, a timer was started, if the timer was already started then it was reset. The passed file name is added to a list.
The timer delay was set to 2 seconds, once the tick event occurred (so it had been 2 seconds since the last client communication) the single instance server would take the appropriate action with the list of file names
This is not the behavior I expected, I want just one instance to start with multiple file entries in the AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData. Can this be achieved, or is my expectation incorrect?
My expectation was incorrect - you can only pass through a single file name to a registered file handler, each file name starts a separate instance of the handler.
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#.
EDIT: Ok I had a problem with one of the string concatenation functions, has nothing to do with threads, but knowing that it couldn't be a problem with threading lead me to the answer thank you for answering.
I am making a simple tcp/ip chat program for practicing threads and tcp/ip. I was using asynchronous methods but had a problem with concurrency so I went to threads and blocking methods (not asynchronous). I have two private variables defined in the class, not static:
string amessage = string.Empty;
int MessageLength;
and a Thread
private Thread BeginRead;
Ok so I call a function called Listen ONCE when the client starts:
public virtual void Listen(int byteLength)
{
var state = new StateObject {Buffer = new byte[byteLength]};
BeginRead = new Thread(ReadThread);
BeginRead.Start(state);
}
and finally the function to receive commands and process them, I'm going to shorten it because it is really long:
private void ReadThread(object objectState)
{
var state = (StateObject)objectState;
int byteLength = state.Buffer.Length;
while (true)
{
var buffer = new byte[byteLength];
int len = MySocket.Receive(buffer);
if (len <= 0) return;
string content = Encoding.ASCII.GetString(buffer, 0, len);
amessage += cleanMessage.Substring(0, MessageLength);
if (OnRead != null)
{
var e = new CommandEventArgs(amessage);
OnRead(this, e);
}
}
}
Now, as I understand it only one thread at a time will enter BeginRead, I call Receive, it blocks until I get data, and then I process it. The problem: the variable amessage will change it's value between statements that do not touch or alter the variable at all, for example at the bottom of the function at: if (OnRead != null) "amessage" will be equal to 'asdf' and at if (OnRead != null) "amessage" will be equal to qwert. As I understand it this is indicative of another thread changing the value/running asynchronously. I only spawn one thread to do the receiving and the Receive function is blocking, how could there be two threads in this function and if there is only one thread how does amessage's value change between statements that don't affect it's value. As a side note sorry for spamming the site with these questions but I'm just getting a hang of this threading story and it's making me want to sip cyanide.
Thanks in advance.
EDIT:
Here is my code that calls the Listen Method in the client:
public void ConnectClient(string ip,int port)
{
client.Connect(ip,port);
client.Listen(5);
}
and in the server:
private void Accept(IAsyncResult result)
{
var client = new AbstractClient(MySocket.EndAccept(result));
var e = new CommandEventArgs(client, null);
Clients.Add(client);
client.Listen(5);
if (OnClientAdded != null)
{
var target = (Control) OnClientAdded.Target;
if (target != null && target.InvokeRequired)
target.Invoke(OnClientAdded, this, e);
else
OnClientAdded(this, e);
}
client.OnRead += OnRead;
MySocket.BeginAccept(new AsyncCallback(Accept), null);
}
All this code is in a class called AbstractClient. The client inherits the Abstract client and when the server accepts a socket it create's it's own local AbstractClient, in this case both modules access the functions above however they are different instances and I couldn't imagine threads from different instances combining especially as no variable is static.
Well, this makes no sense the way you described it. Which probably means that what you think is going on is not what is really happening. Debugging threaded code is quite difficult, very hard to capture the state of the program at the exact moment it misbehaves.
A generic approach is to add logging to your code. Sprinkle your code with Debug.WriteLine() statements that shows the current value of the variable, along with the thread's ManagedId. You get potentially a lot of output, but somewhere you'll see it going wrong. Or you get enough insight in how thread(s) are interacting to guess the source of the problem.
Just adding the logging can in itself solve the problem because it alters the timing of code. Sucks when that happens.
I assume OnRead is firing an event dispatched on a thread pool thread. If any registered event handler is writing to amessage, its value could change any time you're in the reading loop.
Still not very clear where you are gettingthe value assigned to amessage in the loop. Should cleanmessage read content?
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#.