Digging Deeper into the Network Information
The last field in the
WSAQUERYSET structure is a pointer to some binary data. This binary data contains more information about the network connection and is not of a fixed size. It can contain a variable amount of information depending on what features of the network are currently supported. This is why, in the first example, I simply allocated a large block of memory. This is obviously not the best thing to do here.
Looking in the
MSWSock.h file, you can discover that the
NLA_BLOB structure is represented as a structure with nested structures and a union of other structures.
To make your life easier, I have represented the
NLA_BLOB structure as a number of C# classes and structures in
Listing 4. You can see that in order to represent the union, I have used the
LayoutKind.Explicit and the
FieldOffset attributes to indicate to the CLR exactly how I expect the memory to be laid out.
To extract the data from the pointer field in the
WSAQUERYSET, it will be necessary to pin the memory so that it can be mapped between the managed and unmanaged code. In C#, this means that you need to mark your code as unsafe. This needs to be done around the code blocks and for the whole project from the build configuration in the project properties.
To retrieve the network information, you need to start by marshalling the last field (a pointer) in the
WSAQUERYFIELD to a
BLOB object, as defined in
Listing 4.
protected BLOB GetBlob(WSAQUERYSET qsResult)
{
BLOB blob = new BLOB();
Marshal.PtrToStructure(qsResult.lpBlob, blob);
return blob;
}
You can then use the pInfo field in the BLOB class to marshal the data into an instance of an NLA_Info class and extract the data, which is shown in the two methods in
Listing 5.
You can use the WSANSPIoctl function to block the current thread until a change in the currently connected networks has transpired.
|
|
Remember that the
NLA_Info object contains a
nextOffset field within the
NLA_Header structure. This
nextOffset field indicates the byte offset to the next
NLA_Info object in memory. In order to read out all of the network information for a specific connection, you will need to repeat the process for each object, as shown here.
BLOB blob = GetBlob(qsResult);
byte* pInfo = (byte*)blob.pInfo;
NLA_Info info;
do
{
info = SetNLAInfo(new IntPtr(pInfo));
pInfo += info.header.nextOffset;
} while (0 != info.header.nextOffset);
The first method you examined (
Listing 2), allocated a large block of memory to read in the network information. This is obviously not a good practice. You can refine that now by making two calls to the
WSALookupServiceNext function. In the first call, pass in an empty buffer and a null pointer to the
WSAQUERYSET parameter. Doing this causes the
WSALookupServiceNext function to return the size of the buffer required for the network information. You can then use this to allocate the correct amount of memory and begin extracting the details of the network connection, which is done in
Listing 6.
If you have put all of this together, you should now be retrieving some information about the network connections currently available to your application. There is one more step you can take that provides you with a fuller set of information. The first call to
WSALookupServiceBegin uses the second parameter (
dwControlFlags) to determine what information to return. The code thus far has used the
LUP_RETURN_ALL flag that returns all the known information about the currently connected networks. You can combine this with the
LUP_DEEP flag to force the network connections to be queried further. Be aware that you should only use this flag when calling
WSALookupServiceBegin and not on subsequent calls to
WSALookupServiceNext. Using the
LUP_DEEP flag creates network traffic and forces the current set of networks to be rebuiltand doing this on a call to
WSALookupServiceNext causes a problem as the set of networks you are iterating through will have changed. The code snippet below demonstrates using the flags for the
WSALookupServiceBegin and then changing the flags back before iterating through the collection of networks.
dwControlFlags = 0x0FF1;//LUP_RETURN_ALL|LUP_DEEP
int nResult = WSALookupServiceBegin(
qsRestrictions, dwControlFlags, ref valHandle);
if (0 !=nResult)
{
CheckResult(nResult);
}
dwControlFlags = 0x0FF0;
while (0 == nResult)
{ ...
In this article, you have seen how to import the unmanaged NLA functions from the Windows Sockets Library. You have also explored how to use the various unmanaged structures that are required to use those functions and how to become aware of changes to the network connections. Finally, you should now be able to retrieve extra information about the connected networks by marshalling the unmanaged structures into managed classes and structures.
Together, these capabilities let you build applications that are aware of the current connections and the features of those connections. This should enable you to create more valuable software that provides a better user experience.