+
+#if NET_2_0
+ public event DataReceivedEventHandler OutputDataReceived;
+ public event DataReceivedEventHandler ErrorDataReceived;
+
+ void OnOutputDataReceived (string str)
+ {
+ if (OutputDataReceived != null)
+ OutputDataReceived (this, new DataReceivedEventArgs (str));
+ }
+
+ void OnErrorDataReceived (string str)
+ {
+ if (ErrorDataReceived != null)
+ ErrorDataReceived (this, new DataReceivedEventArgs (str));
+ }
+
+ [Flags]
+ enum AsyncModes {
+ NoneYet = 0,
+ SyncOutput = 1,
+ SyncError = 1 << 1,
+ AsyncOutput = 1 << 2,
+ AsyncError = 1 << 3
+ }
+
+ [StructLayout (LayoutKind.Sequential)]
+ sealed class ProcessAsyncReader
+ {
+ /*
+ The following fields match those of SocketAsyncResult.
+ This is so that changes needed in the runtime to handle
+ asynchronous reads are trivial
+ */
+ /* DON'T shuffle fields around. DON'T remove fields */
+ public object Sock;
+ public IntPtr handle;
+ public object state;
+ public AsyncCallback callback;
+ public ManualResetEvent wait_handle;
+
+ public Exception delayedException;
+
+ public object EndPoint;
+ byte [] buffer = new byte [4196];
+ public int Offset;
+ public int Size;
+ public int SockFlags;
+
+ public object acc_socket;
+ public int total;
+ public bool completed_sync;
+ bool completed;
+ bool err_out; // true -> stdout, false -> stderr
+ internal int error;
+ public int operation = 6; // MAGIC NUMBER: Maximum in SocketOperation + 1
+ public object ares;
+
+
+ // These fields are not in SocketAsyncResult
+ Process process;
+ Stream stream;
+ StringBuilder sb = new StringBuilder ();
+ public AsyncReadHandler ReadHandler;
+
+ public ProcessAsyncReader (Process process, IntPtr handle, bool err_out)
+ {
+ this.process = process;
+ this.handle = handle;
+ stream = new FileStream (handle, FileAccess.Read, false);
+ this.ReadHandler = new AsyncReadHandler (AddInput);
+ this.err_out = err_out;
+ }
+
+ public void AddInput ()
+ {
+ lock (this) {
+ int nread = stream.Read (buffer, 0, buffer.Length);
+ if (nread == 0) {
+ completed = true;
+ if (wait_handle != null)
+ wait_handle.Set ();
+ Flush (true);
+ return;
+ }
+
+ try {
+ sb.Append (Encoding.Default.GetString (buffer, 0, nread));
+ } catch {
+ // Just in case the encoding fails...
+ for (int i = 0; i < nread; i++) {
+ sb.Append ((char) buffer [i]);
+ }
+ }
+
+ Flush (false);
+ ReadHandler.BeginInvoke (null, this);
+ }
+ }
+
+ void Flush (bool last)
+ {
+ if (sb.Length == 0 ||
+ (err_out && process.output_canceled) ||
+ (!err_out && process.error_canceled))
+ return;
+
+ string total = sb.ToString ();
+ sb.Length = 0;
+ string [] strs = total.Split ('\n');
+ int len = strs.Length;
+ if (len == 0)
+ return;
+
+ for (int i = 0; i < len - 1; i++) {
+ if (err_out)
+ process.OnOutputDataReceived (strs [i]);
+ else
+ process.OnErrorDataReceived (strs [i]);
+ }
+
+ string end = strs [len - 1];
+ if (last || end == "") {
+ if (err_out)
+ process.OnOutputDataReceived (end);
+ else
+ process.OnErrorDataReceived (end);
+ } else {
+ sb.Append (end);
+ }
+ }
+
+ public bool IsCompleted {
+ get { return completed; }
+ }
+
+ public WaitHandle WaitHandle {
+ get {
+ lock (this) {
+ if (wait_handle == null)
+ wait_handle = new ManualResetEvent (completed);
+ return wait_handle;
+ }
+ }
+ }
+ }
+
+ AsyncModes async_mode;
+ bool output_canceled;
+ bool error_canceled;
+ ProcessAsyncReader async_output;
+ ProcessAsyncReader async_error;
+ delegate void AsyncReadHandler ();
+
+ [ComVisibleAttribute(false)]
+ public void BeginOutputReadLine ()
+ {
+ if (process_handle == IntPtr.Zero || output_stream == null || StartInfo.RedirectStandardOutput == false)
+ throw new InvalidOperationException ("Standard output has not been redirected or process has not been started.");
+
+ if ((async_mode & AsyncModes.SyncOutput) != 0)
+ throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads.");
+
+ async_mode |= AsyncModes.AsyncOutput;
+ output_canceled = false;
+ if (async_output == null) {
+ async_output = new ProcessAsyncReader (this, stdout_rd, true);
+ async_output.ReadHandler.BeginInvoke (null, async_output);
+ }
+ }
+
+ [ComVisibleAttribute(false)]
+ public void CancelOutputRead ()
+ {
+ if (process_handle == IntPtr.Zero || output_stream == null || StartInfo.RedirectStandardOutput == false)
+ throw new InvalidOperationException ("Standard output has not been redirected or process has not been started.");
+
+ if ((async_mode & AsyncModes.SyncOutput) != 0)
+ throw new InvalidOperationException ("OutputStream is not enabled for asynchronous read operations.");
+
+ if (async_output == null)
+ throw new InvalidOperationException ("No async operation in progress.");
+
+ output_canceled = true;
+ }
+
+ [ComVisibleAttribute(false)]
+ public void BeginErrorReadLine ()
+ {
+ if (process_handle == IntPtr.Zero || error_stream == null || StartInfo.RedirectStandardError == false)
+ throw new InvalidOperationException ("Standard error has not been redirected or process has not been started.");
+
+ if ((async_mode & AsyncModes.SyncError) != 0)
+ throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads.");
+
+ async_mode |= AsyncModes.AsyncError;
+ error_canceled = false;
+ if (async_error == null) {
+ async_error = new ProcessAsyncReader (this, stderr_rd, false);
+ async_error.ReadHandler.BeginInvoke (null, async_error);
+ }
+ }
+
+ [ComVisibleAttribute(false)]
+ public void CancelErrorRead ()
+ {
+ if (process_handle == IntPtr.Zero || output_stream == null || StartInfo.RedirectStandardOutput == false)
+ throw new InvalidOperationException ("Standard output has not been redirected or process has not been started.");
+
+ if ((async_mode & AsyncModes.SyncOutput) != 0)
+ throw new InvalidOperationException ("OutputStream is not enabled for asynchronous read operations.");
+
+ if (async_error == null)
+ throw new InvalidOperationException ("No async operation in progress.");
+
+ error_canceled = true;
+ }
+#endif
+
+ [Category ("Behavior")]
+ [MonitoringDescription ("Raised when this process exits.")]
+ public event EventHandler Exited {
+ add {
+ if (process_handle != IntPtr.Zero && HasExited) {
+ value.BeginInvoke (null, null, null, null);
+ } else {
+ exited_event = (EventHandler) Delegate.Combine (exited_event, value);
+ if (exited_event != null)
+ StartExitCallbackIfNeeded ();
+ }
+ }
+ remove {
+ exited_event = (EventHandler) Delegate.Remove (exited_event, value);
+ }
+ }