Use Native WLAN API in managed code

Has anyone here used the new Native WLAN API from a C# .NET application?
I'm trying to p/invoke the functions, but have limited success; when calling the WlanEnumInterfaces function it won't return me a properWLAN_INTERFACE_INFO_LIST.

[DllImport("wlanapi.dll", SetLastError =true)]
private
staticexternint WlanOpenHandle(
[
In]UInt32 dwClientVersion,
[In,Out]IntPtr pReserved,
[Out]outUInt32 pdwNegotiatedVersion,
[
Out]outIntPtr phClientHandle);

[DllImport("wlanapi.dll", SetLastError =true)]
privatestaticexternint WlanEnumInterfaces(
[
In]IntPtr hClientHandle,
[
In,Out]IntPtr pReserved,
[Out]outWLAN_INTERFACE_INFO_LIST ppInterfaceList);

publicstructWLAN_INTERFACE_INFO_LIST
{
publicint dwNumberOfItems;
publicint dwIndex;
publicWLAN_INTERFACE_INFO[] InterfaceInfo;
}

publicstructWLAN_INTERFACE_INFO
{
publicGuid InterfaceGuid;
publicstring strInterfaceDescription;
publicWLAN_INTERFACE_STATE isState;
}

publicenumWLAN_INTERFACE_STATE
{
wlan_interface_state_not_ready,
wlan_interface_state_connected,
wlan_interface_state_ad_hoc_network_formed,
wlan_interface_state_disconnecting,
wlan_interface_state_disconnected,
wlan_interface_state_associating,
wlan_interface_state_discovering,
wlan_interface_state_authenticating
}

uint ver = 0;

ret = WlanOpenHandle(1,IntPtr.Zero,out ver,outthis.clientHandle);

WLAN_INTERFACE_INFO_LIST wIIL;

ret = WlanEnumInterfaces(this.clientHandle,IntPtr.Zero,out wIIL);

[3556 byte] By [TomvE] at [2008-1-4]
# 1
I figured out what I was doing wrong; I probably misread the documentation, because WlanEnumInterfaces returns a pointer to the WLAN_INTERFACE_INFO_LIST data, not an actual object.
So I need to use the pointer to retrieve the data using the Marshal functions available in .NET.
TomvE at 2007-10-11 > top of Msdn Tech,Software Development for Windows Vista,General Windows Vista Development Issues...
# 2

I'm afraid I have to reopen my own topic again. Has anyone implemented these functions in managed code (C#)? I'm having trouble finding the right types needed to p/invoke the API functions in the right way. This is what I have so far:

[DllImport("wlanapi.dll", SetLastError = true)]
privatestaticexternint WlanOpenHandle(
[
In] UInt32 dwClientVersion,
[In,Out] IntPtr pReserved,
[Out] outUInt32 pdwNegotiatedVersion,
[
Out] outIntPtr phClientHandle);

[DllImport("wlanapi.dll", SetLastError = true)]
privatestaticexternint WlanCloseHandle(
[
In] IntPtr hClientHandle,
[
In,Out] IntPtr pReserved);

[DllImport("wlanapi.dll", SetLastError = true)]
privatestaticexternint WlanEnumInterfaces(
[
In] IntPtr hClientHandle,
[
In,Out] IntPtr pReserved,
[Out] outIntPtr ppInterfaceList);

[DllImport("wlanapi.dll", SetLastError = true)]
privatestaticexternint WlanQueryInterface(
[
In] IntPtr hClientHandle,
[
In] IntPtr pInterfaceGuid,
[
In] WLAN_INTF_OPCODE opCode,
[
In,Out] IntPtr pReserved,
[Out] outUInt32 pdwDataSize,
[
Out] outIntPtr ppData,
[
Out] outWLAN_OPCODE_VALUE_TYPE pWlanOpcodeValueType);

[DllImport("wlanapi.dll", SetLastError = true)]
privatestaticexternint WlanSetInterface(
[
In] IntPtr hClientHandle,
[
In] IntPtr pInterfaceGuid,
[
In] WLAN_INTF_OPCODE opCode,
[
In] uint dwDataSize,
[
In] IntPtr pData,
[
In,Out] IntPtr pReserved);

[DllImport("wlanapi.dll", SetLastError = true)]
privatestaticexternint WlanScan(
[
In] IntPtr hClientHandle,
[
In] IntPtr pInterfaceGuid,
[
In] IntPtr pDot11Ssid,
[In] IntPtr pIeData,
[In,Out] IntPtr pReserved);

[DllImport("wlanapi.dll", SetLastError = true)]
privatestaticexternint WlanGetAvailableNetworkList(
[
In] IntPtr hClientHandle,
[
In] IntPtr pInterfaceGuid,
[
In] uint dwFlags,
[
In,Out] IntPtr pReserved,
[Out] outIntPtr ppAvailableNetworkList);

privatestaticuint WLAN_AVAILABLE_NETWORK_INCLUDE_ALL_ADHOC_PROFILES = 0x00000001;
privatestaticuint WLAN_AVAILABLE_NETWORK_INCLUDE_ALL_MANUAL_HIDDEN_PROFILES = 0x00000002;

[DllImport("wlanapi.dll", SetLastError = true)]
privatestaticexternint WlanSetProfile(
[
In] IntPtr hClientHandle,
[
In] IntPtr pInterfaceGuid,
[
In] uint dwFlags,
[In] string strProfileXml,
[
In] string strAllUserProfileSecurity,
[In] bool bOverwrite,
[
In,Out] IntPtr pReserved,
[Out] outuint pdwReasonCode);

[DllImport("wlanapi.dll", SetLastError = true)]
privatestaticexternint WlanGetProfile(
[
In] IntPtr hClientHandle,
[
In] IntPtr pInterfaceGuid,
[
In] string strProfileName,
[
In] IntPtr pReserved,
[Out] outIntPtr pstrProfileXml,
[
Out] outIntPtr pdwFlags,
[
Out] outIntPtr pdwGrantedAccess);

TomvE at 2007-10-11 > top of Msdn Tech,Software Development for Windows Vista,General Windows Vista Development Issues...
# 3

I got everything working except for the WlanSetProfile function. This is the code:

// set new profile
StringWriter sw = newStringWriter();
XmlWriter xw = XmlWriter.Create(sw);
xw.WriteStartDocument();
xw.WriteStartElement(
"WLANProfile",http://www.microsoft.com/networking/WLAN/profile/v1);
xw.WriteElementString(
"name", "MyNetwork");
xw.WriteStartElement(
"SSIDConfig");
xw.WriteStartElement(
"SSID");
xw.WriteElementString("name", "MyNetworkSSID");
xw.WriteEndElement();
xw.WriteEndElement();
xw.WriteElementString(
"connectionType", "ESS");
xw.WriteStartElement(
"MSM");
xw.WriteStartElement(
"security");
xw.WriteStartElement(
"authEncryption");
xw.WriteElementString(
"authentication", "open");
xw.WriteElementString(
"encryption", "WEP");
xw.WriteEndElement();
xw.WriteStartElement(
"sharedKey");
xw.WriteElementString(
"keyType", "networkKey");
xw.WriteElementString(
"protected", "false");
xw.WriteElementString(
"keyMaterial", "MyNetworkKey");
xw.WriteEndElement();
xw.WriteElementString(
"keyIndex", "0");
xw.WriteEndElement();
xw.WriteEndElement();
xw.WriteEndElement();
xw.WriteEndDocument();
xw.Close();

IntPtr profPtr = Marshal.StringToHGlobalUni(sw.ToString());
IntPtr reasonCode = IntPtr.Zero;
ret = WlanSetProfile(
this.clientHandle, this.interfacePtr, 0, profPtr, IntPtr.Zero, true, IntPtr.Zero, out reasonCode);


The problem is that the return value always is 2 (ERROR_FILE_NOT_FOUND), but I have absolutely no clue why it is happening.
To be complete, this is the WlanSetProfile function specified:

[
DllImport("wlanapi.dll", SetLastError = true
)]
privatestaticexternint WlanSetProfile(
[
In] IntPtr hClientHandle,
[
In] IntPtr pInterfaceGuid,
[
In] uint dwFlags,
[In] IntPtr strProfileXml,
[
In] IntPtr strAllUserProfileSecurity,
[In] bool bOverwrite,
[
In] IntPtr pReserved,
[Out] outIntPtr pdwReasonCode);

Does anyone have an idea where I'm going wrong?

TomvE at 2007-10-11 > top of Msdn Tech,Software Development for Windows Vista,General Windows Vista Development Issues...
# 4

Unfortunately I don't have a clue, but another problem ;-)

I'm also trying to access the native wifi api from C#. But I'm already failing at the WlanEnumInterfaces function. To be exact, the function returns ok, but I have trouble converting the pointer to the WLAN_INTERFACE_INFO_LIST structure, just like you had before... When I do something like this:

WLAN_INTERFACE_INFO_LIST interfaceList = new WLAN_INTERFACE_INFO_LIST();
IntPtr
interfaceListPtr;
WlanEnumInterfaces(handle,
IntPtr.Zero, out interfaceListPtr); // returns 0 = OK
interfaceList = (WLAN_INTERFACE_INFO_LIST) Marshal.PtrToStructure(interfaceListPtr, typeof
(WLAN_INTERFACE_INFO_LIST));

I always get an AccessViolationException. I also tried with Marshal.Alloc...ating some memory for the pointer, still the same. Here's my C# definition of the structs:

[StructLayout(LayoutKind.Sequential)]
publicstructWLAN_INTERFACE_INFO
{
publicGuid InterfaceGuid;
[
MarshalAs(UnmanagedType.LPWStr, SizeConst=256)]
publicstring strInterfaceDescription;
publicWLAN_INTERFACE_STATE isState;
}

[StructLayout(LayoutKind.Sequential)]
publicstructWLAN_INTERFACE_INFO_LIST
{
publicint dwNumberOfItems;
publicint dwIndex;
// maybe I need some MarshalAs description here?
publicWLAN_INTERFACE_INFO[] InterfaceInfo;
}

Funny thing is, if I change the last field from WLAN_INTERFACE_INFO[] to WLAN_INTERFACE_INFO, it works. As most computers only have one WLAN adapter anyway, this could be used as a workaround, but I really would like to get it right. So it seems there is some problem with the marshalling of the array, probably because the size is unknown. You mentioned you got it all working except for some other error, maybe you could post the code that works for you (the structs, the dllimports, how you create the struct from the pointer)?

Thanks in advance,
Patrick

bl.p at 2007-10-11 > top of Msdn Tech,Software Development for Windows Vista,General Windows Vista Development Issues...
# 5

Patrick, I created a constructor in the WLAN_INTERFACE_INFO_LIST struct which reads out all the variables manually:

public WLAN_INTERFACE_INFO_LIST(IntPtr pList)
{
dwNumberOfItems = Marshal.ReadInt32(pList, 0);
dwIndex = Marshal.ReadInt32(pList, 4);
InterfaceInfo = new WLAN_INTERFACE_INFO[dwNumberOfItems];

for (int i = 0; i < dwNumberOfItems; i++)
{
IntPtr pItemList = new IntPtr(pList.ToInt32() + (i * 284));
WLAN_INTERFACE_INFO wii = new WLAN_INTERFACE_INFO();

byte[] intGuid = new byte[16];
for (int j = 0; j < 16; j++)
{
intGuid[j] = Marshal.ReadByte(pItemList, 8 + j);
}
wii.InterfaceGuid = new Guid(intGuid);
wii.InterfacePtr = new IntPtr(pItemList.ToInt32() + 8);
wii.strInterfaceDescription =
Marshal.PtrToStringUni(new IntPtr(pItemList.ToInt32() + 24), 256).Replace("\0", "");
wii.isState = (WLAN_INTERFACE_STATE)Marshal.ReadInt32(pItemList, 280);

InterfaceInfo[ i ] = wii;
}
}

I like your solution more, but as you say, there needs to be a way of detecting the number of WLAN_INTERFACE_INFO items for the marshalling.

TomvE at 2007-10-11 > top of Msdn Tech,Software Development for Windows Vista,General Windows Vista Development Issues...
# 6
After some more investigation I do think that a MarshalAs should be possible to solve this problem. There is a SizeParamIndex field which can be set in the MarshalAs attribute. In our case it should be set to 0, because the first parameter of the struct (dwNumberOfItems) tells us the size of the array.

But I'm still playing around with the UnmanagedType, because setting it to LPArray results in a runtime error: "Cannot marshal field 'InterfaceInfo' of type 'WLAN_INTERFACE_INFO_LIST': Invalid managed/unmanaged type combination (Arrays fields must be paired with ByValArray or SafeArray)."

TomvE at 2007-10-11 > top of Msdn Tech,Software Development for Windows Vista,General Windows Vista Development Issues...
# 7

Thanks for your quick answer, I tried that and it works fine. But now I'm stuck at getting the available network list. I'm only able to get the first network on the list, I have no idea where I can get the rest of them. So basically this is what I do:

public static WLAN_AVAILABLE_NETWORK_LIST GetAvailableNetworkList(IntPtr handle, WLAN_INTERFACE_INFO wii)
{
IntPtr ppAvailableNetworkList;
if (WlanGetAvailableNetworkList(handle, wii.InterfacePtr, 0, IntPtr.Zero, out ppAvailableNetworkList) != 0)
thrownew System.IO.IOException("Error getting network list");
returnnewWLAN_AVAILABLE_NETWORK_LIST(ppAvailableNetworkList);
}

In the WLAN_AVAILABLE_NETWORK_LIST constructor I try to get the WLAN_AVAILABLE_NETWORKs just like with the interfaces, but somehow it doesn't work. I can get the number of available networks and the first network, but I don't know how to setup the pointer for the next item:

public WLAN_AVAILABLE_NETWORK_LIST(IntPtr pList)
{
dwNumberOfItems =
Marshal.ReadInt32(pList, 0);
dwIndex =
Marshal.ReadInt32(pFirstNetwork, 4);
Network =
newWLAN_AVAILABLE_NETWORK[dwNumberOfItems];
for (int i = 0; i < dwNumberOfItems; i++)
{
NetworkIdea =
newWLAN_AVAILABLE_NETWORK(new IntPtr(pList.toInt32() + 8 + ...)); // <-- What do I have to insert here?
}
}

The problem is that the WLAN_AVAILABLA_NETWORK struct obviously doesn't have a fixed size. I've tried getting the offset from the WLAN_AVAILABLE_NETWORK constructor, doesn't work. My guess is that there are actually several WLAN_AVAILABLE_NETWORK_LIST structs in memory, each with a different dwIndex and exactly one WLAN_AVAILABLE_NETWORK struct, could that be? The MSDN specification of WLAN_AVAILABLE_NETWORK_LIST defines the size of the WLAN_AVAILABLE_NETWORK array as 1, that's also a hint at that. Now if that were the case, where can I find those ...LIST structs? The pointer returned by WlanGetAvailableNetworkList points exactly on the first ...LIST struct. Could it be that the pointer to the next struct is just behind this pointer in memory? If that is so, how can I access that from C# (if at all)? Or am I maybe completely on the wrong track?

Thanks again,
Patrick

bl.p at 2007-10-11 > top of Msdn Tech,Software Development for Windows Vista,General Windows Vista Development Issues...
# 8
Uh, hadn't read your last post. I thought about that too, and I also tried some combination of the unmanaged array types and parameters, but couldn't get it to work. My guess is that it isn't possible if the marshaller doesn't know the size of the array, but if you find a way I'd be glad to hear. Your solution with the constructor is probably fine for most uses, the only thing is, it relies on an exact number of bytes for the fields, so it probably is very platform-dependent. Shouldn't be to much of a problem though, as these APIs are very platform-dependent, too.
bl.p at 2007-10-11 > top of Msdn Tech,Software Development for Windows Vista,General Windows Vista Development Issues...
# 9

What makes you think that the WLAN_AVAILABLE_NETWORK struct doesn't have a fixed length?
All the strings and arrays have a maximum length, so each struct should just be the maximum number of bytes, but of course with empty ones when the actual values are shorter.

(I haven't actually implemented this function yet, so maybe I'm missing something).

TomvE at 2007-10-11 > top of Msdn Tech,Software Development for Windows Vista,General Windows Vista Development Issues...
# 10

Ok, your're right, it does have a maximum size, but I doubt that that is the size for all of the elements (although it could be). The main reason for this is the DOT11_PHY_TYPE array, with the ulong (64-bit!) uNumberOfPhyTypes variable counting the elements. Of course this array is maxed to WLAN_MAX_PHY_TYPE_NUMBER, but I have no idea what this usually is since I don't have the Wlanapi.h file. But I also tried reading the memory after the first WLAN_AVAILABLE_NETWORK struct bytewise searching for the SSID of the second network, which should be at the beginning of the next struct, and i can't find it (yes I considered it is in unicode). So it seems either the struct is somewhere completely else in memory or the other networks are not returned, which would be strange as the dwNumberOfItems in the WLAN_AVAILABLE_NETWORK_LIST is about right.

I'm really beginning to get frustrated, I just want to get the available networks and connect to one (at first). I've tried every approach from NDIS over WMI and WZC to the Native Wifi API, and nothing seems to work as it should. Well, maybe it's just me not getting it right... Unfortunately there don't seem to be working code samples around, too...

FYI, I use the Native Wifi LAN API for XP SP2.

Patrick

bl.p at 2007-10-11 > top of Msdn Tech,Software Development for Windows Vista,General Windows Vista Development Issues...
# 11
According to the MSDN documentation WLAN_MAX_PHY_TYPE_NUMBER has a value of 8 (you must have read past it).

I know the frustation you feel. I have exactly the same thing; it seems like no one else has used the WLAN API in managed code.
Anyway, I'll continue to do some more investigation tomorrow (UK here) on this WlanGetAvailableNetworkList function.

TomvE at 2007-10-11 > top of Msdn Tech,Software Development for Windows Vista,General Windows Vista Development Issues...
# 12

I finally found the solution for my first problem: it turns out that you don't pass in the interfaceGuid as an IntPtr but as a 'ref Guid'. This makes the WlanSetProfile function working as it should do.

But the WlanGetAvailableNetworkList still baffles me. I see exactly the same things as you do, Patrick. The number of items seems correct, but when I then look down the line I can't see all the SSIDs it has found.

TomvE at 2007-10-11 > top of Msdn Tech,Software Development for Windows Vista,General Windows Vista Development Issues...
# 13

Has anyone had luck with the WlanConnect function in .NET? I am getting an unknown error (1168) when calling the function. I have successfully called the WlanOpenHandle function. I have been using the example in the SDK as guidance, but I am now stuck. Any help would be great! Thanks!

rwampler at 2007-10-11 > top of Msdn Tech,Software Development for Windows Vista,General Windows Vista Development Issues...
# 14
Was anyone able to create a working C# wrapper for the native wireless api?

I followed the code on this thread but was unable to make it function as expected.

Thanks!

lowspeedchase at 2007-10-11 > top of Msdn Tech,Software Development for Windows Vista,General Windows Vista Development Issues...

Software Development for Windows Vista

Site Classified