From: Andy Baker on
Our CE.NET 4.2 application requires a wireless connection between 2
different 'networks', a LAN in infrastructure mode and a printer in ad-hoc
mode. The LAN has a WEP key, the printer does not. I am using the
OpenNETCF.Net namespace (ver 1.4 and VS2003, although hopefully upgrading
to VS2005 soon) to switch between them in my C# application, specifically:
Adapter.SetWirelessSettings(SSId, Infrastructure, WEPKey);
Adapter.BindAdapter();
This works fine for the printer, if I set Infrastructure to false and WEP
key to (byte[]) null, but I have a problem connecting to the LAN. If I set
both networks up in the preferred list, I can switch between them from the
CE screen, but if I then try to connect to the LAN from my application, I
then can no longer do so from the CE screen until I re-enter the WEP key.
This suggests that my application is supplying the wrong WEP key. The
problem is the key that I have is an 8 character hex string, and
SetWirelessSettings requires either a 5 or 13 byte string or a byte array.
How do I convert between them? I have tried
Encoding.Unicode.GetBytes(WepKey), where WepKey is my 8 character string -
this gives me a byte array, but I still cannot connect.
From what I have read on the internet, it seems that there is a later
version of the namespace available that allows SetWirelessSettings with a
single argument of the SSId, if the networks are already in the preferred
list, but I have been unable to find this. Anyway, I would prefer not to
have to set the networks up before if possible.
Finally, I would like my C# class to have an event such as
OnStatusChanged, when I can detect when the status of the network has
changed i.e. from Associating to Assocated to Connected etc. Is this
possible?
Thanks in advance.

Andy Baker


From: "Paul G. Tobey [eMVP]" p space tobey no spam AT no instrument no spam DOT on
You're saying that you want to switch which network you want to connect to
periodically under program control? If so, yes, the latest SDF version will
do that, as will, I think, the last version of v. 1.4 checked in. If you're
sure that there's no 'public bool SetWirelessSettings( string SSID )', I'll
try to post the source here.

I don't follow the problem with connecting to the WEP-enabled network. What
do you mean you have to reenter the WEP key?

Paul T.


"Andy Baker" <abaker(a)NOSPAMvanputer.com> wrote in message
news:463891f8$0$8735$ed2619ec(a)ptn-nntp-reader02.plus.net...
> Our CE.NET 4.2 application requires a wireless connection between 2
> different 'networks', a LAN in infrastructure mode and a printer in ad-hoc
> mode. The LAN has a WEP key, the printer does not. I am using the
> OpenNETCF.Net namespace (ver 1.4 and VS2003, although hopefully upgrading
> to VS2005 soon) to switch between them in my C# application,
> specifically:
> Adapter.SetWirelessSettings(SSId, Infrastructure, WEPKey);
> Adapter.BindAdapter();
> This works fine for the printer, if I set Infrastructure to false and WEP
> key to (byte[]) null, but I have a problem connecting to the LAN. If I set
> both networks up in the preferred list, I can switch between them from the
> CE screen, but if I then try to connect to the LAN from my application, I
> then can no longer do so from the CE screen until I re-enter the WEP key.
> This suggests that my application is supplying the wrong WEP key. The
> problem is the key that I have is an 8 character hex string, and
> SetWirelessSettings requires either a 5 or 13 byte string or a byte array.
> How do I convert between them? I have tried
> Encoding.Unicode.GetBytes(WepKey), where WepKey is my 8 character string -
> this gives me a byte array, but I still cannot connect.
> From what I have read on the internet, it seems that there is a later
> version of the namespace available that allows SetWirelessSettings with a
> single argument of the SSId, if the networks are already in the preferred
> list, but I have been unable to find this. Anyway, I would prefer not to
> have to set the networks up before if possible.
> Finally, I would like my C# class to have an event such as
> OnStatusChanged, when I can detect when the status of the network has
> changed i.e. from Associating to Assocated to Connected etc. Is this
> possible?
> Thanks in advance.
>
> Andy Baker
>
>


From: Andy Baker on
Hello Paul
Thanks for getting back to me. The SetWirelessSettings in the namespace
that I am using has only 2 options, with parameters (string SSId, bool
bInfrastructure, Key), with Key either a byte array or a string. Although I
would prefer to pass the key, if you could post the source to the latest
version, it would allow me to verify if the problem is with the WEP Key,
which is what I suspect.
To try and clarify the problem, I have set up my 2 networks in the
preferred list, with the LAN having a WEP key that I have entered in the
Wireless Properties screen as an 8 character hex string. From the Wireless
Information screen, I can switch between networks by selecting the network
and clicking 'Connect'.
I then try from my application which uses SetWirelessSettings. When I
try to connect to the LAN, I am passing the WEP key as a byte array, using
Encoding.Unicode.GetBytes(WEPKey), where WEPKey is my 8 character hex
string. The connection fails, then if I go back to the Wireless Information
screen, it no longer works from there either. This suggests that my
application has done something to the settings for the network. In the
Wireless Properties screen for the LAN, in the Network key box, there are 8
asterisks as I would expect. If I re-enter the 8 character hex key in this
box, the connection works again. This suggests to me that whatever incorrect
key that my application has supplied is now stored as the Network key - does
that make sense?
Therefore, my question is how do I convert an 8 character hex string
into the format that SetWirelessSettings is expecting?
You mentioned the latest SDF version. Am I correct in thinking that as
we are using VS2003 at the moment, I have to stick with 1.4 until we upgrade
to VS2005?


Andy Baker

"Paul G. Tobey [eMVP]" <p space tobey no spam AT no instrument no spam DOT
com> wrote in message news:uLbzOwMjHHA.3700(a)TK2MSFTNGP06.phx.gbl...
> You're saying that you want to switch which network you want to connect to
> periodically under program control? If so, yes, the latest SDF version
> will do that, as will, I think, the last version of v. 1.4 checked in. If
> you're sure that there's no 'public bool SetWirelessSettings( string
> SSID )', I'll try to post the source here.
>
> I don't follow the problem with connecting to the WEP-enabled network.
> What do you mean you have to reenter the WEP key?
>
> Paul T.
>
>
> "Andy Baker" <abaker(a)NOSPAMvanputer.com> wrote in message
> news:463891f8$0$8735$ed2619ec(a)ptn-nntp-reader02.plus.net...
>> Our CE.NET 4.2 application requires a wireless connection between 2
>> different 'networks', a LAN in infrastructure mode and a printer in
>> ad-hoc mode. The LAN has a WEP key, the printer does not. I am using the
>> OpenNETCF.Net namespace (ver 1.4 and VS2003, although hopefully
>> upgrading to VS2005 soon) to switch between them in my C# application,
>> specifically:
>> Adapter.SetWirelessSettings(SSId, Infrastructure, WEPKey);
>> Adapter.BindAdapter();
>> This works fine for the printer, if I set Infrastructure to false and WEP
>> key to (byte[]) null, but I have a problem connecting to the LAN. If I
>> set both networks up in the preferred list, I can switch between them
>> from the CE screen, but if I then try to connect to the LAN from my
>> application, I then can no longer do so from the CE screen until I
>> re-enter the WEP key. This suggests that my application is supplying the
>> wrong WEP key. The problem is the key that I have is an 8 character hex
>> string, and SetWirelessSettings requires either a 5 or 13 byte string or
>> a byte array. How do I convert between them? I have tried
>> Encoding.Unicode.GetBytes(WepKey), where WepKey is my 8 character
>> string - this gives me a byte array, but I still cannot connect.
>> From what I have read on the internet, it seems that there is a later
>> version of the namespace available that allows SetWirelessSettings with a
>> single argument of the SSId, if the networks are already in the preferred
>> list, but I have been unable to find this. Anyway, I would prefer not to
>> have to set the networks up before if possible.
>> Finally, I would like my C# class to have an event such as
>> OnStatusChanged, when I can detect when the status of the network has
>> changed i.e. from Associating to Assocated to Connected etc. Is this
>> possible?
>> Thanks in advance.
>>
>> Andy Baker
>>
>>
>
>


From: "Paul G. Tobey [eMVP]" p space tobey no spam AT no instrument no spam DOT on
> I then try from my application which uses SetWirelessSettings. When I
> try to connect to the LAN, I am passing the WEP key as a byte array, using
> Encoding.Unicode.GetBytes(WEPKey), where WEPKey is my 8 character hex
> string.

"8 character hex string" sounds like something along the lines of
"652f7542". Is that what you're passing? I think that the right thing is
to actually tell us what the WEP key you enter at the WZC dialog is and what
you are passing when you try to connect from your code. I think you're just
mapping one into the other incorrectly.

Here's the latest Adapter.cs from the v. 1.4 code path:

Paul T.

-----

/*=======================================================================================

OpenNETCF.Net
Copyright 2003, OpenNETCF.org

This library is free software; you can redistribute it and/or modify it
under
the terms of the OpenNETCF.org Shared Source License.

This library is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the OpenNETCF.org Shared Source
License
for more details.

You should have received a copy of the OpenNETCF.org Shared Source
License
along with this library; if not, email licensing(a)opennetcf.org to
request a copy.

If you wish to contact the OpenNETCF Advisory Board to discuss
licensing, please
email licensing(a)opennetcf.org.

For general enquiries, email enquiries(a)opennetcf.org or visit our
website at:
http://www.opennetcf.org

=======================================================================================*/
using System;
using System.Data;
using System.Collections;
using System.Text;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;

using OpenNETCF.Win32;
using OpenNETCF.IO;
using OpenNETCF.Runtime.InteropServices;

namespace OpenNETCF.Net
{
#region -------------- Internal-only Classes --------------

internal class NDISUIO_QUERY_OID
{
protected byte[] data;
protected int ourSize;
public int Size
{
get { return ourSize; }
}

protected const int NDISUIO_QUERY_OID_SIZE = 12;
public NDISUIO_QUERY_OID( int extrasize )
{
// Most of the cases we'll use will have a size
// of just sizeof( DWORD ), but you never know.
ourSize = NDISUIO_QUERY_OID_SIZE + extrasize;
data = new byte[ ourSize ];
}

protected const int OidOffset = 0;
public uint Oid
{
get { return BitConverter.ToUInt32( data, OidOffset ); }
set
{
byte[] bytes = BitConverter.GetBytes( value );
Buffer.BlockCopy( bytes, 0, data, OidOffset, 4 );
}
}

protected const int ptcDeviceNameOffset = OidOffset + 4;
public unsafe byte *ptcDeviceName
{
get
{
return (byte*)BitConverter.ToUInt32( data, ptcDeviceNameOffset );
}
set
{
byte[] bytes = BitConverter.GetBytes( (UInt32)value );
Buffer.BlockCopy( bytes, 0, data, ptcDeviceNameOffset, 4 );
}
}

protected const int DataOffset = ptcDeviceNameOffset + 4;
public byte[] Data
{
get
{
byte[] b = new byte[ ourSize - DataOffset ];
Array.Copy( data, DataOffset, b, 0, ourSize - DataOffset );
return b;
}
}

public byte[] getBytes()
{
return data;
}

public static implicit operator byte[](NDISUIO_QUERY_OID qoid)
{
return qoid.data;
}
}

internal class IP_ADAPTER_INFO
{
protected byte[] data;
protected uint firstnextIndex = 0;
protected uint firstnextOffset = 0;
protected uint ourSize = 0;
protected uint ourBase = 0;

// Main constructor. This figures out how much space
// is needed to hold the list of adapters, allocates
// a byte array for that space, and gets the list
// from GetAdaptersInfo().
unsafe public IP_ADAPTER_INFO()
{
// Find out how much space we need to store the
// adapter list.
int size = 0;
int err = AdapterPInvokes.GetAdaptersInfo( null, ref size );
if ( err != 0 )
{
// This is what we'd expect: there is not enough room in the
// buffer, so the size is set and an error is returned.
if ( err == 111 )
{
// ToDo: Handle buffer-too-small.
}
}

// Check for size = 0 (no adapters, presumably).
ourSize = (uint)size;
if ( ourSize == 0 )
{
data = null;
}
else
{
data = new byte[ size ];

// We need to lock this in memory until we can
// get its address. Since GetAdaptersInfo will
// be storing Next pointers from adapter information
// to adapter information, we need to know what
// the base for those pointers is. We can then
// calculate the offset into the byte array of
// IP_ADAPTER_INFO from that.
// Fix the data array in memory. We need to do
// this to store the base address of the array.
// The GetAdaptersInfo call will put various Next
// pointers in the structure and we need to know
// what the base address against which those are
// measured is. With that, we can figure out what
// offset in the data array they reference.
fixed( byte *b = &data[ 0 ] )
{
// Save the base address.
ourBase = (uint)b;

// Actually call GetAdaptersInfo.
size = (int)ourSize;
err = AdapterPInvokes.GetAdaptersInfo( data, ref size );
}

if ( err != 0 )
data = null;
}
}

protected const int IP_ADAPTER_INFO_SIZE = 640;
protected IP_ADAPTER_INFO( byte[] datain, uint offset )
{
// Create an internal-only copy of this structure,
// making it easy to get to the fields of one
// of the items in the linked list, based on its
// offset within the byte[] of the main
// instance of an IP_ADAPTER_INFO.
ourSize = IP_ADAPTER_INFO_SIZE;
data = new byte[ IP_ADAPTER_INFO_SIZE ];
Array.Copy( datain, (int)offset, data, 0, IP_ADAPTER_INFO_SIZE );
}

public Adapter FirstAdapter()
{
if ( data == null )
return null;

// Reset the indexing.
firstnextIndex = 0;
if ( this.Next == 0 ) // !!!!
firstnextOffset = 0; // !!!!
else // !!!!
firstnextOffset = this.Next - ourBase;

// Since we are creating this adapter based on
// the first entry in our table, we can just pass
// 'this' to do it.
return new Adapter( this );
}
public Adapter NextAdapter()
{
// Starting at the current offset in our 'data'
// member, get the Next field, subtrace its pointer
// from the initial pointer value to find the
// new offset, and create an adapter starting
// at that point in the 'data' member.

if ( data == null )
return null;

// Handle no more case.
if ( firstnextOffset == 0 )
return null;

IP_ADAPTER_INFO newinfo = new IP_ADAPTER_INFO( data,
firstnextOffset );

firstnextIndex++;

// Now, use the Next field of the new info
// structure to update where we find the next
// one after that in our list.
if ( newinfo.Next == 0 )
firstnextOffset = 0;
else
firstnextOffset = newinfo.Next - ourBase;

return new Adapter( newinfo );
}

// Accessors for fields of the item.
protected const int NextOffset = 0;
public uint Next
{
get { return BitConverter.ToUInt32( data, NextOffset ); }
}

protected const int ComboIndexOffset = NextOffset + 4;
public int ComboIndex
{
get { return BitConverter.ToInt32( data, ComboIndexOffset ); }
}

protected const int MAX_ADAPTER_DESCRIPTION_LENGTH = 128;
protected const int MAX_ADAPTER_NAME_LENGTH = 256;
protected const int MAX_ADAPTER_ADDRESS_LENGTH = 8;

protected const int AdapterNameOffset = ComboIndexOffset + 4;
public String AdapterName
{
get
{
String s = Encoding.ASCII.GetString(data, AdapterNameOffset,
MAX_ADAPTER_NAME_LENGTH);
int l = s.IndexOf( '\0' );
if ( l != -1 )
return s.Substring( 0, l );
return s;
}
}

protected const int DescriptionOffset = AdapterNameOffset +
MAX_ADAPTER_NAME_LENGTH + 4;
public String Description
{
get
{
String s = Encoding.ASCII.GetString(data, DescriptionOffset,
MAX_ADAPTER_DESCRIPTION_LENGTH);
int l = s.IndexOf( '\0' );
if ( l != -1 )
return s.Substring( 0, l );
return s;
}
}

protected const int PhysAddressLengthOffset = DescriptionOffset +
MAX_ADAPTER_DESCRIPTION_LENGTH + 4;
public int PhysAddressLength
{
get { return BitConverter.ToInt32( data, PhysAddressLengthOffset ); }
}

protected const int PhysAddressOffset = PhysAddressLengthOffset + 4;
public byte[] PhysAddress
{
get
{
byte[] b = new byte[ MAX_ADAPTER_ADDRESS_LENGTH ];
Array.Copy( data, PhysAddressOffset, b, 0, MAX_ADAPTER_ADDRESS_LENGTH );
return b;
}
}

protected const int IndexOffset = PhysAddressOffset +
MAX_ADAPTER_ADDRESS_LENGTH;
public int Index
{
get { return BitConverter.ToInt32( data, IndexOffset ); }
}

protected const int TypeOffset = IndexOffset + 4;
public int Type
{
get { return BitConverter.ToInt32( data, TypeOffset ); }
}

protected const int DHCPEnabledOffset = TypeOffset + 4;
public bool DHCPEnabled
{
get { return BitConverter.ToBoolean( data, DHCPEnabledOffset ); }
}

protected const int CurrentIpAddressOffset = DHCPEnabledOffset + 4;
public String CurrentIpAddress
{
// The CurrentIpAddress field is a pointer to an
// IP_ADDRESS_STRING structure, not a string itself,
// so we have to do some magic to make this work.
get
{
IntPtr p = new IntPtr( BitConverter.ToInt32( data,
CurrentIpAddressOffset) );
if ( p == IntPtr.Zero )
return null;

// Here, I'm going to extract the 16 bytes of
// the IP address string from the data pointed
// to by the CurrentIpAddress pointer. The
// IP address part of what's pointed to starts
// at offset 4 from the pointer value (skip the
// Next field). From there, we just copy 16
// bytes, the length of the IP address string,
// to a local byte array, which can easily be
// converted to a managed string below.
byte[] b = new byte[ 16 ];
IntPtr p4 = new IntPtr( p.ToInt32()+4 );
MarshalEx.Copy( p4, b, 0, 16 );

// The string itself is stored after the Next
// field in the IP_ADDRESS_STRING structure
// (offset 4).
String s = Encoding.ASCII.GetString(b, 0, 16);
int l = s.IndexOf( '\0' );
if ( l != -1 )
return s.Substring( 0, l );
return s;
}
}

// The current subnet mask is part of the same
// IP_ADDRESS_STRING that contains the CurrentIpAddress.
// We simply extract a different piece of that
// structure to get it.s
public String CurrentSubnetMask
{
// The CurrentIpAddress field is a pointer to an
// IP_ADDRESS_STRING structure, not a string itself,
// so we have to do some magic to make this work.
get
{
IntPtr p = new IntPtr( BitConverter.ToInt32( data,
CurrentIpAddressOffset) );
if ( p == IntPtr.Zero )
return null;

// Here, I'm going to extract the 16 bytes of
// the subnet address string from the data pointed
// to by the CurrentIpAddress pointer. The
// mask address part of what's pointed to starts
// at offset 4+16 from the pointer value (skip
// the Next field and the IP address field,
// which has length 16). From there, we just
// copy 16 bytes, the length of the mask
// string, to a local byte array, which can
// easily be converted to a managed string
// below.
byte[] b = new byte[ 16 ];
IntPtr p4 = new IntPtr( p.ToInt32()+4+16 );
MarshalEx.Copy( p4, b, 0, 16 );

// The string itself is stored after the Next
// and IpAddresss fields in the
// IP_ADDRESS_STRING structure (offset 4 + 16).
String s = Encoding.ASCII.GetString(b, 0, 16);
int l = s.IndexOf( '\0' );
if ( l != -1 )
return s.Substring( 0, l );
return s;
}
}

protected const int IpAddressListOffset = CurrentIpAddressOffset + 4;
#if notdefined
public string IpAddressList
{
get
{
return null; // ????
}
}
#endif

// The offset is the start of the address list plus the
// size of the IP_ADDRESS_STRING structure which includes
// the Next, IpAddress, IpMask, and Context fields.
protected const int GatewayListOffset = IpAddressListOffset + 4 + 16 + 16
+ 4;
public String Gateway
{
// The GatewayList field is a structure of type
// IP_ADDRESS_STRING. We have to extract the bits
// we want.
get
{
// The string itself is stored after the Next
// field in the IP_ADDRESS_STRING structure
// (offset 4).
String s = Encoding.ASCII.GetString(data, GatewayListOffset + 4, 16);
int l = s.IndexOf( '\0' );
if ( l != -1 )
return s.Substring( 0, l );
return s;
}
}

protected const int DHCPServerOffset = GatewayListOffset + 4 + 16 + 16 +
4;
public String DHCPServer
{
// The DhcpServer field is a structure of type
// IP_ADDRESS_STRING. We have to extract the bits
// we want.
get
{
// The string itself is stored after the Next
// field in the IP_ADDRESS_STRING structure
// (offset 4).
String s = Encoding.ASCII.GetString(data, DHCPServerOffset + 4, 16);
int l = s.IndexOf( '\0' );
if ( l != -1 )
return s.Substring( 0, l );
return s;
}
}

protected const int HaveWINSOffset = DHCPServerOffset + 4 + 16 + 16 + 4;
public bool HaveWINS
{
get { return BitConverter.ToBoolean( data, HaveWINSOffset ); }
}

protected const int PrimaryWINSServerOffset = HaveWINSOffset + 4;
public String PrimaryWINSServer
{
get
{
String s = Encoding.ASCII.GetString(data, PrimaryWINSServerOffset+4,
16);
int l = s.IndexOf( '\0' );
if ( l != -1 )
return s.Substring( 0, l );
return s;
}
}

protected const int SecondaryWINSServerOffset = PrimaryWINSServerOffset +
4 + 16 + 16 + 4;
public String SecondaryWINSServer
{
get
{
String s = Encoding.ASCII.GetString(data, SecondaryWINSServerOffset+4,
16);
int l = s.IndexOf( '\0' );
if ( l != -1 )
return s.Substring( 0, l );
return s;
}
}

protected const int LeaseObtainedOffset = SecondaryWINSServerOffset + 4 +
16 + 16 + 4;
public DateTime LeaseObtained
{
get { return MarshalEx.Time_tToDateTime(BitConverter.ToUInt32( data,
LeaseObtainedOffset)); }
}

protected const int LeaseExpiresOffset = LeaseObtainedOffset + 4;
public DateTime LeaseExpires
{
get { return MarshalEx.Time_tToDateTime(BitConverter.ToUInt32( data,
LeaseExpiresOffset)); }
}

/*
IP_ADDR_STRING IpAddressList;
*/

public static implicit operator byte[](IP_ADAPTER_INFO ipinfo)
{
return ipinfo.data;
}
}

/// <summary>
/// Interface Entry for WZC
/// </summary>
internal struct INTF_ENTRY: IDisposable, ICloneable
{
private IntPtr wszGuid;
private IntPtr wszDescr;
public uint ulMediaState;
public uint ulMediaType;
public uint ulPhysicalMediaType;
public int nInfraMode;
public int nAuthMode;
public int nWepStatus;
/// <summary>
/// control flags (see INTFCTL_* defines)
/// </summary>
public uint dwCtlFlags;
/// <summary>
/// capabilities flags (see INTFCAP_* defines)
/// </summary>
public uint dwCapabilities;
/// <summary>
/// encapsulates the SSID raw binary
/// </summary>
public RAW_DATA rdSSID;
/// <summary>
/// encapsulates the BSSID raw binary
/// </summary>
public RAW_DATA rdBSSID;
/// <summary>
/// encapsulates one WZC_802_11_CONFIG_LIST structure
/// </summary>
public RAW_DATA rdBSSIDList;
/// <summary>
/// encapsulates one WZC_802_11_CONFIG_LIST structure
/// </summary>
public RAW_DATA rdStSSIDList;
/// <summary>
/// data for various control actions on the interface
/// </summary>
public RAW_DATA rdCtrlData;
/// <summary>
/// To track caller that freeing the same structure more than one time..
/// </summary>
public int bInitialized;


/// <summary>
/// Creates a new entry with given name in memory
/// </summary>
/// <param name="guid">Name</param>
/// <returns>Entry</returns>
public static INTF_ENTRY GetEntry(string guid)
{
INTF_ENTRY entry = new INTF_ENTRY();
entry.Guid = guid;
INTFFlags dwOutFlags;
uint uret = WZCPInvokes.WZCQueryInterface(null, INTFFlags.INTF_ALL, ref
entry, out dwOutFlags);
if ( uret > 0 )
throw new AdapterException(uret, "WZCQueryInterface");
return entry;
}

/// <summary>
/// SSID
/// </summary>
public string SSID
{
get { return Encoding.ASCII.GetString(rdSSID.lpData, 0,
rdSSID.lpData.Length); }
set { rdSSID.lpData = Encoding.ASCII.GetBytes(value); }
}
public string BSSID
{
get { return BitConverter.ToString( rdBSSID.lpData, 0); }
}

/// <summary>
/// Entry name
/// </summary>
public string Guid
{
get
{
return MarshalEx.PtrToStringUni(wszGuid);
}
set
{
if ( wszGuid != IntPtr.Zero )
{
MarshalEx.FreeHGlobal(wszGuid);
}
wszGuid = MarshalEx.AllocHGlobal((value.Length + 1) * 2);
byte[] chars = Encoding.Unicode.GetBytes(value);
MarshalEx.Copy(chars, 0, wszGuid, chars.Length);
}
}

/// <summary>
/// Entry description
/// </summary>
public string Description
{
get
{
return MarshalEx.PtrToStringUni(wszDescr);
}
set
{
if ( wszDescr != IntPtr.Zero )
{
MarshalEx.FreeHGlobal(wszDescr);
}
wszDescr = MarshalEx.AllocHGlobal((value.Length + 1) * 2);
byte[] chars = Encoding.Unicode.GetBytes(value);
MarshalEx.Copy(chars, 0, wszDescr, chars.Length);
}
}
/// <summary>
/// Overriden
/// </summary>
/// <returns></returns>
public override string ToString()
{
return Guid;
}


#region IDisposable Members

public void Dispose()
{
if ( wszDescr != IntPtr.Zero )
{
MarshalEx.FreeHGlobal(this.wszDescr);
}
if ( wszGuid != IntPtr.Zero )
{
MarshalEx.FreeHGlobal(this.wszGuid);
}
}

#endregion

#region ICloneable Members

public object Clone()
{
INTF_ENTRY entry = (INTF_ENTRY)MemberwiseClone();
entry.rdBSSID.Clear();
entry.rdBSSIDList.Clear();
entry.rdCtrlData.Clear();
entry.rdSSID.Clear();
entry.rdStSSIDList.Clear();
entry.rdBSSID.lpData = this.rdBSSID.lpData;
entry.rdBSSIDList.lpData = this.rdBSSIDList.lpData;
entry.rdCtrlData.lpData = this.rdCtrlData.lpData;
entry.rdSSID.lpData = this.rdSSID.lpData;
entry.rdStSSIDList.lpData = this.rdStSSIDList.lpData;
return entry;
}

#endregion
}

/// <summary>
/// Encapsulates generic data blob
/// </summary>
internal struct RAW_DATA:IDisposable
{
//byte[] m_data;
private uint m_cbData;
private IntPtr m_lpData;

public RAW_DATA(byte[] data)
{
//m_data = new byte[0];
m_lpData = IntPtr.Zero;
m_cbData = (uint)data.Length;
lpData = data;
}
public uint cbData { get { return m_cbData; } }
public byte[] lpData
{
get
{
if ( m_lpData == IntPtr.Zero )
return null;
byte[] data = new byte[m_cbData];
MarshalEx.Copy(m_lpData, data, 0, (int)m_cbData);
return data;
}
set
{
FreeMemory();
m_lpData = MarshalEx.AllocHGlobal(value.Length);
MarshalEx.Copy(value, 0, m_lpData, value.Length);
}
}
public IntPtr lpDataDirect
{
get
{
return m_lpData;
}
}
internal void Clear()
{
m_lpData = IntPtr.Zero;
m_cbData = 0;
}
#region IDisposable Members

public void Dispose()
{
FreeMemory();
}

private void FreeMemory()
{
if ( m_lpData != IntPtr.Zero )
{
MarshalEx.FreeHGlobal(m_lpData);
m_lpData = IntPtr.Zero;
}
}
#endregion
}

/// <summary>
/// Wireless LAN config descriptor
/// </summary>
internal class WZC_WLAN_CONFIG: SelfMarshalledStruct
{
public WZC_WLAN_CONFIG():base(SizeOf)
{
}

/// <summary>
/// Length of this structure
/// </summary>
public int Length { get { return GetInt32(0); } set { SetInt32(0,
value); } }
/// <summary>
/// control flags (NON-NDIS) see WZC_WEPK* constants
/// </summary>
public WZCCTL CtlFlags { get { return (WZCCTL)GetUInt32(4); } set {
SetUInt32(4, (uint)value); }}
/// <summary>
/// MAC Address
/// </summary>
public byte[] MACAddr { get { byte[] ret = new byte[6];
Buffer.BlockCopy( data, 8, ret, 0, 6); return ret; } set {
value.CopyTo(data, 8); } }
/// <summary>
/// SSID
/// </summary>
public string SSID
{
get
{
int cb = GetInt32(16);
return Encoding.ASCII.GetString(data, 20, cb);
}
set
{
if ( value.Length > 32 ) value = value.Substring(32);
SetInt32(16, value.Length);
Buffer.BlockCopy( Encoding.ASCII.GetBytes(value), 0, data, 20,
value.Length);
}
}
/// <summary>
/// WEP status
/// </summary>
public WEPStatus Privacy { get { return (WEPStatus)GetInt32(52); } set {
SetInt32(52, (int)value); } }
/// <summary>
/// Receive signal strength in dBm
/// </summary>
public int Rssi { get { return GetInt32(56); } set { SetInt32(56,
value); } }
/// <summary>
/// Network type
/// </summary>
public NetworkType NetworkTypeInUse { get { return
(NetworkType)GetInt32(60); } set { SetInt32(60, (int)value); } }
/// <summary>
/// Infrastructure mode
/// </summary>
public InfrastructureMode InfrastructureMode { get { return
(InfrastructureMode)GetInt32(96); } set { SetInt32(96, (int)value); } }
/// <summary>
/// Supported data rates
/// </summary>
public byte[] Rates { get { byte[] ret = new byte[8];
Buffer.BlockCopy(data, 100, ret, 0, 8); return ret; } }
/// <summary>
/// Data rates in Mbps
/// </summary>
public float[] RatesConverted
{
get
{
int cnt = 0;
foreach( byte b in Rates ) cnt += b == 0? 0: 1;
float[] arrRates = new float[cnt];
for( int i = 0; i < cnt; i++ ) arrRates[i] = (float)(Rates[i] & 0x7f) /
2;
return arrRates;
}
}
/// <summary>
/// Current connection speed (data rate)
/// </summary>
public float CurrentDataRate
{
get
{
float ret = 0;
foreach( byte b in Rates)
if ( (b & 0x80) != 0 )
ret = (float)(b & 0x7f) / 2;
return ret;
}
}
/// <summary>
/// Selected key index
/// 0 is the per-client key, 1-N are the global keys
/// </summary>
public int KeyIndex { get { return GetInt32(108); } set { SetInt32(108,
value); } }
/// <summary>
/// Key length
/// </summary>
public int KeyLength { get { return GetInt32(112); } set { SetInt32(112,
value); } }
/// <summary>
/// Key data
/// </summary>
public byte[] KeyMaterial
{
get
{
byte[] ret = new byte[KeyLength];
Buffer.BlockCopy(data, 116, ret, 0, KeyLength);
return ret;
}
set
{
KeyLength = Math.Min(WZCCTL_MAX_WEPK_MATERIAL, value.Length);
Buffer.BlockCopy(value, 0, data, 116, KeyLength);
}
}

/// <summary>
/// Authentication mode
/// </summary>
public AuthenticationMode AuthenticationMode { get { return
(AuthenticationMode)GetUInt32(148); } set { SetUInt32(148, (uint)value); }}

// 152 rdUserData

/// <summary>
/// EAP parameters
/// </summary>
public WZC_EAPOL_PARAMS EapolParams
{
get
{
return new WZC_EAPOL_PARAMS( data, 160 );
}
set
{
// Copy the content of the incoming value
// over the top.
Buffer.BlockCopy(value.Data, 0, data, 160, WZC_EAPOL_PARAMS.SizeOf );
}
}

//uint Length; // Length of this
structure
//uint dwCtlFlags; // control flags
(NON-NDIS) see WZC_WEPK* constants
// fields from the NDIS_WLAN_BSSID structure
//NDIS_802_11_MAC_ADDRESS MacAddress; // BSSID
//UCHAR[6]
//UCHAR Reserved[2];
//NDIS_802_11_SSID Ssid; // SSID //36
bytes
//uint Privacy; // WEP encryption
requirement
//int Rssi; // receive signal strength in
dBm
//NDIS_802_11_NETWORK_TYPE NetworkTypeInUse;
//NDIS_802_11_CONFIGURATION Configuration;
// uint Length; // Length of structure
// uint BeaconPeriod; // units are Kusec
// uint ATIMWindow; // units are Kusec
// uint DSConfig; // Frequency, units are kHz
// uint Length; // Length of structure
// uint HopPattern; // As defined by 802.11, MSB set
// uint HopSet; // to one if non-802.11
// uint DwellTime; // units are Kusec
//NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode;
//NDIS_802_11_RATES SupportedRates; UCHAR[8]
// fields from NDIS_802_11_WEP structure
//ULONG KeyIndex; // 0 is the per-client
key, 1-N are the global keys
//ULONG KeyLength; // length of key in
bytes
//UCHAR KeyMaterial[WZCCTL_MAX_WEPK_MATERIAL]; // variable length
depending on above field
// aditional field for the Authentication mode
//NDIS_802_11_AUTHENTICATION_MODE AuthenticationMode;//36
//RAW_DATA rdUserData; // upper level
buffer, attached to this config // 2

//WZC_EAPOL_PARAMS EapolParams; // 802.1x parameters
// 5

public override string ToString()
{
return SSID;
}

public NDIS_WLAN_BSSID ToBssid()
{
// Create a new BSSID structure from the information
// in our own structure. It's in the right order and
// everything. Note that the signal strength does not
// represent the signal strength currently, but when
// the SSID was added to the preferred list, if that's
// what we're constructing here.

// First, since our entry list doesn't perfectly match
// the layout (the length field is not there), we have
// to build a data array. We'll copy everything, starting
// where the length would be (it's a flags field, in our
// case), then rewrite the Length.
/*
4 ULONG Length; // Length of
this structure
6 NDIS_802_11_MAC_ADDRESS MacAddress; // BSSID
2 UCHAR Reserved[2];
36 NDIS_802_11_SSID Ssid; // SSID
4 ULONG Privacy; // WEP
encryption requirement
4 NDIS_802_11_RSSI Rssi; // receive
signal
// strength in dBm
4 NDIS_802_11_NETWORK_TYPE NetworkTypeInUse;
32 NDIS_802_11_CONFIGURATION Configuration;
{
ULONG Length; // Length of structure
ULONG BeaconPeriod; // units are Kusec
ULONG ATIMWindow; // units are Kusec
ULONG DSConfig; // Frequency, units are kHz
NDIS_802_11_CONFIGURATION_FH FHConfig;
{
ULONG Length; // Length of structure
ULONG HopPattern; // As defined by 802.11, MSB set
ULONG HopSet; // to one if non-802.11
ULONG DwellTime; // units are Kusec
}
}
4 NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode;
16 NDIS_802_11_RATES SupportedRates;
*/
int len = 4 + 6 + 2 + 36 + 4 + 4 + 4 + 32 + 4 + 8;
byte[] d = new byte[ len ];

Buffer.BlockCopy( data, 4 /* MacAddressOffset-4, effectively */,
d, 0, d.Length );

Buffer.BlockCopy( BitConverter.GetBytes( len ), 0,
d, 0, 4 );

// Build the NDIS_WLAN_BSSID from this array we've just
// created.
return new NDIS_WLAN_BSSID( d, 0 );
}

public const int WZCCTL_MAX_WEPK_MATERIAL = 32;
public const int SizeOf = 196;
}

[Flags]
internal enum INTFFlags : uint
{
INTF_ALL = 0xffffffff,

INTF_ALL_FLAGS = 0x0000ffff,
/// <summary>
/// mask for the configuration mode (NDIS_802_11_NETWORK_INFRASTRUCTURE
value)
/// </summary>
INTF_CM_MASK = 0x00000007,
/// <summary>
/// zero conf enabled for this interface
/// </summary>
INTF_ENABLED = 0x00008000,
/// <summary>
/// attempt to connect to visible non-preferred networks also
/// </summary>
INTF_FALLBACK = 0x00004000,
/// <summary>
/// 802.11 OIDs are supported by the driver/firmware (can't be set)
/// </summary>
INTF_OIDSSUPP = 0x00002000,
/// <summary>
/// the service parameters are volatile.
/// </summary>
INTF_VOLATILE = 0x00001000,
/// <summary>
/// the service parameters are enforced by the policy.
/// </summary>
INTF_POLICY = 0x00000800,

INTF_DESCR = 0x00010000,
INTF_NDISMEDIA = 0x00020000,
INTF_PREFLIST = 0x00040000,
INTF_CAPABILITIES = 0x00080000,


INTF_ALL_OIDS = 0xfff00000,
INTF_HANDLE = 0x00100000,
INTF_INFRAMODE = 0x00200000,
INTF_AUTHMODE = 0x00400000,
INTF_WEPSTATUS = 0x00800000,
INTF_SSID = 0x01000000,
INTF_BSSID = 0x02000000,
INTF_BSSIDLIST = 0x04000000,
INTF_LIST_SCAN = 0x08000000,
INTF_ADDWEPKEY = 0x10000000,
INTF_REMWEPKEY = 0x20000000,
/// <summary>
/// reload the default WEP_KEY
/// </summary>
INTF_LDDEFWKEY = 0x40000000,
}

/// <summary>
/// List of interface names
/// </summary>
internal struct INTFS_KEY_TABLE: IDisposable
{
private uint dwNumIntfs;
public IntPtr pData;

public INTFS_KEY_TABLE(uint size)
{
dwNumIntfs = size;
if ( size != 0 )
{
pData = MarshalEx.AllocHGlobal(MarshalEx.SizeOf(typeof(IntPtr)) *
(int)size);
}
else
pData = IntPtr.Zero;
}

public string this[uint i]
{
get
{
return MarshalEx.PtrToStringUni((IntPtr)(MarshalEx.ReadInt32( pData,
(int)i * 4)));
}
}

public uint Count
{
get { return dwNumIntfs; }
}

#region IDisposable Members

public void Dispose()
{
if ( pData != IntPtr.Zero )
{
MarshalEx.FreeHGlobal(pData);
}
}

#endregion
}

#endregion

/// <summary>
/// Class representing a single instance of a network
/// adapter, which might include PCMCIA cards, USB network
/// cards, built-in Ethernet chips, etc.
/// </summary>
public class Adapter : StreamInterfaceDriver
{
private const string DEVICE_NAME = "UIO1:";

private void Open()
{
base.Open(FileAccess.All, FileShare.None);
}

internal String name;
/// <summary>
/// The NDIS/driver assigned adapter name.
/// </summary>
public String Name
{
get { return name; }
}
internal String description;
/// <summary>
/// The descriptive name of the adapter.
/// </summary>
public String Description
{
get { return description; }
}
internal int index;
/// <summary>
/// The index in NDIS' list of adapters where this
/// adapter is found.
/// </summary>
public int Index
{
get { return index; }
}
internal int type;
/// <summary>
/// The adapter type. Adapters can be standard
/// Ethernet, RF Ethernet, loopback, dial-up, etc.
/// </summary>
public AdapterType Type
{
get { return (AdapterType)type; }
}
internal byte[] hwaddress;
/// <summary>
/// The hardware address associated with the adapter.
/// For Ethernet-based adapters, including RF Ethernet
/// adapters, this is the Ethernet address.
/// </summary>
public byte[] MacAddress
{
get { return hwaddress; }
}

internal bool dhcpenabled;
/// <summary>
/// Indicator of whether DHCP (for IP address
/// assignment from a server), is enabled for the
/// adapter.
/// </summary>
public bool DhcpEnabled
{
get { return dhcpenabled; }
set
{
// Update the local copy of the state.
// Well, on second thought, maybe we should have to
// be refreshed to get this updated.
// dhcpenabled = value;

// Modify the registry keys associated with this
// adapter to enable DHCP. Once that is done, we
// have to rebind the network adapter to actually
// make the change from static to DHCP. We try to
// only do this if there has been a change.
string regName = "\\comm\\" + this.Name + "\\Parms\\Tcpip";

// Open the base key for the adapter.
RegistryKey tcpipkey = Registry.LocalMachine.OpenSubKey( regName,
true );

// Get the current value of the DHCPEnabled value. If
// it already matches the value we're trying to set, we
// don't have to change it.
UInt32 oldEnableDHCP = (UInt32)tcpipkey.GetValue( "EnableDHCP", 1 );
bool oldEnableDHCPB = ( oldEnableDHCP != 0 ) ? true : false;

if ( oldEnableDHCPB != value )
{
// Make the change.
tcpipkey.SetValue( "EnableDHCP", value ? (UInt32)1 : (UInt32)0 );
}

tcpipkey.Close();
}
}

internal string currentIp;
/// <summary>
/// The currently active IP address of the adapter.
/// </summary>
public string CurrentIpAddress
{
get { return currentIp; }
set
{
// Update the local copy of the state.
// Well, on second thought, maybe we should have to
// be refreshed to get this updated.
// currentIp = value;

// Modify the registry keys associated with this
// adapter to set the new static IP. Once that is done, we
// have to rebind the network adapter to actually
// make the change.
string regName = "\\comm\\" + this.Name + "\\Parms\\Tcpip";

// Open the base key for the adapter.
RegistryKey tcpipkey = Registry.LocalMachine.OpenSubKey( regName,
true );

// Get the current value of the IpAddress value. If
// it already matches the value we're trying to set, we
// don't have to change it.
string oldIpAddress = (string)tcpipkey.GetValue( "IpAddress", "" );

if ( oldIpAddress != value )
{
// Make the change.
tcpipkey.SetValue( "IpAddress", value );
}

tcpipkey.Close();
}
}

internal string currentsubnet;
/// <summary>
/// The currently active subnet mask address of the
/// adapter.
/// </summary>
public string CurrentSubnetMask
{
get { return currentsubnet; }
set
{
// Update the local copy of the state.
// Well, on second thought, maybe we should have to
// be refreshed to get this updated.
// currentsubnet = value;

// Modify the registry keys associated with this
// adapter to set the new subnet mask. Once that is done, we
// have to rebind the network adapter to actually
// make the change.
string regName = "\\comm\\" + this.Name + "\\Parms\\Tcpip";

// Open the base key for the adapter.
RegistryKey tcpipkey = Registry.LocalMachine.OpenSubKey( regName,
true );

// Get the current value of the SubnetMask value. If
// it already matches the value we're trying to set, we
// don't have to change it.
string oldMask = (string)tcpipkey.GetValue( "SubnetMask", "" );

if ( oldMask != value )
{
// Make the change.
tcpipkey.SetValue( "SubnetMask", value );
}

tcpipkey.Close();
}
}

internal string gateway;
/// <summary>
/// The active gateway address.
/// </summary>
public string Gateway
{
get { return gateway; }
set
{
// Update the local copy of the state.
// Well, on second thought, maybe we should have to
// be refreshed to get this updated.
// gateway = value;

// Modify the registry keys associated with this
// adapter to set the new gateway. Once that is done, we
// have to rebind the network adapter to actually
// make the change.
string regName = "\\comm\\" + this.Name + "\\Parms\\Tcpip";

// Open the base key for the adapter.
RegistryKey tcpipkey = Registry.LocalMachine.OpenSubKey( regName,
true );

// Get the current value of the DefaultGateway value. If
// it already matches the value we're trying to set, we
// don't have to change it.
string oldGateway = (string)tcpipkey.GetValue( "DefaultGateway", "" );

if ( oldGateway != value )
{
// Make the change.
tcpipkey.SetValue( "DefaultGateway", value );
}

tcpipkey.Close();
}
}

internal string dhcpserver;
/// <summary>
/// The DHCP server from which the IP address was
/// last assigned.
/// </summary>
public string DhcpServer
{
get { return dhcpserver; }
}

/// <summary>
/// Enables or disables WZC Fallback for the current adapter
/// </summary>
/// <returns>
/// Returns true/false if WZC Fallback is enabled for the current adapter
/// </returns>
public bool WZCFallbackEnabled
{
set
{
if ( Type != AdapterType.Ethernet )
return ;
// Create the entry that will be passed to WZCSetInterface.
INTF_ENTRY entry = new INTF_ENTRY();
entry.Guid = Name;
if (value == false)
entry.dwCtlFlags &= ~(uint) INTFFlags.INTF_FALLBACK;
else
entry.dwCtlFlags |= (uint) INTFFlags.INTF_FALLBACK;
uint uret = WZCPInvokes.WZCSetInterface(null, INTFFlags.INTF_FALLBACK,
ref entry, null);
if ( uret > 0 )
throw new AdapterException(uret, "WZCSetInterface");
entry.Dispose();
}

get
{
if ( Type != AdapterType.Ethernet )
return false;
INTF_ENTRY entry = new INTF_ENTRY();
entry.Guid = Name;
INTFFlags dwOutFlags;
uint uret = WZCPInvokes.WZCQueryInterface(null,
INTFFlags.INTF_FALLBACK, ref entry, out dwOutFlags);
if (uret > 0 || (entry.dwCtlFlags & (uint) INTFFlags.INTF_FALLBACK) ==
0)
return false;
return true;
}
}

internal bool havewins;
/// <summary>
/// Indicates the presence of WINS server addresses
/// for the adapter.
/// </summary>
public bool HasWins
{
get { return havewins; }
}

internal string primarywinsserver;
/// <summary>
/// The IP address of the primary WINS server for the
/// adapter.
/// </summary>
public string PrimaryWinsServer
{
get { return primarywinsserver; }
}
internal string secondarywinsserver;
/// <summary>
/// The IP address of the secondary WINS server for the
/// adapter.
/// </summary>
public string SecondaryWinsServer
{
get { return secondarywinsserver; }
}

internal DateTime leaseobtained;
/// <summary>
/// The date/time at which the IP address lease was
/// obtained from the DHCP server.
/// </summary>
public DateTime LeaseObtained
{
get { return leaseobtained; }
}
internal DateTime leaseexpires;
/// <summary>
/// The date/time at which the IP address lease from
/// the DHCP server will expire (at which time the
/// adapter will have to contact the server to renew
/// the lease or stop using the IP address).
/// </summary>
public DateTime LeaseExpires
{
get { return leaseexpires; }
}

/// <summary>
/// Field, if set, is used, if the NDISUIO method
/// fails, to get the RF signal strength. You might
/// use this on an OS earlier than 4.0, when NDISUIO
/// became available. You'd usually create your own
/// subclass of StrengthAddon, then assign an instance
/// of that subclass to this property, then ask for
/// the signal strength.
/// </summary>
internal StrengthAddon StrengthFetcher = null;

/// <summary>
/// Method called on unbound adapter (maybe when handling
/// changing *both* the IP/subnet/gateway *and* the wireless
/// settings). This method notifies NDIS to bind the
/// adapter to all protocols indicated in the registry, in
/// effect causing the current registry settings to be
/// applied rather than those which the adapter is currently
/// using. Since we are binding, not *re*-binding the
/// protocols, we are implying that the adapter is not
/// currently bound to anything. When making this call,
/// we must refresh any adapter list that we might have,
/// to retrieve the current state of all adapters.
/// Changes to things like the IP address, subnet mask,
/// etc. are not immediately returned.
/// </summary>
public void BindAdapter()
{
// Rather than telling NDISUIO to rebind, we actually open
// NDIS itself and send the message there. So, rather than
// calling this.Open() to open NDISUIO, we need to open the
// driver directly.
IntPtr ndisAccess = FileEx.CreateFile(
NDISPInvokes.NDIS_DEVICE_NAME,
FileAccess.All,
FileShare.None,
FileCreateDisposition.OpenExisting,
NDISUIOPInvokes.FILE_ATTRIBUTE_NORMAL |
NDISUIOPInvokes.FILE_FLAG_OVERLAPPED );

// Send the device command.
UInt32 xcount = 0;
byte[] namebytes = new byte[ (this.Name.Length+1)*2 ];

// Zero the byte array. Since the default value for a byte
// should be zero, this should not be necessary.

// Get the bytes forming the Unicode string which is the
// adapter name.
Encoding.Unicode.GetBytes( this.Name, 0, this.Name.Length,
namebytes, 0);

// Tell NDIS to bind the adapter.
if ( !NDISUIOPInvokes.DeviceIoControl( ndisAccess,
NDISPInvokes.IOCTL_NDIS_BIND_ADAPTER,
namebytes, namebytes.Length,
null, 0, ref xcount, IntPtr.Zero ) )
{
FileEx.CloseHandle( ndisAccess );

// Handle error.
throw new AdapterException(NDISUIOPInvokes.GetLastError(),
"DeviceIoControl( IOCTL_NDIS_BIND_ADAPTER )");
}

FileEx.CloseHandle( ndisAccess );
}

/// <summary>
/// Method called after making some changes to the current
/// IP address, subnet mask, etc. This method notifies NDIS
/// to rebind the adapter to all protocols, in effect causing
/// the current registry settings to be applied rather than
/// those which the current configuration represents. Once you
/// have rebound an adapter, to get its new configuration, you
/// must regenerate the list of adapters. Changes to things
/// like the IP address, subnet mask, etc. are not immediately
/// returned.
/// </summary>
public void RebindAdapter()
{
// Open the NDIS driver.
IntPtr ndisAccess = FileEx.CreateFile(
NDISPInvokes.NDIS_DEVICE_NAME,
FileAccess.All,
FileShare.None,
FileCreateDisposition.OpenExisting,
NDISUIOPInvokes.FILE_ATTRIBUTE_NORMAL |
NDISUIOPInvokes.FILE_FLAG_OVERLAPPED );

// Send the device command.
UInt32 xcount = 0;
byte[] namebytes = new byte[ (this.Name.Length+1)*2 ];

// Zero the byte array. Since the default value for a byte
// should be zero, this should not be necessary.

// Get the bytes forming the Unicode string which is the
// adapter name.
Encoding.Unicode.GetBytes( this.Name, 0, this.Name.Length,
namebytes, 0);

// Tell NDIS to rebind the adapter.
if ( !NDISUIOPInvokes.DeviceIoControl( ndisAccess,
NDISPInvokes.IOCTL_NDIS_REBIND_ADAPTER,
namebytes, namebytes.Length,
null, 0, ref xcount, IntPtr.Zero ) )
{
FileEx.CloseHandle( ndisAccess );

// Handle error.
throw new AdapterException(NDISUIOPInvokes.GetLastError(),
"DeviceIoControl( IOCTL_NDIS_REBIND_ADAPTER )");
}

FileEx.CloseHandle( ndisAccess );
}

/// <summary>
/// Method called to unbind a given adapter. You might
/// perform this operation before attempting to change
/// *both* the protocol configuration of an adapter (IP,
/// subnet, gateway), *and* the wireless configuration of
/// the same adapter (WEP, SSID, etc.) To do that, first
/// unbind the adapter, then change the settings, then
/// bind the adapter (UnbindAdapter(), make changes,
/// BindAdapter()). Once you have bound/unbound an
/// adapter, to get its new configuration, you must
/// regenerate the list of adapters. Changes to things
/// like the IP address, subnet mask, etc. are not
/// immediately returned.
/// </summary>
public void UnbindAdapter()
{
// Open the NDIS driver.
IntPtr ndisAccess = FileEx.CreateFile(
NDISPInvokes.NDIS_DEVICE_NAME,
FileAccess.All,
FileShare.None,
FileCreateDisposition.OpenExisting,
NDISUIOPInvokes.FILE_ATTRIBUTE_NORMAL |
NDISUIOPInvokes.FILE_FLAG_OVERLAPPED );

// Send the device command.
UInt32 xcount = 0;
byte[] namebytes = new byte[ (this.Name.Length+1)*2 ];

// Zero the byte array. Since the default value for a byte
// should be zero, this should not be necessary.

// Get the bytes forming the Unicode string which is the
// adapter name.
Encoding.Unicode.GetBytes( this.Name, 0, this.Name.Length,
namebytes, 0);

// Tell NDIS to unbind the adapter.
if ( !NDISUIOPInvokes.DeviceIoControl( ndisAccess,
NDISPInvokes.IOCTL_NDIS_UNBIND_ADAPTER,
namebytes, namebytes.Length,
null, 0, ref xcount, IntPtr.Zero ) )
{
FileEx.CloseHandle( ndisAccess );

// Handle error.
throw new AdapterException(NDISUIOPInvokes.GetLastError(),
"DeviceIoControl( IOCTL_NDIS_UNBIND_ADAPTER )");
}

FileEx.CloseHandle( ndisAccess );
}

internal void FromIP_ADAPTER_INFO( IP_ADAPTER_INFO info )
{
// Copy the name, description, index, etc.
name = info.AdapterName;
description = info.Description;
index = info.Index;

// The adapter type should not change, so we
// can store that.
type = info.Type;

// The hardware address should not change, so
// we can store that, too.
hwaddress = info.PhysAddress;

// Get the flag concerning whether DHCP is enabled
// or not.
dhcpenabled = info.DHCPEnabled;

// Get the current IP address and subnet mask.
currentIp = info.CurrentIpAddress;
currentsubnet = info.CurrentSubnetMask;

// Get the gateway address and the DHCP server.
gateway = info.Gateway;
dhcpserver = info.DHCPServer;

// Get the WINS information.
havewins = info.HaveWINS;
primarywinsserver = info.PrimaryWINSServer;
secondarywinsserver = info.SecondaryWINSServer;

// DHCP lease information.
leaseobtained = info.LeaseObtained;
leaseexpires = info.LeaseExpires;
}

internal Adapter( IP_ADAPTER_INFO info ) : base(DEVICE_NAME)
{
this.FromIP_ADAPTER_INFO( info );
}

/// <summary>
/// Returns a Boolean indicating if the adapter is
/// an RF Ethernet adapter.
/// </summary>
/// <returns>
/// true if adapter is RF Ethernet; false otherwise
/// </returns>
public bool IsWireless
{
// Deciding if the adapter is RF Ethernet is a
// little more complicated than just looking at
// a bit somewhere.
// The original scheme is below:
// get {return ( (Type == AdapterType.Ethernet) &&
(SignalStrengthInDecibels != 0) ); }
// I figured that, if you can get a signal strength,
// it's a wireless card. However, there are a fair
// number of errors that would cause that strength
// to be zero. So, now we try to use WZC to do this.
get
{
if ( Type != AdapterType.Ethernet )
return false;

// Call WZC and ask it about this adapter name.
// If we get something back, it's wireless.
INTF_ENTRY entry = new INTF_ENTRY();
entry.Guid = Name;
INTFFlags dwOutFlags;
uint uret = WZCPInvokes.WZCQueryInterface(null, INTFFlags.INTF_ALL, ref
entry, out dwOutFlags);
if ( uret > 0 )
{
// Returned error. Seems not to be wireless.
return false;
}

// Got something. Seems to be wireless. Note that,
// even if the adapter is not associated with an
// AP, this seems to work, which might not have been
// the case with the old way.
return true;
}
}

/// <summary>
/// Returns a Boolean indicating if the adapter is
/// supported by WZC.
/// </summary>
/// <returns>
/// true if adapter is supported by WZC; false otherwise
/// </returns>
public bool IsWirelessZeroConfigCompatible
{
// Deciding if the adapter is working with WZC
// requires call call to WZCQueryInterface().
get
{
if ( !this.IsWireless )
return false;

// Attempt to get the status of the indicated
// interface by calling WZCQueryInterface. If
// it works, we return true; if not, false.
// Note that the first parameter, the WZC server,
// is set to null, apparently indicating that the
// local machine is the target.
INTF_ENTRY entry = new INTF_ENTRY();
entry.Guid = this.Name;
INTFFlags dwOutFlags;
uint uret = WZCPInvokes.WZCQueryInterface(null,
INTFFlags.INTF_ALL,
ref entry, out dwOutFlags);
if ( uret > 0 )
return false;
return true;
}
}

/// <summary>
/// Converts the Adapter to a string representation. We
/// use the adapter's name for this.
/// </summary>
/// <returns>
/// string representing the adapter
/// </returns>
public override string ToString()
{
return this.Name;
}

#region -------------- Changing connection parameters --------------

/// <summary>
/// This routine is used, internally, to make entries
/// for the preferred SSID list. It centralizes the
/// mapping of key data into the structure, etc.
/// </summary>
/// <param name="SSID">
/// The SSID string for the new entry
/// </param>
/// <param name="bInfrastructure">
/// Set to true for infrastucture mode; false for
/// ad hoc mode
/// </param>
/// <param name="Key">
/// WEP key material
/// </param>
/// <param name="keyIndex">
/// Key index. Valid values are 1-4
/// </param>
/// <param name="authMode">
/// Authentication mode for the connection
/// </param>
/// <param name="privacyMode">
/// Privacy (encryption) mode for the connection
/// </param>
/// <returns>
/// New WZC_WLAN_CONFIG object or null on failure
/// </returns>
private WZC_WLAN_CONFIG MakeSSIDEntry(string SSID, bool bInfrastructure,
byte[] Key, int keyIndex,
AuthenticationMode authMode, WEPStatus privacyMode)
{
WZC_WLAN_CONFIG thisConfig = new WZC_WLAN_CONFIG();

// Set the length.
thisConfig.Length = thisConfig.Data.Length;

// Set the MAC address.
thisConfig.MACAddr = this.MacAddress;

// Set the SSID.
thisConfig.SSID = SSID;

// Save the privacy mode.
thisConfig.Privacy = privacyMode;

// Set the key index. Note that, since the 'first'
// key is key #1 in all of the wireless specifications,
// we have to decrement the value for WZC, which expects
// it to be 0.
thisConfig.KeyIndex = keyIndex - 1;

// Proceed with configuration.
byte [] arrKey = null;
if ( Key != null )
{
// Key size has already been checked (this
// is an entry invariant).

arrKey = Key.Clone() as byte[];
thisConfig.KeyLength = arrKey.Length;
thisConfig.CtlFlags |= WZCCTL.WZCCTL_WEPK_PRESENT |
WZCCTL.WZCCTL_WEPK_XFORMAT;
if ( arrKey.Length == 10 )
thisConfig.CtlFlags |= WZCCTL.WZCCTL_WEPK_40BLEN;
byte[] chFakeKeyMaterial = new byte[]{0x56, 0x09, 0x08, 0x98, 0x4D,
0x08, 0x11, 0x66, 0x42, 0x03, 0x01, 0x67, 0x66};
for( int i = 0; i < arrKey.Length; i ++ )
arrKey[i] ^= chFakeKeyMaterial[(7*i)%13];
thisConfig.KeyMaterial = arrKey;
}
else
{
// Clear the key material, as well as setting
// the length to zero.
byte[] key = new byte[]
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

thisConfig.KeyMaterial = key;
thisConfig.KeyLength = 0;
}
thisConfig.AuthenticationMode = authMode;

// ???? do the right thing, based on the mode.

// If we have no key, we should probably set this to WEP Off.
thisConfig.InfrastructureMode = bInfrastructure?
InfrastructureMode.Infrastructure: InfrastructureMode.AdHoc;

return thisConfig;
}

/// <summary>
/// The KeyType enumeration tells the Adapter what
/// sort of key material is being passed. There are
/// several types for various forms of WEP and WPA
/// keys. External callers of Adapter methods should
/// set it to None, however.
/// </summary>
internal enum KeyType
{
None,
WPAPassphrase,
WPABinary,
WEP
}

/// <summary>
/// The ProcessKey routine makes necessary modifications
/// to the key material of a WPA key before it is passed
/// to WZC routines. The processing done to it depends
/// on how it was generated.
/// </summary>
/// <param name="kt">
/// The key type, indicating how the key material in
/// the structure was originally generated
/// </param>
/// <param name="config">
/// The configuration being changed
/// </param>
/// <param name="passphrase">
/// For WPA-PSK passphrase type, the passphrase.
/// </param>
internal void ProcessKey( KeyType kt, ref WZC_WLAN_CONFIG config,
string passphrase )
{
// Define fake key material for 'encrypting' the
// keys.
byte[] chFakeKeyMaterial = new byte[] {0x56, 0x09, 0x08, 0x98, 0x4D,
0x08, 0x11, 0x66, 0x42, 0x03, 0x01, 0x67, 0x66};
byte[] key;
uint i;

switch ( kt )
{
case KeyType.WPAPassphrase:
// We set this explicitly here. It was set
// out of line in the NetUI code.
config.Privacy = WEPStatus.Ndis802_11Encryption2Enabled;

config.CtlFlags |= WZCCTL.WZCCTL_WEPK_XFORMAT;

WZCPInvokes.WZCPassword2Key(
ref config, passphrase);

// Tell it to just encrypt and no conversion.
config.KeyLength = WZC_WLAN_CONFIG.WZCCTL_MAX_WEPK_MATERIAL;
config.CtlFlags |= WZCCTL.WZCCTL_WEPK_PRESENT;

// Note that, since the config structure doesn't
// actually have a byte[] for key material, we
// can't modify bytes of that 'array' in-place.
key = config.KeyMaterial;
for (i = 0; i < WZC_WLAN_CONFIG.WZCCTL_MAX_WEPK_MATERIAL; i++)
key[i] ^= chFakeKeyMaterial[(7*i)%13];
config.KeyMaterial = key;
break;
case KeyType.WPABinary:
// We set this explicitly here. It was set
// out of line in the NetUI code.
config.Privacy = WEPStatus.Ndis802_11Encryption2Enabled;

config.KeyLength = WZC_WLAN_CONFIG.WZCCTL_MAX_WEPK_MATERIAL;
config.CtlFlags |= WZCCTL.WZCCTL_WEPK_PRESENT;

// Note that, since the config structure doesn't
// actually have a byte[] for key material, we
// can't modify bytes of that 'array' in-place.
key = config.KeyMaterial;
for (i = 0; i < WZC_WLAN_CONFIG.WZCCTL_MAX_WEPK_MATERIAL; i++)
config.KeyMaterial[i] ^= chFakeKeyMaterial[(7*i)%13];
config.KeyMaterial = key;
break;
}
}

/// <summary>
/// The CheckKeySize routine checks the incoming
/// WEP or WPA key from the user and throws an
/// exception of an appropriate type, if the key
/// size is wrong or if it contains invalid
/// characters. In this case, it also maps the
/// string key into a byte array of the binary
/// values corresponding to the string, which should
/// represent the hex values in the key.
/// </summary>
/// <param name="sKey">
/// The string key to be used. Must consist of a
/// string of hexadecimal digits, for WEP keys and
/// WPA binary keys. May be a passphrase for WPA-PSK,
/// though, which can be 8-63 characters long.
/// </param>
/// <param name="authMode">
/// Can be any of the authentication mode types,
/// including WEP and WPA-PSK.
/// </param>
/// <param name="Key">
/// Reference parameter into which the returned
/// binary key value is written. Will be set to
/// null if the input string key is empty.
/// </param>
/// <returns>
/// KeyType value indicating the type of the key. Note:
/// for WPAPassphrase type, the caller will still have
/// to encode the password before using it (Key is not
/// set). Note: for WPABinary type, the caller will
/// still have to encrypt the key, although it is
/// converted from hex string to binary by this routine.
/// </returns>
internal KeyType CheckKeySize( string sKey, AuthenticationMode authMode,
ref byte[] Key )
{
Key = null;

// If the key is empty, just return.
if ( ( sKey == null ) || ( sKey.Length == 0 ) )
return KeyType.None;

// Handle two cases: WEP key and WPA-PSK
// password/binary key.
if ( ( authMode == AuthenticationMode.Ndis802_11AuthModeWPAPSK ) ||
( authMode == AuthenticationMode.Ndis802_11AuthModeWPANone ) )
{
// User can only enter either 64 hex entries, or 8/63 any ASCII entries
which is always
// converted to XFORMAT.
if ( ( sKey.Length >= 8 ) && ( sKey.Length <= 63 ))
{
return KeyType.WPAPassphrase;
}
else if (sKey.Length == 64)
{
Key = new byte[sKey.Length >> 1];
try
{
for ( int i = 0; i < sKey.Length >> 1; i++ )
Key[i] = byte.Parse(sKey.Substring(i*2, 2),
System.Globalization.NumberStyles.HexNumber);
}
catch
{
throw new ArgumentException("Key may contain hexadecimal digits
only");
}

return KeyType.WPABinary;
}
else
{
// Throw exception: Invalid WPA key
// material.
throw new ArgumentException("WPA key must contain more than 16 and less
than 128 hex digits or exactly 64 bytes of binary key data", "sKey");
}
}
else // WEP
{
if ( sKey.Length != 10 && sKey.Length != 26 )
throw new ArgumentException("Key must contain 10 or 26 hexadecimal
digits", "sKey");

Key = new byte[sKey.Length >> 1];
try
{
for ( int i = 0; i < sKey.Length >> 1; i++ )
Key[i] = byte.Parse(sKey.Substring(i*2, 2),
System.Globalization.NumberStyles.HexNumber);
}
catch
{
throw new ArgumentException("Key may contain hexadecimal digits only");
}

return KeyType.WEP;
}
}

/// <summary>
/// The CheckKeySize routine checks the incoming
/// WEP or WPA key from the user and throws an
/// exception of an appropriate type, if the key
/// size is wrong.
/// </summary>
/// <param name="Key">
/// The binary key to be used.
/// </param>
/// <param name="authMode">
/// Can be any of the authentication mode types,
/// including WEP and WPA-PSK.
/// </param>
internal KeyType CheckKeySize( byte[] Key, AuthenticationMode authMode )
{
if ( ( Key == null ) || ( Key.Length == 0 ) )
return KeyType.None;

// Handle two cases: WEP key and WPA-PSK
// password/binary key.
if ( ( authMode == AuthenticationMode.Ndis802_11AuthModeWPAPSK ) ||
( authMode == AuthenticationMode.Ndis802_11AuthModeWPANone ) )
{
// The only binary key material that you can send here is
// 32 bytes long.
if (Key.Length != 32)
{
throw new ArgumentException("Key must contain 32 bytes of key data",
"Key");
}

return KeyType.WPABinary;
}
else
{
// Length must be 5 or 13.
if ( ( Key.Length != 5 ) && ( Key.Length != 13 ) )
{
throw new ArgumentException("Key must contain 5 or 13 bytes of key
data", "Key");
}

return KeyType.WEP;
}
}

#region -------------- Obsolete SetWirelessSettings Methods --------------

/// <summary>
/// Modifies wireless settings associated with a given interface and AP
/// </summary>
/// <param name="SSID">Target SSID to connect</param>
/// <param name="bInfrastructure">Is infrastructure</param>
/// <param name="sKey">WEP key</param>
/// <returns>True if successful</returns>
[Obsolete("OpenNETCF.Net.Adapter.SetWirelessSettings is deprecated. Please
use OpenNETCF.Net.Adapter.SetWirelessSettingsAddEx()",false)]
public bool SetWirelessSettings(string SSID, bool bInfrastructure, string
sKey)
{
// Check the key size and throw exception if there
// is a problem. Return binary key, if OK.
byte[] arrKey = null;
KeyType kt = CheckKeySize( sKey,
AuthenticationMode.Ndis802_11AuthModeOpen, ref arrKey );
if ( kt != KeyType.WEP )
{
// This routine only handles WEP. Throw exception.
throw new ArgumentException("SetWirelessSettings non-WEP key and
authentication mode passed");
}

return SetWirelessSettings(SSID, bInfrastructure, arrKey);
}

/// <summary>
/// Modifies wireless settings associated with a given interface and AP
/// </summary>
/// <param name="SSID">Target SSID to connect</param>
/// <param name="bInfrastructure">Is infrastructure</param>
/// <param name="Key">binary wep key - 5 or 13 bytes</param>
/// <returns>true if succeeded</returns>
[Obsolete("OpenNETCF.Net.Adapter.SetWirelessSettings is deprecated. Please
use OpenNETCF.Net.Adapter.SetWirelessSettingsAddEx()",false)]
public bool SetWirelessSettings(string SSID, bool bInfrastructure, byte[]
Key)
{
// First, we need to get an INTF_ENTRY for this adapter.
INTF_ENTRY entry = new INTF_ENTRY();
entry.Guid = this.Name;
INTFFlags dwOutFlags;
uint uret = WZCPInvokes.WZCQueryInterface(null,
INTFFlags.INTF_ALL,
ref entry, out dwOutFlags);
if ( uret > 0 )
{
// As you can see, we presently don't support
// total configuration of the adapter with no
// WZC intervention at all. Somehow, you have
// to set things up, other than SSID value,
// infrastructure mode, and WEP key, so that we
// have a starting place.
throw new AdapterException(uret, "WZCQueryInterface");
}
else
{
// Perform the 'standard' WZC stuff to set the entry's
// configuration.
int cConfig = BitConverter.ToInt32( entry.rdBSSIDList.lpData, 0 );
int Index = 8;
WZC_WLAN_CONFIG thisConfig = null;
for( int i = 0; i < cConfig; i ++ )
{
WZC_WLAN_CONFIG cfg = new WZC_WLAN_CONFIG();
int cbCfg = BitConverter.ToInt32( entry.rdBSSIDList.lpData, Index );
Buffer.BlockCopy(entry.rdBSSIDList.lpData, Index, cfg.Data, 0, cbCfg);
Index += cbCfg;
if ( cfg.SSID == SSID )
thisConfig = cfg;
cfg = null;
}

// There are a couple of things going on here:
// 1. It might be that the user is trying to associate
// with an access point that we don't know about.
// For now, we don't allow this.
// 2. It is also possible that the adapter is to be
// placed in ad hoc mode and it just so happens that
// this is the first adapter to be enabled with the
// SSID. We need to allow this.
if ( ( thisConfig == null ) && ( bInfrastructure ) )
return false;

// If the config is null, but we are going to continue,
// we have to create a new one and set it up for us to
// use.
if ( thisConfig == null )
{
int keyIndex = 1; // First key.
thisConfig = this.MakeSSIDEntry( SSID, bInfrastructure, Key, keyIndex,
AuthenticationMode.Ndis802_11AuthModeOpen, ( Key != null ) ?
WEPStatus.Ndis802_11WEPEnabled : WEPStatus.Ndis802_11WEPDisabled );
}

byte [] FullConfig = new byte[thisConfig.Data.Length + 8 ];
FullConfig[0] = 1;
thisConfig.Data.CopyTo(FullConfig, 8);
RAW_DATA dt = new RAW_DATA(FullConfig);
entry.rdStSSIDList = dt;
uret = WZCPInvokes.WZCSetInterface(null, INTFFlags.INTF_PREFLIST, ref
entry, null);
if ( uret > 0 )
throw new AdapterException(uret, "WZCSetInterface");
entry.Dispose();
return true;
}
From: Andy Baker on
Hello Paul.

The WEP key that I enter at the WZC dialog is "1148BC4667". If I use
Encoding.Unicode.GetBytes("1148BC4667") to create a byte array, I get a 20
character array (49, 0, 49, 0, 52, 0, 56, 0, 66, 0, 67, 0, 52, 0, 54, 0, 54,
0, 55, 0). This doesn't connect. However I tried it again by passing the
string directly, and it worked. I was getting confused when it didn't work
previously when passing an empty string to connect to the non-wep enabled
printer. However, if I use the byte array form and pass (byte[]) null to
connect to the printer, that works. So, if I use the 2 different forms of
SetWirelessSettings for each different case, I can switch between them OK.
Possibly it would also work if I passed null instead of an empty string - I
have not tried that yet.
I do have one more question. The device will be connected to the printer
most of the time, and only use the LAN at the start at end of the day. For
this reason I tried to disconnect to the LAN as soon as I had finished using
it, by using UnbindAdapter. However when connected via USB to the device
from my desktop, I lost the connection, and when looking in Start ->
Networking and Dial-Up connections, the WiFi adapter was no longer present
and I had to reset the device to get it back. Is this normal behaviour? I
can disconnect from the LAN simply by reconnecting to the printer and don't
need to use UnbindAdapter, I was just concerned that there might be
something more seriously wrong.
Thanks for your help.

Andy Baker

"Paul G. Tobey [eMVP]" <p space tobey no spam AT no instrument no spam DOT
com> wrote in message news:eI4R1NNjHHA.4676(a)TK2MSFTNGP02.phx.gbl...
>> I then try from my application which uses SetWirelessSettings. When I
>> try to connect to the LAN, I am passing the WEP key as a byte array,
>> using Encoding.Unicode.GetBytes(WEPKey), where WEPKey is my 8 character
>> hex string.
>
> "8 character hex string" sounds like something along the lines of
> "652f7542". Is that what you're passing? I think that the right thing is
> to actually tell us what the WEP key you enter at the WZC dialog is and
> what you are passing when you try to connect from your code. I think
> you're just mapping one into the other incorrectly.
>