I have a project with ftp and sftp calls (using System.Net.FtpClient and Renci.SshNet). I would like to have standard calls in both cases, i.e. call a Connect function and Upload function with the same parameters. I feel like I have to use an interface but I am green with interfaces and I am stuck. I need some directions here. I implemented 2 functions so far to test but it's not elegant and there must be some other way. For example I am passing an object as a parameter in GetWorkingDirectory but this feels wrong and I can't see how to do this correctly.
Here is the interface I have:
interface IRemoteCopy
{
object Connect(string Host, int Port, string Username, string Password, string Fingerprint);
string GetWorkingDirectory(object objRemote, string Directory);
}
Here are the classes I have:
public class FTPCopy : IRemoteCopy
{
public object Connect(string Host, int Port, string Username, string Password, string Fingerprint)
{
int ftpPort = 21; // default ftp port
if (Port == 0)
ftpPort = Port;
FtpClient ftp = new FtpClient();
ftp.Host = Host;
ftp.Port = ftpPort;
ftp.Credentials = new NetworkCredential(Username, Password);
return ftp;
}
public string GetWorkingDirectory(object objftp, string Directory)
{
FtpClient ftp = (FtpClient)objftp;
ftp.SetWorkingDirectory(Directory);
return ftp.GetWorkingDirectory();
}
}
public class SFTPCopy : IRemoteCopy
{
public object Connect(string Host, int Port, string Username, string Password, string Fingerprint)
{
int sftpPort = 22; // default sftp port
if (Port == 0)
sftpPort = Port;
ConnectionInfo connInfo = new ConnectionInfo(Host, sftpPort, Username, new AuthenticationMethod[]{
new PasswordAuthenticationMethod(Username, Password)
});
SftpClient sftp = new SftpClient(connInfo);
sftp.HostKeyReceived += delegate(object sender, HostKeyEventArgs e)
{
if (Fingerprint.ToLower() == (e.HostKeyName + " " + e.KeyLength + " " + BitConverter.ToString(e.FingerPrint).Replace("-", ":")).ToLower())
e.CanTrust = true;
else
e.CanTrust = false;
};
sftp.Connect();
return sftp;
}
public string GetWorkingDirectory(object objftp, string Directory)
{
return Directory;
}
}
Can anyone guide me here?
There are several ways you could solve this. I would suggest you write an interface IWorkingDirectory which the ftp clients implement, that has a method
GetWorkingDirectory(string dir).
The interface of IRemoteCopy's Connect method would become:
IWorkingDirectory Connect(string Host, int Port, string Username, string Password, string Fingerprint);
Then you could simplify the call to
public string GetWorkingDirectory(IWorkingDirectory client, string dir)
{
return client.GetWorkingDirectory(dir);
}
Depending on how this is called, you might even consider not implementing this method in IRemoteCopy at all, because most likely you will call Connect before that anyway, so you will be able to call the simple
var client = remoteCopy.Connect(...);
client.GetWorkingDirectory(dir);
You could then effectively make IRemoteCopy an abstract factory of IFtpClient (as the two client classes probably share more than the GetWorkingDirectory method I would suggest making an interface that offers all of the shared functionality).
I propose you check out this site about the open/closed principle:
http://joelabrahamsson.com/a-simple-example-of-the-openclosed-principle/
EDIT:
I just realized, the client classes were in different assemblies, so are not within your code. In this case it might be best to change the IRemoteCopy method signatures to not receive the clients as parameters. You could have a private field in the concrete classes that hold the respective clients, as obviously they will be always the same per class.
You can then set those fields on the Connect call, or directly at class instantiation. I would prefer the latter, because it seems less error prone for the users of IRemoteCopy.
Another possibilty would be to pack those clients into wrappers of your own (eiter by building an adapter - https://en.wikipedia.org/wiki/Adapter_pattern - or if the base clients are not sealed by inheriting from them), so you could use the interface base approach I proposed earlier.
After exploring generics, overloaded methods and interfaces, I came up with this solution. Let me know if you think there is something better:
IDirectory.cs:
using System;
using System.Text;
namespace ReleaseManager.Interfaces
{
interface IDirectory
{
void Connect(string Host, int Port, string Username, string Password, string Fingerprint);
string GetWorkingDirectory(string Directory);
void ListDirectory(string Directory);
void CreateDirectory(string Directory);
void DeleteDirectory(string Directory);
Boolean DirectoryExists(string Directory);
void WriteAllText(string FileName, string Content, Encoding enc);
void CopyFile(string srcFile, string dstFile);
void Disconnect();
}
}
Directory.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace ReleaseManager.BusinessObjects
{
public class RemoteDirInfo
{
public string Name { get; set; }
public string ModifiedDate { get; set; }
}
public class RemoteFileInfo
{
public string Name { get; set; }
public string ModifiedDate { get; set; }
public long Size { get; set; }
}
public class RemoteInfo
{
public string Error { get; set; }
public string CurrentDirectory { get; set; }
public List<RemoteDirInfo> DirInfo { get; set; }
public List<RemoteFileInfo> FileInfo { get; set; }
}
}
FTPDirectory.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using ReleaseManager.Interfaces;
using System.Net.FtpClient;
using System.Net;
using System.Text;
using System.IO;
namespace ReleaseManager.BusinessObjects
{
public class FTPDirectory : IDirectory
{
private FtpClient client;
public RemoteInfo Info;
public FTPDirectory()
{
this.client = new FtpClient();
this.Info = new RemoteInfo();
this.Info.DirInfo = new List<RemoteDirInfo>();
this.Info.FileInfo = new List<RemoteFileInfo>();
this.Info.Error = "";
}
public void Connect(string Host, int Port, string Username, string Password, string Fingerprint)
{
int ftpPort = 21; // default ftp port
if (Port != 0)
ftpPort = Port;
this.client.Host = Host;
this.client.Port = ftpPort;
this.client.Credentials = new NetworkCredential(Username, Password);
}
public string GetWorkingDirectory(string Directory)
{
this.client.SetWorkingDirectory(Directory);
return this.client.GetWorkingDirectory();
}
public void ListDirectory(string Directory)
{
this.client.SetWorkingDirectory(Directory);
foreach (var item in this.client.GetListing(this.client.GetWorkingDirectory()))
{
switch (item.Type)
{
case FtpFileSystemObjectType.Directory:
if (item.Name != "." && item.Name != "..")
this.Info.DirInfo.Add(new RemoteDirInfo
{
Name = item.Name,
ModifiedDate = item.Modified.ToString("yyyy/MM/dd HH:mm:ss")
});
break;
case FtpFileSystemObjectType.File:
this.Info.FileInfo.Add(new RemoteFileInfo
{
Name = item.Name,
ModifiedDate = item.Modified.ToString("yyyy/MM/dd HH:mm:ss"),
Size = item.Size
});
break;
}
}
}
public void CreateDirectory(string Directory)
{
this.client.CreateDirectory(Directory);
}
public void DeleteDirectory(string Directory)
{
this.client.DeleteDirectory(Directory, true, FtpListOption.Recursive);
}
public void CopyFile(string srcFile, string dstFile)
{
using (var fileStream = System.IO.File.OpenRead(srcFile))
using (var ftpStream = this.client.OpenWrite(dstFile.Replace("\\", "/")))
{
var buffer = new byte[8 * 1024];
int count;
while ((count = fileStream.Read(buffer, 0, buffer.Length)) > 0)
{
ftpStream.Write(buffer, 0, count);
}
}
}
public void WriteAllText(string Filename, string Content, Encoding enc = null)
{
byte[] byteArray;
if (enc == null)
byteArray = Encoding.ASCII.GetBytes(Content);
else
byteArray = enc.GetBytes(Content);
using (MemoryStream stream = new MemoryStream(byteArray))
using (var ftpStream = this.client.OpenWrite(Filename.Replace("\\", "/")))
{
var buffer = new byte[8 * 1024];
int count;
while ((count = stream.Read(buffer, 0, buffer.Length)) > 0)
{
ftpStream.Write(buffer, 0, count);
}
}
}
public Boolean DirectoryExists(string Directory)
{
return this.client.DirectoryExists(Directory);
}
public void Disconnect()
{
this.client.Disconnect();
}
}
}
SFTPDirectory.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using ReleaseManager.Interfaces;
using Renci.SshNet;
using Renci.SshNet.Common;
using Renci.SshNet.Sftp;
using System.Text;
using System.IO;
namespace ReleaseManager.BusinessObjects
{
public class SFTPDirectory : IDirectory
{
public SftpClient client;
public RemoteInfo Info;
public SFTPDirectory()
{
this.Info = new RemoteInfo();
this.Info.DirInfo = new List<RemoteDirInfo>();
this.Info.FileInfo = new List<RemoteFileInfo>();
this.Info.Error = "";
}
public void Connect(string Host, int Port, string Username, string Password, string Fingerprint)
{
int sftpPort = 22; // default sftp port
if (Port != 0)
sftpPort = Port;
ConnectionInfo connInfo = new ConnectionInfo(Host, sftpPort, Username, new AuthenticationMethod[]{
new PasswordAuthenticationMethod(Username, Password)
});
this.client = new SftpClient(connInfo);
this.client.HostKeyReceived += delegate(object sender, HostKeyEventArgs e)
{
if (Fingerprint.ToLower() == (e.HostKeyName + " " + e.KeyLength + " " + BitConverter.ToString(e.FingerPrint).Replace("-", ":")).ToLower())
e.CanTrust = true;
else
e.CanTrust = false;
};
this.client.Connect();
}
public string GetWorkingDirectory(string Directory)
{
return Directory;
}
public void ListDirectory(string Directory)
{
List<SftpFile> files = this.client.ListDirectory(Directory).ToList();
foreach (var file in files)
{
if (file.IsDirectory)
{
if (file.Name != "." && file.Name != "..")
this.Info.DirInfo.Add(new RemoteDirInfo
{
Name = file.Name,
ModifiedDate = file.LastWriteTime.ToString("yyyy/MM/dd HH:mm:ss")
});
}
else
{
this.Info.FileInfo.Add(new RemoteFileInfo
{
Name = file.Name,
ModifiedDate = file.LastWriteTime.ToString("yyyy/MM/dd HH:mm:ss"),
Size = file.Length
});
}
}
}
public void CreateDirectory(string Directory)
{
this.client.CreateDirectory(Directory);
}
public void DeleteDirectory(string Directory)
{
this.client.DeleteDirectory(Directory);
}
public void CopyFile(string srcFile, string dstFile)
{
using (var uplfileStream = System.IO.File.OpenRead(srcFile))
{
this.client.UploadFile(uplfileStream, dstFile.Replace("\\", "/"), true);
}
}
public void WriteAllText(string Filename, string Content, Encoding enc = null)
{
byte[] byteArray;
if (enc == null)
byteArray = Encoding.ASCII.GetBytes(Content);
else
byteArray = enc.GetBytes(Content);
using (MemoryStream stream = new MemoryStream(byteArray))
{
this.client.UploadFile(stream, Filename.Replace("\\", "/"), true);
}
}
public Boolean DirectoryExists(string Directory)
{
return this.client.Exists(Directory);
}
public void Disconnect()
{
this.client.Disconnect();
}
}
}
LocalDirectory.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using ReleaseManager.Interfaces;
using System.Net;
using System.Text;
using System.IO;
namespace ReleaseManager.BusinessObjects
{
public class LocalDirectory : IDirectory
{
public RemoteInfo Info;
public LocalDirectory()
{
this.Info = new RemoteInfo();
this.Info.DirInfo = new List<RemoteDirInfo>();
this.Info.FileInfo = new List<RemoteFileInfo>();
this.Info.Error = "";
}
public void Connect(string Host, int Port, string Username, string Password, string Fingerprint)
{
}
public string GetWorkingDirectory(string Directory)
{
return Directory;
}
public void ListDirectory(string Directory)
{
DirectoryInfo di = new DirectoryInfo(Directory);
foreach (var item in di.EnumerateDirectories("*"))
{
if (item.Name != "." && item.Name != "..")
this.Info.DirInfo.Add(new RemoteDirInfo
{
Name = item.Name,
ModifiedDate = item.LastWriteTime.ToString("yyyy/MM/dd HH:mm:ss")
});
}
foreach (var item in di.EnumerateFiles("*"))
{
this.Info.FileInfo.Add(new RemoteFileInfo
{
Name = item.Name,
ModifiedDate = item.LastWriteTime.ToString("yyyy/MM/dd HH:mm:ss"),
Size = item.Length
});
}
}
public void CreateDirectory(string Directory)
{
System.IO.Directory.CreateDirectory(Directory);
}
public void DeleteDirectory(string Directory)
{
System.IO.Directory.Delete(Directory, true);
}
public void CopyFile(string srcFile, string dstFile)
{
System.IO.File.Copy(srcFile, dstFile);
}
public void WriteAllText(string Filename, string Content, Encoding enc = null)
{
if (enc == null)
System.IO.File.WriteAllText(Filename, Content);
else
System.IO.File.WriteAllText(Filename, Content, enc);
}
public Boolean DirectoryExists(string Directory)
{
return System.IO.Directory.Exists(Directory);
}
public void Disconnect()
{
}
}
}
Implementation of RemoteBrowse method:
public JsonResult RemoteBrowse(string Protocol, string Host, int Port, string Username, string Password, string Directory, string Fingerprint = "")
{
dynamic Connector = null;
MyMember.Init(User.Identity.Name);
if (MyMember.ID_Member > 0)
{
if (Protocol == "ftp")
Connector = new FTPDirectory();
else if (Protocol == "sftp")
Connector = new SFTPDirectory();
else if (Protocol == "local")
Connector = new LocalDirectory();
if (Connector != null)
{
try
{
Connector.Connect(Host, Port, Username, Password, Fingerprint);
while (true)
{
Boolean Mod = false;
if (Directory.Length >= 2)
{
if (Directory.Substring(0, 2) == "//")
{
Directory = Directory.Substring(1);
Mod = true;
}
else if (Directory.Substring(0, 2) == "..")
{
Directory = Directory.Substring(2);
Mod = true;
}
}
else if (Directory.Length >= 3)
{
if (Directory.Substring(0, 3) == "/..")
{
Directory = Directory.Substring(3);
Mod = true;
}
}
if (!Mod)
break;
}
if (Directory.Length > 1 && Directory != "/")
{
if (Directory.Substring(0, 1) != "/")
Directory = "/" + Directory;
if (Directory.Substring(Directory.Length - 1) == "/")
Directory = Directory.Substring(0, Directory.Length - 1);
if (Directory.Substring(Directory.Length - 3) == "/..") // go one directory up
{
Directory = Directory.Substring(0, Directory.Length - 3);
Directory = Directory.Substring(0, Directory.LastIndexOf('/'));
}
}
if (Directory == "")
Directory = "/";
Connector.Info.CurrentDirectory = Connector.GetWorkingDirectory(Directory);
Connector.ListDirectory(Directory);
Connector.Disconnect();
}
catch (Exception ex)
{
Connector.Info.Error = ex.Message;
if (ex.InnerException != null)
Connector.Info.Error += '\n' + ex.InnerException.Message;
}
}
}
return Json((Connector != null) ? Connector.Info : null, JsonRequestBehavior.AllowGet);
}
Related
I need to implement a Linux file utility class with SSH as below:
class LinuxFileOperation
{
private string ip, username, password;
public LinuxFileOperation(string ip, string username, string password)
{
this.ip = ip;
this.username = username;
this.password = password;
}
public void CopyFileOnDevice(string remoteFileNameToBeCopied, string remoteFileNameToBePasted)
{
using (var sshClient = new SshClient(ip, username, password))
{
sshClient.RunCommand($"cp {remoteFileNameToBeCopied} {remoteFileNameToBePasted}");
}
}
public void DeleteFile(string remoteFilePath)
{
using (var sftpClient = new SftpClient(ip, username, password))
{
sftpClient.DeleteFile(remoteFilePath);
}
}
public bool FileExists(string file)
{
using (var sftpClient = new SftpClient(ip, username, password))
{
var fileAttr = sftpClient.GetAttributes(file);
return fileAttr.IsRegularFile;
}
}
public List<string> GetFileList(string fullSearchPath)
{
using (var sftpClient = new SftpClient(ip, username, password))
{
return sftpClient.ListDirectory(fullSearchPath).Select(s => s.FullName).ToList();
}
}
}
Almost every methods with the same code using (var sftpClient/sshClient = new SftpClient/SshClient(ip, username, password)).
Any pattern would reduce the code?
You can do something like:
class LinuxFileOperation
{
private T WithClient<T>(Func<SftpClient, T> action)
{
using (var sftpClient = new SftpClient(ip, username, password))
{
return action(sftpClient);
}
}
public bool FileExists(string file)
{
return WithClient(client => client.GetAttributes(file).IsRegularFile);
}
}
... although it doesn't reduce the line count that much.
You can also make use of using declarations:
class LinuxFileOperation
{
private sftpClient CreateClient() => new SftpClient(ip, username, password)[
public bool FileExists(string file)
{
using var client = CreateClient();
return client.GetAttributes(file).IsRegularFile;
}
}
I tried to write an autoupdater for a program from me.
I already got rid of a stackOverflow and such but now my Program seems to run endless when he comes to a variable. And do nothing.
I tried to get info with cw and check where it is hanging but i get nothing and can not find it.
My main
{
updater = new Updater(this);
updater.DoUpdate();
}
public string ApplicationName {
get { return "MyProgram"; }
}
public string ApplicationID {
get { return "MyProgramID"; }
}
public Assembly ApplicationAssembly {
get { return System.Reflection.Assembly.GetExecutingAssembly(); }
}
public Icon ApplicationIcon {
get { return this.Icon; }
}
public Uri UpdateXmlLocation {
get { return new Uri("UrlToXml"); }
}
public Form Context {
get { return this; }
}
in my XML class
public class UpdateXml
{
private Version version;
public Uri uri;
private string fileName;
private string md5;
private string description;
private string launchArgs;
internal Version Version {
get { return this.Version; }
}
internal Uri Uri {
get { return this.Uri; }
}
internal string FileName {
get { return this.fileName; }
}
internal string MD5 {
get { return this.md5; }
}
internal string Description {
get { return this.description; }
}
internal string LaunchArgs {
get { return this.launchArgs; }
}
after a while (code running fine it come to the part that crash)
private void DwnloadUpdate(UpdateXml update)
{
updateDownloadForm form = new updateDownloadForm(update.Uri, this.applicationInfo.ApplicationIcon);
after this code I expect that my dl windows open and the dl starts and the program get update
My Updater class
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
namespace updater
{
public class Updater
{
private Iupdater applicationInfo;
private BackgroundWorker bgWorker;
public Updater(Iupdater applicationInfo)
{
this.applicationInfo = applicationInfo;
this.bgWorker = new BackgroundWorker();
this.bgWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork);
this.bgWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgWorker_RunWorkerCompleted);
}
public void DoUpdate()
{
if (!this.bgWorker.IsBusy)
this.bgWorker.RunWorkerAsync(this.applicationInfo);
}
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
Iupdater application = (Iupdater)e.Argument;
if (!UpdateXml.ExistOnServer(application.UpdateXmlLocation))
{
e.Cancel = true;
}
else
{
UpdateXml ux = UpdateXml.Parse(application.UpdateXmlLocation, application.ApplicationID);
if (ux == null)
{
e.Cancel = true;
}
else
{
e.Result = ux;
}
}
}
void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if(!e.Cancelled)
{
UpdateXml update = (UpdateXml)e.Result;
if(update == null)
{
Console.WriteLine("Update NULL");
}
Console.WriteLine("test3.1");
Console.WriteLine(this.applicationInfo.ApplicationAssembly.GetName().Version);
if(this.applicationInfo.ApplicationAssembly.GetName().Version != null)
{
Console.WriteLine("YES!");
} else
{
Console.WriteLine("NO!");
}
Console.WriteLine("test3.2");
if (update != null && update.IsNewerThan(this.applicationInfo.ApplicationAssembly.GetName().Version))
{
Console.WriteLine("test4");
if (new updateInformation(applicationInfo, update).ShowDialog(this.applicationInfo.Context) == DialogResult.Yes)
this.DwnloadUpdate(update);
}
}
}
private void DwnloadUpdate(UpdateXml update)
{
Console.WriteLine(update.Uri);
if(update.Uri == null)
Console.WriteLine("null");
updateDownloadForm form = new updateDownloadForm(update.Uri, this.applicationInfo.ApplicationIcon);
Console.WriteLine("ich bin hier drinnen");
DialogResult result = form.ShowDialog(this.applicationInfo.Context);
if(result == DialogResult.OK)
{
string currentPath = this.applicationInfo.ApplicationAssembly.Location;
string newPath = Path.GetDirectoryName(currentPath) + "\\" + update.FileName;
UpdateApplication(form.TempFilePath, currentPath, newPath, update.LaunchArgs);
Application.Exit();
}
else if(result == DialogResult.Abort)
{
MessageBox.Show("The update download was cancelled. \nThis programm has not been modified.", "Update Download Cancelled", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else
{
MessageBox.Show("There was a Problem downloading the Updat. \nThis programm has not been modified.", "Update Download Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void UpdateApplication(string tempFilePath, string currentPath, string newPath, string launchArgs)
{
string argument = "/C Choice /C Y /N /D Y /T 4 & Del /F /Q \"{0}\" & Choice /C Y /N /D Y /T 2 & Move /Y \"{1}\" \"{2}\" & Start \"\" /D \"{3}\" \"{4}\"{5}";
ProcessStartInfo info = new ProcessStartInfo();
info.Arguments = string.Format(argument, currentPath, tempFilePath, newPath, Path.GetDirectoryName(newPath), Path.GetFileName(newPath), launchArgs);
info.CreateNoWindow = true;
info.FileName = "cmd.exe";
Process.Start(info);
}
}
}
my XML Updater class
using System;
using System.Net;
using System.Xml;
namespace updater
{
public class UpdateXml
{
private Version version;
public Uri uri;
private string fileName;
private string md5;
private string description;
private string launchArgs;
internal Version Version {
get { return this.Version; }
}
internal Uri Uri {
get { return this.Uri; }
}
internal string FileName {
get { return this.fileName; }
}
internal string MD5 {
get { return this.md5; }
}
internal string Description {
get { return this.description; }
}
internal string LaunchArgs {
get { return this.launchArgs; }
}
internal UpdateXml(Version version, Uri uri, string fileName, string md5, string description, string launchArgs)
{
Console.WriteLine("run in1");
this.version = version;
this.uri = uri;
this.fileName = fileName;
this.md5 = md5;
this.description = description;
this.launchArgs = launchArgs;
Console.WriteLine("run out 1");
}
internal bool IsNewerThan(Version version)
{
Console.WriteLine("run in 2");
return this.version > version;
}
internal static bool ExistOnServer(Uri location)
{
try
{
Console.WriteLine("run in 3");
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(location.AbsoluteUri);
HttpWebResponse res = (HttpWebResponse)req.GetResponse();
Console.WriteLine("run out 3");
return res.StatusCode == HttpStatusCode.OK;
}
catch { return false; }
}
internal static UpdateXml Parse(Uri location, string appID)
{
Console.WriteLine("run in 4");
Version version = null;
string url = "", fileName = "", md5 = "", description = "", launchArgs = "";
try
{
XmlDocument doc = new XmlDocument();
doc.Load(location.AbsoluteUri);
XmlNode node = doc.DocumentElement.SelectSingleNode("//update");
if(node == null)
{
return null;
}
version = Version.Parse(node["version"].InnerText);
url = node["url"].InnerText;
fileName = node["fileName"].InnerText;
md5 = node["md5"].InnerText;
description = node["description"].InnerText;
launchArgs = node["launchArgs"].InnerText;
Console.WriteLine("run out 4");
return new UpdateXml(version, new Uri(url), fileName, md5, description, launchArgs);
}
catch
{
return null;
}
}
}
}
My interfaces
using System;
using System.Reflection;
using System.Drawing;
using System.Windows.Forms;
namespace updater
{
public interface Iupdater
{
string ApplicationName { get; }
string ApplicationID { get; }
Assembly ApplicationAssembly { get; }
Icon ApplicationIcon { get; }
Uri UpdateXmlLocation { get; }
Form Context { get; }
}
}
my update start form where it seems to go into a loop
using System;
using updater;
using System.Windows.Forms;
namespace updater
{
internal partial class updateInformation : Form
{
private Iupdater applicationInfo;
private UpdateXml updateInfo;
private UpdateInoForm updateInoForm;
public updateInformation(Iupdater applicationInfo, UpdateXml updateInfo)
{
InitializeComponent();
this.applicationInfo = applicationInfo;
this.updateInfo = updateInfo;
this.Text = this.applicationInfo.ApplicationName + " - Update in Process";
if (this.applicationInfo.ApplicationIcon != null)
this.Icon = this.applicationInfo.ApplicationIcon;
//this.lblNewVersion.Text = String.Format("New Version: {0}", this.updateInfo.Version.ToString());
Timer wait = new Timer();
wait.Interval = 5000;
wait.Tick += new EventHandler(wait_Tick);
wait.Start();
}
void wait_Tick(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Yes;
}
private void Details_Click(object sender, EventArgs e)
{
if (this.updateInfo == null)
this.updateInoForm = new UpdateInoForm(this.applicationInfo, this.updateInfo);
this.updateInoForm.ShowDialog(this);
}
}
}
You need to return the field values, instead of the properties returning the property value. Do not write properties like this:
/// THIS PROPERTY TRIES TO RETURN ITSELF!!
internal Version Version {
get {
return this.Version; // you wanted to return 'this.version'
}
}
You can use auto-properties like this:
// No more private fields
public class UpdateXml
{
public Version Version { get; set; }
public string FileName { get; } // Only expose a getter, can be set in the ctor
public Uri Uri { get; set; }
// add more properties here...
public UpdateXml(string filename)
{
FileName = filename;
}
}
Or learn to use a convention seen allot in C#. Prefix the private variable names:
public class UpdateXml
{
private Version _version;
private string _fileName;
private Uri _uri;
public Version Version => _version;
public string FileName => _filename;
public Uri Uri {
get => _uri;
set => _uri = value;
}
// add more properties here...
// use the ctor to set some field values
public UpdateXml(string filename)
{
_filename = filename;
}
// fields allow setting values later on
public void SetLatestVersion(Version v)
{
if (_version == null || v > _version)
_version = v;
}
}
All in all, take care when writing code. Case does matter ;-)
I have a blocking collection which gets filled with data by some app1.
I have subscribed to that blocking collection and need to write a file with below case,
start writing a file .
if the file size crossed 100kb, close the first file and starts a new file.
and if there is no data coming from app1, lets say for 1 minute, then close the file.
Currently with below code, I'm only able to write per blocking collection into per file, how to proceed with my above requirement, please suggest.
class Program
{
private static BlockingCollection<Message> messages = new BlockingCollection<Message>();
private static void Producer()
{
int ctr = 1;
while (true)
{
messages.Add(new Message { Id = ctr, Name = $"Name-{ctr}" });
Thread.Sleep(1000);
ctr++;
}
}
private static void Consumer()
{
foreach (var message in messages.GetConsumingEnumerable())
{
Console.WriteLine(JsonConvert.SerializeObject(message));
using (var streamWriter = File.CreateText(Path.Combine(#"C:\TEMP", $"File-{ DateTime.Now.ToString("yyyyMMddHHmmssfff")}.json")))
{
using (var writer = new JsonTextWriter(streamWriter))
{
writer.Formatting = Formatting.Indented;
writer.WriteStartObject();
writer.WritePropertyName("Data");
writer.WriteStartArray();
writer.WriteRawValue(JsonConvert.SerializeObject(message));
writer.WriteEndArray();
writer.WriteEndObject();
}
}
}
}
static void Main(string[] args)
{
var producer = Task.Factory.StartNew(() => Producer());
var consumer = Task.Factory.StartNew(() => Consumer());
Console.Read();
}
}
This will take the messages with any size and divided it to a different JSON file. You need to specify the size using maxchar. before that, you have to check the size of the last file like this and you have to pass the same file name and the new size if else create a new file and divide the message.
using System;
using System.IO;
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Concurrent;
using System.Collections;
using Newtonsoft.Json;
using System.Runtime.Remoting.Messaging;
using System.Text;
namespace Program
{
class Program
{
public static string last_path = "";
public static readonly string BYE = "bye";
private static BlockingCollection<Message> messages = new BlockingCollection<Message>();
private static void Producer()
{
int ctr = 1;
while (true)
{
messages.Add(new Message { Id = ctr, Name = $"Name-{ctr}" });
Thread.Sleep(1000);
ctr++;
}
}
private static void Consumer()
{
foreach (var message in messages.GetConsumingEnumerable())
{
Console.WriteLine(JsonConvert.SerializeObject(message));
string json = JsonConvert.SerializeObject(message);
int maxchar = 102400;
if (last_path != "")
{
long length = new FileInfo(last_path).Length;
if (length < maxchar)
{
maxchar = maxchar - unchecked((int)length);
dividefile(last_path, maxchar, json);
}
else
{
dividefile("", maxchar, json);
}
}
else
{
dividefile("", maxchar, json);
}
}
}
public static void dividefile(string path, int maxchar, string message)
{
//FileStream fileStream = new FileStream(yourfile, FileMode.Open, FileAccess.Read);
byte[] byteArray = Encoding.UTF8.GetBytes(message);
MemoryStream stream = new MemoryStream(byteArray);
using (StreamReader streamReader = new StreamReader(stream))
{
Int64 x1 = stream.Length;
char[] fileContents = new char[maxchar];
int charsRead = streamReader.Read(fileContents, 0, maxchar);
// Can't do much with 0 bytes
if (charsRead == 0)
throw new Exception("File is 0 bytes");
while (charsRead > 0)
{
x1 = x1 - maxchar;
if (x1 > 0)
{
string s = new string(fileContents);
if (path == "")
{
last_path = Path.Combine(#"C:\TEMP", $"File-{ DateTime.Now.ToString("yyyyMMddHHmmssfff")}.json");
path = "";
}
else
{
last_path = path;
}
AppendTransaction(last_path, s);
charsRead = streamReader.Read(fileContents, 0, maxchar);
}
else
{
int m = (int)(((x1 + maxchar) % maxchar));
string messagechunk = new string(fileContents, 0, m);
if (path == "")
{
last_path = Path.Combine(#"C:\TEMP", $"File-{ DateTime.Now.ToString("yyyyMMddHHmmssfff")}.json");
path = "";
}
else
{
last_path = path;
}
AppendTransaction(last_path, messagechunk);
charsRead = streamReader.Read(fileContents, 0, m);
}
}
}
}
private static void AppendTransaction(string path , string transaction)
{
string filename = path;
bool firstTransaction = !File.Exists(filename);
JsonSerializer ser = new JsonSerializer();
ser.Formatting = Formatting.Indented;
ser.TypeNameHandling = TypeNameHandling.Auto;
using (var fs = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read))
{
Encoding enc = firstTransaction ? new UTF8Encoding(true) : new UTF8Encoding(false);
using (var sw = new StreamWriter(fs, enc))
using (var jtw = new JsonTextWriter(sw))
{
if (firstTransaction)
{
sw.Write("[");
sw.Flush();
}
else
{
fs.Seek(-Encoding.UTF8.GetByteCount("]"), SeekOrigin.End);
sw.Write(",");
sw.Flush();
}
ser.Serialize(jtw, transaction);
sw.Write(']');
}
}
}
static void Main(string[] args)
{
var producer = Task.Factory.StartNew(() => Producer());
var consumer = Task.Factory.StartNew(() => Consumer());
Console.Read();
}
class Message
{
public int ProductorThreadID { get; set; }
public int CustomerThreadID { get; set; }
public string key { get; set; }
public string content { get; set; }
public string Name { get; internal set; }
public int Id { get; internal set; }
public bool endThread()
{
return string.Compare(key, Program.BYE) == 0;
}
public string ToString(bool isProductor)
{
return string.Format("{0} Thread ID {1} : {2}", isProductor ? "Productor" : "Customer",
isProductor ? ProductorThreadID.ToString() : CustomerThreadID.ToString(),
content);
}
}
}
}
I am trying to find the correct way of finding the COM port name if I know the PID and VID. So far I wrote the workaround but i do not believe that there is no more elegant and correct way. BTW (I know that I can use REGEX). This was written only to test the workaround.
I know that there is a lots of space for improvements and I am not asking for a code review
Any ideas are more than welcome :)
namespace ConsoleApplication1
{
using System;
using System.Globalization;
using System.Collections.Generic;
using System.Management;
using System.Reflection;
class Program
{
static void Main(string[] args)
{
VCOMDetect VCOMPorts = new VCOMDetect(0x0483, 0xA27D);
Console.WriteLine("BootPort: {0}", VCOMPorts.BootPort() ?? "Not detected");
Console.WriteLine("Oscilloscope Port: {0}", VCOMPorts.OscilloscopePort() ?? "Not detected");
Console.WriteLine("Serial/USB Port: {0}", VCOMPorts.SerialPort() ?? "Not detected");
Console.WriteLine("Programming Port: {0}", VCOMPorts.ProgPort() ?? "Not detected");
Console.Read();
}
}
class VCOMDetect
{
private const string USB_BOOTLOADER_SERIAL = "000000000b00";
private const string USB_OSCILLOSCOPE_SERIAL = "000000000c00";
private const string USB_VCOM_SERIAL = "000000000d00";
private const string USB_PRG_SERIAL = "000000000e00";
private List<VCOM_USBDeviceInfo> PortsList;
public VCOMDetect(UInt16 vid, UInt16 pid)
{
PortsList = GetUSBDevices(vid, pid);
}
public string BootPort()
{
foreach(VCOM_USBDeviceInfo VCOM in PortsList)
{
if (VCOM.Serial.ToLower() == USB_BOOTLOADER_SERIAL.ToLower()) return VCOM.COMPort;
}
return null;
}
public string OscilloscopePort()
{
foreach (VCOM_USBDeviceInfo VCOM in PortsList)
{
if (VCOM.Serial.ToLower() == USB_OSCILLOSCOPE_SERIAL.ToLower()) return VCOM.COMPort;
}
return null;
}
public string SerialPort()
{
foreach (VCOM_USBDeviceInfo VCOM in PortsList)
{
if (VCOM.Serial.ToLower() == USB_VCOM_SERIAL.ToLower()) return VCOM.COMPort;
}
return null;
}
public string ProgPort()
{
foreach (VCOM_USBDeviceInfo VCOM in PortsList)
{
if (VCOM.Serial.ToLower() == USB_PRG_SERIAL.ToLower()) return VCOM.COMPort;
}
return null;
}
private List<VCOM_USBDeviceInfo> GetUSBDevices(UInt16 vid, UInt16 pid)
{
List<VCOM_USBDeviceInfo> VCOM_devices = new List<VCOM_USBDeviceInfo>();
ManagementObjectCollection collection;
using (var searcher = new ManagementObjectSearcher(#"Select * From Win32_PnPEntity"))
collection = searcher.Get();
foreach (var device in collection)
{
var USBInfo = new VCOM_USBDeviceInfo((string)device.GetPropertyValue("DeviceID"));
if (USBInfo.PID == 0 || USBInfo.VID == 0) continue;
if (USBInfo.PID != pid || USBInfo.VID != vid) continue;
USBInfo.Caption = (string)device.GetPropertyValue("Caption");
if (USBInfo.COMPort == "") continue;
USBInfo.PnpDeviceID = (string)device.GetPropertyValue("PNPDeviceID");
USBInfo.Description = (string)device.GetPropertyValue("Description");
VCOM_devices.Add(USBInfo);
}
collection.Dispose();
return VCOM_devices;
}
}
class VCOM_USBDeviceInfo
{
private UInt16 _PID, _VID;
private string _Caption;
private void _ResetData()
{
this.PID = 0;
this.VID = 0;
this.COMnumber = -1;
this.COMPort = "";
this.Serial = "";
}
public VCOM_USBDeviceInfo(string DeviceID)
{
int VIDIndex = DeviceID.IndexOf("VID_");
int PIDIndex = DeviceID.IndexOf("PID_");
int VIDIndexEnd = -1;
int PIDIndexEnd = -1;
string PIDSubstring, VIDSubstring;
if (PIDIndex == -1 || VIDIndex == -1)
{
_ResetData();
}
else
{
bool result = true;
PIDSubstring = DeviceID.Substring(PIDIndex + 4);
VIDSubstring = DeviceID.Substring(VIDIndex + 4);
PIDIndexEnd = PIDSubstring.IndexOf("\\");
VIDIndexEnd = VIDSubstring.IndexOf("&");
if(PIDIndexEnd == -1 || VIDIndexEnd == -1)
{
_ResetData();
}
else
{
result = result && UInt16.TryParse(PIDSubstring.Substring(0, PIDIndexEnd), NumberStyles.AllowHexSpecifier, null, out _PID) && UInt16.TryParse(VIDSubstring.Substring(0, VIDIndexEnd), NumberStyles.AllowHexSpecifier, null, out _VID);
if(!result)
{
_ResetData();
}
else
{
PID = _PID;
VID = _VID;
Serial = PIDSubstring.Substring(PIDIndexEnd + 1);
}
}
}
}
public string DeviceID { get; set; }
public string PnpDeviceID { get; set; }
public string Description { get; set; }
public string Caption
{
get
{
return _Caption;
}
set
{
int COMindex = value.IndexOf("(COM");
string tmpCOMPort = COMindex == -1 ? "" : value.Substring(COMindex + 1, 4);
if(COMPort == null || COMPort.Length == 0)
{
COMPort = tmpCOMPort;
}
else
{
if(COMPort != tmpCOMPort)
{
Console.WriteLine("Inconsistent COM port information");
}
}
_Caption = value;
}
}
public UInt16 PID { get; set; }
public UInt16 VID { get; set; }
public string Serial { get; set; }
public int COMnumber { get; set; }
public string COMPort { get; set; }
}
}
It does the job - but I di not like it
I actually think your code looks good. Yes, I'd use the Regex and there are places, where things can be condensed. But, that stuff won't help improve performance or do anything else. Just, mostly, make the code smaller. I'm also assuming this app will run on Windows only.
Anyhow, can any of this code help:
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Text.RegularExpressions;
namespace PortNames
{
class Program
{
static List<string> ComPortNames(String VID, String PID)
{
RegistryKey rk1 = Registry.LocalMachine;
RegistryKey rk2 = rk1.OpenSubKey("SYSTEM\\CurrentControlSet\\Enum");
String pattern = String.Format("^VID_{0}.PID_{1}", VID, PID);
Regex _rx = new Regex(pattern, RegexOptions.IgnoreCase);
List<string> ports = new List<string>();
foreach (String s3 in rk2.GetSubKeyNames())
{
RegistryKey rk3 = rk2.OpenSubKey(s3);
foreach (String s in rk3.GetSubKeyNames())
{
if (_rx.Match(s).Success)
{
RegistryKey rk4 = rk3.OpenSubKey(s);
foreach (String s2 in rk4.GetSubKeyNames())
{
RegistryKey rk5 = rk4.OpenSubKey(s2);
RegistryKey rk6 = rk5.OpenSubKey("Device Parameters");
ports.Add((string)rk6.GetValue("PortName"));
}
}
}
}
return ports;
}
static void Main(string[] args)
{
List<string> names = ComPortNames("0403", "6001");
if (names.Count > 0) foreach (String s in SerialPort.GetPortNames()) { Console.WriteLine(s); }
Console.ReadLine();
}
}
}
or
using System;
using System.Collections.Generic;
using System.Linq;
using System.Management;
using System.Text.RegularExpressions;
namespace PortNames
{
class Program
{
private const string vidPattern = #"VID_([0-9A-F]{4})";
private const string pidPattern = #"PID_([0-9A-F]{4})";
struct ComPort // custom struct with our desired values
{
public string name;
public string vid;
public string pid;
public string description;
}
private static List<ComPort> GetSerialPorts()
{
using (var searcher = new ManagementObjectSearcher
("SELECT * FROM WIN32_SerialPort"))
{
var ports = searcher.Get().Cast<ManagementBaseObject>().ToList();
return ports.Select(p =>
{
ComPort c = new ComPort();
c.name = p.GetPropertyValue("DeviceID").ToString();
c.vid = p.GetPropertyValue("PNPDeviceID").ToString();
c.description = p.GetPropertyValue("Caption").ToString();
Match mVID = Regex.Match(c.vid, vidPattern, RegexOptions.IgnoreCase);
Match mPID = Regex.Match(c.vid, pidPattern, RegexOptions.IgnoreCase);
if (mVID.Success)
c.vid = mVID.Groups[1].Value;
if (mPID.Success)
c.pid = mPID.Groups[1].Value;
return c;
}).ToList();
}
}
static void Main(string[] args)
{
List<ComPort> ports = GetSerialPorts();
//if we want to find one device
ComPort com = ports.FindLast(c => c.vid.Equals("0483") && c.pid.Equals("5740"));
//or if we want to extract all devices with specified values:
List<ComPort> coms = ports.FindAll(c => c.vid.Equals("0483") && c.pid.Equals("5740"));
Console.ReadLine();
}
}
}
im working in mvc 4 and try to implement jquery file uploader.
but in my FileStatus class and UploadHandler give me several errors.
-namespace FileStatus cannot be found,
-Method FileStatus need a return type.
i have an example and have seen that the method FileStatus dont return anything,
i dont´t know what is going on..
can someone give a hand?
FIleStatusCLASS
public class FileStatus
{
public const string HandlerPath = "/Upload/";
public string group { get; set; }
public string name { get; set; }
public string type { get; set; }
public int size { get; set; }
public string progress { get; set; }
public string url { get; set; }
public string thumbnail_url { get; set; }
public string delete_url { get; set; }
public string delete_type { get; set; }
public string error { get; set; }
public FilesStatus() { }
public FilesStatus(FileInfo fileInfo) { SetValues(fileInfo.Name, (int)fileInfo.Length, fileInfo.FullName); }
public FilesStatus(string fileName, int fileLength, string fullPath) { SetValues(fileName, fileLength, fullPath); }
private void SetValues(string fileName, int fileLength, string fullPath)
{
name = fileName;
type = "image/png";
size = fileLength;
progress = "1.0";
url = HandlerPath + "UploadHandler.ashx?f=" + fileName;
delete_url = HandlerPath + "UploadHandler.ashx?f=" + fileName;
delete_type = "DELETE";
var ext = Path.GetExtension(fullPath);
var fileSize = ConvertBytesToMegabytes(new FileInfo(fullPath).Length);
if (fileSize > 3 || !IsImage(ext)) thumbnail_url = "/Content/img/generalFile.png";
else thumbnail_url = #"data:image/png;base64," + EncodeFile(fullPath);
}
private bool IsImage(string ext)
{
return ext == ".gif" || ext == ".jpg" || ext == ".png";
}
private string EncodeFile(string fileName)
{
return Convert.ToBase64String(System.IO.File.ReadAllBytes(fileName));
}
static double ConvertBytesToMegabytes(long bytes)
{
return (bytes / 1024f) / 1024f;
}
}
UPLOADHANDLER
public class UploadHandler : IHttpHandler
{
private readonly JavaScriptSerializer js;
private string StorageRoot
{
get { return Path.Combine(System.Web.HttpContext.Current.Server.MapPath("~/Files/")); } //Path should! always end with '/'
}
public UploadHandler()
{
js = new JavaScriptSerializer();
js.MaxJsonLength = 41943040;
}
public bool IsReusable { get { return false; } }
public void ProcessRequest(HttpContext context)
{
context.Response.AddHeader("Pragma", "no-cache");
context.Response.AddHeader("Cache-Control", "private, no-cache");
HandleMethod(context);
}
// Handle request based on method
private void HandleMethod(HttpContext context)
{
switch (context.Request.HttpMethod)
{
case "HEAD":
case "GET":
if (GivenFilename(context)) DeliverFile(context);
else ListCurrentFiles(context);
break;
case "POST":
case "PUT":
UploadFile(context);
break;
case "DELETE":
DeleteFile(context);
break;
case "OPTIONS":
ReturnOptions(context);
break;
default:
context.Response.ClearHeaders();
context.Response.StatusCode = 405;
break;
}
}
private static void ReturnOptions(HttpContext context)
{
context.Response.AddHeader("Allow", "DELETE,GET,HEAD,POST,PUT,OPTIONS");
context.Response.StatusCode = 200;
}
// Delete file from the server
private void DeleteFile(HttpContext context)
{
var filePath = StorageRoot + context.Request["f"];
if (File.Exists(filePath))
{
File.Delete(filePath);
}
}
// Upload file to the server
private void UploadFile(HttpContext context)
{
var statuses = new List<FilesStatus>();
var headers = context.Request.Headers;
if (string.IsNullOrEmpty(headers["X-File-Name"]))
{
UploadWholeFile(context, statuses);
}
else
{
UploadPartialFile(headers["X-File-Name"], context, statuses);
}
WriteJsonIframeSafe(context, statuses);
}
// Upload partial file
private void UploadPartialFile(string fileName, HttpContext context, List<FilesStatus> statuses)
{
if (context.Request.Files.Count != 1) throw new HttpRequestValidationException("Attempt to upload chunked file containing more than one fragment per request");
var inputStream = context.Request.Files[0].InputStream;
var fullName = StorageRoot + Path.GetFileName(fileName);
using (var fs = new FileStream(fullName, FileMode.Append, FileAccess.Write))
{
var buffer = new byte[1024];
var l = inputStream.Read(buffer, 0, 1024);
while (l > 0)
{
fs.Write(buffer, 0, l);
l = inputStream.Read(buffer, 0, 1024);
}
fs.Flush();
fs.Close();
}
statuses.Add(new FilesStatus(new FileInfo(fullName)));
}
// Upload entire file
private void UploadWholeFile(HttpContext context, List<FilesStatus> statuses)
{
for (int i = 0; i < context.Request.Files.Count; i++)
{
var file = context.Request.Files[i];
var fullPath = StorageRoot + Path.GetFileName(file.FileName);
file.SaveAs(fullPath);
string fullName = Path.GetFileName(file.FileName);
statuses.Add(new FilesStatus(fullName, file.ContentLength, fullPath));
}
}
private void WriteJsonIframeSafe(HttpContext context, List<FilesStatus> statuses)
{
context.Response.AddHeader("Vary", "Accept");
try
{
if (context.Request["HTTP_ACCEPT"].Contains("application/json"))
context.Response.ContentType = "application/json";
else
context.Response.ContentType = "text/plain";
}
catch
{
context.Response.ContentType = "text/plain";
}
var jsonObj = js.Serialize(statuses.ToArray());
context.Response.Write(jsonObj);
}
private static bool GivenFilename(HttpContext context)
{
return !string.IsNullOrEmpty(context.Request["f"]);
}
private void DeliverFile(HttpContext context)
{
var filename = context.Request["f"];
var filePath = StorageRoot + filename;
if (File.Exists(filePath))
{
context.Response.AddHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");
context.Response.ContentType = "application/octet-stream";
context.Response.ClearContent();
context.Response.WriteFile(filePath);
}
else
context.Response.StatusCode = 404;
}
private void ListCurrentFiles(HttpContext context)
{
var files =
new DirectoryInfo(StorageRoot)
.GetFiles("*", SearchOption.TopDirectoryOnly)
.Where(f => !f.Attributes.HasFlag(FileAttributes.Hidden))
.Select(f => new FilesStatus(f))
.ToArray();
string jsonObj = js.Serialize(files);
context.Response.AddHeader("Content-Disposition", "inline; filename=\"files.json\"");
context.Response.Write(jsonObj);
context.Response.ContentType = "application/json";
}
}
For UploadHandler i use this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.IO;
using System.Web.Script.Serialization;
and for fileclass this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.IO;
i know its a lot of code and sorry for that but i think its better to show you all the code.
Thanks in advance for the help.
You're declaring a class called FileStatus:
public class FileStatus
... but here you're trying to declare a constructor for FilesStatus:
public FilesStatus(FileInfo fileInfo)
(And the other constructors.)
Basically, your class name and the name specified in the constructor don't match, and they need to.