Add ErrorOccured to BaseClient to allow catching of exceptions that happened on the other thread

Fix SymbolicLink method
Minor changes
This commit is contained in:
olegkap_cp
2011-01-05 23:12:10 +00:00
parent 24b902f27b
commit e488bc516e
12 changed files with 188 additions and 135 deletions
@@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Renci.SshClient.Security;
using Renci.SshClient.Tests.Properties;
@@ -68,6 +64,18 @@ namespace Renci.SshClient.Tests.Security
}
}
// TODO: Add test to test re-keying
[TestMethod]
public void Test_KeyExchange_Rekeying()
{
var connectionInfo = new PasswordConnectionInfo(Resources.HOST, 22, Resources.USERNAME, Resources.PASSWORD);
using (var client = new SshClient(connectionInfo))
{
client.Connect();
// TODO: Add test to test re-keying
Assert.Inconclusive();
client.Disconnect();
}
}
}
}
+21 -5
View File
@@ -1,4 +1,5 @@
using System;
using System.IO;
using System.Threading;
using Renci.SshClient.Common;
@@ -68,6 +69,11 @@ namespace Renci.SshClient
}
}
/// <summary>
/// Occurs when an error occurred.
/// </summary>
public event EventHandler<ErrorEventArgs> ErrorOccurred;
/// <summary>
/// Initializes a new instance of the <see cref="BaseClient"/> class.
/// </summary>
@@ -92,6 +98,7 @@ namespace Renci.SshClient
this.Session = new Session(this.ConnectionInfo);
this.Session.Connect();
this.Session.ErrorOccured += Session_ErrorOccured;
this.OnConnected();
}
@@ -164,12 +171,20 @@ namespace Renci.SshClient
throw new SshConnectionException("Client not connected.");
}
private void Session_ErrorOccured(object sender, System.IO.ErrorEventArgs e)
{
if (this.ErrorOccurred != null)
{
this.ErrorOccurred(this, e);
}
}
#region IDisposable Members
private bool _isDisposed = false;
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages.
/// </summary>
public void Dispose()
{
@@ -181,17 +196,19 @@ namespace Renci.SshClient
/// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged ResourceMessages.</param>
protected virtual void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
if (!this._isDisposed)
{
// If disposing equals true, dispose all managed
// and unmanaged resources.
// and unmanaged ResourceMessages.
if (disposing)
{
// Dispose managed resources.
// Dispose managed ResourceMessages.
this.Session.ErrorOccured -= Session_ErrorOccured;
if (this.Session != null)
{
this.Session.Dispose();
@@ -202,7 +219,6 @@ namespace Renci.SshClient
this._keepAliveTimer.Dispose();
this._keepAliveTimer = null;
}
}
// Note disposing has been done.
@@ -12,7 +12,7 @@ namespace Renci.SshClient
/// Gets or sets the command that async result was created for.
/// </summary>
/// <value>The channel.</value>
internal SshCommand Command { get; private set; }
private SshCommand _command;
/// <summary>
/// Gets or sets the bytes received. If SFTP only file bytes are counted.
@@ -60,7 +60,20 @@ namespace Renci.SshClient
/// <param name="command">The command.</param>
internal CommandAsyncResult(SshCommand command)
{
this.Command = command;
this._command = command;
}
/// <summary>
/// Validates that command object is valid to be used with this instance.
/// </summary>
/// <param name="command">The command.</param>
public void ValidateCommand(SshCommand command)
{
if (this._command != command)
{
throw new InvalidOperationException("Invalid IAsyncResult parameter");
}
}
}
}
@@ -120,7 +120,8 @@ namespace Renci.SshClient.Channels
// data transfer can take longer then connection specified timeout
System.Threading.WaitHandle.WaitAny(new WaitHandle[] { this._channelEof, readerTaskError });
this._socket.Close();
this._socket.Dispose();
this._socket = null;
// Wait for task to finish and will throw any errors if any
readerTask.Wait();
@@ -162,6 +163,12 @@ namespace Renci.SshClient.Channels
protected override void Dispose(bool disposing)
{
if (this._socket != null)
{
this._socket.Dispose();
this._socket = null;
}
if (this._channelEof != null)
{
this._channelEof.Dispose();
@@ -3,8 +3,6 @@ using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using Renci.SshClient.Channels;
using Renci.SshClient.Common;
using System.Diagnostics;
namespace Renci.SshClient
{
@@ -37,7 +35,6 @@ namespace Renci.SshClient
{
while (true)
{
// TODO: Check what the best place to call dispose for this socket.
var socket = this._listener.AcceptSocket();
Task.Factory.StartNew(() =>
@@ -99,7 +96,7 @@ namespace Renci.SshClient
private bool _isDisposed = false;
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages.
/// </summary>
public void Dispose()
{
@@ -111,17 +108,17 @@ namespace Renci.SshClient
/// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged ResourceMessages.</param>
protected virtual void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
if (!this._isDisposed)
{
// If disposing equals true, dispose all managed
// and unmanaged resources.
// and unmanaged ResourceMessages.
if (disposing)
{
// Dispose managed resources.
// Dispose managed ResourceMessages.
if (this._listenerTask != null)
{
this._listenerTask.Dispose();
@@ -1,20 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
namespace Renci.SshClient.Security
{
/// <summary>
/// Represents base class for hash algoritm classes
/// Represents base class for hash algorithm classes
/// </summary>
public abstract class HMac : Algorithm, IDisposable
{
/// <summary>
/// Instance of initialized hash algorithm that being used
/// </summary>
protected System.Security.Cryptography.HMAC _hmac;
// TODO: Refactor code so this member will not be protected
protected abstract HMAC Hash { get; }
/// <summary>
/// Initializes algorithm with specified key.
@@ -29,7 +27,7 @@ namespace Renci.SshClient.Security
/// <returns>The hash</returns>
internal byte[] ComputeHash(byte[] hashData)
{
return this._hmac.ComputeHash(hashData);
return this.Hash.ComputeHash(hashData);
}
/// <summary>
@@ -42,16 +40,14 @@ namespace Renci.SshClient.Security
{
get
{
return this._hmac.HashSize;
return this.Hash.HashSize;
}
}
#region IDisposable Members
private bool _disposed = false;
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages.
/// </summary>
public void Dispose()
{
@@ -63,27 +59,9 @@ namespace Renci.SshClient.Security
/// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged ResourceMessages.</param>
protected virtual void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
if (!this._disposed)
{
// If disposing equals true, dispose all managed
// and unmanaged resources.
if (disposing)
{
// Dispose managed resources.
if (this._hmac != null)
{
this._hmac.Dispose();
this._hmac = null;
}
}
// Note disposing has been done.
this._disposed = true;
}
}
/// <summary>
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
namespace Renci.SshClient.Security
{
@@ -10,6 +9,13 @@ namespace Renci.SshClient.Security
/// </summary>
internal class HMacMD5 : HMac
{
private HMACMD5 _hash;
protected override HMAC Hash
{
get { return this._hash; }
}
/// <summary>
/// Gets algorithm name.
/// </summary>
@@ -24,7 +30,19 @@ namespace Renci.SshClient.Security
/// <param name="key">The hash key.</param>
public override void Init(IEnumerable<byte> key)
{
this._hmac = new System.Security.Cryptography.HMACMD5(key.Take(16).ToArray());
this._hash = new HMACMD5(key.Take(16).ToArray());
}
protected override void Dispose(bool disposing)
{
// Dispose managed ResourceMessages.
if (this._hash != null)
{
this._hash.Dispose();
this._hash = null;
}
base.Dispose(disposing);
}
}
}
@@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
namespace Renci.SshClient.Security
@@ -11,6 +9,13 @@ namespace Renci.SshClient.Security
/// </summary>
internal class HMacSha1 : HMac
{
private HMACSHA1 _hash;
protected override HMAC Hash
{
get { return this._hash; }
}
/// <summary>
/// Gets algorithm name.
/// </summary>
@@ -25,7 +30,19 @@ namespace Renci.SshClient.Security
/// <param name="key">The hash key.</param>
public override void Init(IEnumerable<byte> key)
{
this._hmac = new System.Security.Cryptography.HMACSHA1(key.Take(20).ToArray());
this._hash = new HMACSHA1(key.Take(20).ToArray());
}
protected override void Dispose(bool disposing)
{
// Dispose managed ResourceMessages.
if (this._hash != null)
{
this._hash.Dispose();
this._hash = null;
}
base.Dispose(disposing);
}
}
}
@@ -156,6 +156,10 @@ namespace Renci.SshClient.Security
}
}
/// <summary>
/// Creates the server side cipher to use.
/// </summary>
/// <returns></returns>
public Cipher CreateServerCipher()
{
// Resolve Session ID
@@ -177,6 +181,10 @@ namespace Renci.SshClient.Security
return serverCipher;
}
/// <summary>
/// Creates the client side cipher to use.
/// </summary>
/// <returns></returns>
public Cipher CreateClientCipher()
{
// Resolve Session ID
@@ -198,6 +206,10 @@ namespace Renci.SshClient.Security
return clientCipher;
}
/// <summary>
/// Creates the server side hash algorithm to use.
/// </summary>
/// <returns></returns>
public HMac CreateServerHash()
{
// Resolve Session ID
@@ -211,6 +223,10 @@ namespace Renci.SshClient.Security
return serverHMac;
}
/// <summary>
/// Creates the client side hash algorithm to use.
/// </summary>
/// <returns></returns>
public HMac CreateClientHash()
{
// Resolve Session ID
@@ -224,6 +240,10 @@ namespace Renci.SshClient.Security
return clientHMac;
}
/// <summary>
/// Creates the compression algorithm to use to deflate data.
/// </summary>
/// <returns></returns>
public Compressor CreateCompressor()
{
if (this._compressionType == null)
@@ -236,6 +256,10 @@ namespace Renci.SshClient.Security
return compressor;
}
/// <summary>
/// Creates the compression algorithm to use to inflate data.
/// </summary>
/// <returns></returns>
public Compressor CreateDecompressor()
{
if (this._compressionType == null)
@@ -374,7 +398,7 @@ namespace Renci.SshClient.Security
#region IDisposable Members
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages.
/// </summary>
public void Dispose()
{
@@ -386,7 +410,7 @@ namespace Renci.SshClient.Security
/// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged ResourceMessages.</param>
protected virtual void Dispose(bool disposing)
{
}
+37 -56
View File
@@ -430,6 +430,7 @@ namespace Renci.SshClient
Name = messageAttribute.Name,
Number = messageAttribute.Number,
Enabled = false,
Activated = false,
Type = type,
}).ToList();
@@ -440,11 +441,8 @@ namespace Renci.SshClient
Match versionMatch = null;
// Get server version from the server,
// ignore text lines which are sent before if any
NetworkStream ns = null;
try
using (var ns = new NetworkStream(this._socket))
{
ns = new NetworkStream(this._socket);
using (var sr = new StreamReader(ns))
{
while (true)
@@ -464,14 +462,6 @@ namespace Renci.SshClient
}
}
}
finally
{
if (ns != null)
{
ns.Dispose();
ns = null;
}
}
// Get server SSH version
var version = versionMatch.Result("${protoversion}");
@@ -528,6 +518,21 @@ namespace Renci.SshClient
throw new SshAuthenticationException("User cannot be authenticated.");
}
// Register Connection messages
this.RegisterMessage("SSH_MSG_GLOBAL_REQUEST");
this.RegisterMessage("SSH_MSG_REQUEST_SUCCESS");
this.RegisterMessage("SSH_MSG_REQUEST_FAILURE");
this.RegisterMessage("SSH_MSG_CHANNEL_OPEN_CONFIRMATION");
this.RegisterMessage("SSH_MSG_CHANNEL_OPEN_FAILURE");
this.RegisterMessage("SSH_MSG_CHANNEL_WINDOW_ADJUST");
this.RegisterMessage("SSH_MSG_CHANNEL_EXTENDED_DATA");
this.RegisterMessage("SSH_MSG_CHANNEL_REQUEST");
this.RegisterMessage("SSH_MSG_CHANNEL_SUCCESS");
this.RegisterMessage("SSH_MSG_CHANNEL_FAILURE");
this.RegisterMessage("SSH_MSG_CHANNEL_DATA");
this.RegisterMessage("SSH_MSG_CHANNEL_EOF");
this.RegisterMessage("SSH_MSG_CHANNEL_CLOSE");
Monitor.Pulse(this);
}
}
@@ -1025,22 +1030,12 @@ namespace Renci.SshClient
{
this._keyExchangeCompletedWaitHandle.Reset();
// Connection type messages are not allowed during key exchange phase
this.UnRegisterMessage("SSH_MSG_GLOBAL_REQUEST");
this.UnRegisterMessage("SSH_MSG_REQUEST_SUCCESS");
this.UnRegisterMessage("SSH_MSG_REQUEST_FAILURE");
this.UnRegisterMessage("SSH_MSG_CHANNEL_OPEN_CONFIRMATION");
this.UnRegisterMessage("SSH_MSG_CHANNEL_OPEN_FAILURE");
this.UnRegisterMessage("SSH_MSG_CHANNEL_WINDOW_ADJUST");
this.UnRegisterMessage("SSH_MSG_CHANNEL_EXTENDED_DATA");
this.UnRegisterMessage("SSH_MSG_CHANNEL_REQUEST");
this.UnRegisterMessage("SSH_MSG_CHANNEL_SUCCESS");
this.UnRegisterMessage("SSH_MSG_CHANNEL_FAILURE");
this.UnRegisterMessage("SSH_MSG_CHANNEL_DATA");
this.UnRegisterMessage("SSH_MSG_CHANNEL_EOF");
this.UnRegisterMessage("SSH_MSG_CHANNEL_CLOSE");
// TODO: Replace it with algorithm which disables all messages but relevant for key exchange
// Disable all registered messages except key exchange related
foreach (var messageMetadata in this._messagesMetadata)
{
if (messageMetadata.Activated == true && messageMetadata.Number < 20 || messageMetadata.Number > 30)
messageMetadata.Enabled = false;
}
var keyExchangeAlgorithmName = (from c in this.ConnectionInfo.KeyExchangeAlgorithms.Keys
from s in message.KeyExchangeAlgorithms
@@ -1116,20 +1111,12 @@ namespace Renci.SshClient
this._keyExchange = null;
}
// Register Connection messages
this.RegisterMessage("SSH_MSG_GLOBAL_REQUEST");
this.RegisterMessage("SSH_MSG_REQUEST_SUCCESS");
this.RegisterMessage("SSH_MSG_REQUEST_FAILURE");
this.RegisterMessage("SSH_MSG_CHANNEL_OPEN_CONFIRMATION");
this.RegisterMessage("SSH_MSG_CHANNEL_OPEN_FAILURE");
this.RegisterMessage("SSH_MSG_CHANNEL_WINDOW_ADJUST");
this.RegisterMessage("SSH_MSG_CHANNEL_EXTENDED_DATA");
this.RegisterMessage("SSH_MSG_CHANNEL_REQUEST");
this.RegisterMessage("SSH_MSG_CHANNEL_SUCCESS");
this.RegisterMessage("SSH_MSG_CHANNEL_FAILURE");
this.RegisterMessage("SSH_MSG_CHANNEL_DATA");
this.RegisterMessage("SSH_MSG_CHANNEL_EOF");
this.RegisterMessage("SSH_MSG_CHANNEL_CLOSE");
// Enable all active registered messages
foreach (var messageMetadata in this._messagesMetadata)
{
if (messageMetadata.Activated == true)
messageMetadata.Enabled = true;
}
if (this.NewKeysReceived != null)
{
@@ -1460,7 +1447,7 @@ namespace Renci.SshClient
{
Parallel.ForEach(
from m in this._messagesMetadata where m.Name == messageName select m,
(item) => { item.Enabled = true; });
(item) => { item.Enabled = true; item.Activated = true; });
}
}
@@ -1474,7 +1461,7 @@ namespace Renci.SshClient
{
Parallel.ForEach(
from m in this._messagesMetadata where m.Name == messageName select m,
(item) => { item.Enabled = false; });
(item) => { item.Enabled = false; item.Activated = false; });
}
}
@@ -1486,8 +1473,7 @@ namespace Renci.SshClient
private Message LoadMessage(IEnumerable<byte> data)
{
var messageType = data.FirstOrDefault();
var messageMetadata = (from m in this._messagesMetadata where m.Number == messageType && m.Enabled == true select m).SingleOrDefault();
var messageMetadata = (from m in this._messagesMetadata where m.Number == messageType && m.Enabled && m.Activated select m).SingleOrDefault();
if (messageMetadata == null)
throw new SshException(string.Format("Message type {0} is not valid.", messageType));
@@ -1512,8 +1498,6 @@ namespace Renci.SshClient
{
var message = this.ReceiveMessage();
//Debug.WriteLine(string.Format("{0} : {1}", DateTime.Now, message));
if (message == null)
{
throw new NullReferenceException("The 'message' variable cannot be null");
@@ -1527,10 +1511,6 @@ namespace Renci.SshClient
catch (Exception exp)
{
this.RaiseError(exp);
// TODO: This exception can be swallowed if it occurs while running in the background, look for possible solutions
// It can be swollen if call async, when asynch method is finished ensure all async method check for exception to be raised
// Or if it is running in port forwarding mode, some event should be raised at this case to handle and error and if not handle then throw
// throw an exception and let it be swallowed then if not handled by the event handler
}
}
@@ -1568,7 +1548,7 @@ namespace Renci.SshClient
private bool _disposed = false;
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages.
/// </summary>
public void Dispose()
{
@@ -1580,14 +1560,14 @@ namespace Renci.SshClient
/// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged ResourceMessages.</param>
protected virtual void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
if (!this._disposed)
{
// If disposing equals true, dispose all managed
// and unmanaged resources.
// and unmanaged ResourceMessages.
if (disposing)
{
this._isDisconnecting = true;
@@ -1690,8 +1670,9 @@ namespace Renci.SshClient
public bool Enabled { get; set; }
public bool Activated { get; set; }
public Type Type { get; set; }
}
}
}
@@ -213,12 +213,10 @@ namespace Renci.SshClient
/// <summary>
/// Creates a symbolic link from old path to new path.
/// </summary>
/// <param name="linkPath">The old path.</param>
/// <param name="path">The new path.</param>
public void SymbolicLink(string linkPath, string path)
/// <param name="path">The old path.</param>
/// <param name="linkPath">The new path.</param>
public void SymbolicLink(string path, string linkPath)
{
// TODO: Need to be tested, currently does not work
// Ensure that connection is established.
this.EnsureConnection();
@@ -226,7 +224,7 @@ namespace Renci.SshClient
var linkFullPath = this._sftpSession.ResolvePath(linkPath);
var cmd = new SymbolicLinkCommand(this._sftpSession, linkFullPath, fullPath);
var cmd = new SymbolicLinkCommand(this._sftpSession, fullPath, linkFullPath);
cmd.CommandTimeout = this.OperationTimeout;
@@ -413,7 +411,7 @@ namespace Renci.SshClient
/// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged ResourceMessages.</param>
protected override void Dispose(bool disposing)
{
if (this._sftpSession != null)
+8 -12
View File
@@ -172,11 +172,7 @@ namespace Renci.SshClient
{
CommandAsyncResult channelAsyncResult = asynchResult as CommandAsyncResult;
// TODO: Create a method to perform this check in the method
if (channelAsyncResult.Command != this)
{
throw new InvalidOperationException("Invalid IAsyncResult parameter");
}
channelAsyncResult.ValidateCommand(this);
// Make sure that operation completed if not wait for it to finish
this.WaitHandle(this._asyncResult.AsyncWaitHandle);
@@ -334,7 +330,7 @@ namespace Renci.SshClient
private bool _isDisposed = false;
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages.
/// </summary>
public void Dispose()
{
@@ -346,42 +342,42 @@ namespace Renci.SshClient
/// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged ResourceMessages.</param>
protected virtual void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
if (!this._isDisposed)
{
// If disposing equals true, dispose all managed
// and unmanaged resources.
// and unmanaged ResourceMessages.
if (disposing)
{
this._session.Disconnected -= Session_Disconnected;
this._session.ErrorOccured -= Session_ErrorOccured;
// Dispose managed resources.
// Dispose managed ResourceMessages.
if (this.OutputStream != null)
{
this.OutputStream.Dispose();
this.OutputStream = null;
}
// Dispose managed resources.
// Dispose managed ResourceMessages.
if (this.ExtendedOutputStream != null)
{
this.ExtendedOutputStream.Dispose();
this.ExtendedOutputStream = null;
}
// Dispose managed resources.
// Dispose managed ResourceMessages.
if (this._sessionErrorOccuredWaitHandle != null)
{
this._sessionErrorOccuredWaitHandle.Dispose();
this._sessionErrorOccuredWaitHandle = null;
}
// Dispose managed resources.
// Dispose managed ResourceMessages.
if (this._channel != null)
{
this._channel.DataReceived -= Channel_DataReceived;