2
0
Gert Driesen 9 жил өмнө
parent
commit
f03b8999a1

+ 422 - 422
src/Renci.SshNet/BaseClient.cs

@@ -1,422 +1,422 @@
-using System;
-using System.Net.Sockets;
-using System.Threading;
-using Renci.SshNet.Common;
-using Renci.SshNet.Messages.Transport;
-
-namespace Renci.SshNet
-{
-    /// <summary>
-    /// Serves as base class for client implementations, provides common client functionality.
-    /// </summary>
-    public abstract class BaseClient : IDisposable
-    {
-        /// <summary>
-        /// Holds value indicating whether the connection info is owned by this client.
-        /// </summary>
-        private readonly bool _ownsConnectionInfo;
-
-        private readonly IServiceFactory _serviceFactory;
-        private readonly object _keepAliveLock = new object();
-        private TimeSpan _keepAliveInterval;
-        private Timer _keepAliveTimer;
-        private ConnectionInfo _connectionInfo;
-
-        /// <summary>
-        /// Gets the current session.
-        /// </summary>
-        /// <value>
-        /// The current session.
-        /// </value>
-        internal ISession Session { get; private set; }
-
-        /// <summary>
-        /// Gets the factory for creating new services.
-        /// </summary>
-        /// <value>
-        /// The factory for creating new services.
-        /// </value>
-        internal IServiceFactory ServiceFactory
-        {
-            get { return _serviceFactory; }
-        }
-
-        /// <summary>
-        /// Gets the connection info.
-        /// </summary>
-        /// <value>
-        /// The connection info.
-        /// </value>
-        /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
-        public ConnectionInfo ConnectionInfo
-        {
-            get
-            {
-                CheckDisposed();
-                return _connectionInfo;
-            }
-            private set
-            {
-                _connectionInfo = value;
-            }
-        }
-
-        /// <summary>
-        /// Gets a value indicating whether this client is connected to the server.
-        /// </summary>
-        /// <value>
-        /// <c>true</c> if this client is connected; otherwise, <c>false</c>.
-        /// </value>
-        /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
-        public bool IsConnected
-        {
-            get
-            {
-                CheckDisposed();
-                return Session != null && Session.IsConnected;
-            }
-        }
-
-        /// <summary>
-        /// Gets or sets the keep-alive interval.
-        /// </summary>
-        /// <value>
-        /// The keep-alive interval. Specify negative one (-1) milliseconds to disable the
-        /// keep-alive. This is the default value.
-        /// </value>
-        /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
-        public TimeSpan KeepAliveInterval
-        {
-            get
-            {
-                CheckDisposed();
-                return _keepAliveInterval;
-            }
-            set
-            {
-                CheckDisposed();
-
-                if (value == _keepAliveInterval)
-                    return;
-
-                if (value == SshNet.Session.InfiniteTimeSpan)
-                {
-                    // stop the timer when the value is -1 milliseconds
-                    StopKeepAliveTimer();
-                }
-                else
-                {
-                    // change the due time and interval of the timer if has already
-                    // been created (which means the client is connected)
-                    // 
-                    // if the client is not yet connected, then the timer will be
-                    // created with the new interval when Connect() is invoked
-                    if (_keepAliveTimer != null)
-                        _keepAliveTimer.Change(value, value);
-                }
-                _keepAliveInterval = value;
-            }
-        }
-
-        /// <summary>
-        /// Occurs when an error occurred.
-        /// </summary>
-        /// <example>
-        ///   <code source="..\..\Renci.SshNet.Tests\Classes\SshClientTest.cs" region="Example SshClient Connect ErrorOccurred" language="C#" title="Handle ErrorOccurred event" />
-        /// </example>
-        public event EventHandler<ExceptionEventArgs> ErrorOccurred;
-
-        /// <summary>
-        /// Occurs when host key received.
-        /// </summary>
-        /// <example>
-        ///   <code source="..\..\Renci.SshNet.Tests\Classes\SshClientTest.cs" region="Example SshClient Connect HostKeyReceived" language="C#" title="Handle HostKeyReceived event" />
-        /// </example>
-        public event EventHandler<HostKeyEventArgs> HostKeyReceived;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="BaseClient"/> class.
-        /// </summary>
-        /// <param name="connectionInfo">The connection info.</param>
-        /// <param name="ownsConnectionInfo">Specified whether this instance owns the connection info.</param>
-        /// <exception cref="ArgumentNullException"><paramref name="connectionInfo"/> is null.</exception>
-        /// <remarks>
-        /// If <paramref name="ownsConnectionInfo"/> is <c>true</c>, then the
-        /// connection info will be disposed when this instance is disposed.
-        /// </remarks>
-        protected BaseClient(ConnectionInfo connectionInfo, bool ownsConnectionInfo)
-            : this(connectionInfo, ownsConnectionInfo, new ServiceFactory())
-        {
-        }
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="BaseClient"/> class.
-        /// </summary>
-        /// <param name="connectionInfo">The connection info.</param>
-        /// <param name="ownsConnectionInfo">Specified whether this instance owns the connection info.</param>
-        /// <param name="serviceFactory">The factory to use for creating new services.</param>
-        /// <exception cref="ArgumentNullException"><paramref name="connectionInfo"/> is <c>null</c>.</exception>
-        /// <exception cref="ArgumentNullException"><paramref name="serviceFactory"/> is <c>null</c>.</exception>
-        /// <remarks>
-        /// If <paramref name="ownsConnectionInfo"/> is <c>true</c>, then the
-        /// connection info will be disposed when this instance is disposed.
-        /// </remarks>
-        internal BaseClient(ConnectionInfo connectionInfo, bool ownsConnectionInfo, IServiceFactory serviceFactory)
-        {
-            if (connectionInfo == null)
-                throw new ArgumentNullException("connectionInfo");
-            if (serviceFactory == null)
-                throw new ArgumentNullException("serviceFactory");
-
-            ConnectionInfo = connectionInfo;
-            _ownsConnectionInfo = ownsConnectionInfo;
-            _serviceFactory = serviceFactory;
-            _keepAliveInterval = SshNet.Session.InfiniteTimeSpan;
-        }
-
-        /// <summary>
-        /// Connects client to the server.
-        /// </summary>
-        /// <exception cref="InvalidOperationException">The client is already connected.</exception>
-        /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
-        /// <exception cref="SocketException">Socket connection to the SSH server or proxy server could not be established, or an error occurred while resolving the hostname.</exception>
-        /// <exception cref="SshConnectionException">SSH session could not be established.</exception>
-        /// <exception cref="SshAuthenticationException">Authentication of SSH session failed.</exception>
-        /// <exception cref="ProxyException">Failed to establish proxy connection.</exception>
-        public void Connect()
-        {
-            CheckDisposed();
-
-            // TODO (see issue #1758):
-            // we're not stopping the keep-alive timer and disposing the session here
-            // 
-            // we could do this but there would still be side effects as concrete
-            // implementations may still hang on to the original session
-            // 
-            // therefore it would be better to actually invoke the Disconnect method
-            // (and then the Dispose on the session) but even that would have side effects
-            // eg. it would remove all forwarded ports from SshClient
-            // 
-            // I think we should modify our concrete clients to better deal with a
-            // disconnect. In case of SshClient this would mean not removing the 
-            // forwarded ports on disconnect (but only on dispose ?) and link a
-            // forwarded port with a client instead of with a session
-            //
-            // To be discussed with Oleg (or whoever is interested)
-            if (Session != null && Session.IsConnected)
-                throw new InvalidOperationException("The client is already connected.");
-
-            OnConnecting();
-            Session = _serviceFactory.CreateSession(ConnectionInfo);
-            Session.HostKeyReceived += Session_HostKeyReceived;
-            Session.ErrorOccured += Session_ErrorOccured;
-            Session.Connect();
-            StartKeepAliveTimer();
-            OnConnected();
-        }
-
-        /// <summary>
-        /// Disconnects client from the server.
-        /// </summary>
-        /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
-        public void Disconnect()
-        {
-            CheckDisposed();
-
-            OnDisconnecting();
-
-            // stop sending keep-alive messages before we close the
-            // session
-            StopKeepAliveTimer();
-
-            // disconnect and dispose the SSH session
-            if (Session != null)
-            {
-                // a new session is created in Connect(), so we should dispose and
-                // dereference the current session here
-                Session.ErrorOccured -= Session_ErrorOccured;
-                Session.HostKeyReceived -= Session_HostKeyReceived;
-                Session.Disconnect();
-                Session.Dispose();
-                Session = null;
-            }
-
-            OnDisconnected();
-        }
-
-        /// <summary>
-        /// Sends a keep-alive message to the server.
-        /// </summary>
-        /// <remarks>
-        /// Use <see cref="KeepAliveInterval"/> to configure the client to send a keep-alive at regular
-        /// intervals.
-        /// </remarks>
-        /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
-        [Obsolete("Use KeepAliveInterval to send a keep-alive message at regular intervals.")]
-        public void SendKeepAlive()
-        {
-            CheckDisposed();
-
-            SendKeepAliveMessage();
-        }
-
-        /// <summary>
-        /// Called when client is connecting to the server.
-        /// </summary>
-        protected virtual void OnConnecting()
-        {
-        }
-
-        /// <summary>
-        /// Called when client is connected to the server.
-        /// </summary>
-        protected virtual void OnConnected()
-        {
-        }
-
-        /// <summary>
-        /// Called when client is disconnecting from the server.
-        /// </summary>
-        protected virtual void OnDisconnecting()
-        {
-            if (Session != null)
-                Session.OnDisconnecting();
-        }
-
-        /// <summary>
-        /// Called when client is disconnected from the server.
-        /// </summary>
-        protected virtual void OnDisconnected()
-        {
-        }
-
-        private void Session_ErrorOccured(object sender, ExceptionEventArgs e)
-        {
-            var handler = ErrorOccurred;
-            if (handler != null)
-            {
-                handler(this, e);
-            }
-        }
-
-        private void Session_HostKeyReceived(object sender, HostKeyEventArgs e)
-        {
-            var handler = HostKeyReceived;
-            if (handler != null)
-            {
-                handler(this, e);
-            }
-        }
-
-        #region IDisposable Members
-
-        private bool _isDisposed;
-
-        /// <summary>
-        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
-        /// </summary>
-        public void Dispose()
-        {
-            Dispose(true);
-            GC.SuppressFinalize(this);
-        }
-
-        /// <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>
-        protected virtual void Dispose(bool disposing)
-        {
-            if (_isDisposed)
-                return;
-
-            if (disposing)
-            {
-                Disconnect();
-
-                if (_ownsConnectionInfo && _connectionInfo != null)
-                {
-                    var connectionInfoDisposable = _connectionInfo as IDisposable;
-                    if (connectionInfoDisposable != null)
-                        connectionInfoDisposable.Dispose();
-                    _connectionInfo = null;
-                }
-
-                _isDisposed = true;
-            }
-        }
-
-        /// <summary>
-        /// Check if the current instance is disposed.
-        /// </summary>
-        /// <exception cref="ObjectDisposedException">THe current instance is disposed.</exception>
-        protected void CheckDisposed()
-        {
-            if (_isDisposed)
-                throw new ObjectDisposedException(GetType().FullName);
-        }
-
-        /// <summary>
-        /// Releases unmanaged resources and performs other cleanup operations before the
-        /// <see cref="BaseClient"/> is reclaimed by garbage collection.
-        /// </summary>
-        ~BaseClient()
-        {
-            Dispose(false);
-        }
-
-        #endregion
-
-        /// <summary>
-        /// Stops the keep-alive timer, and waits until all timer callbacks have been
-        /// executed.
-        /// </summary>
-        private void StopKeepAliveTimer()
-        {
-            if (_keepAliveTimer == null)
-                return;
-
-            _keepAliveTimer.Dispose();
-            _keepAliveTimer = null;
-        }
-
-        private void SendKeepAliveMessage()
-        {
-            // do nothing if we have disposed or disconnected
-            if (Session == null)
-                return;
-
-            // do not send multiple keep-alive messages concurrently
-            if (Monitor.TryEnter(_keepAliveLock))
-            {
-                try
-                {
-                    Session.TrySendMessage(new IgnoreMessage());
-                }
-                finally
-                {
-                    Monitor.Exit(_keepAliveLock);
-                }
-            }
-        }
-
-        /// <summary>
-        /// Starts the keep-alive timer.
-        /// </summary>
-        /// <remarks>
-        /// When <see cref="KeepAliveInterval"/> is negative one (-1) milliseconds, then
-        /// the timer will not be started.
-        /// </remarks>
-        private void StartKeepAliveTimer()
-        {
-            if (_keepAliveInterval == SshNet.Session.InfiniteTimeSpan)
-                return;
-
-            if (_keepAliveTimer != null)
-                // timer is already started
-                return;
-
-            _keepAliveTimer = new Timer(state => SendKeepAliveMessage(), null, _keepAliveInterval, _keepAliveInterval);
-        }
-    }
-}
+using System;
+using System.Net.Sockets;
+using System.Threading;
+using Renci.SshNet.Common;
+using Renci.SshNet.Messages.Transport;
+
+namespace Renci.SshNet
+{
+    /// <summary>
+    /// Serves as base class for client implementations, provides common client functionality.
+    /// </summary>
+    public abstract class BaseClient : IDisposable
+    {
+        /// <summary>
+        /// Holds value indicating whether the connection info is owned by this client.
+        /// </summary>
+        private readonly bool _ownsConnectionInfo;
+
+        private readonly IServiceFactory _serviceFactory;
+        private readonly object _keepAliveLock = new object();
+        private TimeSpan _keepAliveInterval;
+        private Timer _keepAliveTimer;
+        private ConnectionInfo _connectionInfo;
+
+        /// <summary>
+        /// Gets the current session.
+        /// </summary>
+        /// <value>
+        /// The current session.
+        /// </value>
+        internal ISession Session { get; private set; }
+
+        /// <summary>
+        /// Gets the factory for creating new services.
+        /// </summary>
+        /// <value>
+        /// The factory for creating new services.
+        /// </value>
+        internal IServiceFactory ServiceFactory
+        {
+            get { return _serviceFactory; }
+        }
+
+        /// <summary>
+        /// Gets the connection info.
+        /// </summary>
+        /// <value>
+        /// The connection info.
+        /// </value>
+        /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
+        public ConnectionInfo ConnectionInfo
+        {
+            get
+            {
+                CheckDisposed();
+                return _connectionInfo;
+            }
+            private set
+            {
+                _connectionInfo = value;
+            }
+        }
+
+        /// <summary>
+        /// Gets a value indicating whether this client is connected to the server.
+        /// </summary>
+        /// <value>
+        /// <c>true</c> if this client is connected; otherwise, <c>false</c>.
+        /// </value>
+        /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
+        public bool IsConnected
+        {
+            get
+            {
+                CheckDisposed();
+                return Session != null && Session.IsConnected;
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets the keep-alive interval.
+        /// </summary>
+        /// <value>
+        /// The keep-alive interval. Specify negative one (-1) milliseconds to disable the
+        /// keep-alive. This is the default value.
+        /// </value>
+        /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
+        public TimeSpan KeepAliveInterval
+        {
+            get
+            {
+                CheckDisposed();
+                return _keepAliveInterval;
+            }
+            set
+            {
+                CheckDisposed();
+
+                if (value == _keepAliveInterval)
+                    return;
+
+                if (value == SshNet.Session.InfiniteTimeSpan)
+                {
+                    // stop the timer when the value is -1 milliseconds
+                    StopKeepAliveTimer();
+                }
+                else
+                {
+                    // change the due time and interval of the timer if has already
+                    // been created (which means the client is connected)
+                    // 
+                    // if the client is not yet connected, then the timer will be
+                    // created with the new interval when Connect() is invoked
+                    if (_keepAliveTimer != null)
+                        _keepAliveTimer.Change(value, value);
+                }
+                _keepAliveInterval = value;
+            }
+        }
+
+        /// <summary>
+        /// Occurs when an error occurred.
+        /// </summary>
+        /// <example>
+        ///   <code source="..\..\src\Renci.SshNet.Tests\Classes\SshClientTest.cs" region="Example SshClient Connect ErrorOccurred" language="C#" title="Handle ErrorOccurred event" />
+        /// </example>
+        public event EventHandler<ExceptionEventArgs> ErrorOccurred;
+
+        /// <summary>
+        /// Occurs when host key received.
+        /// </summary>
+        /// <example>
+        ///   <code source="..\..\src\Renci.SshNet.Tests\Classes\SshClientTest.cs" region="Example SshClient Connect HostKeyReceived" language="C#" title="Handle HostKeyReceived event" />
+        /// </example>
+        public event EventHandler<HostKeyEventArgs> HostKeyReceived;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BaseClient"/> class.
+        /// </summary>
+        /// <param name="connectionInfo">The connection info.</param>
+        /// <param name="ownsConnectionInfo">Specified whether this instance owns the connection info.</param>
+        /// <exception cref="ArgumentNullException"><paramref name="connectionInfo"/> is null.</exception>
+        /// <remarks>
+        /// If <paramref name="ownsConnectionInfo"/> is <c>true</c>, then the
+        /// connection info will be disposed when this instance is disposed.
+        /// </remarks>
+        protected BaseClient(ConnectionInfo connectionInfo, bool ownsConnectionInfo)
+            : this(connectionInfo, ownsConnectionInfo, new ServiceFactory())
+        {
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BaseClient"/> class.
+        /// </summary>
+        /// <param name="connectionInfo">The connection info.</param>
+        /// <param name="ownsConnectionInfo">Specified whether this instance owns the connection info.</param>
+        /// <param name="serviceFactory">The factory to use for creating new services.</param>
+        /// <exception cref="ArgumentNullException"><paramref name="connectionInfo"/> is <c>null</c>.</exception>
+        /// <exception cref="ArgumentNullException"><paramref name="serviceFactory"/> is <c>null</c>.</exception>
+        /// <remarks>
+        /// If <paramref name="ownsConnectionInfo"/> is <c>true</c>, then the
+        /// connection info will be disposed when this instance is disposed.
+        /// </remarks>
+        internal BaseClient(ConnectionInfo connectionInfo, bool ownsConnectionInfo, IServiceFactory serviceFactory)
+        {
+            if (connectionInfo == null)
+                throw new ArgumentNullException("connectionInfo");
+            if (serviceFactory == null)
+                throw new ArgumentNullException("serviceFactory");
+
+            ConnectionInfo = connectionInfo;
+            _ownsConnectionInfo = ownsConnectionInfo;
+            _serviceFactory = serviceFactory;
+            _keepAliveInterval = SshNet.Session.InfiniteTimeSpan;
+        }
+
+        /// <summary>
+        /// Connects client to the server.
+        /// </summary>
+        /// <exception cref="InvalidOperationException">The client is already connected.</exception>
+        /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
+        /// <exception cref="SocketException">Socket connection to the SSH server or proxy server could not be established, or an error occurred while resolving the hostname.</exception>
+        /// <exception cref="SshConnectionException">SSH session could not be established.</exception>
+        /// <exception cref="SshAuthenticationException">Authentication of SSH session failed.</exception>
+        /// <exception cref="ProxyException">Failed to establish proxy connection.</exception>
+        public void Connect()
+        {
+            CheckDisposed();
+
+            // TODO (see issue #1758):
+            // we're not stopping the keep-alive timer and disposing the session here
+            // 
+            // we could do this but there would still be side effects as concrete
+            // implementations may still hang on to the original session
+            // 
+            // therefore it would be better to actually invoke the Disconnect method
+            // (and then the Dispose on the session) but even that would have side effects
+            // eg. it would remove all forwarded ports from SshClient
+            // 
+            // I think we should modify our concrete clients to better deal with a
+            // disconnect. In case of SshClient this would mean not removing the 
+            // forwarded ports on disconnect (but only on dispose ?) and link a
+            // forwarded port with a client instead of with a session
+            //
+            // To be discussed with Oleg (or whoever is interested)
+            if (Session != null && Session.IsConnected)
+                throw new InvalidOperationException("The client is already connected.");
+
+            OnConnecting();
+            Session = _serviceFactory.CreateSession(ConnectionInfo);
+            Session.HostKeyReceived += Session_HostKeyReceived;
+            Session.ErrorOccured += Session_ErrorOccured;
+            Session.Connect();
+            StartKeepAliveTimer();
+            OnConnected();
+        }
+
+        /// <summary>
+        /// Disconnects client from the server.
+        /// </summary>
+        /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
+        public void Disconnect()
+        {
+            CheckDisposed();
+
+            OnDisconnecting();
+
+            // stop sending keep-alive messages before we close the
+            // session
+            StopKeepAliveTimer();
+
+            // disconnect and dispose the SSH session
+            if (Session != null)
+            {
+                // a new session is created in Connect(), so we should dispose and
+                // dereference the current session here
+                Session.ErrorOccured -= Session_ErrorOccured;
+                Session.HostKeyReceived -= Session_HostKeyReceived;
+                Session.Disconnect();
+                Session.Dispose();
+                Session = null;
+            }
+
+            OnDisconnected();
+        }
+
+        /// <summary>
+        /// Sends a keep-alive message to the server.
+        /// </summary>
+        /// <remarks>
+        /// Use <see cref="KeepAliveInterval"/> to configure the client to send a keep-alive at regular
+        /// intervals.
+        /// </remarks>
+        /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
+        [Obsolete("Use KeepAliveInterval to send a keep-alive message at regular intervals.")]
+        public void SendKeepAlive()
+        {
+            CheckDisposed();
+
+            SendKeepAliveMessage();
+        }
+
+        /// <summary>
+        /// Called when client is connecting to the server.
+        /// </summary>
+        protected virtual void OnConnecting()
+        {
+        }
+
+        /// <summary>
+        /// Called when client is connected to the server.
+        /// </summary>
+        protected virtual void OnConnected()
+        {
+        }
+
+        /// <summary>
+        /// Called when client is disconnecting from the server.
+        /// </summary>
+        protected virtual void OnDisconnecting()
+        {
+            if (Session != null)
+                Session.OnDisconnecting();
+        }
+
+        /// <summary>
+        /// Called when client is disconnected from the server.
+        /// </summary>
+        protected virtual void OnDisconnected()
+        {
+        }
+
+        private void Session_ErrorOccured(object sender, ExceptionEventArgs e)
+        {
+            var handler = ErrorOccurred;
+            if (handler != null)
+            {
+                handler(this, e);
+            }
+        }
+
+        private void Session_HostKeyReceived(object sender, HostKeyEventArgs e)
+        {
+            var handler = HostKeyReceived;
+            if (handler != null)
+            {
+                handler(this, e);
+            }
+        }
+
+        #region IDisposable Members
+
+        private bool _isDisposed;
+
+        /// <summary>
+        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+        /// </summary>
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        /// <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>
+        protected virtual void Dispose(bool disposing)
+        {
+            if (_isDisposed)
+                return;
+
+            if (disposing)
+            {
+                Disconnect();
+
+                if (_ownsConnectionInfo && _connectionInfo != null)
+                {
+                    var connectionInfoDisposable = _connectionInfo as IDisposable;
+                    if (connectionInfoDisposable != null)
+                        connectionInfoDisposable.Dispose();
+                    _connectionInfo = null;
+                }
+
+                _isDisposed = true;
+            }
+        }
+
+        /// <summary>
+        /// Check if the current instance is disposed.
+        /// </summary>
+        /// <exception cref="ObjectDisposedException">THe current instance is disposed.</exception>
+        protected void CheckDisposed()
+        {
+            if (_isDisposed)
+                throw new ObjectDisposedException(GetType().FullName);
+        }
+
+        /// <summary>
+        /// Releases unmanaged resources and performs other cleanup operations before the
+        /// <see cref="BaseClient"/> is reclaimed by garbage collection.
+        /// </summary>
+        ~BaseClient()
+        {
+            Dispose(false);
+        }
+
+        #endregion
+
+        /// <summary>
+        /// Stops the keep-alive timer, and waits until all timer callbacks have been
+        /// executed.
+        /// </summary>
+        private void StopKeepAliveTimer()
+        {
+            if (_keepAliveTimer == null)
+                return;
+
+            _keepAliveTimer.Dispose();
+            _keepAliveTimer = null;
+        }
+
+        private void SendKeepAliveMessage()
+        {
+            // do nothing if we have disposed or disconnected
+            if (Session == null)
+                return;
+
+            // do not send multiple keep-alive messages concurrently
+            if (Monitor.TryEnter(_keepAliveLock))
+            {
+                try
+                {
+                    Session.TrySendMessage(new IgnoreMessage());
+                }
+                finally
+                {
+                    Monitor.Exit(_keepAliveLock);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Starts the keep-alive timer.
+        /// </summary>
+        /// <remarks>
+        /// When <see cref="KeepAliveInterval"/> is negative one (-1) milliseconds, then
+        /// the timer will not be started.
+        /// </remarks>
+        private void StartKeepAliveTimer()
+        {
+            if (_keepAliveInterval == SshNet.Session.InfiniteTimeSpan)
+                return;
+
+            if (_keepAliveTimer != null)
+                // timer is already started
+                return;
+
+            _keepAliveTimer = new Timer(state => SendKeepAliveMessage(), null, _keepAliveInterval, _keepAliveInterval);
+        }
+    }
+}

+ 2 - 2
src/Renci.SshNet/ConnectionInfo.cs

@@ -122,7 +122,7 @@ namespace Renci.SshNet
         /// The connection timeout. The default value is 30 seconds.
         /// </value>
         /// <example>
-        ///   <code source="..\..\Renci.SshNet.Tests\Classes\SshClientTest.cs" region="Example SshClient Connect Timeout" language="C#" title="Specify connection timeout" />
+        ///   <code source="..\..\src\Renci.SshNet.Tests\Classes\SshClientTest.cs" region="Example SshClient Connect Timeout" language="C#" title="Specify connection timeout" />
         /// </example>
         public TimeSpan Timeout { get; set; }
 
@@ -156,7 +156,7 @@ namespace Renci.SshNet
         /// Occurs when authentication banner is sent by the server.
         /// </summary>
         /// <example>
-        ///     <code source="..\..\Renci.SshNet.Tests\Classes\PasswordConnectionInfoTest.cs" region="Example PasswordConnectionInfo AuthenticationBanner" language="C#" title="Display authentication banner" />
+        ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\PasswordConnectionInfoTest.cs" region="Example PasswordConnectionInfo AuthenticationBanner" language="C#" title="Display authentication banner" />
         /// </example>
         public event EventHandler<AuthenticationBannerEventArgs> AuthenticationBanner;
 

+ 1 - 1
src/Renci.SshNet/ForwardedPortLocal.cs

@@ -51,7 +51,7 @@ namespace Renci.SshNet
         /// <exception cref="ArgumentNullException"><paramref name="host"/> is <c>null</c>.</exception>
         /// <exception cref="ArgumentOutOfRangeException"><paramref name="port" /> is greater than <see cref="F:System.Net.IPEndPoint.MaxPort" />.</exception>
         /// <example>
-        ///     <code source="..\..\Renci.SshNet.Tests\Classes\ForwardedPortLocalTest.cs" region="Example SshClient AddForwardedPort Start Stop ForwardedPortLocal" language="C#" title="Local port forwarding" />
+        ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\ForwardedPortLocalTest.cs" region="Example SshClient AddForwardedPort Start Stop ForwardedPortLocal" language="C#" title="Local port forwarding" />
         /// </example>
         public ForwardedPortLocal(uint boundPort, string host, uint port)
             : this(string.Empty, boundPort, host, port)

+ 1 - 1
src/Renci.SshNet/ForwardedPortRemote.cs

@@ -106,7 +106,7 @@ namespace Renci.SshNet
         /// <param name="host">The host.</param>
         /// <param name="port">The port.</param>
         /// <example>
-        ///     <code source="..\..\Renci.SshNet.Tests\Classes\ForwardedPortRemoteTest.cs" region="Example SshClient AddForwardedPort Start Stop ForwardedPortRemote" language="C#" title="Remote port forwarding" />
+        ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\ForwardedPortRemoteTest.cs" region="Example SshClient AddForwardedPort Start Stop ForwardedPortRemote" language="C#" title="Remote port forwarding" />
         /// </example>
         public ForwardedPortRemote(uint boundPort, string host, uint port)
             : this(string.Empty, boundPort, host, port)

+ 1 - 1
src/Renci.SshNet/IConnectionInfo.cs

@@ -71,7 +71,7 @@ namespace Renci.SshNet
         /// The connection timeout. The default value is 30 seconds.
         /// </value>
         /// <example>
-        ///   <code source="..\..\Renci.SshNet.Tests\Classes\SshClientTest.cs" region="Example SshClient Connect Timeout" language="C#" title="Specify connection timeout" />
+        ///   <code source="..\..\src\Renci.SshNet.Tests\Classes\SshClientTest.cs" region="Example SshClient Connect Timeout" language="C#" title="Specify connection timeout" />
         /// </example>
         TimeSpan Timeout { get; }
 

+ 2 - 2
src/Renci.SshNet/KeyboardInteractiveConnectionInfo.cs

@@ -8,7 +8,7 @@ namespace Renci.SshNet
     /// Provides connection information when keyboard interactive authentication method is used
     /// </summary>
     /// <example>
-    ///     <code source="..\..\Renci.SshNet.Tests\Classes\KeyboardInteractiveConnectionInfoTest.cs" region="Example KeyboardInteractiveConnectionInfo AuthenticationPrompt" language="C#" title="Connect using interactive method" />
+    ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\KeyboardInteractiveConnectionInfoTest.cs" region="Example KeyboardInteractiveConnectionInfo AuthenticationPrompt" language="C#" title="Connect using interactive method" />
     /// </example>
     public class KeyboardInteractiveConnectionInfo : ConnectionInfo, IDisposable
     {
@@ -16,7 +16,7 @@ namespace Renci.SshNet
         /// Occurs when server prompts for more authentication information.
         /// </summary>
         /// <example>
-        ///     <code source="..\..\Renci.SshNet.Tests\Classes\KeyboardInteractiveConnectionInfoTest.cs" region="Example KeyboardInteractiveConnectionInfo AuthenticationPrompt" language="C#" title="Connect using interactive method" />
+        ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\KeyboardInteractiveConnectionInfoTest.cs" region="Example KeyboardInteractiveConnectionInfo AuthenticationPrompt" language="C#" title="Connect using interactive method" />
         /// </example>
         public event EventHandler<AuthenticationPromptEventArgs> AuthenticationPrompt;
 

+ 5 - 5
src/Renci.SshNet/PasswordConnectionInfo.cs

@@ -9,9 +9,9 @@ namespace Renci.SshNet
     /// Provides connection information when password authentication method is used
     /// </summary>
     /// <example>
-    ///     <code source="..\..\Renci.SshNet.Tests\Classes\PasswordConnectionInfoTest.cs" region="Example PasswordConnectionInfo" language="C#" title="Connect using username and password" />
-    ///     <code source="..\..\Renci.SshNet.Tests\Classes\PasswordConnectionInfoTest.cs" region="Example PasswordConnectionInfo PasswordExpired" language="C#" title="Change password when connecting" />
-    ///     <code source="..\..\Renci.SshNet.Tests\Classes\PasswordConnectionInfoTest.cs" region="Example PasswordConnectionInfo AuthenticationBanner" language="C#" title="Display authentication banner" />
+    ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\PasswordConnectionInfoTest.cs" region="Example PasswordConnectionInfo" language="C#" title="Connect using username and password" />
+    ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\PasswordConnectionInfoTest.cs" region="Example PasswordConnectionInfo PasswordExpired" language="C#" title="Change password when connecting" />
+    ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\PasswordConnectionInfoTest.cs" region="Example PasswordConnectionInfo AuthenticationBanner" language="C#" title="Display authentication banner" />
     /// </example>
     public class PasswordConnectionInfo : ConnectionInfo, IDisposable
     {
@@ -19,7 +19,7 @@ namespace Renci.SshNet
         /// Occurs when user's password has expired and needs to be changed.
         /// </summary>
         /// <example>
-        ///     <code source="..\..\Renci.SshNet.Tests\Classes\PasswordConnectionInfoTest.cs" region="Example PasswordConnectionInfo PasswordExpired" language="C#" title="Change password when connecting" />
+        ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\PasswordConnectionInfoTest.cs" region="Example PasswordConnectionInfo PasswordExpired" language="C#" title="Change password when connecting" />
         /// </example>
         public event EventHandler<AuthenticationPasswordChangeEventArgs> PasswordExpired;
 
@@ -30,7 +30,7 @@ namespace Renci.SshNet
         /// <param name="username">Connection username.</param>
         /// <param name="password">Connection password.</param>
         /// <example>
-        ///     <code source="..\..\Renci.SshNet.Tests\Classes\PasswordConnectionInfoTest.cs" region="Example PasswordConnectionInfo" language="C#" title="Connect using username and password" />
+        ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\PasswordConnectionInfoTest.cs" region="Example PasswordConnectionInfo" language="C#" title="Connect using username and password" />
         /// </example>
         /// <exception cref="ArgumentNullException"><paramref name="password"/> is null.</exception>
         /// <exception cref="ArgumentException"><paramref name="host"/> is invalid, or <paramref name="username"/> is null or contains whitespace characters.</exception>

+ 3 - 3
src/Renci.SshNet/PrivateKeyConnectionInfo.cs

@@ -9,7 +9,7 @@ namespace Renci.SshNet
     /// Provides connection information when private key authentication method is used
     /// </summary>
     /// <example>
-    ///   <code source="..\..\Renci.SshNet.Tests\Classes\PrivateKeyConnectionInfoTest.cs" region="Example PrivateKeyConnectionInfo PrivateKeyFile" language="C#" title="Connect using private key" />
+    ///   <code source="..\..\src\Renci.SshNet.Tests\Classes\PrivateKeyConnectionInfoTest.cs" region="Example PrivateKeyConnectionInfo PrivateKeyFile" language="C#" title="Connect using private key" />
     ///   </example>
     public class PrivateKeyConnectionInfo : ConnectionInfo, IDisposable
     {
@@ -25,8 +25,8 @@ namespace Renci.SshNet
         /// <param name="username">Connection username.</param>
         /// <param name="keyFiles">Connection key files.</param>
         /// <example>
-        ///     <code source="..\..\Renci.SshNet.Tests\Classes\PrivateKeyConnectionInfoTest.cs" region="Example PrivateKeyConnectionInfo PrivateKeyFile" language="C#" title="Connect using private key" />
-        ///     <code source="..\..\Renci.SshNet.Tests\Classes\PrivateKeyConnectionInfoTest.cs" region="Example PrivateKeyConnectionInfo PrivateKeyFile Multiple" language="C#" title="Connect using multiple private key" />
+        ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\PrivateKeyConnectionInfoTest.cs" region="Example PrivateKeyConnectionInfo PrivateKeyFile" language="C#" title="Connect using private key" />
+        ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\PrivateKeyConnectionInfoTest.cs" region="Example PrivateKeyConnectionInfo PrivateKeyFile Multiple" language="C#" title="Connect using multiple private key" />
         /// </example>
         public PrivateKeyConnectionInfo(string host, string username, params PrivateKeyFile[] keyFiles)
             : this(host, DefaultPort, username, ProxyTypes.None, string.Empty, 0, string.Empty, string.Empty, keyFiles)

+ 1 - 1
src/Renci.SshNet/PrivateKeyFile.cs

@@ -18,7 +18,7 @@ namespace Renci.SshNet
     /// Represents private key information.
     /// </summary>
     /// <example>
-    ///     <code source="..\..\Renci.SshNet.Tests\Data\Key.RSA.txt" language="Text" title="Private RSA key example" />
+    ///     <code source="..\..\src\Renci.SshNet.Tests\Data\Key.RSA.txt" language="Text" title="Private RSA key example" />
     /// </example>
     /// <remarks>
     /// <para>

+ 13 - 13
src/Renci.SshNet/SshClient.cs

@@ -45,10 +45,10 @@ namespace Renci.SshNet
         /// </summary>
         /// <param name="connectionInfo">The connection info.</param>
         /// <example>
-        ///     <code source="..\..\Renci.SshNet.Tests\Classes\PasswordConnectionInfoTest.cs" region="Example PasswordConnectionInfo" language="C#" title="Connect using PasswordConnectionInfo object" />
-        ///     <code source="..\..\Renci.SshNet.Tests\Classes\PasswordConnectionInfoTest.cs" region="Example PasswordConnectionInfo PasswordExpired" language="C#" title="Connect using PasswordConnectionInfo object with passwod change option" />
-        ///     <code source="..\..\Renci.SshNet.Tests\Classes\PrivateKeyConnectionInfoTest.cs" region="Example PrivateKeyConnectionInfo PrivateKeyFile" language="C#" title="Connect using PrivateKeyConnectionInfo" />
-        ///     <code source="..\..\Renci.SshNet.Tests\Classes\SshClientTest.cs" region="Example SshClient Connect Timeout" language="C#" title="Specify connection timeout when connecting" />
+        ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\PasswordConnectionInfoTest.cs" region="Example PasswordConnectionInfo" language="C#" title="Connect using PasswordConnectionInfo object" />
+        ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\PasswordConnectionInfoTest.cs" region="Example PasswordConnectionInfo PasswordExpired" language="C#" title="Connect using PasswordConnectionInfo object with passwod change option" />
+        ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\PrivateKeyConnectionInfoTest.cs" region="Example PrivateKeyConnectionInfo PrivateKeyFile" language="C#" title="Connect using PrivateKeyConnectionInfo" />
+        ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\SshClientTest.cs" region="Example SshClient Connect Timeout" language="C#" title="Specify connection timeout when connecting" />
         /// </example>
         /// <exception cref="ArgumentNullException"><paramref name="connectionInfo"/> is null.</exception>
         public SshClient(ConnectionInfo connectionInfo)
@@ -79,7 +79,7 @@ namespace Renci.SshNet
         /// <param name="username">Authentication username.</param>
         /// <param name="password">Authentication password.</param>
         /// <example>
-        ///     <code source="..\..\Renci.SshNet.Tests\Classes\SshClientTest.cs" region="Example SshClient(host, username) Connect" language="C#" title="Connect using username and password" />
+        ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\SshClientTest.cs" region="Example SshClient(host, username) Connect" language="C#" title="Connect using username and password" />
         /// </example>
         /// <exception cref="ArgumentNullException"><paramref name="password"/> is null.</exception>
         /// <exception cref="ArgumentException"><paramref name="host"/> is invalid, or <paramref name="username"/> is null or contains whitespace characters.</exception>
@@ -96,8 +96,8 @@ namespace Renci.SshNet
         /// <param name="username">Authentication username.</param>
         /// <param name="keyFiles">Authentication private key file(s) .</param>
         /// <example>
-        ///     <code source="..\..\Renci.SshNet.Tests\Classes\SshClientTest.cs" region="Example SshClient(host, username) Connect PrivateKeyFile" language="C#" title="Connect using username and private key" />
-        ///     <code source="..\..\Renci.SshNet.Tests\Classes\SshClientTest.cs" region="Example SshClient(host, username) Connect PrivateKeyFile PassPhrase" language="C#" title="Connect using username and private key and pass phrase" />
+        ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\SshClientTest.cs" region="Example SshClient(host, username) Connect PrivateKeyFile" language="C#" title="Connect using username and private key" />
+        ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\SshClientTest.cs" region="Example SshClient(host, username) Connect PrivateKeyFile PassPhrase" language="C#" title="Connect using username and private key and pass phrase" />
         /// </example>
         /// <exception cref="ArgumentNullException"><paramref name="keyFiles"/> is null.</exception>
         /// <exception cref="ArgumentException"><paramref name="host"/> is invalid, -or- <paramref name="username"/> is null or contains whitespace characters.</exception>
@@ -115,8 +115,8 @@ namespace Renci.SshNet
         /// <param name="username">Authentication username.</param>
         /// <param name="keyFiles">Authentication private key file(s) .</param>
         /// <example>
-        ///     <code source="..\..\Renci.SshNet.Tests\Classes\SshClientTest.cs" region="Example SshClient(host, username) Connect PrivateKeyFile" language="C#" title="Connect using private key" />
-        ///     <code source="..\..\Renci.SshNet.Tests\Classes\SshClientTest.cs" region="Example SshClient(host, username) Connect PrivateKeyFile PassPhrase" language="C#" title="Connect using private key and pass phrase" />
+        ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\SshClientTest.cs" region="Example SshClient(host, username) Connect PrivateKeyFile" language="C#" title="Connect using private key" />
+        ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\SshClientTest.cs" region="Example SshClient(host, username) Connect PrivateKeyFile PassPhrase" language="C#" title="Connect using private key and pass phrase" />
         /// </example>
         /// <exception cref="ArgumentNullException"><paramref name="keyFiles"/> is null.</exception>
         /// <exception cref="ArgumentException"><paramref name="host"/> is invalid, -or- <paramref name="username"/> is null or contains whitespace characters.</exception>
@@ -178,8 +178,8 @@ namespace Renci.SshNet
         /// </summary>
         /// <param name="port">The port.</param>
         /// <example>
-        ///     <code source="..\..\Renci.SshNet.Tests\Classes\ForwardedPortRemoteTest.cs" region="Example SshClient AddForwardedPort Start Stop ForwardedPortRemote" language="C#" title="Remote port forwarding" />
-        ///     <code source="..\..\Renci.SshNet.Tests\Classes\ForwardedPortLocalTest.cs" region="Example SshClient AddForwardedPort Start Stop ForwardedPortLocal" language="C#" title="Local port forwarding" />
+        ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\ForwardedPortRemoteTest.cs" region="Example SshClient AddForwardedPort Start Stop ForwardedPortRemote" language="C#" title="Remote port forwarding" />
+        ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\ForwardedPortLocalTest.cs" region="Example SshClient AddForwardedPort Start Stop ForwardedPortLocal" language="C#" title="Local port forwarding" />
         /// </example>
         /// <exception cref="InvalidOperationException">Forwarded port is already added to a different client.</exception>
         /// <exception cref="ArgumentNullException"><paramref name="port"/> is null.</exception>
@@ -259,8 +259,8 @@ namespace Renci.SshNet
         /// <returns>Returns an instance of <see cref="SshCommand"/> with execution results.</returns>
         /// <remarks>This method internally uses asynchronous calls.</remarks>
         /// <example>
-        ///     <code source="..\..\Renci.SshNet.Tests\Classes\SshCommandTest.cs" region="Example SshCommand RunCommand Result" language="C#" title="Running simple command" />
-        ///     <code source="..\..\Renci.SshNet.Tests\Classes\SshCommandTest.NET40.cs" region="Example SshCommand RunCommand Parallel" language="C#" title="Run many commands in parallel" />
+        ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\SshCommandTest.cs" region="Example SshCommand RunCommand Result" language="C#" title="Running simple command" />
+        ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\SshCommandTest.NET40.cs" region="Example SshCommand RunCommand Parallel" language="C#" title="Run many commands in parallel" />
         /// </example>
         /// <exception cref="ArgumentException">CommandText property is empty.</exception>
         /// <exception cref="T:Renci.SshNet.Common.SshException">Invalid Operation - An existing channel was used to execute this command.</exception>

+ 11 - 11
src/Renci.SshNet/SshCommand.cs

@@ -38,7 +38,7 @@ namespace Renci.SshNet
         /// The command timeout.
         /// </value>
         /// <example>
-        ///     <code source="..\..\Renci.SshNet.Tests\Classes\SshCommandTest.cs" region="Example SshCommand CreateCommand Execute CommandTimeout" language="C#" title="Specify command execution timeout" />
+        ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\SshCommandTest.cs" region="Example SshCommand CreateCommand Execute CommandTimeout" language="C#" title="Specify command execution timeout" />
         /// </example>
         public TimeSpan CommandTimeout { get; set; }
 
@@ -46,7 +46,7 @@ namespace Renci.SshNet
         /// Gets the command exit status.
         /// </summary>
         /// <example>
-        ///     <code source="..\..\Renci.SshNet.Tests\Classes\SshCommandTest.cs" region="Example SshCommand RunCommand ExitStatus" language="C#" title="Get command execution exit status" />
+        ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\SshCommandTest.cs" region="Example SshCommand RunCommand ExitStatus" language="C#" title="Get command execution exit status" />
         /// </example>
         public int ExitStatus { get; private set; }
 
@@ -54,7 +54,7 @@ namespace Renci.SshNet
         /// Gets the output stream.
         /// </summary>
         /// <example>
-        ///     <code source="..\..\Renci.SshNet.Tests\Classes\SshCommandTest.cs" region="Example SshCommand CreateCommand Execute OutputStream" language="C#" title="Use OutputStream to get command execution output" />
+        ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\SshCommandTest.cs" region="Example SshCommand CreateCommand Execute OutputStream" language="C#" title="Use OutputStream to get command execution output" />
         /// </example>
         public Stream OutputStream { get; private set; }
 
@@ -62,7 +62,7 @@ namespace Renci.SshNet
         /// Gets the extended output stream.
         /// </summary>
         /// <example>
-        ///     <code source="..\..\Renci.SshNet.Tests\Classes\SshCommandTest.cs" region="Example SshCommand CreateCommand Execute ExtendedOutputStream" language="C#" title="Use ExtendedOutputStream to get command debug execution output" />
+        ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\SshCommandTest.cs" region="Example SshCommand CreateCommand Execute ExtendedOutputStream" language="C#" title="Use ExtendedOutputStream to get command debug execution output" />
         /// </example>
         public Stream ExtendedOutputStream { get; private set; }
 
@@ -71,7 +71,7 @@ namespace Renci.SshNet
         /// Gets the command execution result.
         /// </summary>
         /// <example>
-        ///     <code source="..\..\Renci.SshNet.Tests\Classes\SshCommandTest.cs" region="Example SshCommand RunCommand Result" language="C#" title="Running simple command" />
+        ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\SshCommandTest.cs" region="Example SshCommand RunCommand Result" language="C#" title="Running simple command" />
         /// </example>
         public string Result
         {
@@ -98,7 +98,7 @@ namespace Renci.SshNet
         /// Gets the command execution error.
         /// </summary>
         /// <example>
-        ///     <code source="..\..\Renci.SshNet.Tests\Classes\SshCommandTest.cs" region="Example SshCommand CreateCommand Error" language="C#" title="Display command execution error" />
+        ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\SshCommandTest.cs" region="Example SshCommand CreateCommand Error" language="C#" title="Display command execution error" />
         /// </example>
         public string Error
         {
@@ -157,7 +157,7 @@ namespace Renci.SshNet
         /// An <see cref="System.IAsyncResult" /> that represents the asynchronous command execution, which could still be pending.
         /// </returns>
         /// <example>
-        ///     <code source="..\..\Renci.SshNet.Tests\Classes\SshCommandTest.cs" region="Example SshCommand CreateCommand BeginExecute IsCompleted EndExecute" language="C#" title="Asynchronous Command Execution" />
+        ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\SshCommandTest.cs" region="Example SshCommand CreateCommand BeginExecute IsCompleted EndExecute" language="C#" title="Asynchronous Command Execution" />
         /// </example>
         /// <exception cref="System.InvalidOperationException">Asynchronous operation is already in progress.</exception>
         /// <exception cref="SshException">Invalid operation.</exception>
@@ -283,7 +283,7 @@ namespace Renci.SshNet
         /// <param name="asyncResult">The reference to the pending asynchronous request to finish.</param>
         /// <returns>Command execution result.</returns>
         /// <example>
-        ///     <code source="..\..\Renci.SshNet.Tests\Classes\SshCommandTest.cs" region="Example SshCommand CreateCommand BeginExecute IsCompleted EndExecute" language="C#" title="Asynchronous Command Execution" />
+        ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\SshCommandTest.cs" region="Example SshCommand CreateCommand BeginExecute IsCompleted EndExecute" language="C#" title="Asynchronous Command Execution" />
         /// </example>
         /// <exception cref="ArgumentException">Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult.</exception>
         /// <exception cref="ArgumentNullException"><paramref name="asyncResult"/> is <c>null</c>.</exception>
@@ -329,9 +329,9 @@ namespace Renci.SshNet
         /// </summary>
         /// <returns>Command execution result</returns>
         /// <example>
-        ///     <code source="..\..\Renci.SshNet.Tests\Classes\SshCommandTest.cs" region="Example SshCommand CreateCommand Execute" language="C#" title="Simple command execution" />
-        ///     <code source="..\..\Renci.SshNet.Tests\Classes\SshCommandTest.cs" region="Example SshCommand CreateCommand Error" language="C#" title="Display command execution error" />
-        ///     <code source="..\..\Renci.SshNet.Tests\Classes\SshCommandTest.cs" region="Example SshCommand CreateCommand Execute CommandTimeout" language="C#" title="Specify command execution timeout" />
+        ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\SshCommandTest.cs" region="Example SshCommand CreateCommand Execute" language="C#" title="Simple command execution" />
+        ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\SshCommandTest.cs" region="Example SshCommand CreateCommand Error" language="C#" title="Display command execution error" />
+        ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\SshCommandTest.cs" region="Example SshCommand CreateCommand Execute CommandTimeout" language="C#" title="Specify command execution timeout" />
         /// </example>
         /// <exception cref="Renci.SshNet.Common.SshConnectionException">Client is not connected.</exception>
         /// <exception cref="Renci.SshNet.Common.SshOperationTimeoutException">Operation has timed out.</exception>