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.
Related
Im making some kind of IP setting tool and i have a problem with EnableDHCP method via WMI (Win32_NetworkAdapterConfiguration). Adapter was set to DHCP but there is still IP and gateway. So i am using netsh function which works to me but i want to use only WMI. Any advice?
I try some methods like Lease and send null to static but it doesnt work.
s//THIS ONE I WANT TO USE BUT ITS LET IP AT ADAPTER EVEN DHCP IS ENABLED
public void button1_Click(object sender, EventArgs e)
{
ManagementClass objMC = new ManagementClass("Win32_NetworkAdapterConfiguration");
ManagementObjectCollection objMOC = objMC.GetInstances();
//check numbe of aktive ports
foreach (ManagementObject objMO in objMOC)
{
if ((bool)objMO["IPEnabled"])
{
try
{
// write parameters to active port
ManagementBaseObject setIP;
setIP = objMO.InvokeMethod("EnableDHCP", null, null);
}
catch (Exception x)
{
}
}
}
}
Process p = new Process();//netsh work well but i dont want to use
ProcessStartInfo psi = new ProcessStartInfo("netsh", "interface ip set address \"" + adapter.Name + "\" dhcp");
p.StartInfo = psi;
p.Start();
I expect using EnableDHCP over WMI and IP adress was cleared, but IP adress is still there
This(Picture)
public Collection<PSObject> Invoke(string Query)
{
Collection<PSObject> ps = new Collection<PSObject>();
try
{
using (PowerShell PSI = PowerShell.Create())
{
PSI.AddScript(Query);
ps = PSI.Invoke();
}
}
catch (Exception ex)
{
ps = null;
throw;
}
return ps;
}
Create a class to invoke PS Query
public List<YourObjectList> GetInfo(string ParametersIFYouWant)
{
List<YourObjectList> ListInfo = new List<YourObjectList>();
InvokePowerShellQuery powerShellQuery = new InvokePowerShellQuery();
string PSQuery = "Query Here as a string";
try
{
var PSOutput = powerShellQuery.Invoke(PSQuery);
//In your case you wont need unless in you PS Query you return a message or some other object you want to report back for success or failure.
foreach (var item in PSOutput)
{
//Fill ListInfo
}
}
catch (Exception ex)
{
Listinfo = null;
//handle however you want here
}
return Listinfo;
}
Send the query to your class
Quick Google search on PS Queries you may use
https://www.pdq.com/blog/using-powershell-to-set-static-and-dhcp-ip-addresses-part-1/
https://4sysops.com/archives/set-an-ip-address-and-configure-dhcp-with-powershell
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;
}
}
I have accessed remote server but it can be some problem. So i want to restart the remote via client side using c#. Is that possible to restart?
EDIT: See #amitdayama's answer below for a more reasonable approach
Yes this is possible.
First, add this using namespace statements:
using System.Diagnostics;
using System.Runtime.InteropServices;
To shut down your computer, use:
Process.Start("shutdown","/s /t 0"); // starts the shutdown application
// the argument /s is to shut down the computer
// the argument /t 0 is to tell the process that
// the specified operation needs to be completed
// after 0 seconds
To restart your computer, use:
Process.Start("shutdown","/r /t 0"); // the argument /r is to restart the computer
Source: Codeproject.com
using System;
using System.Management;
namespace WMI3
{
class Class1
{
static void Main(string[] args)
{
Console.WriteLine("Computer details retrieved using Windows Management Instrumentation (WMI)");
//Connect to the remote computer
ConnectionOptions co = new ConnectionOptions();
co.Username = "username";
co.Password = "Pass";
string serverName="servername";
System.Management.ManagementScope ms = new System.Management.ManagementScope(servername + "\\root\\cimv2", co);
//Query remote computer across the connection
System.Management.ObjectQuery oq = new System.Management.ObjectQuery("SELECT * FROM Win32_OperatingSystem");
ManagementObjectSearcher query1 = new ManagementObjectSearcher(ms,oq);
ManagementObjectCollection queryCollection1 = query1.Get();
foreach( ManagementObject mo in queryCollection1 )
{
string[] ss={""};
mo.InvokeMethod("Reboot",ss);
Console.WriteLine(mo.ToString());
}
}
}
}
This is my solution which supports silent mode, "fire and forget" and delayed reboot. In can simply become enhanced with an individual logon for the process start.
public static bool RebootRemoteMachineSOVersion(ContentControl parentControl, string remoteHostNameOrIp, int waitSeconds = 60, bool silent = false, bool waitForExit = true)
{
waitSeconds = Math.Max(0, waitSeconds);
if (!silent && MessageBox.Show($"Reboot remote computer ({ remoteHostNameOrIp }) in { waitSeconds } seconds?", "Reboot remote machine", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No) == MessageBoxResult.No)
{
return false;
//<-----------
}
ProcessStartInfo processInfo = new ProcessStartInfo();
processInfo.FileName = "shutdown.exe";
processInfo.Arguments = $#"-r -t { waitSeconds } -m \\{ remoteHostNameOrIp }";
processInfo.WindowStyle = ProcessWindowStyle.Hidden;
processInfo.CreateNoWindow = true;
Process proc;
try
{
proc = Process.Start(processInfo);
if (waitForExit) proc.WaitForExit();
else return true;
//<----------
}
catch (Exception ex)
{
if (!silent) MessageBox.Show($"An error happened:\n\n{ ex.Message }", "Reboot remote machine", MessageBoxButton.OK, MessageBoxImage.Error);
return false;
//<-----------
}
{
string message = "";
const int ERROR_BAD_NETPATH = 53;
const int ERROR_SHUTDOWN_IN_PROGRESS = 1115;
const int RPC_S_UNKNOWN_IF = 1717;
switch (proc.ExitCode)
{
case 0:
if (!silent) MessageBox.Show($"Remote computer is rebooting ({ remoteHostNameOrIp }) in { waitSeconds } seconds.", "Reboot remote computer", MessageBoxButton.OK, MessageBoxImage.Information);
return true;
//<----------
case ERROR_BAD_NETPATH:
message = $"Remote computer not found ({ remoteHostNameOrIp })";
break;
case ERROR_SHUTDOWN_IN_PROGRESS:
message = $"A shutdown is already in progress ({ remoteHostNameOrIp })";
break;
case RPC_S_UNKNOWN_IF:
message = $"Remote computer does not accept shutdown. Probably it is currently booting. ({ remoteHostNameOrIp })";
break;
default:
message = $"Could not shut down - errorcode: { proc.ExitCode } ({ remoteHostNameOrIp })";
break;
}
if (!silent) MessageBox.Show($"{ message }", "Reboot remote computer", MessageBoxButton.OK, MessageBoxImage.Error);
return false;
}
}
I cannot reconnect to COM port after device on the other end abruptly drops connection.
I can connect again only if I close and re-open the application.
Here is my connection function:
public bool connectTurboPump()
{
try
{
if (TPumpSerialPort != null && TPumpSerialPort.IsOpen == true)
return true;
DataRow dr = tblTPump.Rows[0];
Types.Connection TPumpConnection = new Types.Connection();
TPumpConnection.PORT = dr["port"].ToString();
TPumpConnection.BAUD_RATE = Convert.ToInt32(dr["baud"]);
TPumpConnection.PARITY = (Parity)Enum.Parse(typeof(Parity), dr["parity"].ToString(), true);
TPumpConnection.DATA_BITS = Convert.ToInt32(dr["dataBits"]);
TPumpConnection.STOP_BITS = (StopBits)Enum.Parse(typeof(StopBits), dr["stopBits"].ToString(), true);
TPumpConnection.HANDSHAKE = (Handshake)Enum.Parse(typeof(Handshake), dr["handshake"].ToString(), true);
TPumpSerialPort = new SerialPort(TPumpConnection.PORT, TPumpConnection.BAUD_RATE, TPumpConnection.PARITY, TPumpConnection.DATA_BITS, TPumpConnection.STOP_BITS);
TPumpSerialPort.Handshake = TPumpConnection.HANDSHAKE;
TPumpSerialPort.Open();
TPumpSerialPort.NewLine = "\r";
TPumpSerialPort.ReadTimeout = 10000;
TPumpSerialPort.WriteTimeout = 10000;
if (TPumpSerialPort.IsOpen != true)
return false;
return true;
}
catch { return false; }
}
And here is my re-connection function:
public bool reconnectTurboPump(int attempts = 3)
{
try
{
if (TPumpSerialPort != null && TPumpSerialPort.IsOpen == true)
{
TPumpSerialPort.Close();
TPumpSerialPort.Dispose();
}
int i = 1;
while (true)
{
Log(string.Format("Reconnecting Turbo Pump attempt {0}", i));
if (connectTurboPump())
break;
if (i == attempts)
return false;
i++;
}
return true;
}
catch (Exception ex)
{
Log(string.Format("Could not reconnect to Turbo Pump: {0}", ex.Message));
return false;
}
}
Would really appreciate if someone could help.
Thank you.
This doesn't make much sense if this is a true serial port connection. There is no "connected" state, serial ports are very simple devices that have no underlying protocol that establishes a connection.
If this is actually a USB device that emulates a serial port then you'll indeed have this kind of problem. The driver that emulates the serial port invariably gets very sulky when you unplug the USB connector while the port is in use. There actually is a connection protocol for USB devices, the negotiation is done by the driver. They most typically make the port just disappear, this tends to give user code a heart-attack from which it can't recover. Behavior is very unpredictable and varies from one driver to another. There is no cure for this, glue the connector to the port and never assume that unplugging it will solve any problems in your code, even though that's the only thing you can do with USB.
Following Thomas' advice I've changed reconnection script to the following. Now in testing.
public bool reconnectTurboPump(int attempts = 3)
{
try
{
//if (TPumpSerialPort != null && TPumpSerialPort.IsOpen == true)
if (TPumpSerialPort != null)
{
TPumpSerialPort.Close();
TPumpSerialPort.Dispose();
}
int i = 1;
while (true)
{
Log(string.Format("Reconnecting Turbo Pump attempt {0}", i));
Thread.Sleep(2000);
if (connectTurboPump())
break;
if (i == attempts)
return false;
i++;
}
return true;
}
catch (Exception ex)
{
Log(string.Format("Could not reconnect to Turbo Pump: {0}", ex.Message));
return false;
}
}
Is there an easy way of programmatically checking if a serial COM port is already open/being used?
Normally I would use:
try
{
// open port
}
catch (Exception ex)
{
// handle the exception
}
However, I would like to programatically check so I can attempt to use another COM port or some such.
I needed something similar some time ago, to search for a device.
I obtained a list of available COM ports and then simply iterated over them, if it didn't throw an exception i tried to communicate with the device. A bit rough but working.
var portNames = SerialPort.GetPortNames();
foreach(var port in portNames) {
//Try for every portName and break on the first working
}
This is how I did it:
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern SafeFileHandle CreateFile(string lpFileName, int dwDesiredAccess, int dwShareMode, IntPtr securityAttrs, int dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile);
then later on
int dwFlagsAndAttributes = 0x40000000;
var portName = "COM5";
var isValid = SerialPort.GetPortNames().Any(x => string.Compare(x, portName, true) == 0);
if (!isValid)
throw new System.IO.IOException(string.Format("{0} port was not found", portName));
//Borrowed from Microsoft's Serial Port Open Method :)
SafeFileHandle hFile = CreateFile(#"\\.\" + portName, -1073741824, 0, IntPtr.Zero, 3, dwFlagsAndAttributes, IntPtr.Zero);
if (hFile.IsInvalid)
throw new System.IO.IOException(string.Format("{0} port is already open", portName));
hFile.Close();
using (var serialPort = new SerialPort(portName, 115200, Parity.None, 8, StopBits.One))
{
serialPort.Open();
}
For people that cannot use SerialPort.GetPortNames(); because they are not targeting .net framework (like in my case I am using .Net Core and NOT .Net Framework) here is what I ended up doing:
In command prompt if you type mode you get something like this:
mode is an executable located at C:\Windows\System32\mode.com. Just parse the results of that executable with a regex like this:
// Code that answers the question
var proc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = #"C:\Windows\System32\mode.com",
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
}
};
proc.Start();
proc.WaitForExit(4000); // wait up to 4 seconds. It usually takes less than a second
// get ports being used
var output = proc.StandardOutput.ReadToEnd();
Now if you want to parse the output this is how I do it:
List<string> comPortsBeingUsed = new List<string>();
Regex.Replace(output, #"(?xi) status [\s\w]+? (COM\d) \b ", regexCapture =>
{
comPortsBeingUsed.Add(regexCapture.Groups[1].Value);
return null;
});
foreach(var item in comPortsBeingUsed)
{
Console.WriteLine($"COM port {item} is in use");
}
I wanted to open the next available port and did it like this.
Please note, is it not for WPF but for Windows Forms.
I populated a combobox with the com ports available.
Then I try to open the first one. If it fails, I select the next available item from the combobox. If the selected index did not change in the end, there were no alternate com ports available and we show a message.
private void GetPortNames()
{
comboBoxComPort.Items.Clear();
foreach (string s in SerialPort.GetPortNames())
{
comboBoxComPort.Items.Add(s);
}
comboBoxComPort.SelectedIndex = 0;
}
private void OpenSerialPort()
{
try
{
serialPort1.PortName = comboBoxComPort.SelectedItem.ToString();
serialPort1.Open();
}
catch (Exception ex)
{
int SelectedIndex = comboBoxComPort.SelectedIndex;
if (comboBoxComPort.SelectedIndex >= comboBoxComPort.Items.Count - 1)
{
comboBoxComPort.SelectedIndex = 0;
}
else
{
comboBoxComPort.SelectedIndex++;
}
if (comboBoxComPort.SelectedIndex == SelectedIndex)
{
buttonOpenClose.Text = "Open Port";
MessageBox.Show("Error accessing port." + Environment.NewLine + ex.Message, "Port Error!!!", MessageBoxButtons.OK);
}
else
{
OpenSerialPort();
}
}
if (serialPort1.IsOpen)
{
StartAsyncSerialReading();
}
}
The SerialPort class has an Open method, which will throw a few exceptions.
The reference above contains detailed examples.
See also, the IsOpen property.
A simple test:
using System;
using System.IO.Ports;
using System.Collections.Generic;
using System.Text;
namespace SerPort1
{
class Program
{
static private SerialPort MyPort;
static void Main(string[] args)
{
MyPort = new SerialPort("COM1");
OpenMyPort();
Console.WriteLine("BaudRate {0}", MyPort.BaudRate);
OpenMyPort();
MyPort.Close();
Console.ReadLine();
}
private static void OpenMyPort()
{
try
{
MyPort.Open();
}
catch (Exception ex)
{
Console.WriteLine("Error opening my port: {0}", ex.Message);
}
}
}
}
Sharing what worked for me (a simple helper method):
private string portName { get; set; } = string.Empty;
/// <summary>
/// Returns SerialPort Port State (Open / Closed)
/// </summary>
/// <returns></returns>
internal bool HasOpenPort()
{
bool portState = false;
if (portName != string.Empty)
{
using (SerialPort serialPort = new SerialPort(portName))
{
foreach (var itm in SerialPort.GetPortNames())
{
if (itm.Contains(serialPort.PortName))
{
if (serialPort.IsOpen) { portState = true; }
else { portState = false; }
}
}
}
}
else { System.Windows.Forms.MessageBox.Show("Error: No Port Specified."); }
return portState;
}
Notes:
- For more advanced technique(s) I recommend using ManagementObjectSearcher Class.
More info Here.
- For Arduino devices I would leave the Port Open.
- Recommend using a Try Catch block if you need to catch exceptions.
- Check also: "TimeoutException"
- More information on how to get SerialPort (Open) Exceptions Here.
public void MobileMessages(string ComNo, string MobileMessage, string MobileNo)
{
if (SerialPort.IsOpen )
SerialPort.Close();
try
{
SerialPort.PortName = ComNo;
SerialPort.BaudRate = 9600;
SerialPort.Parity = Parity.None;
SerialPort.StopBits = StopBits.One;
SerialPort.DataBits = 8;
SerialPort.Handshake = Handshake.RequestToSend;
SerialPort.DtrEnable = true;
SerialPort.RtsEnable = true;
SerialPort.NewLine = Constants.vbCrLf;
string message;
message = MobileMessage;
SerialPort.Open();
if (SerialPort.IsOpen )
{
SerialPort.Write("AT" + Constants.vbCrLf);
SerialPort.Write("AT+CMGF=1" + Constants.vbCrLf);
SerialPort.Write("AT+CMGS=" + Strings.Chr(34) + MobileNo + Strings.Chr(34) + Constants.vbCrLf);
SerialPort.Write(message + Strings.Chr(26));
}
else
("Port not available");
SerialPort.Close();
System.Threading.Thread.Sleep(5000);
}
catch (Exception ex)
{
message.show("The port " + ComNo + " does not exist, change port no ");
}
}
I have been fighting with this problem for a few weeks now. Thanks to the suggestions on here and from the site, https://www.dreamincode.net/forums/topic/91090-c%23-serial-port-unauthorizedaccessexception/ .
I finally came up with a solution that seems to work.
The application I am working on allows a user to connect to a USB device and display data from it.
The Problem I was battling. Along side the application I am writing, I use another serial terminal application for doing my testing. Sometimes I forget to disconnect the COMport being used on the other application. If I do, and try to connect with the application I am writing, I would get an “UnAuthorizedAccessException” error. Along with this exception came some side effects, such as double lines of data being spit out and the application locking up on closing down.
My Solution
Thanks to the advice on here and the other site referenced, this was my solution.
private void checkAndFillPortNameList()
{
SerialPort _testingSerialPort;
AvailablePortNamesFound.Clear();
List<string> availablePortNames = new List<string>();//mySerial.GetAvailablePortNames();
foreach (string portName in SerialPortDataAccess.GetAvailablePortNames())
{
try
{
_testingSerialPort = new SerialPort(portName);
_testingSerialPort.Open();
if (_testingSerialPort.IsOpen)
{
availablePortNames.Add(portName);
_testingSerialPort.Close();
}
}
catch (Exception ex)
{
}
}
availablePortNames.Sort();
AvailablePortNamesFound = new ObservableCollection<string>(availablePortNames);
}
This routine connects to a combobox which holds the available Comports for selection. If a Comport is already, in use by another application, that port name will not appear in the combo box.
You can try folloing code to check whether a port already open or not. I'm assumming you dont know specificaly which port you want to check.
foreach (var portName in Serial.GetPortNames()
{
SerialPort port = new SerialPort(portName);
if (port.IsOpen){
/** do something **/
}
else {
/** do something **/
}
}