[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