Hi,
I'm working on a scenario in D365 F&O where I need to connect to an SFTP server to send and read files. The SFTP server requires authentication using a username and private key (no password).
To achieve this, I created a C# class library that uses Renci.SshNet to establish the SFTP connection. I then call this C# code from X++. The connection returns success, which means the authentication is working.
However, when I try to access the SftpClient instance from X++ -- for example, to check if the connection is still active or to perform operations like file listing -- I encounter the below error:
Cannot access a disposed object. Object name: 'Renci.SshNet.SftpClient'.
Has anyone encountered this issue before? Is there a recommended pattern for handling SftpClient in a way that it works reliably with X++?
Any help or examples would be greatly appreciated.
My X++ code:
using SFTPConnection;
using Renci.SshNet;
using Renci.SshNet.Sftp;
using Renci.SshNet.SftpClient;
public class ConnectSFTPServer
{
public static void main(Args args)
{
System.Exception ex;
//Renci.SshNet.SftpClient sftpClient;
SftpClient sftpClient;
System.IO.Stream stream;
str connectionStatus;
try
{
System.IO.Stream publicKeyStream;
//I get my private key's stream here
sftpClient = SFTPConnection.SFTPConnectionAndUploadFile::ConnectSFTP('000.000.000.00', 22, 'XYZXYZ', 'XYZXYZ', publicKeyStream, 'XYZXYZ');
if(sftpClient != null)
{
ttsBegin;
if (sftpClient.IsConnected) //I got error in this line
connectionStatus += 'Connected';
else
{
sftpClient.Connect();
}
sftpClient.Disconnect();
sftpClient.Dispose();
ttscommit;
}
Info(connectionStatus);
}
catch(ex)
{
System.String messageEx = ex.Message;
info(strFmt("%1 - %2", connectionStatus, messageEx));
}
}
}
My C# code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Renci.SshNet;
using Renci.SshNet.Sftp;
using System.IO;
using System.Net;
using System.Linq;
using System.Runtime.CompilerServices;
namespace SFTPConnection
{
public class SFTPConnectionAndUploadFile
{
public static SftpClient ConnectSFTP(string host, int port, string username, string password, Stream privateKeyFileStream, string passphrase)
{
List<AuthenticationMethod> methods;
SftpClient sftpClient = (SftpClient)null;
if (privateKeyFileStream != null)
{
PrivateKeyFile privateKeyFile = new PrivateKeyFile(privateKeyFileStream, passphrase);
methods = new List<AuthenticationMethod>
{
new PrivateKeyAuthenticationMethod(username, privateKeyFile)
};
}
else
{
methods = new List<AuthenticationMethod>
{
new PasswordAuthenticationMethod(username, password)
};
}
try
{
var connectionInfo = new ConnectionInfo(host, port, username, methods.ToArray());
using (sftpClient = new SftpClient(connectionInfo))
{
sftpClient.Connect();
if (!sftpClient.IsConnected)
{
sftpClient = null;
}
}
return sftpClient;
}
catch (Exception ex)
{
((BaseClient)sftpClient)?.Dispose();
return (SftpClient)null;
}
}
}
}
Note: If I call the C# method from X++, I got "Success" message only.
C# Code:
public static string SFTPConnect(string host, int port, string username, string password, Stream privateKeyFileStream, string passphrase)
{
List<AuthenticationMethod> methods;
string successStr;
if (privateKeyFileStream != null)
{
PrivateKeyFile privateKeyFile = new PrivateKeyFile(privateKeyFileStream, passphrase);
methods = new List<AuthenticationMethod>
{
new PrivateKeyAuthenticationMethod(username, privateKeyFile)
};
}
else
{
methods = new List<AuthenticationMethod>
{
new PasswordAuthenticationMethod(username, password)
};
}
try
{
var connectionInfo = new ConnectionInfo(host, port, username, methods.ToArray());
using (SftpClient sftpclient = new SftpClient(connectionInfo))
{
sftpclient.Connect();
if (sftpclient.IsConnected)
successStr = "Connected";
else
successStr = "Not Connected";
sftpclient.Disconnect();
sftpclient.Dispose();
}
}
catch (Exception ex)
{
successStr = "Fail";
}
return successStr;
}