[MonoDevelop] Process with Mono

guybrush.d thera at interfree.it
Wed Dec 8 14:58:15 EST 2010


Ciao Adam,
thanks for you  interest, i just followed the tutorial on the mono
website for the thread programming, and i looked over google what i need
is to update the textview (and next a progressbar) while the standard output
change, i've tried with the vte terminal class too, but the application
freezes till
the process has completed, maybe i can try to swap the tread! Usually on
windows i use 
two classes that i add to my apps i tried to translate it for mono but i'm
not yet so skilled, 
in fact as soon as i'll solve this i will prepare a "standard" class to
insert into my future GTK# apps, 
here is the two classes i use on windows:

First class implement the execution of the process:

namespace TestProcessCaller
{
    /// <summary>
    /// Delegate used by the events StdOutReceived and
    /// StdErrReceived...
    /// </summary>
    public delegate void DataReceivedHandler(object sender,
        DataReceivedEventArgs e);

    /// <summary>
    /// Event Args for above delegate
    /// </summary>
    public class DataReceivedEventArgs : EventArgs
    {
        /// <summary>
        /// The text that was received
        /// </summary>
        public string Text;
        /// <summary>
        /// Constructor
        /// </summary>
        /// The text that was received for this event to be triggered.
        public DataReceivedEventArgs(string text)
        {
            Text = text;
        }
    }

    /// <summary>
    /// This class can launch a process (like a bat file, perl
    /// script, etc) and return all of the StdOut and StdErr
    /// to GUI app for display in textboxes, etc.
    /// </summary>
    /// <remarks>
    /// This class (c) 2003 Michael Mayer
    /// Use it as you like (public domain licensing).
    /// Please post any bugs / fixes to the page where
    /// you downloaded this code.
    /// </remarks>
	public class ProcessCaller : AsyncOperation
	{

        /// <summary>
        /// The command to run (should be made into a property)
        /// </summary>
        public string FileName;
        /// <summary>
        /// The Arguments for the cmd (should be made into a property)
        /// </summary>
        public string Arguments;

        /// <summary>
        /// The WorkingDirectory (should be made into a property)
        /// </summary>
        public string WorkingDirectory;

        /// <summary>
        /// Fired for every line of stdOut received.
        /// </summary>
        public event DataReceivedHandler StdOutReceived;

        /// <summary>
        /// Fired for every line of stdErr received.
        /// </summary>
        public event DataReceivedHandler StdErrReceived;

        /// <summary>
        /// Amount of time to sleep on threads while waiting
        /// for the process to finish.
        /// </summary>
        public int SleepTime = 500;

        /// <summary>
        /// The process used to run your task
        /// </summary>
        private Process process;

        /// <summary>
        /// Initialises a ProcessCaller with an association to the
        /// supplied ISynchronizeInvoke.  All events raised from this
        /// object will be delivered via this target.  (This might be a
        /// Control object, so events would be delivered to that Control's
        /// UI thread.)
        /// </summary>
        /// An object implementing the
        /// ISynchronizeInvoke interface.  All events will be delivered
        /// through this target, ensuring that they are delivered to the
        /// correct thread.
        public ProcessCaller(ISynchronizeInvoke isi)
            : base(isi)
        {
        }

// This constructor only works with changes to AsyncOperation...
//        /// <summary>
//        /// Initialises a ProcessCaller without an association to an
//        /// ISynchronizeInvoke.  All events raised from this object
//        /// will be delievered via the worker thread.
//        /// </summary>
//        public ProcessCaller()
//        {
//        }

        /// <summary>
        /// Launch a process, but do not return until the process has
exited.
        /// That way we can kill the process if a cancel is requested.
        /// </summary>
        protected override void DoWork()
        {
            StartProcess();

            // Wait for the process to end, or cancel it
            while (! process.HasExited)
            {
                Thread.Sleep(SleepTime); // sleep
                if (CancelRequested)
                {
                    // Not a very nice way to end a process,
                    // but effective.
                    process.Kill();
                    AcknowledgeCancel();
                }
            }
        }

        /// <summary>
        /// This method is generally called by DoWork()
        /// which is called by the base classs Start()
        /// </summary>
        protected virtual void StartProcess()
        {
            // Start a new process for the cmd
            process = new Process();
            process.StartInfo.UseShellExecute = false;
            process.StartInfo.RedirectStandardOutput = true;
            process.StartInfo.RedirectStandardError = true;
            process.StartInfo.CreateNoWindow = true;
            process.StartInfo.FileName = FileName;
            process.StartInfo.Arguments = Arguments;
            process.StartInfo.WorkingDirectory = WorkingDirectory;
            process.Start();

            
            // Invoke stdOut and stdErr readers - each
            // has its own thread to guarantee that they aren't
            // blocked by, or cause a block to, the actual
            // process running (or the gui).
            new MethodInvoker(ReadStdOut).BeginInvoke(null, null);
            new MethodInvoker(ReadStdErr).BeginInvoke(null, null);

        }

        /// <summary>
        /// Handles reading of stdout and firing an event for
        /// every line read
        /// </summary>
        protected virtual void ReadStdOut()
        {
            string str;
            while ((str = process.StandardOutput.ReadLine()) != null)
            {
                FireAsync(StdOutReceived, this, new
DataReceivedEventArgs(str));
            }
        }

        /// <summary>
        /// Handles reading of stdErr
        /// </summary>
        protected virtual void ReadStdErr()
        {
            string str;
            while ((str = process.StandardError.ReadLine()) != null)
            {
                FireAsync(StdErrReceived, this, new
DataReceivedEventArgs(str));
            }
        }

	}
}


Second class make the asynchronous operations:

namespace TestProcessCaller
{
    /// <summary>
    /// Exception thrown by AsyncUtils.AsyncOperation.Start when an
    /// operation is already in progress.
    /// </summary>
    public class AlreadyRunningException : System.ApplicationException
    {
        /// <summary>
        /// 
        /// </summary>
        public AlreadyRunningException() : base("Asynchronous operation
already running")
        { }
    }

    /// <summary>
    /// This base class is designed to be used by lengthy operations that
wish to
    /// support cancellation.  It also allows those operations to invoke
delegates
    /// on the UI Thread of a hosting control.
    /// </summary>
    /// <remarks>
    /// This class is from the MSDN article:
    ///
http://msdn.microsoft.com/msdnmag/issues/03/02/Multithreading/default.aspx
    /// (C) 2001-2002 I D Griffiths
    /// Please see the article for a complete description of the intentions
and
    /// operation of this class.
    /// </remarks>
    public abstract class AsyncOperation
    {
        /// <summary>
        /// Initialises an AsyncOperation with an association to the
        /// supplied ISynchronizeInvoke.  All events raised from this
        /// object will be delivered via this target.  (This might be a
        /// Control object, so events would be delivered to that Control's
        /// UI thread.)
        /// </summary>
        /// An object implementing the
        /// ISynchronizeInvoke interface.  All events will be delivered
        /// through this target, ensuring that they are delivered to the
        /// correct thread.
        public AsyncOperation(ISynchronizeInvoke target)
        {
            isiTarget = target;
            isRunning = false;
        }

        /// <summary>
        /// Launch the operation on a worker thread.  This method will
        /// return immediately, and the operation will start asynchronously
        /// on a worker thread.
        /// </summary>
        public void Start()
        {
            lock(this)
            {
                if (isRunning)
                {
                    throw new AlreadyRunningException();
                }
                // Set this flag here, not inside InternalStart, to avoid
                // race condition when Start called twice in quick
                // succession.
                isRunning = true;
            }
            new MethodInvoker(InternalStart).BeginInvoke(null, null);
        }


        /// <summary>
        /// Attempt to cancel the current operation.  This returns
        /// immediately to the caller.  No guarantee is made as to
        /// whether the operation will be successfully cancelled.  All
        /// that can be known is that at some point, one of the
        /// three events Completed, Cancelled, or Failed will be raised
        /// at some point.
        /// </summary>
        public virtual void Cancel()
        {
            lock(this)
            {
                cancelledFlag = true;
            }
        }

        /// <summary>
        /// Attempt to cancel the current operation and block until either
        /// the cancellation succeeds or the operation completes.
        /// </summary>
        /// <returns>true if the operation was successfully cancelled
        /// or it failed, false if it ran to completion.</returns>
        public bool CancelAndWait()
        {
            lock(this)
            {
                // Set the cancelled flag

                cancelledFlag = true;


                // Now sit and wait either for the operation to
                // complete or the cancellation to be acknowledged.
                // (Wake up and check every second - shouldn't be
                // necessary, but it guarantees we won't deadlock
                // if for some reason the Pulse gets lost - means
                // we don't have to worry so much about bizarre
                // race conditions.)
                while(!IsDone)
                {
                    Monitor.Wait(this, 1000);
                }
            }
            return !HasCompleted;
        }

        /// <summary>
        /// Blocks until the operation has either run to completion, or has
        /// been successfully cancelled, or has failed with an internal
        /// exception.
        /// </summary>
        /// <returns>true if the operation completed, false if it was
        /// cancelled before completion or failed with an internal
        /// exception.</returns>
        public bool WaitUntilDone()
        {
            lock(this)
            {
                // Wait for either completion or cancellation.  As with
                // CancelAndWait, we don't sleep forever - to reduce the
                // chances of deadlock in obscure race conditions, we wake
                // up every second to check we didn't miss a Pulse.
                while (!IsDone)
                {
                    Monitor.Wait(this, 1000);
                }
            }
            return HasCompleted;
        }


        /// <summary>
        /// Returns false if the operation is still in progress, or true if
        /// it has either completed successfully, been cancelled
        ///  successfully, or failed with an internal exception.
        /// </summary>
        public bool IsDone
        {
            get
            {
                lock(this)
                {
                    return completeFlag || cancelAcknowledgedFlag ||
failedFlag;
                }
            }
        }

        /// <summary>
        /// This event will be fired if the operation runs to completion
        /// without being cancelled.  This event will be raised through the
        /// ISynchronizeTarget supplied at construction time.  Note that
        /// this event may still be received after a cancellation request
        /// has been issued.  (This would happen if the operation completed
        /// at about the same time that cancellation was requested.)  But
        /// the event is not raised if the operation is cancelled
        /// successfully.
        /// </summary>
        public event EventHandler Completed;


        /// <summary>
        /// This event will be fired when the operation is successfully
        /// stoped through cancellation.  This event will be raised through
        /// the ISynchronizeTarget supplied at construction time.
        /// </summary>
        public event EventHandler Cancelled;


        /// <summary>
        /// This event will be fired if the operation throws an exception.
        /// This event will be raised through the ISynchronizeTarget
        /// supplied at construction time.
        /// </summary>
        public event System.Threading.ThreadExceptionEventHandler Failed;


        /// <summary>
        /// The ISynchronizeTarget supplied during construction - this can
        /// be used by deriving classes which wish to add their own events.
        /// </summary>
        protected ISynchronizeInvoke Target
        {
            get { return isiTarget; } 
        }
        private ISynchronizeInvoke isiTarget;


        /// <summary>
        /// To be overridden by the deriving class - this is where the work
        /// will be done.  The base class calls this method on a worker
        /// thread when the Start method is called.
        /// </summary>
        protected abstract void DoWork();


        /// <summary>
        /// Flag indicating whether the request has been cancelled.  Long-
        /// running operations should check this flag regularly if they can
        /// and cancel their operations as soon as they notice that it has
        /// been set.
        /// </summary>
        protected bool CancelRequested
        {
            get
            {
                lock(this) { return cancelledFlag; }
            }
        }
        private bool cancelledFlag;


        /// <summary>
        /// Flag indicating whether the request has run through to
        /// completion.  This will be false if the request has been
        /// successfully cancelled, or if it failed.
        /// </summary>
        protected bool HasCompleted
        {
            get
            {
                lock(this) { return completeFlag; }
            }
        }
        private bool completeFlag;


        /// <summary>
        /// This is called by the operation when it wants to indicate that
        /// it saw the cancellation request and honoured it.
        /// </summary>
        protected void AcknowledgeCancel()
        {
            lock(this)
            {
                cancelAcknowledgedFlag = true;
                isRunning = false;

                // Pulse the event in case the main thread is blocked
                // waiting for us to finish (e.g. in CancelAndWait or
                // WaitUntilDone).
                Monitor.Pulse(this);

                // Using async invocation to avoid a potential deadlock
                // - using Invoke would involve a cross-thread call
                // whilst we still held the object lock.  If the event
                // handler on the UI thread tries to access this object
                // it will block because we have the lock, but using
                // async invocation here means that once we've fired
                // the event, we'll run on and release the object lock,
                // unblocking the UI thread.
                FireAsync(Cancelled, this, EventArgs.Empty);
            }
        }
        private bool cancelAcknowledgedFlag;


        // Set to true if the operation fails with an exception.
        private bool failedFlag;
        // Set to true if the operation is running
        private bool isRunning;


        // This method is called on a worker thread (via asynchronous
        // delegate invocation).  This is where we call the operation (as
        // defined in the deriving class's DoWork method).
        private void InternalStart()
        {
            // Reset our state - we might be run more than once.
            cancelledFlag = false;
            completeFlag = false;
            cancelAcknowledgedFlag = false;
            failedFlag = false;
            // isRunning is set during Start to avoid a race condition
            try
            {
                DoWork();
            }
            catch (Exception e)
            {
                // Raise the Failed event.  We're in a catch handler, so we
                // had better try not to throw another exception.
                try
                {
                    FailOperation(e);
                }
                catch
                { }

                // The documentation recommends not catching
                // SystemExceptions, so having notified the caller we
                // rethrow if it was one of them.
                if (e is SystemException)
                {
                    throw;
                }
            }

            lock(this)
            {
                // If the operation wasn't cancelled (or if the UI thread
                // tried to cancel it, but the method ran to completion
                // anyway before noticing the cancellation) and it
                // didn't fail with an exception, then we complete the
                // operation - if the UI thread was blocked waiting for
                // cancellation to complete it will be unblocked, and
                // the Completion event will be raised.
                if (!cancelAcknowledgedFlag && !failedFlag)
                {
                    CompleteOperation();
                }
            }
        }


        // This is called when the operation runs to completion.
        // (This is private because it is called automatically
        // by this base class when the deriving class's DoWork
        // method exits without having cancelled

        private void CompleteOperation()
        {
            lock(this)
            {
                completeFlag = true;
                isRunning = false;
                Monitor.Pulse(this);
                // See comments in AcknowledgeCancel re use of
                // Async.
                FireAsync(Completed, this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        private void FailOperation(Exception e)
        {
            lock(this)
            {
                failedFlag = true;
                isRunning = false;
                Monitor.Pulse(this);
                FireAsync(Failed, this, new ThreadExceptionEventArgs(e));
            }
        }

        /// <summary>
        /// Utility function for firing an event through the target.
        /// It uses C#'s variable length parameter list support
        /// to build the parameter list.
        /// This functions presumes that the caller holds the object lock.
        /// (This is because the event list is typically modified on the UI
        /// thread, but events are usually raised on the worker thread.)
        /// </summary>
        /// 
        /// 
        protected void FireAsync(Delegate dlg, params object[] pList)
        {
            if (dlg != null)
            {
                Target.BeginInvoke(dlg, pList);
            }
        }
    }
}


Thank everybody for you help! Ciao.
-- 
View this message in context: http://mono.1490590.n4.nabble.com/Process-with-Mono-tp3042268p3078937.html
Sent from the Mono - MonoDevelop IDE mailing list archive at Nabble.com.


More information about the Monodevelop-list mailing list