Introduction
With this article, I would like to demonstrate how to query 802.11 OIDs from a C# .NET application. Noramlly, we use the DeviceIOControl API for querying an NDIS miniport driver. The DeviceIOControl API sends a control code directly to a specified device driver and the device performs the corresponding operation.
How to use the DeviceIOControl
function from a C# application is ambiguous. Because the developer has to implement the platform invoke mechanism and need to take care of the marshaling of data between managed and unmanaged code.
I have seen a lot of queries in discussion forums about accessing a miniport driver from a C# application. This article demonstrates how to use the DeviceIOControl
function from a C# application. As an example, we will see how to query for the signal strength of a wireless network.
Background
Device Driver programmers are familiar with the NDIS miniport driver. An NDIS miniport driver has two main functions: managing the network card and interfacing with other drivers like a protocol driver. Miniport drivers that support 802.11 interfaces for network interface cards must support all mandatory native IEEE 802.11 OIDs.
Using the code
In this article, I will show you a C# console application which queries the signal strength of the currently connected wireless network. This application uses the platform invoke mechanism and calls the DeviceIOControl
function with control code OID_802_11_RSSI.
A OID_802_11_RSSI OID requests the miniport driver to return the signal strength of the wireless network.
public void printSignalStrength(string wlanInterfaceGuid)
{
int hFileHandle = new int();
IntPtr ptrOutput = IntPtr.Zero;
IntPtr ptrInput = IntPtr.Zero;
try
{
hFileHandle = INVALID_HANDLE_VALUE;
string devName = string.Format("\\\\.\\{0}", wlanInterfaceGuid);
hFileHandle = CreateFile(devName, GENERIC_READ | GENERIC_WRITE,
0,
(IntPtr)0,
OPEN_EXISTING,
0,
NULL);
if (hFileHandle == INVALID_HANDLE_VALUE)
{
Console.WriteLine("Cannot open driver handle");
return;
}
else
{
int IOCTL_NDIS_QUERY = new int();
IOCTL_NDIS_QUERY = IOCTL_NDIS_QUERY_GLOBAL_STATS();
uint oidCodeLength = 4;
uint OutputBufferSize = 1000;
int bytesReturned = new int();
GCHandle handle = GCHandle.Alloc(OID_802_11_RSSI, GCHandleType.Pinned);
ptrInput = handle.AddrOfPinnedObject();
ptrOutput = Marshal.AllocHGlobal((int)OutputBufferSize);
ZeroMemory(ptrOutput, (int)OutputBufferSize);
if (DeviceIoControl(hFileHandle, IOCTL_NDIS_QUERY, ptrInput,
oidCodeLength, ptrOutput, OutputBufferSize, out bytesReturned, 0))
{
Console.WriteLine("DeviceIOControl success");
Int32 rssi = (Int32)Marshal.PtrToStructure(ptrOutput, typeof(Int32));
Console.WriteLine("Received Signal Strength: {0}", rssi);
if (ptrOutput != IntPtr.Zero)
{
Marshal.FreeHGlobal(ptrOutput);
ptrOutput = IntPtr.Zero;
}
handle.Free();
}
else
{
Console.WriteLine("DeviceIOControl failed");
}
}
}
catch (Exception e)
{
Console.WriteLine("Exception: {0}", e.Message);
}
finally
{
if (ptrOutput != IntPtr.Zero)
Marshal.FreeHGlobal(ptrOutput);
CloseHandle(hFileHandle);
}
}
The above code snippet shows how to query a 802.11 OID using the DeviceIOControl
function. Let me explain the details here.
string devName = string.Format("\\\\.\\{0}", wlanInterfaceGuid);
hFileHandle = CreateFile(devName, GENERIC_READ | GENERIC_WRITE,
0,
(IntPtr)0,
OPEN_EXISTING,
0,
NULL);
if (hFileHandle == INVALID_HANDLE_VALUE)
{
Console.WriteLine("Cannot open driver handle");
}
else
{
}
The CreateFile()
function opens a handle to the device. In this case, the device is a wireless network adapter. The first argument accepts the device name in the format “\\\\.\\{A5128180-8811-451C-BBBD-2BE2F69F65C9}”. “{A5128180-8811-451C-BBBD-2BE2F69F65C9}” specifies the GUID for the wireless card. I have hardcoded this card GUID for my wireless card. You can check the system Registry for the list of available cards in your machine. This is available under the following Registry key:
- HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\Current Version\NetworkCards
This key lists down all the available network adapters in the system. The ‘ServiceName’ Registry sub-key specifies the GUID value for that card. Network adapters can be enumerated using IPHelper functions also. For more reference about enumerating network cards, please go through this MSDN link: http://msdn2.microsoft.com/en-us/library/aa365798(VS.85).aspx.
Next, we will see the DeviceIOControl()
calling section.
if (DeviceIoControl(hFileHandle, IOCTL_NDIS_QUERY, ptrInput, oidCodeLength,
ptrOutput, OutputBufferSize, out bytesReturned, 0))
{
Console.WriteLine("DeviceIOControl success");
}
DeviceIOControl()
accepts the following arguments:
- Handle to the network adapter
- IO control code; in our case, it’s
IOCTL_NDIS_QUERY_GLOBAL_STATS
- Pointer to an input buffer that contains 802.11 OID
- Specifies the size of the input buffer used to query 802.11 OID
- Pointer to the output buffer that receives the result
- Specifies the output buffer size
- Pointer to the buffer that contains the actual size of the result
- Specifies ‘0’ for a synchronous query
I am not going to explain more about the GCHandle
and Marshal
classes as that is beyond the scope of this article.
If DeviceIOControl
returns a non-zero value, it indicates that the query is success, and the result can be received in the output buffer.
if (DeviceIoControl(hFileHandle, IOCTL_NDIS_QUERY, ptrInput,
oidCodeLength, ptrOutput, OutputBufferSize, out bytesReturned, 0))
{
Console.WriteLine("DeviceIOControl success");
Int32 rssi = (Int32)Marshal.PtrToStructure(ptrOutput, typeof(Int32));
Console.WriteLine("Received Signal Strength: {0}", rssi);
if (ptrOutput != IntPtr.Zero)
{
Marshal.FreeHGlobal(ptrOutput);
ptrOutput = IntPtr.Zero;
}
handle.Free();
}
Now, the result is available in the output buffer and we have to marshal that data and transfer it into a .NET data type.
Int32 rssi = (Int32)Marshal.PtrToStructure(ptrOutput, typeof(Int32));
Marshal.PtrToStructure()
will do the marshaling of the unmanaged data to managed data, and the user can see the received signal strength value in the console window.
Conclusion
This article demonstrates how to query 802.11 OIDs using the DeviceIOControl()
function from a C# application. As a sample, we have seen querying the signal strength of the currently connected wireless network. In my next article, I will show you how to access the same 802.11 OID using the Windows Management Instrumentation (WMI) mechanism.
References
- How to query miniport driver information (802.11 OIDs) using Windows Management Instrumentation (WMI) mechanism
- How to access wireless network parameters using native WiFi API