I am currently working on a C#-UWP app that needs to be able to discovery bluetooth devices (Not BLE) on the network and ones that have been previously connected to/paired.
Im sure anyone who is new to this task will have quickly found the documentation and example are of little help. I have learned more from Stackoverflow questions about peoples experimentations than from the docs and examples, but anyways.
My main question/problem is this: After setting up the device watcher to find bluetooth devices I found that I consistently get multiple additions of the same device but having a different bluetooth address (this is a device that was previously paired but not live on the network). After much investigate and brainstorming, we discovered that each device id is actually a pairing of the devices MAC address and the BT receivers MAC address.
The reason I was getting 3 device additions per 1 physical device is because I have connected to that same device with 3 different BT receiver dongles in the past. So my question is, is there anyway to make the device watcher return the device that corresponds to the currently active BT receiver dongle?
Otherwise I will need to find the currently active BT receivers MAC address and filter out the devices that do not have this, because otherwise the user will see 3 identical devices to select and only 1 of them will pass while the other 2 will fail.
While on this subject I would also like to mention that the device properties dont seem to be working. Before creating the watcher, I have a list of properties like this for example:
requestedProperties.Add("System.Devices.Aep.DeviceAddress");
requestedProperties.Add("System.Devices.Aep.IsConnected");
requestedProperties.Add("System.Devices.Aep.Bluetooth.Le.IsConnectable");
requestedProperties.Add("System.Devices.Aep.IsPresent");
requestedProperties.Add("System.Devices.Aep.ContainerId");
requestedProperties.Add("System.Devices.Aep.ModelId");
requestedProperties.Add("System.Devices.Aep.Manufacturer");
requestedProperties.Add("System.Devices.Aep.ProtocolId");
requestedProperties.Add("System.Devices.Aep.SignalStrength");
But when I debug the device that is added, it doesnt have any properties:
debug info showing no properties for added device
I would be useful to have this information.
Thank you for any input or suggestions.
Update
I found a quick solution that overcomes this problem (although i did find a lot more problems with the device watcher but that is probably a topic for another question).
For now I simply get the current BT adaptors MAC address and then check each incoming device if it has this MAC address in its pair. If it does not that means the device is paired with an old/unused BT adaptor.
/* NOTE:
Windows allows only 1 BT adapter to be active on 1 machine at a time
Therefore this function will either return the active dongle or a null if
there is no BT adapter on the machine or its disabled.
*/
BluetoothAdapter BTAdaptor = await BluetoothAdapter.GetDefaultAsync();
if (BTAdaptor == null)
{
// Log error
// return
}
//
// Code block to check if the BT adaptor can support the BT tech stack you are interested in.
//
// Format into hex with 12 characters (12 being the number of characters for MAC address)
string tempMac = BTAdaptor.BluetoothAddress.ToString("X12").ToLower();
// Pattern for MAC address.
string pattern = "(.{2})(.{2})(.{2})(.{2})(.{2})(.{2})";
string replace = "$1:$2:$3:$4:$5:$6";
m_strBTAdapterMAC = Regex.Replace(tempMac, pattern, replace);
Then when the device is added/updated/removed by the watcher event, check it:
// If device is not paired with the currently used BT adapter.
if (deviceInfo.Id.Contains(m_strBTAdapterMAC) == false)
{
// Device paired with old dongle, dont want to show the user.
return;
}
If anyone ever figures out how to make the device watcher just not give old devices, please let me know, its probably a better solution.
Related
I need to read a machine's MAC id in C# which stays fixed irrespective of the connection types e.g. connected to Work through network cable, wifi, VPN through Home wifi, through a Dongle or even can be offline.
So basically it need not necessarily be the MAC id of network interface which is "Up", i just need the MAC id which is/stays constant.
With the help of answers in the below link and other suggestions,
Reliable method to get machine's MAC address in C#
I am able to read the MAC ids, but still don't understand which one to consider that will reliably give me the fixed MAC id, which i can use in my application for some sort of verification in that particular system.
Here are the details of all network interfaces of that system, when the system is connected to "Work" through VPN using Home Wifi and connected to "Work" network directly:
Please suggest which one i should consider and what should be the right condition to filter out the interface with fixed MAC id in C#.
As indicated as "Related" to my question and also as pointed by #Caius Jard, i got the solution from the below link which will suit for my need:
How to determine MAC Address of the actual physical network card -- not virtual network interfaces created by VPN's (.NET C#)
because i want to:
-> take the MAC id of the physical card (i.e. excluding all virtual, pseudo, logical, usb) and don't allow my application without any physical card
-> consider the 1st card, in case more than 1 cards are installed
I have modified the query as per my need with the help of details given in below link:
https://weblogs.sqlteam.com/mladenp/2010/11/04/find-only-physical-network-adapters-with-wmi-win32_networkadapter-class/
So, my final solution looks like below:
ManagementObjectSearcher searcher = new ManagementObjectSearcher
(#"SELECT Index,MACAddress,PNPDeviceID
FROM Win32_NetworkAdapter
WHERE MACAddress IS NOT NULL AND PNPDeviceID IS NOT NULL AND Manufacturer != 'Microsoft' AND PNPDeviceID LIKE '%PCI\\%'");
IList<ManagementObject> mObject = searcher.Get()
.Cast<ManagementObject>()
.OrderBy(p => Convert.ToUInt32(p.Properties["Index"].Value))
.ToList();
foreach (var obj in mObject)
{
var mac = obj["MACAddress"].ToString();
mac = mac.Replace(":", string.Empty);
return mac;
}
Your physical (vs. virtual, logical) interface MAC addresses should stay same (at least until the physical device is replaced). Your "Ethernet 3", for example.
I'm making a winform application that monitors things happening on the computer like processes, wifi, screenshots,... and I'm working on Bluetooth connection.
I need to get the notification when a Bluetooth device connects or disconnects to the computer (or laptop), for example mobile phones. In addition, I need something to identify the device like ID or just the name of it if possible.
One thing to notice, I'm using C# and it's a winform app, it would be nice if the solution uses Windows native library (like winapi, pInvoke). Third party library is fine but it would be my last choice.
Update: I've followed Rita Han's answer and got some results.
Below is my code inside the override WndProc method. It works totally fine with USB but not with Bluetooth devices.
While the app is running, I turn on Bluetooth on both my laptop and mobile phone but they have not paired yet → the function gets called and it says Bluetooth came, then I connect the two devices → now they are connected but no notification except the one above.
After that, while they are connected, I try to remove the mobile phone device, they are disconnected and I connect them back again and one more time I get no notification about a new Bluetooth connection has been scanned or connected.
So in short, I get notified when Bluetooth radio scans for a new device but not when connecting in the first time. After that, disconnecting and reconnecting do not give me any notifications also.
Am I missing some events?
case USB.DBT_DEVICEARRIVAL:
devType = Marshal.ReadInt32(m.LParam, 4);
if (devType == USB.DBT_DEVTYP_VOLUME)
{
USB.DEV_BROADCAST_VOLUME vol;
vol = (USB.DEV_BROADCAST_VOLUME)
Marshal.PtrToStructure(m.LParam, typeof(USB.DEV_BROADCAST_VOLUME));
// Get the drive letter
c = USB.DriveMaskToLetter(vol.dbcv_unitmask);
listBox1.Items.Add("New USB has come with name " + c);
}
else if (devType == BluetoothDeviceNotification.DbtDevtypDeviceinterface)
{
BluetoothDeviceNotification.DevBroadcastDeviceinterface vol;
vol = (BluetoothDeviceNotification.DevBroadcastDeviceinterface)Marshal.PtrToStructure(m.LParam, typeof(BluetoothDeviceNotification.DevBroadcastDeviceinterface));
listBox1.Items.Add("Bluetooth came GUID " + vol.ClassGuid + "\t Name: " + vol.Name);
break;
}
break;
For using Windows native API you can use RegisterDeviceNotification function.
Refer to "Registering for Device Notification" sample for getting started.
Bluetooth interface GUID:
GUID BluetoothGUID = {0x0850302A, 0xB344, 0x4fda, 0x9BE9, 0x90, 0x57, 0x6B, 0x8D, 0x46, 0xF0 };
Bluetooth and WM_DEVICECHANGE Messages.
I am trying to use a HID (DualShock 4) connected to my R-PI 3 running Windows 10 IoT in a C# universal app. I was using the Device Enumeration sample as a guideline and managed to display the IDs of all connected devices. I could see that the connected DualShock was correctly enumerated because it logged the \\?\HID#VID_054C&PID_05C4....
So naturally the next thing I wanted to do was opening the device for communication. Since it is an HID I was using the HidDevice class with var device = await HidDevice.FromIdAsync(args.Id, FileAccessMode.ReadWrite);. Sadly the returned device is always null, nothing on the screen is prompted or so.
Next I assumed there at least has to be some kind of USB interface accessible so I went for var device = await UsbDevice.FromIdAsync(args.Id);
But now I am getting A device attached to the system is not functioning. (Exception from HRESULT: 0x8007001F) or sometimes also The process cannot access the file because it is being used by another process.
How can I talk to my device? :(
I'm trying to create program that connects to certain wifi network when it's in range, even if already connected to another wifi.
I'm using SimpleWifi, and basically it works great. Except that it does not see new wifi networks before I clicked wifi icon in Windows 10 taskbar to show list of networks.
How can I force c# program to update wifi network list?
Currently using IEnumerable<AccessPoint> accessPoints = wifi.GetAccessPoints().OrderByDescending(ap => ap.SignalStrength); to update wifi networks, but as I said, it does not see new networks before refreshed manually from Windows.
It's almost 3 years ago but here is my take on this issue anyways.
In that library you can call:
SimpleWifi.Wifi.Disconnect()
Which I do before re-connect and get the list of access points again. This works sort of, new networks introduced after windows discovery does actually show up, but are way slower than if you click "wifi" button in Windows, which will bring up newly discovered networks right away.
If someone knows a solution to "trigger" Windows/Managed wifi connections to update it's list, just like you do in Windows, I would appreciate that
SimpleWifi, like other Wifi libraries have this feature built-in. And its required as Windows does not always show all the wifi networks available correctly, unless queried..
the sample code can be found here: https://pastebin.com/1iCp41SP
, not the most elegant code , but worked in a WPF project.
This part of the code scans/refreshes the Wifi List in SimpleWifi
var testClient = new WlanClient();
foreach (WlanInterface wlanIface in testClient.Interfaces)
{
wlanIface.Scan();
}
TL;DR version of this question: WMI Win32_NetworkAdapter class contains information I need, but is too slow. What's a faster method of getting information for the MACAddress, ConfigManagerErrorCode, and PNPDeviceID columns on Windows?
I need to retrieve information for attached network adapters so I can get a MAC address to uniquely identify the local Microsoft Windows computer. The WMI Win32_NetworkAdapter class seems to have the information I'm looking for. The MACAddress, ConfigManagerErrorCode, and PNPDeviceID columns are the only ones I really need:
MACAddress: the MAC address (goal of this operation)
ConfigManagerErrorCode: allows me to determine if the adapter is enabled and running or not. (If it's disabled then I should use a MAC address previously cached by my app, if available).
PNPDeviceID: By checking for a prefix of "PCI" (and possibly other interfaces, if necessary) I can filter out non-physical adapters, of which there are several on my Windows 7 box (including virtual adapters, like for VMware / VirtualBox).
My plan was to filter out non-physical devices using PNPDeviceID. Then I would use the MACAddress column on any remaining table entries (saving the address to a cache). When the device is disabled (as possibly indicated by a non-zero ConfigManagerErrorCode) and the MACAddress is null, I can use a previously-seen MACAddress for that device from my cache.
You can see the contents of this table on my Windows 7 computer. You can see there's tons of junk in there, but only one entry with a "PCI" PNPDeviceID.
wmic:root\cli>NIC GET Caption, ConfigManagerErrorCode, MACAddress, PNPDeviceID
Caption ConfigManagerErrorCode MACAddress PNPDeviceID
[00000000] WAN Miniport (SSTP) 0 ROOT\MS_SSTPMINIPORT\0000
[00000001] WAN Miniport (IKEv2) 0 ROOT\MS_AGILEVPNMINIPORT\0000
[00000002] WAN Miniport (L2TP) 0 ROOT\MS_L2TPMINIPORT\0000
[00000003] WAN Miniport (PPTP) 0 ROOT\MS_PPTPMINIPORT\0000
[00000004] WAN Miniport (PPPOE) 0 ROOT\MS_PPPOEMINIPORT\0000
[00000005] WAN Miniport (IPv6) 0 ROOT\MS_NDISWANIPV6\0000
[00000006] WAN Miniport (Network Monitor) 0 ROOT\MS_NDISWANBH\0000
[00000007] Intel(R) 82567LM-2 Gigabit Network Connection 0 00:1C:C0:B0:C4:89 PCI\VEN_8086&DEV_10CC&SUBSYS_00008086&REV_00\3&33FD14CA&0&C8
[00000008] WAN Miniport (IP) 0 ROOT\MS_NDISWANIP\0000
[00000009] Microsoft ISATAP Adapter 0 ROOT\*ISATAP\0000
[00000010] RAS Async Adapter 0 20:41:53:59:4E:FF SW\{EEAB7790-C514-11D1-B42B-00805FC1270E}\ASYNCMAC
[00000011] Microsoft Teredo Tunneling Adapter 0 ROOT\*TEREDO\0000
[00000012] VirtualBox Bridged Networking Driver Miniport 0 00:1C:C0:B0:C4:89 ROOT\SUN_VBOXNETFLTMP\0000
[00000013] VirtualBox Host-Only Ethernet Adapter 0 08:00:27:00:C4:A1 ROOT\NET\0000
[00000014] Microsoft ISATAP Adapter 0 ROOT\*ISATAP\0001
[00000015] VMware Virtual Ethernet Adapter for VMnet1 0 00:50:56:C0:00:01 ROOT\VMWARE\0000
[00000016] Microsoft ISATAP Adapter 0 ROOT\*ISATAP\0002
[00000017] VMware Virtual Ethernet Adapter for VMnet8 0 00:50:56:C0:00:08 ROOT\VMWARE\0001
[00000018] Microsoft ISATAP Adapter 0 ROOT\*ISATAP\0003
(If I disable my physical adapter, then the MACAddress column goes to null, and ConfigManagerErrorCode changes to non-zero).
Unfortunately, this class is simply too slow. Any query on Win32_NetworkAdapter consistently takes 0.3 seconds on my relatively modern Windows 7 Core i7-based computer. So using this will add yet another 0.3 seconds to application startup (or worse), which I find unacceptable. This is especially because I can't think of a single valid reason why it should take so long to figure out what MAC addresses and plug-and-play device IDs are on the local computer.
Searching for other methods to get a MAC address yielded the GetAdaptersInfo and the newer GetAdaptersAddresses functions. They don't have the 0.3 second penalty that WMI imposes. These functions are the ones used by the .NET Framework's NetworkInterface class (as determined by examining .NET source code), and the "ipconfig" command line tool (as determined by using Dependency Walker).
I made a simple example in C# that lists all network adapters using the NetworkInterface class. Unfortunately, using these APIs seems to have two shortcomings:
These APIs don't even list disabled network adapters to begin with. That means I can't look up the MAC address for a disabled adapter from my cache.
I don't see how I can get the PNPDeviceID to filter out non-physical adapters.
My question is: what method can I use to get the MAC address of the local computer's physical adapters (whether it is enabled or not) in a few tens of milliseconds at most?
(I'm experienced at both C# and C++ and am fine with reading other languages, so I really don't care what language might be used in answers).
EDIT: In response to Alex K's suggestion for using return immediate and forward only, and also to provide some sample WMI code for what I am doing - here is some C# code that lists the columns of interest:
public static void NetTest() {
System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();
EnumerationOptions opt = new EnumerationOptions();
// WMI flag suggestions from Alex K:
opt.ReturnImmediately = true;
opt.Rewindable = false;
ManagementObjectSearcher searcher = new ManagementObjectSearcher("root\\cimv2", "select MACAddress, PNPDeviceID, ConfigManagerErrorCode from Win32_NetworkAdapter", opt);
foreach (ManagementObject obj in searcher.Get()) {
Console.WriteLine("=========================================");
foreach (PropertyData pd in obj.Properties) {
Console.WriteLine("{0} = {1}", pd.Name, pd.Value);
}
}
Console.WriteLine(sw.Elapsed.TotalSeconds);
}
I called this function 3 times and each time got about 0.36 seconds printed on the last line. So the suggested flags don't seem to have any effect: positive or negative. This is not too surprising, as the answer at How to make forward-only, read-only WMI queries in C#? seems to indicate that no change in performance will be observed unless there are a large number of records (e.g. hundreds to thousands), which is not the case with Win32_NetworkAdapter table.
EDIT 2: Multiple answers have been proposed to use SendARP from the IP helper API (this is the same API that has the GetAdaptersInfo function). What advantages does this give over GetAdaptersInfo for finding the local MAC address? I cannot think of any - on the surface, GetAdaptersInfo seems to return a more thorough set of information than SendARP does for local adapters. Now that I think about it, I think that a big part of my question centers over the concept of enumeration: what adapters exist on the computer in the first place? SendARP does not perform enumeration: it assumes you already know the IP address of the adapter you want a MAC for. I need to figure out what adapters exist on the system. Some issues this raises:
What happens if the network cable is unplugged? This would be very common on a laptop, for example (unplugged Ethernet, disconnected WiFi card). I tried using NetworkInterface.GetAllNetworkInterfaces() and listing all the unicast addresses using GetIPProperties().UnicastAddresses when the media is unplugged. No addresses are listed by Windows, so I can't think of any address that could be passed to SendARP. Intuitively, it makes sense that an unplugged adapter would still have a physical address, but no IP address (since it is not on a network with a DHCP server).
Which brings me to: how do I get a list of local IP addresses to test with SendARP?
How do I get the PNPDeviceID (or similar ID that can be used to filter non-physical adapters) for each adapter?
How do I list disabled adapters, so I can look up a MAC address from my cache (i.e. the MAC address I found when it was last enabled)?
These issues don't seem to be addressed by SendARP, and are the major reason I asked this question (otherwise I'd be using GetAdaptersInfo and moving on with things...).
I wound up cutting WMI completely out of the equation and made a significant improvement while still getting the information I wanted. As noted with WMI, it was taking > 0.30 seconds to get results. With my version, I can get the same information in about 0.01 seconds.
I used the setup API, configuration manager API, and then made OID requests directly on the NDIS network driver to get the MAC address. The setup API seems obnoxiously slow, especially when getting things like property values. It's imperative to keep setup API calls at a minimum. (You can actually see just how bad it is by looking at how long it takes to load the "Details" tab of a device in Device manager).
A guess on why WMI was so slow: I noted that WMI's Win32_NetworkAdapter always took the same amount of time no matter which subset of properties I queried. Seems like the WMI Win32_NetworkAdapter class programmers were lazy and didn't optimize their class to only gather requested information like other WMI classes do. They probably gather all information, whether requested or not. They probably significantly rely on Setup API to do this, and the excessive calls to the slow Setup API to get unwanted information is what makes it so slow.
High level overview of what I did:
Use SetupDiGetClassDevs to get all network devices that are present on the system.
I filter out all results that don't have an enumerator of "PCI" (use SetupDiGetDeviceRegistryProperty with SPDRP_ENUMERATOR_NAME to get the enumerator).
For the remainder, I can use CM_Get_DevNode_Status to get the device status and error code. All devices with removable device status codes are filtered out.
If DN_HAS_PROBLEM is set such that there is a non-zero error code then the device is probably disabled (or has some other problem). The driver isn't loaded so we can't make a request to the driver. Therefore in this case I load the MAC address for the network card from a cache I maintain.
A parent device could be removable so I filter those out too by recursively examining the device tree using CM_Get_Parent and CM_Get_DevNode_Status to look for parent removable devices.
Any remaining devices are nonremovable network cards on the PCI bus.
For each network device, I use SetupDiGetClassDevs with GUID_NDIS_LAN_CLASS GUID and DIGCF_DEVICEINTERFACE flag to get its interfaces (this only works if the device is enabled / does not have a problem).
Use IOCTL_NDIS_QUERY_GLOBAL_STATS with OID_802_3_PERMANENT_ADDRESS on the driver's interface to get the permanent MAC address. Save it in the cache, and then return.
The result is a robust indication of MAC addresses on the PC that should be immune to "fake" network cards made by VMware, VirtualBox, largely immune to network cards that are temporarily disabled, and immune to transient network cards attached via USB, ExpressCard, PC Card, or any future removable interface.
EDIT: IOCTL_NDIS_QUERY_GLOBAL_STATS isn't supported by all network cards. The vast majority work, but some Intel cards do not. See How to reliably and quickly get the MAC address of a network card given its device instance ID
You should be able to get everything you need from the System.Net namespace. For example, the following sample is lifted from MSDN and does what you asked for in the original version of the question. It displays the physical addresses of all interfaces on the local computer.
public static void ShowNetworkInterfaces()
{
IPGlobalProperties computerProperties = IPGlobalProperties.GetIPGlobalProperties();
NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
Console.WriteLine("Interface information for {0}.{1} ",
computerProperties.HostName, computerProperties.DomainName);
if (nics == null || nics.Length < 1)
{
Console.WriteLine(" No network interfaces found.");
return;
}
Console.WriteLine(" Number of interfaces .................... : {0}", nics.Length);
foreach (NetworkInterface adapter in nics)
{
IPInterfaceProperties properties = adapter.GetIPProperties(); // .GetIPInterfaceProperties();
Console.WriteLine();
Console.WriteLine(adapter.Description);
Console.WriteLine(String.Empty.PadLeft(adapter.Description.Length,'='));
Console.WriteLine(" Interface type .......................... : {0}", adapter.NetworkInterfaceType);
Console.Write(" Physical address ........................ : ");
PhysicalAddress address = adapter.GetPhysicalAddress();
byte[] bytes = address.GetAddressBytes();
for(int i = 0; i< bytes.Length; i++)
{
// Display the physical address in hexadecimal.
Console.Write("{0}", bytes[i].ToString("X2"));
// Insert a hyphen after each byte, unless we are at the end of the
// address.
if (i != bytes.Length -1)
{
Console.Write("-");
}
}
Console.WriteLine();
}
}