Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I programmatically use the "using" keyword in C#?

Tags:

c#

process

I have some System.Diagnotics.Processes to run. I'd like to call the close method on them automatically. Apparently the "using" keyword does this for me.

Is this the way to use the using keyword?

foreach(string command in S) // command is something like "c:\a.exe"
{
    try
    {
        using(p = Process.Start(command))
        {
            // I literally put nothing in here.
        }
    }
    catch (Exception e)
    {
        // notify of process failure
    }
}

I'd like to start multiple processes to run concurrently.

like image 347
MrDatabase Avatar asked Oct 08 '08 23:10

MrDatabase


1 Answers

using(p = Process.Start(command))

This will compile, as the Process class implements IDisposable, however you actually want to call the Close method.
Logic would have it that the Dispose method would call Close for you, and by digging into the CLR using reflector, we can see that it does in fact do this for us. So far so good.

Again using reflector, I looked at what the Close method does - it releases the underlying native win32 process handle, and clears some member variables. This (releasing external resources) is exactly what the IDisposable pattern is supposed to do.

However I'm not sure if this is what you want to achieve here.

Releasing the underlying handles simply says to windows 'I am no longer interested in tracking this other process'. At no point does it actually cause the other process to quit, or cause your process to wait.

If you want to force them quit, you'll need to use the p.Kill() method on the processes - however be advised it is never a good idea to kill processes as they can't clean up after themselves, and may leave behind corrupt files, and so on.

If you want to wait for them to quit on their own, you could use p.WaitForExit() - however this will only work if you're waiting for one process at a time. If you want to wait for them all concurrently, it gets tricky.

Normally you'd use WaitHandle.WaitAll for this, but as there's no way to get a WaitHandle object out of a System.Diagnostics.Process, you can't do this (seriously, wtf were microsoft thinking?).

You could spin up a thread for each process, and call `WaitForExit in those threads, but this is also the wrong way to do it.

You instead have to use p/invoke to access the native win32 WaitForMultipleObjects function.

Here's a sample (which I've tested, and actually works)

[System.Runtime.InteropServices.DllImport( "kernel32.dll" )]
static extern uint WaitForMultipleObjects( uint nCount, IntPtr[] lpHandles, bool bWaitAll, uint dwMilliseconds );

static void Main( string[] args )
{
    var procs = new Process[] {
        Process.Start( @"C:\Program Files\ruby\bin\ruby.exe", "-e 'sleep 2'" ),
        Process.Start( @"C:\Program Files\ruby\bin\ruby.exe", "-e 'sleep 3'" ),
        Process.Start( @"C:\Program Files\ruby\bin\ruby.exe", "-e 'sleep 4'" ) };
    // all started asynchronously in the background

    var handles = procs.Select( p => p.Handle ).ToArray();
    WaitForMultipleObjects( (uint)handles.Length, handles, true, uint.MaxValue ); // uint.maxvalue waits forever

}
like image 163
Orion Edwards Avatar answered Nov 10 '22 00:11

Orion Edwards