I am making a Windows Form Program where I can be able to connect to an FTP Server to download data to show it to the user. I have to make the app in a way that the user can also connect through a gateway. I made the next function to log on through a gateway:
private Boolean addRoute(string ip, string gw)
{
string arg = String.Format("ADD {0} MASK 255.255.255.255 {1}", ip, gw);
return startProcess("route.exe", arg, 10000, true);
}
private Boolean startProcess(String fileName, String arguments, int timeout, bool admin)
{
try
{
Process process = new Process();
process.StartInfo.FileName = fileName;
if (System.Environment.OSVersion.Version.Major >= 6)
{
//if (admin) { process.StartInfo.UserName = "admin"; }
//process.StartInfo.UserName = "admin";
}
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.Arguments = arguments;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.Start();
if (timeout > 0)
{
if (process.WaitForExit(timeout))
{
return true;
}
else
{
return false;
}
}
else
{
process.WaitForExit();
return true;
}
}
catch (Exception e)
{
Global.LogMessageToFile(e.Message);
return false;
}
}
This code works perfect with my PC but when I test it in other PC's with Windows 7 doesn't work anymore.
I thought there was a problem with the UAC permissions so I did the solution adapted to the second answer of the link Selectively disabling UAC for specific programs on Windows Programatically but it doesn't seem to be the problem.
Do you have any other ideas about how can I accomplish this with the code of my program?
EDIT
I have been blocked to post comments, so I will answer that I have no exceptions, and what I need is connect to a lower level, from one network to another, through a gateway. That's why I have adopted this solution.
Related
How can i check if two servers are connected from a third server in c#?
I am in server A and i want to know if Server B and Server C are connected.
I only have the code to check if I am connected to server B or C.
What I have:
public bool AreConnected(string ip)
{
bool connected= false;
Ping p = new Ping();
try
{
PingReply reply = p.Send(ip);
connected = reply.Status == IPStatus.Success;
}
catch (PingException)
{
// Discard PingExceptions and return false;
}
return connected;
}
This might not be the best approach, and it requires admin privileges on machine B, but it works.
Use PsExec. This tool allows you to run a command on a remote machine.
Create a command line program that take the ip address as a command line parameter, pings the ip address and outputs the result.
Then run PsExec (from C# code) to execute such program on machine B and collect the result (from code also).
You will need to use Process.Start to be able to execute the PsExec command from C# code.
I used PsExec and works fine, below my code maybe could help someone else,
public bool IsPingable(string servA, string servB)
{
string path = Path.GetDirectoryName(Path.GetDirectoryName(System.IO.Directory.GetCurrentDirectory())) + "\\Resources\\PsExec.exe";
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.FileName = path;
p.StartInfo.Arguments = #"\\" + servA + " ping " + servB + " -n 1";
p.StartInfo.RedirectStandardOutput = true;
p.Start();
string output = p.StandardOutput.ReadToEnd();
if (!output.Contains("100% loss"))
{
return true;
}
else
{
return false;
}
}
So I have a windows service running as Local System.
this windows service then starts off a WCF service.
From my machine there is no problem and works fine.
From a test console application, on the target machine, it works fine
From a windows service, on the target machine, it does not work. Nor does it throw an exception...
I am really stuck on this. :(
Could this be permissions?
m_tknCancelToken = new CancellationTokenSource();
/**************************************************************************************/
/*** Create and start the task ***/
/**************************************************************************************/
m_tskService = Task.Factory.StartNew((object o) =>
{
RunService();
},
m_tknCancelToken);
/**************************************************************************************/
/*** Set the handler when the task is cancelled or faulted ***/
/**************************************************************************************/
m_tskService.ContinueWith(
TaskEndedHandler,
TaskContinuationOptions.OnlyOnFaulted);
m_tskService.ContinueWith(
TaskEndedHandler,
TaskContinuationOptions.OnlyOnCanceled);
and then to catch the errors.
private void TaskEndedHandler(Task tskTask)
{
Log.Log(String.Format("{0} has ended", ServiceName), "WHS010CI");
if (tskTask.Exception != null)
{
Log.LogEx(tskTask.Exception, "WHS0103E");
if (tskTask.Exception.InnerExceptions != null)
{
foreach (Exception ex in tskTask.Exception.InnerExceptions)
{
Log.LogEx(ex, "WHS0104E");
}
}
}
if(tskTask.IsCanceled)
{
Log.Log(String.Format("[{0}] has been cancelled", ServiceName), "WHS0104W");
}
}
As usual it was a stupid mistake.
In my console application I was binding an SSL certificate to a port, this was removed as the powers that be did not want this in production code, which is understandable. So I removed it to then have a seperate batch file or otherwise which has to be manually run... however this is what I forgot to do. :(
for those that are interested, below is the code from my test app.
process = new Process();
process.StartInfo = BindCertToPort(port, certificate);
process.Start();
method:
private static ProcessStartInfo BindCertToPort(int iPort, X509Certificate2 certificate, bool bRemove = false)
{
string strAction = null;
string strExtraArguments = null;
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
startInfo.FileName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.SystemX86), "netsh.exe");
if (bRemove)
{
strAction = "delete";
}
else
{
strAction = "add";
strExtraArguments = string.Format(" certhash={0} appid={{{1}}}", certificate.Thumbprint, Guid.NewGuid());
}
startInfo.Arguments = string.Format("http {0} sslcert ipport=0.0.0.0:{1}{2}", strAction, iPort, strExtraArguments);
startInfo.RedirectStandardOutput = true;
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
return startInfo;
}
File.Move System.IO.IOException: "No more connections can be made to this remote computer at this time because there are already as many connections as the computer can accept".
I have a process running under SYS account. It is processing files on local HD and move them to a remote drive on a domain using impersonation.
Edit, added code sample:
The method bellow is called repeatedly (The Impersonation is a utility class I use for impersonation, this is irrelevant to the issue).
private void moveFileUsingImpersonation(string srcFilePath, string dstFilePath, string userName, string passWord)
{
WindowsImpersonationContext wic = null;
// move it to destination
try
{
wic = Impersonation.Impersonate(userName, passWord);
if (wic != null)
{
File.Move(srcFilePath, dstFilePath);
}
else
{
Console.WriteLine("moveFileUsingImpersonation, Failure to impersonate!");
}
}
catch(Exception ex)
{
Console.WriteLine("moveFileUsingImpersonation, Exception={0}", ex.ToString());
}
finally
{
Impersonation.UndoImpersonate(wic);
}
}
Edit, added code sample.
When the process is running on XP machine and the remote Drive is on either XP or Win7 machine the call to File.Move works just fine and move the required files. However when the process is running on Win7 and remote Drive is on Win7 machine the mentioned exception is thrown after 20 files have been moved.
I've also tried to call the win32 API MoveFileEx with the MOVEFILE_REPLACE_EXISTING & MOVEFILE_COPY_ALLOWED & MOVEFILE_WRITE_THROUGH flags, with the same result - ERROR_REQ_NOT_ACCEP 71 (0x47).
It seems that the underlying connection made by the call to File.Move isn't closed properly on Win7.
Is there a way to overcome this?
What am I missing here?
Thanks, Ilan
Based on your code, you're probably copying using a UNC path. I've alway had issues doing this, and I've learned it's best to just map and then disconnect drives in code as needed. It saves me from having to deal with permissions issues, and also issues like the one you're describing.
We have a class that handles this for us. We've been using it for over 5 years with no issues, including on Win7 machines on both the code and remote side. Hoefully it will work for you as well.
public static class NetworkDrives
{
public static bool MapDrive(string DriveLetter, string Path, string Username, string Password)
{
bool ReturnValue = false;
if(System.IO.Directory.Exists(DriveLetter + ":\\"))
{
DisconnectDrive(DriveLetter);
}
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "net.exe";
p.StartInfo.Arguments = " use " + DriveLetter + ": " + '"' + Path + '"' + " " + Password + " /user:" + Username;
p.Start();
p.WaitForExit();
string ErrorMessage = p.StandardError.ReadToEnd();
string OuputMessage = p.StandardOutput.ReadToEnd();
if (ErrorMessage.Length > 0)
{
throw new Exception("Error:" + ErrorMessage);
}
else
{
ReturnValue = true;
}
return ReturnValue;
}
public static bool DisconnectDrive(string DriveLetter)
{
bool ReturnValue = false;
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "net.exe";
p.StartInfo.Arguments = " use " + DriveLetter + ": /DELETE";
p.Start();
p.WaitForExit();
string ErrorMessage = p.StandardError.ReadToEnd();
string OuputMessage = p.StandardOutput.ReadToEnd();
if (ErrorMessage.Length > 0)
{
throw new Exception("Error:" + ErrorMessage);
}
else
{
ReturnValue = true;
}
return ReturnValue;
}
}
I am using folderBrowserDialog in my winform.
I need the default or initial path to be a network location.
for eg:
folderBrowserDialog1.SelectedPath = #"\\server1\foo\bar\";
This does not work. My system is on the right network and I am able to access the directory thru my browser and run command.
Is this a non-feature? or is there a work-around?
I would appreciate it if someone can guide me thru!
Thanks,
Ivar
In my experience, .NET has always been hit-or-miss with UNC paths. Sometimes it works and sometimes it doesn't. I'm sure there's a good explanation for it, but early on, I searched and searched without finding an answer.
Rather than deal with the issue, I just adopted the policy that it's better to map a drive myself and then disconnect when done in code. (If you find the answer, I'd be interested in knowing why this is, but since I have a working solution, I don't care enough to research it myself.) It works for us 100% of the time, and it's very easy. I created a class for doing it, since it's such a common task in our shop.
I don't know if you're open to the idea, at any rate, but if you're interested, and don't already have the code, our routine is pasted in below. It would be fairly simple to check for an open drive letter, and just map it, then disconnect when done.
public static class NetworkDrives
{
public static bool MapDrive(string DriveLetter, string Path, string Username, string Password)
{
bool ReturnValue = false;
if(System.IO.Directory.Exists(DriveLetter + ":\\"))
{
DisconnectDrive(DriveLetter);
}
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "net.exe";
p.StartInfo.Arguments = " use " + DriveLetter + ": " + Path + " " + Password + " /user:" + Username;
p.Start();
p.WaitForExit();
string ErrorMessage = p.StandardError.ReadToEnd();
string OuputMessage = p.StandardOutput.ReadToEnd();
if (ErrorMessage.Length > 0)
{
throw new Exception("Error:" + ErrorMessage);
}
else
{
ReturnValue = true;
}
return ReturnValue;
}
public static bool DisconnectDrive(string DriveLetter)
{
bool ReturnValue = false;
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "net.exe";
p.StartInfo.Arguments = " use " + DriveLetter + ": /DELETE";
p.Start();
p.WaitForExit();
string ErrorMessage = p.StandardError.ReadToEnd();
string OuputMessage = p.StandardOutput.ReadToEnd();
if (ErrorMessage.Length > 0)
{
throw new Exception("Error:" + ErrorMessage);
}
else
{
ReturnValue = true;
}
return ReturnValue;
}
}
Windows makes a temporary mapping when you access a network resource using the \\name convention. I am not sure if there's a provision to do the same from a .net app in a concise manner. You may want to map the drive first to a letter then access it using #"Z:\foo\bar\" but obviously mapping a drive may not be something you want to do if your app is deployed in a way that prevents it.
I have a program that only requires elevation to Admin on very rare occasions so I do not want to set-up my manifest to require permanent elevation.
How can I Programmatically request elevation only when I need it?
I am using C#
WindowsPrincipal principal = new WindowsPrincipal(WindowsIdentity.GetCurrent());
bool hasAdministrativeRight = principal.IsInRole(WindowsBuiltInRole.Administrator);
if (!hasAdministrativeRight)
{
RunElevated(Application.ExecutablePath);
this.Close();
Application.Exit();
}
private static bool RunElevated(string fileName)
{
//MessageBox.Show("Run: " + fileName);
ProcessStartInfo processInfo = new ProcessStartInfo();
processInfo.Verb = "runas";
processInfo.FileName = fileName;
try
{
Process.Start(processInfo);
return true;
}
catch (Win32Exception)
{
// Do nothing. Probably the user canceled the UAC window
}
return false;
}