Is there a way to resolve the mac address off the default gateway using c#?
update im working with
var x = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces()[0].GetIPProperties().GatewayAddresses;
but I feel like I'm missing something.
Something like this should work for you, although you probably want to add more error checking:
[DllImport("iphlpapi.dll", ExactSpelling = true)]
public static extern int SendARP(uint destIP, uint srcIP, byte[] macAddress, ref uint macAddressLength);
public static byte[] GetMacAddress(IPAddress address)
{
byte[] mac = new byte[6];
uint len = (uint)mac.Length;
byte[] addressBytes = address.GetAddressBytes();
uint dest = ((uint)addressBytes[3] << 24)
+ ((uint)addressBytes[2] << 16)
+ ((uint)addressBytes[1] << 8)
+ ((uint)addressBytes[0]);
if (SendARP(dest, 0, mac, ref len) != 0)
{
throw new Exception("The ARP request failed.");
}
return mac;
}
What you really want is to perform an Adress Resolution Protocol (ARP) request.
There are good and not soo good ways to do this.
Use an existing method in the .NET framework (though I doubt it exists)
Write your own ARP request method (probably more work than you're looking for)
Use a managed library (if exists)
Use an unmanaged library (such as iphlpapi.dll as suggested by Kevin)
If you know you only need remote to get the MAC adress of a remote Windows computer within your network you may use Windows Management Instrumentation (WMI)
WMI example:
using System;
using System.Management;
namespace WMIGetMacAdr
{
class Program
{
static void Main(string[] args)
{
ManagementScope scope = new ManagementScope(#"\\localhost"); // TODO: remote computer (Windows WMI enabled computers only!)
//scope.Options = new ConnectionOptions() { Username = ... // use this to log on to another windows computer using a different l/p
scope.Connect();
ObjectQuery query = new ObjectQuery("SELECT * FROM Win32_NetworkAdapterConfiguration");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
foreach (ManagementObject obj in searcher.Get())
{
string macadr = obj["MACAddress"] as string;
string[] ips = obj["IPAddress"] as string[];
if (ips != null)
{
foreach (var ip in ips)
{
Console.WriteLine("IP address {0} has MAC address {1}", ip, macadr );
}
}
}
}
}
}
You'll probably need to use P/Invoke and some native Win API functions.
Have a look at this tutorial.
Related
I am looking for an elegant way to get the OS version like: "Windows XP Professional Service Pack 1" or "Windows Server 2008 Standard Edition" etc.
Is there an elegant way of doing that?
I am also interested in the processor architecture (like x86 or x64).
You can use WMI to get the product name ("Microsoft® Windows Server® 2008 Enterprise "):
using System.Management;
var name = (from x in new ManagementObjectSearcher("SELECT Caption FROM Win32_OperatingSystem").Get().Cast<ManagementObject>()
select x.GetPropertyValue("Caption")).FirstOrDefault();
return name != null ? name.ToString() : "Unknown";
You should really try to avoid WMI for local use. It is very convenient but you pay dearly for it in terms of performance. This is quick and simple:
public string HKLM_GetString(string path, string key)
{
try
{
RegistryKey rk = Registry.LocalMachine.OpenSubKey(path);
if (rk == null) return "";
return (string)rk.GetValue(key);
}
catch { return ""; }
}
public string FriendlyName()
{
string ProductName = HKLM_GetString(#"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "ProductName");
string CSDVersion = HKLM_GetString(#"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CSDVersion");
if (ProductName != "")
{
return (ProductName.StartsWith("Microsoft") ? "" : "Microsoft ") + ProductName +
(CSDVersion != "" ? " " + CSDVersion : "");
}
return "";
}
Why not use Environment.OSVersion? It will also tell you what operating this is - Windows, Mac OS X, Unix, etc. To find out if you are running in 64bit or 32bit, use IntPtr.Size - this will return 4 bytes for 32bit and 8 bytes for 64bit.
Try:
new ComputerInfo().OSVersion;
Output:
Microsoft Windows 10 Enterprise
Note:
Add reference to Microsoft.VisualBasic.Devices;
For me below line works which gives me output like:
Microsoft Windows 10.0.18362
System.Runtime.InteropServices.RuntimeInformation.OSDescription
It can be used to get information like architecture as well
https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.runtimeinformation?view=netframework-4.8
Properties
FrameworkDescription: Returns a string that indicates the name of the .NET installation on which an app is running.
OSArchitecture: Gets the platform architecture on which the current app is running.
OSDescription: Gets a string that describes the operating system on which the app is running.
ProcessArchitecture: Gets the process architecture of the currently running app.
Little late, but this is how I did it. Might help someone in the future.
using Microsoft.Win32;
RegistryKey registryKey = Registry.LocalMachine.OpenSubKey("Software\\Microsoft\\Windows NT\\CurrentVersion");
string pathName = (string)registryKey.GetValue("productName");
Sample output:
Name = Windows Vista
Edition = Home Premium
Service Pack = Service Pack 1
Version = 6.0.6001.65536
Bits = 64
Sample class:
class Program
{
static void Main( string[] args )
{
Console.WriteLine( "Operation System Information" );
Console.WriteLine( "----------------------------" );
Console.WriteLine( "Name = {0}", OSInfo.Name );
Console.WriteLine( "Edition = {0}", OSInfo.Edition );
Console.WriteLine( "Service Pack = {0}", OSInfo.ServicePack );
Console.WriteLine( "Version = {0}", OSInfo.VersionString );
Console.WriteLine( "Bits = {0}", OSInfo.Bits );
Console.ReadLine();
}
}
Source code for OSInfo class: http://www.csharp411.com/determine-windows-version-and-edition-with-c/ However there is an error in the code, you will need to replace the "case 6" statement (it's just before #endregion NAME) with this:
case 6:
switch (minorVersion)
{
case 0:
switch (productType)
{
case 1:
name = "Windows Vista";
break;
case 3:
name = "Windows Server 2008";
break;
}
break;
case 1:
switch (productType)
{
case 1:
name = "Windows 7";
break;
case 3:
name = "Windows Server 2008 R2";
break;
}
break;
}
break;
And if you want to go a step further and see if your program is running in 64 or 32 bit:
public static class Wow
{
public static bool Is64BitProcess
{
get { return IntPtr.Size == 8; }
}
public static bool Is64BitOperatingSystem
{
get
{
// Clearly if this is a 64-bit process we must be on a 64-bit OS.
if (Is64BitProcess)
return true;
// Ok, so we are a 32-bit process, but is the OS 64-bit?
// If we are running under Wow64 than the OS is 64-bit.
bool isWow64;
return ModuleContainsFunction("kernel32.dll", "IsWow64Process") && IsWow64Process(GetCurrentProcess(), out isWow64) && isWow64;
}
}
static bool ModuleContainsFunction(string moduleName, string methodName)
{
IntPtr hModule = GetModuleHandle(moduleName);
if (hModule != IntPtr.Zero)
return GetProcAddress(hModule, methodName) != IntPtr.Zero;
return false;
}
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
extern static bool IsWow64Process(IntPtr hProcess, [MarshalAs(UnmanagedType.Bool)] out bool isWow64);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
extern static IntPtr GetCurrentProcess();
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
extern static IntPtr GetModuleHandle(string moduleName);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
extern static IntPtr GetProcAddress(IntPtr hModule, string methodName);
}
One thing to be careful of is this information is usually localized and will report differently depending on the language of the OS.
You can get a lot of info from WMI look for the Win32_OperatingSystem class
Note that the processor architecture question is complex:
do you mean (higher numers require lower numbers to be true):
The CPU is capable for handling 64bit (in the sense that it supports AMD/intel x64 or Itanium)
The Operating system is 64bit
GPR and pointers are 64bits, i.e. XP 64, Vista 64, a 64 bit server release or a 64bit OS for mono
The currently executing process is a 64 bit process (not executing under Wow64 for example)
if you are happy that all 3 must be true then
IntPtr.Size == 8
Indicates that all three are true
You can use Visual Basic Devices to get version information.
Code:
using Microsoft.VisualBasic.Devices;
var versionID = new ComputerInfo().OSVersion;//6.1.7601.65536
var versionName = new ComputerInfo().OSFullName;//Microsoft Windows 7 Ultimate
var verionPlatform = new ComputerInfo().OSPlatform;//WinNT
Console.WriteLine(versionID);
Console.WriteLine(versionName);
Console.WriteLine(verionPlatform);
Output:
6.1.7601.65536
Microsoft Windows 10 Enterprise
WinNT
Note:
You will need to add a reference to Microsoft.VisualBasic;
I know it is no direct answer to the question and it's also a little bit late, but for those who are only looking for a way to determine whether the OS is a Client OS or a server there is a way to use the following: (you need to include the System.Management reference)
using System;
using System.Management;
ManagementClass osClass = new ManagementClass("Win32_OperatingSystem");
foreach (ManagementObject queryObj in osClass.GetInstances())
{
foreach (PropertyData prop in queryObj.Properties)
{
if (prop.Name == "ProductType")
{
ProdType = int.Parse(prop.Value.ToString());
}
}
}
while the variable ProdType is an integer that was initialized before. It will contain a value between 1 and 3 while 1 stands for Workstation, 2 for Domain Controller and 3 for a server.
This was taken from Accessing the properties of Win32_OperatingSystem and changed a little bit...
Disclosure: After posting this, I realized that I am depending on a Nuget extension method library called Z.ExntensionMethods which contains IndexOf()
using Microsoft.VisualBasic.Devices;
string SimpleOSName()
{
var name = new ComputerInfo().OSFullName;
var parts = name.Split(' ').ToArray();
var take = name.Contains("Server")?3:2;
return string.Join(" ", parts.Skip(parts.IndexOf("Windows")).Take(take));
}
Faster performance using System.Management;
string SimpleOSName()
{
var name = new ManagementObjectSearcher("SELECT Caption FROM Win32_OperatingSystem")
.Get().Cast<ManagementObject>()
.Select(x => x.GetPropertyValue("Caption").ToString())
.First();
var parts = name.Split(' ').ToArray();
var take = name.Contains("Server")?3:2;
return string.Join(" ", parts.Skip(parts.IndexOf("Windows")).Take(take));
}
output example:
Windows 7
Windows Server 2008
I'm trying to get the total pages count from all the printers connected to a computer. To do that i try in different ways but nothing give me the expected result:
SNMP: Very useful but impossible use it on a local printer.
Win32_printer: No way to get the total page count of a network printer and more of the local printers not store the page count on it.
printui.dll: total page count not supported.
Now i'm trying a different approach: there is a way to programmatically print the device configuration page (Example: THAT) to a file? you can print it whitout connect the printer to a PC's so it shoud be stored somewhere in some digital format. the only thing i found on internet is the Bidi Communication but i can't found any documentation that can help me... I not need a particular programming language, but if possible i prefere c#,c++ or java.
#Soner Gönül: Ok, so i choose c# just because is the language that i use to write the snmp test, but examples in other languages are appreciated:
SNMP:
SNMP snmp = new SNMP();
// Aprire la connessione verso la stampante
try
{
snmp.Open(IPAddressOfPrinter, CommunityString, Retries, TimeoutInMS);
} catch {
MessageBox.Show("Unable to reach the printer");
}
//Sides Counter
lblSides.Text = snmp.Get(".1.3.6.1.2.1.43.10.2.1.4.1.1").ToString();
rtbLog.Text += "Printer Counter: " + lblSides.Text;
rtbLog.Text += " Sides.\n";
That code give me the number of sides printed by the printer, that is ok, but need that the printer is a network printer, on a USB printer i can't use SNMP.
Win32_Printer:
ConnectionOptions options = new ConnectionOptions();
ManagementScope scope = new ManagementScope(#"\\" + System.Environment.MachineName.ToString() + #"\root\cimv2"); //yeah... i know #"root\cimv2" is enought, i notice only now...
scope.Connect();
ObjectQuery PRNquery = new ObjectQuery("SELECT * FROM Win32_Printer");
ManagementObjectSearcher PRNsearcher = new ManagementObjectSearcher(scope, PRNquery);
ManagementObjectCollection PRNqueryCollection = PRNsearcher.Get();
foreach (ManagementObject m in PRNqueryCollection)
{
MessageBox.Show(m["JobCountSinceLastReset"].Tostring());
//But the result is Zero or random error the 90% of the times.
}
priuntui.dll:
If i launch
rundll32 printui.dll PrintUIEntry /something
or try to export the report with:
public static class PrintTest
{
[DllImport("printui.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern void PrintUIEntryW(IntPtr hwnd,
IntPtr hinst, string lpszCmdLine, int nCmdShow);
public static void Print(string printerName)
{
var printParams = string.Format(#"/f C:\test.dat /Xg /n{0}", printerName);
PrintUIEntryW(IntPtr.Zero, IntPtr.Zero, printParams, 0);
}
}
i not found a page count report, but only generic information.
I am using this code to add IP addresses to a nic card:
[DllImport("iphlpapi.dll", SetLastError = true)]
private static extern UInt32 AddIPAddress(UInt32 address, UInt32 ipMask, int ifIndex, out IntPtr nteContext,
out IntPtr nteInstance);
public static UInt32 AddIpAddressToInterface(string ipAddress, string subnetMask, int ifIndex)
{
var ipAdd = System.Net.IPAddress.Parse(ipAddress);
var subNet = System.Net.IPAddress.Parse(subnetMask);
unsafe
{
var nteContext = 0;
var nteInstance = 0;
IntPtr ptrNteContext;
var ptrNteInstance = new IntPtr(nteInstance);
return AddIPAddress((uint)BitConverter.ToInt32(ipAdd.GetAddressBytes(), 0), (uint)BitConverter.ToInt32(subNet.GetAddressBytes(), 0), ifIndex, out ptrNteContext,
out ptrNteInstance);
}
}
It seems to be working, but I noticed if I reboot the machine, the IPs are removed. Also, I can see them if I perform an ipconfig from a command line, but I do not see them in the Advanced TCP/IP settings dialog. So, are the IPS really added or do I need do something else to ensure the IPs are bound to the nic card?
the IPs are, in fact, added, but AddIPAddress is non-persistent:
The IPv4 address added by the AddIPAddress function is not persistent. The IPv4 address exists only as long as the adapter object exists. Restarting the computer destroys the IPv4 address, as does manually resetting the network interface card (NIC). Also, certain PnP events may destroy the address.
To create an IPv4 address that persists, the EnableStatic method of the Win32_NetworkAdapterConfiguration Class in the Windows Management Instrumentation (WMI) controls may be used. The netsh commands can also be used to create a persistent IPv4 address.
Source: http://msdn.microsoft.com/en-us/library/windows/desktop/aa365801%28v=vs.85%29.aspx
You can execute the EnableStatic method using WMI.NET (System.Management Namespace) like:
var q = new ObjectQuery("select * from Win32_NetworkAdapterConfiguration where InterfaceIndex=25");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(q);
foreach (ManagementObject nic in searcher.Get())
{
ManagementBaseObject newIP = nic.GetMethodParameters("EnableStatic");
newIP["IPAddress"] = new string[]{"192.168.0.1"};
newIP["SubnetMask"] = new string[]{"255.255.255.0"};
nic.InvokeMethod("EnableStatic", newIP, null);
}
As noted, AddIPAddress and all of iphlpapi.dll show and control the dynamic configuration, which doesn't get persisted.
You can set the static persisted configuration, which would show up in the TCP/IP settings dialogs, using netsh - run netsh interface ipv4 set /? to see how. It's programmatically accessible via the INetCfg interface, but I think some of it is undocumented.
The WMI interfaces are wrappers which mix stuff from both sources, which is why I recommend against using them (as you've noticed, they won't configure disconnected NICs).
I want to programatically enable TCP connections on SQL Server. I believe we can achieve this by modifying registry entries and restarting SQL Server service. What registry should I edit?
Unless you have a good reason for modifying the registry directly, I suggest you consider using WMI. WMI will provide you with a more version agnostic implementation. WMI can be accessed through the System.Management namespace. You could have code that looks something like this.
public void EnableSqlServerTcp(string serverName, string instanceName)
{
ManagementScope scope =
new ManagementScope(#"\\" + serverName +
#"\root\Microsoft\SqlServer\ComputerManagement");
ManagementClass sqlService =
new ManagementClass(scope,
new ManagementPath("SqlService"), null);
ManagementClass serverProtocol =
new ManagementClass(scope,
new ManagementPath("ServerNetworkProtocol"), null);
sqlService.Get();
serverProtocol.Get();
foreach (ManagementObject prot in serverProtocol.GetInstances())
{
prot.Get();
if ((string)prot.GetPropertyValue("ProtocolName") == "Tcp" &&
(string)prot.GetPropertyValue("InstanceName") == instanceName)
{
prot.InvokeMethod("SetEnable", null);
}
}
uint sqlServerService = 1;
uint sqlServiceStopped = 1;
foreach (ManagementObject instance in sqlService.GetInstances())
{
if ((uint)instance.GetPropertyValue("SqlServiceType") == sqlServerService &&
(string)instance.GetPropertyValue("ServiceName") == instanceName)
{
instance.Get();
if ((uint)instance.GetPropertyValue("State") != sqlServiceStopped)
{
instance.InvokeMethod("StopService", null);
}
instance.InvokeMethod("StartService", null);
}
}
}
This code assumes a project reference to System.Management.dll and the following using statement:
using System.Management;
The Sql Protocols blog has an article that goes into some detail as to what the above code is doing.
Note: If a firewall is blocking the port(s) you will still be unable to access the server via TCP.
Take a look at HKLM\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL10.MSSQLSERVER\MSSQLServer\SuperSocketNetLib\Tcp hive. There are keys like Enabled, ListenOnAllIPs and a list of IP addresses to listen on.
I am looking for pointers towards APIs in c# that will allow me to control my Internet connection by turning the connection on and off.
I want to write a little console app that will allow me to turn my access on and off , allowing for productivity to skyrocket :) (as well as learning something in the process)
Thanks !!
If you're using Windows Vista you can use the built-in firewall to block any internet access.
The following code creates a firewall rule that blocks any outgoing connections on all of your network adapters:
using NetFwTypeLib; // Located in FirewallAPI.dll
...
INetFwRule firewallRule = (INetFwRule)Activator.CreateInstance(
Type.GetTypeFromProgID("HNetCfg.FWRule"));
firewallRule.Action = NET_FW_ACTION_.NET_FW_ACTION_BLOCK;
firewallRule.Description = "Used to block all internet access.";
firewallRule.Direction = NET_FW_RULE_DIRECTION_.NET_FW_RULE_DIR_OUT;
firewallRule.Enabled = true;
firewallRule.InterfaceTypes = "All";
firewallRule.Name = "Block Internet";
INetFwPolicy2 firewallPolicy = (INetFwPolicy2)Activator.CreateInstance(
Type.GetTypeFromProgID("HNetCfg.FwPolicy2"));
firewallPolicy.Rules.Add(firewallRule);
Then remove the rule when you want to allow internet access again:
INetFwPolicy2 firewallPolicy = (INetFwPolicy2)Activator.CreateInstance(
Type.GetTypeFromProgID("HNetCfg.FwPolicy2"));
firewallPolicy.Rules.Remove("Block Internet");
This is a slight modification of some other code that I’ve used, so I can’t make any guarantees that it’ll work. Once again, keep in mind that you'll need Windows Vista (or later) and administrative privileges for this to work.
Link to the firewall API documentation.
This is what I am currently using (my idea, not an api):
System.Diagnostics;
void InternetConnection(string str)
{
ProcessStartInfo internet = new ProcessStartInfo()
{
FileName = "cmd.exe",
Arguments = "/C ipconfig /" + str,
WindowStyle = ProcessWindowStyle.Hidden
};
Process.Start(internet);
}
Disconnect from internet: InternetConnection("release");
Connect to internet: InternetConnection("renew");
Disconnecting will just remove the access to internet (it will show a caution icon in the wifi icon).
Connecting might take five seconds or more.
Out of topic:
In any cases you might want to check if you're connected or not (when you use the code above), I better suggest this:
System.Net.NetworkInformation;
public static bool CheckInternetConnection()
{
try
{
Ping myPing = new Ping();
String host = "google.com";
byte[] buffer = new byte[32];
int timeout = 1000;
PingOptions pingOptions = new PingOptions();
PingReply reply = myPing.Send(host, timeout, buffer, pingOptions);
return (reply.Status == IPStatus.Success);
}
catch (Exception)
{
return false;
}
}
There are actually a myriad of ways to turn off (Read: break) your internet access, but I think the simplest one would be to turn of the network interface that connects you to the internet.
Here is a link to get you started:
Identifying active network interface
Here's a sample program that does it using WMI management objects.
In the example, I'm targeting my wireless adapter by looking for network adapters that have "Wireless" in their name. You could figure out some substring that identifies the name of the adapter that you are targeting (you can get the names by doing ipconfig /all at a command line). Not passing a substring would cause this to go through all adapters, which is kinda severe. You'll need to add a reference to System.Management to your project.
using System;
using System.Management;
namespace ConsoleAdapterEnabler
{
public static class NetworkAdapterEnabler
{
public static ManagementObjectSearcher GetWMINetworkAdapters(String filterExpression = "")
{
String queryString = "SELECT * FROM Win32_NetworkAdapter";
if (filterExpression.Length > 0)
{
queryString += String.Format(" WHERE Name LIKE '%{0}%' ", filterExpression);
}
WqlObjectQuery query = new WqlObjectQuery(queryString);
ManagementObjectSearcher objectSearcher = new ManagementObjectSearcher(query);
return objectSearcher;
}
public static void EnableWMINetworkAdapters(String filterExpression = "")
{
foreach (ManagementObject adapter in GetWMINetworkAdapters(filterExpression).Get())
{
//only enable if not already enabled
if (((bool)adapter.Properties["NetEnabled"].Value) != true)
{
adapter.InvokeMethod("Enable", null);
}
}
}
public static void DisableWMINetworkAdapters(String filterExpression = "")
{
foreach (ManagementObject adapter in GetWMINetworkAdapters(filterExpression).Get())
{
//If enabled, then disable
if (((bool)adapter.Properties["NetEnabled"].Value)==true)
{
adapter.InvokeMethod("Disable", null);
}
}
}
}
class Program
{
public static int Main(string[] args)
{
NetworkAdapterEnabler.DisableWMINetworkAdapters("Wireless");
Console.WriteLine("Press any key to continue");
var key = Console.ReadKey();
NetworkAdapterEnabler.EnableWMINetworkAdapters("Wireless");
Console.WriteLine("Press any key to continue");
key = Console.ReadKey();
return 0;
}
}
}
public static void BlockingOfData()
{
INetFwPolicy2 firewallPolicy = (INetFwPolicy2)Activator.CreateInstance(Type.GetTypeFromProgID("HNetCfg.FwPolicy2"));
firewallPolicy.set_DefaultOutboundAction(NET_FW_PROFILE_TYPE2_.NET_FW_PROFILE2_DOMAIN, NET_FW_ACTION_.NET_FW_ACTION_BLOCK);
firewallPolicy.set_DefaultOutboundAction(NET_FW_PROFILE_TYPE2_.NET_FW_PROFILE2_PRIVATE, NET_FW_ACTION_.NET_FW_ACTION_BLOCK);
firewallPolicy.set_DefaultOutboundAction(NET_FW_PROFILE_TYPE2_.NET_FW_PROFILE2_PUBLIC, NET_FW_ACTION_.NET_FW_ACTION_BLOCK);
}