Call function from method in one classes in another class - c#

Hello so i got very easy function to download files from FTP. Looks like this:
//Download files from FTP, return true of false if succed
public static bool DownloadFileFromFTP( string ip, string RemoteFilePath, string LocalFilePath, string username, string password)
{
try
{
FtpClient client = new FtpClient(ip);
client.Credentials = new NetworkCredential(username, password);
client.Connect();
ProgressBar progressBar;
progressBar = new ProgressBar();
Progress<double> progress = new Progress<double>(x => {
if (x > 0)
{
progressBar.Report((double)x / 100);
}
});
bool succes = client.DownloadFile(LocalFilePath, RemoteFilePath, FtpLocalExists.Overwrite, FluentFTP.FtpVerify.Retry, progress);
if(succes == true)
{
succes = true;
}
else
{
succes = false;
}
client.Disconnect();
progressBar.Dispose();
return succes;
}
catch(Exception e)
{
MessageBox.Show(e.ToString());
return false;
}
}
And this method is in one class and i call it in another class like this:
Functions_General.DownloadFileFromFTP("192.168.240.86", "Ultra_Script/path", #"C:\Windows\Temp\Adobe_Reader.exe", "username", "password");
Everything works as expected. But the function itself have progressbar inside it and its disposing after finished downloading with this:
progressBar.Dispose();
But there comes the problem i need to dispose it in class where im calling the method is there any option how can i achieve that?
I need to have 3 synchronous progress bars and dispose them after all of 3 downloads are complete.
Thanks,

Related

How to change a control property during main thread execution?

I know there are several question related to my issues, I've studied them all but it seems that I still can't get it.
Something like this or like this.
I have a method that downloads some files through FTP (it takes somewhere around 5 seconds). When I click the button to download the files I also want to change a control property so that a "loading" kind of thing is visible.
For this I have a CircleProgressBar with "animated" property set by default to false. When I call the previous method I want first to change that property to true and after the download is complete to set it back to false as it was.
I tried many solutions but in vain:
void UpdateMessage(bool value)
{
Action action = () => DownloadLC_Normal_CircleProgressBar.animated = value;
Invoke(action);
}
private void DownloadLC_Normal_Button_Click(object sender, EventArgs e)
{
// try 1
//UpdateMessage(true);
// try 2
//DownloadLC_Normal_CircleProgressBar.Invoke((MethodInvoker)(() =>
//{
// DownloadLC_Normal_CircleProgressBar.animated = true;
//}));
// try 3
if (DownloadLC_Normal_CircleProgressBar.InvokeRequired)
{
DownloadLC_Normal_CircleProgressBar.BeginInvoke((MethodInvoker)delegate () { DownloadLC_Normal_CircleProgressBar.animated = true; });
}
else
{
DownloadLC_Normal_CircleProgressBar.animated = false;
}
// DOWNLOAD FILES THROUGH FTP BY CALLING A METHOD FROM A .cs FILE
// FROM THE PROJECT
//UpdateMessage(false);
//DownloadLC_Normal_CircleProgressBar.animated = false;
}
The CircleProgressBar never animates. What am I missing? What am I doing wrong, please? :(
EDIT:
My missing part of code:
ftp ftpClient = new ftp("ftp://" + "192.168.1.200" + "/", "anonymous", "anonymous");
NetworkCredential credentials = new NetworkCredential("anonymous", "anonymous");
string url = "ftp://" + "192.168.1.200" + "/Documents and Settings/";
ftpClient.DownloadFtpDirectory(url, credentials, newDirectoryDownloadLocation);
I'm assuming you're using framework 4.5/higher or 4.0 with Microsoft.Bcl.Async installed ok.
Try it:
private async void DownloadLC_Normal_Button_Click(object sender, EventArgs e)
{
try
{
DownloadLC_Normal_Button.Enabled = false;
DownloadLC_Normal_CircleProgressBar.animated = true;
ftp ftpClient = new ftp("ftp://" + "192.168.1.200" + "/", "anonymous", "anonymous");
NetworkCredential credentials = new NetworkCredential("anonymous", "anonymous");
string url = "ftp://" + "192.168.1.200" + "/Documents and Settings/";
//the code you post + change this line from:
//ftpClient.DownloadFtpDirectory(url, credentials, newDirectoryDownloadLocation);
//to: It makes the call be async
await Task.Run(() => ftpClient.DownloadFtpDirectory(url, credentials, newDirectoryDownloadLocation));
}
finally
{
DownloadLC_Normal_CircleProgressBar.animated = false;
DownloadLC_Normal_Button.Enabled = true;
}
}
One of the easiest options is to use async/await:
async void DownloadLC_Normal_Button_Click(object sender, EventArgs e)
{
DownloadLC_Normal_CircleProgressBar.animated = true;
DownloadLC_Normal_Button.Enabled = false; // prevent further clicks
await Task.Run(() =>
{
... // long running code, use `Invoke` to update UI controls
});
DownloadLC_Normal_CircleProgressBar.animated = false;
DownloadLC_Normal_Button.Enabled = true;
}

WPF C# - Standard (non - privilege) user not able to accss the webclient?

Here is the code I am using to check the internet connection. But it always fails. This runs fine with my admin user but not working for standard (non-privilege) user I made. What could be missing here..
if (!App.IsInternetConnected)
{
await Task.Factory.StartNew(() =>
{
App.IsInternetConnected = Utils.IsInternetConnected();
});
}
if (!App.IsInternetConnected)
{
App.ShowMessage("FeatureNotAvailable");
LoginProgress.Visibility = System.Windows.Visibility.Hidden;
return;
}
IsInternetConnected method is as below.
internal static bool IsInternetConnected()
{
try
{
WebClient webclient = new WebClientPool().GetIdleWebClientObject();
try
{
webclient.DownloadString("http://www.google.com");
return true;
}
}
catch (Exception)
{
return false;
}
}
GetIdleWebClientObject is as below.
[MethodImpl(MethodImplOptions.Synchronized)]
public WebClient GetIdleWebClientObject()
{
foreach (WebClient item in WebClientItems)
{
if (!item.IsBusy )
{
Log.Instance.WriteLine("reused webclient");
item.Headers.Clear();
return item;
}
}
Log.Instance.WriteLine("new webclient");
WebClient NewItem = new WebClient();
NewItem.UseDefaultCredentials = true;
//NewItem.Credentials = CredentialCache.DefaultNetworkCredentials;
NewItem.Proxy = WebRequest.GetSystemWebProxy();
WebClientItems.Add(NewItem);
return NewItem;
}
My issue was bit out of way but may help anyone.
It was in line this Log.Instance.WriteLine("new webclient"); This line was creating and writing log file in C:\ drive. Which was not allowed to standard user and throwing exception.
which lead me to show no internet error which was not actually related.

How to continuously ping and result changes an image

Hoping you can help me out here.
I am having a issue figuring out how to loop a piece of ping code and result ( if true or false ) changes an image.
here is my code running on a button click,
My aim here is to basically automise the app so the user does not have to click the refresh icon,
I have read a bit and am thinking I could use a while loop, But am unsure on how to use sleep with it.
The other method would be to use a thread with sleep?
I have also asked on MSDN to get the most feedback possible.
//Declaration of Global Variables (IP's)
public static string IP1, IP2, IP3;
//Method Setting Variables(IP's)
public static void setIP()
{
IP1 = Properties.Settings.Default.settingIP1;
IP2 = Properties.Settings.Default.settingIP2;
IP3 = Properties.Settings.Default.settingIP3;
}
//Method running Ping command
public static bool PingTest(string toPing)
{
string host = toPing;
bool result = false;
Ping p = new Ping();
try
{
PingReply reply = p.Send(host, 3000);
if (reply.Status == IPStatus.Success)
return true;
}
catch { }
return result;
}
//Refreshes IP1 then changes label to result
private void btnRefreshIP1_Click(object sender, RoutedEventArgs e)
{
setIP();
bool isConnected = false;
isConnected = PingTest(IP1);
if(isConnected == true)
{
lblsIP1.Foreground = Brushes.White;
lblsIP1.Content = "Online";
}
else
{
lblsIP1.Foreground = Brushes.Red;
lblsIP1.Content = "Offline";
}
}

C# Keeping Pipes Open

I've Got two Programs (Server / Client)
I'm trying to setup IPC for them (They both run on the same box)
Using System.IO.Pipes & Net 3.5
When I call ComOpen, it opens the Pipe correctly, sends the Process ID to the server, but then the Pipe closes and I get an error when it tries to send "Second Write Test"
So Question is.
How do I keep the Pipe open for the Life of the Program?
(I use the Process ID on the server to close everything down if the Client crashes)
private static StreamWriter MyWriter;
private static StreamReader MyReader;
private static NamedPipeClientStream IPCPipe = new NamedPipeClientStream(".", "MyPipe", PipeDirection.InOut);
public static bool MyWrite(string DataOut)
{
bool ValidPipeOut = false;
if(ValidComPort)
try
{
// Send Data
using (QstWriter = new StreamWriter(IPCPipe))
{
QstWriter.AutoFlush = true;
QstWriter.WriteLine(QstDataOut);
QstWriter.Close();
QstWriter.Dispose();
}
ValidPipeOut = true;
}
catch
{
ValidPipeOut = false;
}
return ValidPipeOut;
}
public static bool ComOpen()
{
ValidComPort = true;
try { IPCPipe.Connect(1000); }
catch (Exception ex)
{
string Erroris;
Erroris = ex.Message;
if (Erroris == "Already in a connected state.")
{
// We're Already Connected, Ignore this error.
ValidComPort = true;
}
else
{
ValidComPort = false;
MessageBox.Show(Erroris);
}
}
if (ValidComPort)
{
string ClientProcessID = System.Diagnostics.Process.GetCurrentProcess().Id.ToString();
MyReader = new StreamReader(IPCPipe);
ValidComPort = MyWrite(ClientProcessID);
ValidComPort = MyWrite("Second Write Test");
}
return ValidComPort;
}
The problem is the following line:
using (QstWriter = new StreamWriter(IPCPipe))
At the end of the using statement, the StreamWriter will be disposed and that will in turn dispose the IPCPipe. You are also explicitly calling Dispose and Close on QstWriter, which will close the pipe too.
To fix this, remove the using statement and the calls to Dispose and Close on QstWriter. And assign+initialize QstWriter only once.

Boolean value magically changed to false from ViewModel to View

I need to validate two password fields. If they match then the credential has to be validated in the validation viewmodel. I set a bool value validPassword in the validation viewmodel and need to refer it in the view. Then do something in the view according to validPassword value. However, validPassword always is false when I refer it in the view even though it is true in the viewmodel.
ViewModel:
internal static bool validPassword;
public static bool CheckCredentials(string username, string password, string domain)
{
string userPrincipalName = username + "#" + domain + ".com";
try
{
using(var context = new PrincipalContext(ContextType.Domain, domain))
{
validPassword = true;
return context.ValidateCredentials(userPrincipalName, password);
}
}
catch // a bogus domain causes an LDAP error
{
errorsForPassword.Add("Invalid Login!");
validPassword = false;
return false;
}
}
Code behind the view:
private void PwBox_OnKeyDown(object sender, RoutedEventArgs e)
{
System.Windows.Controls.ToolTip toolTip = new System.Windows.Controls.ToolTip();
//PasswordBox passwordBox = sender as PasswordBox;
passwordAgain = PasswordAgainBox.Password;
if(string.IsNullOrEmpty(passwordAgain) || !string.Equals(passwordAgain, MiscParameterViewModel.password))
{
PwBoxBorder.BorderBrush = new SolidColorBrush(Colors.Red);
MiscParameterViewModel.nextButtonIsEnabled = false;
if(string.IsNullOrEmpty(passwordAgain))
{
toolTip.Content = "Please enter the password again!";
ToolTipService.SetToolTip(PasswordAgainBox, toolTip);
}
else if(!string.Equals(passwordAgain, MiscParameterViewModel.password))
{
toolTip.Content = "Passwords don't match!";
ToolTipService.SetToolTip(PasswordAgainBox, toolTip);
}
}
else
{
//ToolTipService.SetToolTip(PasswordAgainBox, null);
//PwBoxBorder.BorderBrush = new SolidColorBrush(Colors.Transparent);
_validationViewModel.Authenticate();
if(!ValidationViewModel.validPassword)
{
toolTip.Content = "Invalid password!";
ToolTipService.SetToolTip(PasswordBox, toolTip);
ToolTipService.SetToolTip(PasswordAgainBox, toolTip);
PwBoxBorder.BorderBrush = new SolidColorBrush(Colors.Red);
PwBoxAgainBorder.BorderBrush = new SolidColorBrush(Colors.Red);
}
else
{
ToolTipService.SetToolTip(PasswordBox, null);
ToolTipService.SetToolTip(PasswordAgainBox, null);
PwBoxBorder.BorderBrush = new SolidColorBrush(Colors.Transparent);
PwBoxAgainBorder.BorderBrush = new SolidColorBrush(Colors.Transparent);
}
}
}
Authenticate is an async method in the viewmodel and it calls CheckCredentials method.
Here is the Authenticate method:
public async void Authenticate()
{
MiscParameterViewModel.nextButtonIsEnabled = false;
NotifyPropertyChanged("NextButtonIsEnabled");
const string propertyKey = "Password";
bool isValid = false;
/* Call service asynchronously */
if(MiscParameterViewModel.servServiceLoginType == ServiceLoginTypes.Windows)
{
if(errorKeys.ContainsKey(propertyKey))
{
errorsForPassword.Clear();
errorKeys.TryRemove(propertyKey, out errorsForPassword);
/* Raise event to tell WPF to execute the GetErrors method */
RaiseErrorsChanged(propertyKey);
}
//if(string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password) || string.IsNullOrEmpty(passwordAgain))
if(string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
{
errorsForPassword.Add("Login is required!");
errorKeys.TryAdd(propertyKey, errorsForPassword);
isValid = false;
}
else
{
isValid = await Task<bool>.Run(() =>
{
return CheckCredentials(username, password, domain);
})
.ConfigureAwait(false);
}
}
What's happening is that your Authenticate method is executing CheckCredentials on another thread, then returning control to your view. What this means is that you will (sometimes) get to this line:
if(!ValidationViewModel.validPassword)
before CheckCredentials has been called. You're seeing false because that's the default value for booleans - it hasn't been set yet.
You could fix this in a couple of different ways. You could return a Task from your authenticate method, and then call .Wait() on the task before checking validPassword.
Or you could simply remove the async/await from your Authenticate method and make it a synchronous method. Which is right depends on the rest of your application.
Edit: Here's my attempt at your authenticate method. I had to guess on some of the functionality you want.
public async Task<bool> Authenticate()
{
MiscParameterViewModel.nextButtonIsEnabled = false;
NotifyPropertyChanged("NextButtonIsEnabled");
const string propertyKey = "Password";
/* Call service asynchronously */
if(MiscParameterViewModel.servServiceLoginType == ServiceLoginTypes.Windows)
{
if(errorKeys.ContainsKey(propertyKey))
{
errorsForPassword.Clear();
errorKeys.TryRemove(propertyKey, out errorsForPassword);
/* Raise event to tell WPF to execute the GetErrors method */
RaiseErrorsChanged(propertyKey);
}
//if(string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password) || string.IsNullOrEmpty(passwordAgain))
if(string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
{
errorsForPassword.Add("Login is required!");
errorKeys.TryAdd(propertyKey, errorsForPassword);
return false;
}
else
{
return await Task<bool>.Factory.StartNew(() => CheckCredentials(username, password, domain));
}
}
return false;
}
Once you get the task back, you'll have to decide what to do with it. If you just call .Wait(), it will work, but you'll get the same problem you had where the GUI freezes while you're waiting.
You might want to use the .ContinueWith() method instead, which will be called once the task is complete, and then in there you can update your password box. You might need to marshall the changes back onto the GUI thread (ContinueWith will be on another thread) to set the password box's values - not sure without the complete solution. Hope that helps

Categories