NetAddresses always null in Win32_NetworkAdapter query - c#

I am trying to get the active network adapter's IP address, so that I avoid virtual and other VPN adapters that are not connected at present.
On my current laptop the following code returns 3 IP V4 addresses, and I don't know how to get the "real", in use IP address from that list.
IPAddress[] ipV4Addresses = Array.FindAll(
Dns.GetHostEntry(String.Empty).AddressList,
a => a.AddressFamily == AddressFamily.InterNetwork);
Looking at MSDN doc here, I thought maybe I was on the right track. Has anyone obtained the list of IP addresses per adapter successfully? If so, please share your wisdom. Thanks!
This is all prototype at this point. I have this code (thanks to SO!), and the string[] netAddresses is always null, even with the computer connected to a network and with a functioning IP address.
string wmiQuery = "SELECT * FROM Win32_NetworkAdapter WHERE NetConnectionId != NULL";
ManagementObjectSearcher moSearch = new ManagementObjectSearcher(wmiQuery);
ManagementObjectCollection moCollection = moSearch.Get();
foreach (ManagementObject mo in moCollection)
{
Console.WriteLine("{0} is {1}", mo["Name"], mo["NetConnectionStatus"]);
string[] netAddresses = (string[])mo["NetworkAddresses"];
if (netAddresses != null)
{
foreach (string netAddress in netAddresses)
{
Console.WriteLine("\tnet addresses:");
Console.WriteLine("\t\t{0}", netAddress);
}
}
}

from MS docs: http://msdn.microsoft.com/en-us/library/windows/desktop/aa394216%28v=vs.85%29.aspx
NetworkAddresses
Data type: string array
Access type: Read-only
Array of network addresses for an adapter. This property is inherited from CIM_NetworkAdapter.
This property has not been implemented yet. It returns a NULL value by default.

There's no such thing as a "real" IP address. Each packet you send is routed to its destination based on the metric of the adapter and the available routing paths. You can guess which IP is primarily being used for internet traffic by doing the following:
Order your adapters by metric, lowest first.
Prioritise WAN IPs, then 192.168.x.x, then 10.x.x.x, and totally ignore loopback addresses.
This will give you a guess as to the "preferred" address, though it's hardly definitive.

Look at WIn32_NetworkAdapterConfiguration, by using the InterfaceIndex from Win32_NetworkAdapter you should be able to get the configuration you need with the ipaddresses assigned to that nic.

Related

Get name of network that a network interface is connected to

In Windows Control Panel, you can find a list of network interfaces/connections which displays the following:
In the .NET framework these are represented in the NetworkInterface class (and found via NetworkInterface.GetAllNetworkInterfaces).
For reference, say I'm reading properties from the Ethernet interface - the NetworkInterface.Name property returns "Ethernet", the NetworkInterface.Description property returns "Realtek PCIe FE Family Controller".
However, nothing in the class seems to be able to get me the name of the network it's connected to (in this case "BELL024"). How would I go about getting that string? I have to know what network the interface is associated with, not just a list of the networks that exist.
It turns out information about each network is stored as a 'network profile' by Windows, storing it's name and other info like whether it's public or not. The name can be changed by users in the control panel, but in my situation that's not a problem.
The Windows API Code Pack from Microsoft contains the APIs necessary to get the collection of network profiles. As it contains a lot of bloat that I don't need, the bare minimum code to wrap the Windows API can be found here.
A collection of the network profiles can then be found like so:
//Get the networks that are currently connected to
var networks = NetworkListManager.GetNetworks(NetworkConnectivityLevels.Connected);
Each object in the collection represents a network profile and contains a collection of NetworkConnection objects. Each NetworkConnection object appears to be info about an interface's connection to the base network.
foreach(Network network in networks)
{
//Name property corresponds to the name I originally asked about
Console.WriteLine("[" + network.Name + "]");
Console.WriteLine("\t[NetworkConnections]");
foreach(NetworkConnection conn in network.Connections)
{
//Print network interface's GUID
Console.WriteLine("\t\t" + conn.AdapterId.ToString());
}
}
The NetworkConnection.AdapterId property is the same network interface GUID that the NetworkInterface.Id property knows.
So, you can determine what network an interface is connected to, by checking if one of the network's connections have the same ID as the interface. Note that they're represented differently, so you'll have to do a bit more work:
Both my Wi-Fi and Ethernet interfaces are connected to the BELL024 network in the above example.
On Windows 8 and Windows 2012 and higher you can query WMI class MSFT_NetConnectionProfile from root/StandardCimv2 namespace.
It shouldn't be too hard to convert this to C#.
Get-WmiObject -Namespace root/StandardCimv2 -Class MSFT_NetConnectionProfile | Format-Table InterfaceAlias, Name
You can use WMI to query for your network name. You can use this code as a sample:
ManagementScope oMs = new ManagementScope();
ObjectQuery oQuery =
new ObjectQuery("Select * From Win32_NetworkAdapter");
ManagementObjectSearcher oSearcher = new ManagementObjectSearcher(oMs, oQuery);
ManagementObjectCollection oReturnCollection = oSearcher.Get();
foreach (ManagementObject oReturn in oReturnCollection)
{
if (oReturn.Properties["NetConnectionID"].Value != null)
{
Console.WriteLine("Name : " + oReturn.Properties["NetConnectionID"].Value);
}
}

Retrieve host VM MAC address via guest VM

This has been an issue I have been looking to for two days. I will share my findings.
I am currently working on an in-house license management system for our software. It's nothing too fancy - as long as it can uniquely identify a user, it's good enough. Our mechanism currently relies on user sign-in + password + MAC address.
99% of the users so far have had no issues, but there is a small subset, the 1%, that has been returning an issue. This 1% is so important to us, because one failure means one hole in our system, something we would like to weed out. Okay - onto the main topic.
Method 1:
public static string returnMAC1()
{
ManagementObjectSearcher searcher = new ManagementObjectSearcher("Select MACAddress, PNPDeviceID FROM Win32_NetworkAdapter WHERE MACAddress IS NOT NULL AND PNPDEVICEID IS NOT NULL");
ManagementObjectCollection mObject = searcher.Get();
foreach (ManagementObject obj in mObject)
{
string pnp = obj["PNPDeviceID"].ToString();
if (pnp.Contains("PCI\\"))
{
string mac = obj["MACAddress"].ToString();
mac = mac.Replace(":", string.Empty);
return mac;
}
}
return "Nothing happened...";
}
Method 1 retrieves the MAC address based on the fact that the physical card is connected to the PCI interface.
Method 2:
public static string returnMAC2()
{
string mac = string.Empty;
foreach (System.Net.NetworkInformation.NetworkInterface nic in System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces())
{
if (nic.OperationalStatus == System.Net.NetworkInformation.OperationalStatus.Up)
{
macAddresses += nic.GetPhysicalAddress().ToString();
break;
}
}
return mac;
}
The second method is a standard method retrieved from MSDN documentation in-regards to MAC addresses.
Based on some tests, it seems the second method is a tad unreliable to retrieve MAC addresses, since it retrieves the wireless card's address. We've had some users returning null addresses as a result of using that method, and while I don't know why that would happen, it could be because there's a lack of a wireless card in their computers. With that said, that's only conjecture. Method #1 relies on using SQL queries to retrieve the PCI MAC. This one has been reliable.
Tests:
Using a Windows 8.1 Enterprise Evaluation edition (free 90-day trial, yay!) installed onto the VirtualBox VM, the tests confirmed that there are major differences in the MAC addresses returned via the guest VM and the host VM.
According to my research, in most cases, the virtual machine is assigned the same MAC address every time it is powered on, so long as the virtual machine is not moved and no changes are made to the certain settings in the configuration file. With that said, and here's the bad news... The guest VM MAC could be anything. So it seems, this is one of the reasons the MAC addresses are inconsistent when used as unique identifiers, which is an issue I found out when some users were on their company VMs. I never knew that's the way people worked, but here we are, so no gloating about it at this point.
My question is - is there any way, without making the user change any settings on their end, to retrieve the host VM's MAC as opposed to the guest VM?
At this point I don't see any reason why someone won't assign the same MAC to every single guest machine to get around our floating license mechanism. Retrieving the host VM MAC would get around this issue, as it would show as one MAC.
We decided this is both impossible and unnecessary. We also decided to use the motherboard UUID as the primary unique identifier, and falling back to the MAC address using the MAC address method below, in case the UUID returns a FFFF-FFFF....... on the rare occasion the vendor does not supply a UUID to that motherboard.
public static string returnMAC1()
{
ManagementObjectSearcher searcher = new ManagementObjectSearcher("Select MACAddress, PNPDeviceID FROM Win32_NetworkAdapter WHERE MACAddress IS NOT NULL AND PNPDEVICEID IS NOT NULL");
ManagementObjectCollection mObject = searcher.Get();
foreach (ManagementObject obj in mObject)
{
string pnp = obj["PNPDeviceID"].ToString();
if (pnp.Contains("PCI\\"))
{
string mac = obj["MACAddress"].ToString();
mac = mac.Replace(":", string.Empty);
return mac;
}
}
return "Nothing happened...";
}

Is there really any way to uniquely identify any computer at all

I know there are a number of similar questions in stackoverflow such as the followings:
What's a good way to uniquely identify a computer?
What is a good unique PC identifier?
Unique computer id C#
WIN32_Processor::Is ProcessorId Unique for all computers
How to uniquely identify computer using C#?
... and dozens more and I have studied them all.
The problem is that some of the accepted answers have suggested MAC address as an unique identifier which is entirely incorrect. Some other answers have suggested to use a combination of various components which seems more logical. However, in case of using a combination it should be considered which component is naturally unlikely to be changed frequently. A few days ago we developed a key generator for a software licensing issue where we used the combination of CPUID and MAC to identify a windows pc uniquely and till practical testing we thought our approach was good enough. Ironically when we went testing it we found three computers returning the same id with our key generator!
So, is there really any way to uniquely identify any computer at all? Right now we just need to make our key generator to work on windows pc. Some way (if possible at all) using c# would be great as our system is developed on .net.
Update:
Sorry for creating some confusions and an apparently false alarm. We found out some incorrectness in our method of retrieving HW info. Primarily I thought of deleting this question as now my own confusion has gone and I do believe that a combination of two or more components is good enough to identify a computer. However, then I decided to keep it because I think I should clarify what was causing the problem as the same thing might hurt some other guy in future.
This is what we were doing (excluding other codes):
We were using a getManagementInfo function to retrieve MAC and Processor ID
private String getManagementInfo(String StrKey_String, String strIndex)
{
String strHwInfo = null;
try
{
ManagementObjectSearcher searcher = new ManagementObjectSearcher("select * from " + StrKey_String);
foreach (ManagementObject share in searcher.Get())
{
strHwInfo += share[strIndex];
}
}
catch (Exception ex)
{
// show some error message
}
return strHwInfo;
}
Then where needed we used that function to retrieve MAC Address
string strMAC = getManagementInfo("Win32_NetworkAdapterConfiguration", "MacAddress");
and to retrieve ProcessorID
string strProcessorId = getManagementInfo("Win32_Processor", "ProcessorId");
At this point, strMAC would contain more than one MAC address if there are more than one. To take only one we just took the first 17 characters (12 MAC digits and 5 colons in between).
strMAC = strMAC.Length > 17 ? strMAC.Remove(17) : strMAC;
This is where we made the mistake. Because getManagementInfo("Win32_NetworkAdapterConfiguration", "MacAddress") was returning a number of extra MAC addresses that were really in use. For example, when we searched for MAC addresses in the command prompt by getmac command then it showed one or two MAC addresses for each pc which were all different. But getManagementInfo("Win32_NetworkAdapterConfiguration", "MacAddress") returned four to five MAC addresses some of which were identical for all computers. As we just took the first MAC address that our function returned instead of checking anything else, the identical MAC addresses were taken in strMAC incidently.
The following code by Sowkot Osman does the trick by returning only the first active/ enabled MAC address:
private static string macId()
{
return identifier("Win32_NetworkAdapterConfiguration", "MACAddress", "IPEnabled");
}
private static string identifier(string wmiClass, string wmiProperty, string wmiMustBeTrue)
{
string result = "";
System.Management.ManagementClass mc = new System.Management.ManagementClass(wmiClass);
System.Management.ManagementObjectCollection moc = mc.GetInstances();
foreach (System.Management.ManagementObject mo in moc)
{
if (mo[wmiMustBeTrue].ToString() == "True")
{
//Only get the first one
if (result == "")
{
try
{
result = mo[wmiProperty].ToString();
break;
}
catch
{
}
}
}
}
return result;
}
//Return a hardware identifier
private static string identifier(string wmiClass, string wmiProperty)
{
string result = "";
System.Management.ManagementClass mc = new System.Management.ManagementClass(wmiClass);
System.Management.ManagementObjectCollection moc = mc.GetInstances();
foreach (System.Management.ManagementObject mo in moc)
{
//Only get the first one
if (result == "")
{
try
{
result = mo[wmiProperty].ToString();
break;
}
catch
{
}
}
}
return result;
}
However, I was absolutely right about the identical Processor ID issue. All three returned the same Processor ID when we put wmic cpu get ProcessorId command in their command prompts.
Now we have decided to use Motherboard serial number instead of Processor ID to make a combination with MAC address. I think our purpose will be served with this way and if it doesn't in some cases then we should let it go in those few cases.
How about adding motherboard serial number as well e.g.:
using System.management;
//Code for retrieving motherboard's serial number
ManagementObjectSearcher MOS = new ManagementObjectSearcher("Select * From Win32_BaseBoard");
foreach (ManagementObject getserial in MOS.Get())
{
textBox1.Text = getserial["SerialNumber"].ToString();
}
//Code for retrieving Processor's Identity
MOS = new ManagementObjectSearcher("Select * From Win32_processor");
foreach (ManagementObject getPID in MOS.Get())
{
textBox2.Text = getPID["ProcessorID"].ToString();
}
//Code for retrieving Network Adapter Configuration
MOS = new ManagementObjectSearcher("Select * From Win32_NetworkAdapterConfiguration");
foreach (ManagementObject mac in MOS.Get())
{
textBox3.Text = mac["MACAddress"].ToString();
}
The fact in getting a globally unique ID is, only MAC address is the ID that will not change if you set up your system all over. IF you are generating a key for a specific product, the best way to do it is assigning unique IDs for products and combining the product ID with MAC address. Hope it helps.
I Completely agree with just the above comment.
For Software licensening, you can use:
Computer MAC Address (Take all if multiple NIC Card) + Your software Product Code
Most of the renowned telecom vendor is using this technique.
However, I was absolutely right about the identical Processor ID
issue. All three returned the same Processor ID when we put wmic cpu
get ProcessorId command in their command prompts.
Processor ID will be same if all the systems are running as virtual machines on the same hypervisor.
MAC ID seems fine. Only thing is users must be provided the option to reset the application, in case the MAC changes.
It looks like custom kitchen is the way for that.
SMBIOS UUID (motherboard serial) is not robust, but works fine in 99% cases. However some brands will set the same UUID for multiple computers (same production batch maybe). Getting it requires WMI access for the user (if he's not administrator), you can solve that by starting an external process asking administrator priviledges (check codeproject.com/Articles/15848/WMI-Namespace-Security)
Windows Product ID might be good, but I read it could be identical in some circumstances (https://www.nextofwindows.com/the-best-way-to-uniquely-identify-a-windows-machine)
Could someone clarify if the same Product ID (not product key) might be present on multiple computers ?
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\MachineGuid seems interesting. It's generated when installing Windows and if changed, it requires to reactivate Windows.
Mac Addresses are interresting but you can only take the first one or your unique ID will change when the interface is disabled, or when another network interface is added and appears first etc.
Hard Drive serial number is nice but when installing a ghost, it might also override the serial number from the original drive... And the HD serial is very easy to change.
The best might be to generate an ID with a combination of those machine identifiers and decide if the machine is the same by comparing those identifiers (ie if at least one Mac address + either SMBIOS UUID or Product ID is ok, accept)

Identifying active network interface

In a .NET application, how can I identify which network interface is used to communicate to a given IP address?
I am running on workstations with multiple network interfaces, IPv4 and v6, and I need to get the address of the "correct" interface used for traffic to my given database server.
The simplest way would be:
UdpClient u = new UdpClient(remoteAddress, 1);
IPAddress localAddr = ((IPEndPoint)u.Client.LocalEndPoint).Address;
Now, if you want the NetworkInterface object you do something like:
foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces())
{
IPInterfaceProperties ipProps = nic.GetIPProperties();
// check if localAddr is in ipProps.UnicastAddresses
}
Another option is to use P/Invoke and call GetBestInterface() to get the interface index, then again loop over all the network interfaces. As before, you'll have to dig through GetIPProperties() to get to the IPv4InterfaceProperties.Index property).
Neither of these will actually give the OP the info he's looking for -- he wants to know which interface will be used to reach a given destination. One way of doing what you want would be to shell out to the route command using System.Diagnostics.Process class, then screen-scrape the output. route PRINT (destination IP) will get you something useable. That's probably not the best solution, but it's the only one I can give you right now.
The info you are after will be in WMI.
This example using WMI may get you most of the way:
using System.Management;
string query = "SELECT * FROM Win32_NetworkAdapterConfiguration";
ManagementObjectSearcher moSearch = new ManagementObjectSearcher(query);
ManagementObjectCollection moCollection = moSearch.Get();// Every record in this collection is a network interface
foreach (ManagementObject mo in moCollection)
{
// Do what you need to here....
}
The Win32_NetworkAdapterConfiguration class will give you info about the configuration of your adapters e.g. ip addresses etc.
You can also query the Win32_NetworkAdapter class to find out 'static'about each adapter (max speed, manufacturer etc)
At least you can start with that, giving you all addresses from dns for the local machine.
IPHostEntry hostEntry = Dns.GetHostEntry(Environment.MachineName);
foreach (System.Net.IPAddress address in hostEntry.AddressList)
{
Console.WriteLine(address);
}
Just to give a complete picture: another approach would be to use Socket.IOControl( SIO_ROUTING_INTERFACE_QUERY, ... )
ConferenceXP includes rather comprehensive function wrapping this, works with IPv4/6 and multicast addresses: https://github.com/conferencexp/conferencexp/blob/master/MSR.LST.Net.Rtp/NetworkingBasics/utility.cs#L84

How do i set the dns search suffix for a network adapter in .net?

I've written a command line utility that detects which network interface is connected, and sets the staitc ip address and dns servers for it (by calling netsh). However, I can't seem to figure out how to set the dns search suffixes. netsh doesnt appear capable of doing that. How do I do that otherwise (WMI perhaps)?
I think you have to set the value(s) you want in the DNSDomainSuffixSearchOrder property of the Win32_NetworkAdapterConfiguration WMI object.
Here's and example of setting values in WMI, if you need it:
Modifying Objects & Running Methods
The dns search suffixes are valid for the whole machine, not for a single network adapter. You can also get them from registry:
string searchList = "";
try
{
using (var reg = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(tcpSettingsSubKey))
{
searchList = (reg.GetValue("SearchList") as string);
}
}
catch(Exception ex)
{
// something went wrong
}
(This is not the default dns suffix when the machine is an AD member)

Categories

Resources