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;
}
}
Related
This question already has answers here:
Restart Server from ASP.NET application when AppPool is ran under LocalSystem or LocalService account
(2 answers)
Shutdown or Restart Machine In C# and ASP.NET
(1 answer)
Closed 3 years ago.
I have a web app c # hosted on IIS on a computer with windows server 2008, I ran a command on a windows server cmd through C#, but it doesn't work, I tried it locally on my computer and the command works, I don't know why it doesn't work on the computer with windows server, I use this source code,I put a log but doesn't throw any error.
protected void btnReboot_Click(object sender, EventArgs e)
{
try
{
//StartShutDown("-l");
StartShutDown("-f -r -t 5");
Log2("MNS OK");
}
catch (Exception ex)
{
Log2("MNS ERROR " + ex.ToString());
}
}
private static void StartShutDown(string param)
{
ProcessStartInfo proc = new ProcessStartInfo();
proc.FileName = "cmd";
proc.WindowStyle = ProcessWindowStyle.Hidden;
proc.Arguments = "/C shutdown " + param;
Process.Start(proc);
}
You can actually capture the error output from the process that was launched by redirecting the standard error. An example would be like this:
private static void StartShutDown(string param)
{
Process p = new Process();
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true; // You need to set this
p.StartInfo.UseShellExecute = false;
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.Arguments = "/C shutdown " + param;
p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
p.Start();
string stdoutx = p.StandardOutput.ReadToEnd();
string stderrx = p.StandardError.ReadToEnd(); // here is where you get the error output string
p.WaitForExit();
Console.WriteLine("Exit code : {0}", p.ExitCode);
Console.WriteLine("Stdout : {0}", stdoutx);
Console.WriteLine("Stderr : {0}", stderrx);
}
Once you have the Stderr you can check its contents and, if it's not empty then you know an error occurred.
I want to scan a network and enumerate hostname of all windows machines. There is an interface method that takes an ip range as input and returns hostnames. I have to implement it. So, here is my code:
public ICollection<string> EnumerateWindowsComputers(ICollection<string> ipList)
{
ICollection<string> hostNames = new List<string>();
foreach (var ip in ipList)
{
var hostName = GetHostName(ip);
if (string.IsNullOrEmpty(hostName) == false)
{
hostNames.Add(hostName)
}
}
return hostNames;
}
private static string GetHostName(string ipAddress)
{
try
{
IPHostEntry entry = Dns.GetHostEntry(ipAddress);
if (entry != null)
{
return entry.HostName;
}
}
catch (SocketException ex)
{
System.Console.WriteLine(ex.Message + " - " + ipAddress);
}
return null;
}
This method enumerates all windows machines successfully, but there are network printers in it. I can easily ignore my printers' hostname, but it will not be a good solution. I have to make sure that only the devices with the Windows operating system returned.
Any idea how to do it without a third party library? If there is a better way, we don't have to use GetHostName method.
P.S. Linux, MacOS, Android and IOS devices are not found as expected.
Service detection would not be true as there may be linux or other box emulating Windows FileSharing
Use systeminfo /s IPADDRESS shell command from Windows Machine to reliably fetch remote Windows OS details. You code will be like following:
string IPADDRESS = "192.168.1.1";
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.startInfo.FileName = "cmd.exe";
p.startInfo.Arguments = "/C systeminfo /s IPADDRESS";
p.Start();
p.WaitForExit();
string output = p.StandardOutput.ReadToEnd();
string error = p.StandardError.ReadToEnd();
p.WaitForExit();
if(output.Contains("Microsoft Windows")) { Console.WriteLine("Windows OS"); }
One way you can attempt to detect the OS in a remote machine is through the use of ping. Ping each IP address and get the TTL. That should give you an idea of the OS you're dealing with. A table matching TTL to OS can be found here: http://www.kellyodonnell.com/content/determining-os-type-ping
According to #Jeroen van Langen's comment, I changed my GetHostName method with GetWindowsHostName.
private string GetWindowsHostName(string ipAddress)
{
try
{
IPHostEntry entry = Dns.GetHostEntry(ipAddress);
if (entry != null)
{
try
{
using (TcpClient tcpClient = new TcpClient())
{
// 445 is default TCP SMB port
tcpClient.Connect(ipAddress, 445);
}
using (TcpClient tcpClient = new TcpClient())
{
// 139 is default TCP NetBIOS port.
tcpClient.Connect(ipAddress, 139);
}
return entry.HostName;
}
catch (Exception ex)
{
System.Console.WriteLine(ex.Message);
}
}
}
catch (SocketException ex)
{
System.Console.WriteLine(ex.Message + " - " + ipAddress);
}
return null;
}
There can be false positive, but this is unlikely and acceptable for me.
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.
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.