Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

How to use WlanOpenHandle

852 views
Skip to first unread message

Dolf van den Berg

unread,
Jun 24, 2008, 5:25:16 AM6/24/08
to
Hi.

I am new to using windows api. Can someone please give me some tips on how
to use a api call like WlanOpenHandle this is for Wierless Lan.

Thanks

Dolf

Peter Below (TeamB)

unread,
Jun 24, 2008, 3:22:12 PM6/24/08
to

Well, the first step is to find documentation about the API function
you intend to use. Usually you will also need some background
information about the specific API subset (i.e. WIFI). All this can be
found on msdn.microsoft.com, or in the platform SDK help files that
come with newer Delphi versions. Then you need a declaration of the API
function in Delphi syntax. While Delphi comes with a lot of the more
common API calls predefined in a the Windows unit and a couple of
others more specialized APIs (especially new ones only found on Vista)
are not included. You can find header translations for some of them on
the web, e.g. here: http://jedi-apilib.sourceforge.net/ . If you cannot
find one you have to translate the relevant parts of the header file
(the function itself and any constants and types it may require)
yourself. For that refer to
http://rvelthuis.de/articles/articles-convert.html . It is very
important to get the calling convention and data types right, errors
usually lead to access violations when you call the function. Of course
you also need the header file itself. You already have it if you own a
full RAD. Studio with C++Builder personality (there's an "include"
folder under the Delphi root). Otherwise you can download and install
the MS platform SDK from microsoft.com.

The API is declared in literally hundreds of C header files, and the
first thing you need to do is find the correct one, since the
translated Delphi units usually have a name derived fom the C header
name. The help file entry for an API function usually mentions which
header it is declared in near the bottom of the help topic, for the
WIFI API that would be Wlanapi.h (the JEDI translation would then be
named JwaWLanapi.pas iI think). In that help topic section you usually
also find information about the Windows versions that support the API,
and, if you're lucky, which of the Windows DLLs contains the function.
The version information is particularly important, since it determines
basically how you need to bind to the API function.

There are two types of ways to call an API (or any other DLL) function:
static binding via external statements and dynamic binding via
LoadLIbary and GetProcAddress. If you use static binding your program
will fail to even load on a Windows version that does not support the
API you want to use, since the DLL itself may be missing, or it may not
export the function you want. Using dynamic binding you can get around
that and implement a sensible fallback mechanism if the program runs on
a platform not supporting the API.

OK, the WIDI API is definitely version-specific (XP SP2 or later only),
so you would use dynamic binding for it. The help topic fails to
mention the DLL name in the help that comes with RAD Studio but the
online entry at
http://msdn.microsoft.com/en-us/library/ms706759(VS.85).aspx tells us
that it is in Wlanapi.dll.

With this info we can now create a wrapper function that takes care of
the dynamic binding for us. I like to create a singleton object that
represent dynamically bound APIs in this case. There are certainly
other ways to do it but since I'm writing this message you get it my
way <g>. This approach makes it easier to write thread-safe code and to
"delphinize" the API by hiding irrelevant parameters and turn return
codes into exceptions where appropriate.

Note that the following sample unit compiles but is otherwise untested.
My system does not support the Wifi API, there is no wlanapi.dll on it.
This also means that I could not check the exported names for the
functions in question.

unit WiFiWrapperU;

interface

uses Windows, Sysutils;

type
IWiFi = interface(IInterface)
['{1E37F054-AF27-43A5-A518-A4AE6C8A9533}']
{: <summary>Closes the passed API handle.</summary>
<exception cref="EWifiError">if the Wifi APi is not supported or
the
API function returns an error.</exception> }
procedure CloseHandle(aHandle: THandle);
{: <summary>Checks if the Wifi API is supported on the current
installation.</summary>
<returns>true if Wifi support is present, false if
not.</returns> }
function IsSupported: Boolean;
{: <summary>Creates a handle for use with the WLan API.</summary>
<returns>The created handle. Pass it to <see
cref="IWiFi.CloseHandle"/>
when it is no longer needed.
</returns>
<param name="ClientVersion">is the client version that needs to
be
present, currently 1 or 2. See API docs for WlanOpenHandle.
</param>
<param name="dwNegotiatedVersion">Returns the version that is
supported
and will be used. </param>
<exception cref="EWifiError">if the Wifi APi is not supported or
the
API function returns an error.</exception> }
function OpenHandle(ClientVersion: DWORD; out dwNegotiatedVersion:
DWORD): THandle;
end;

EWiFiError = class(Exception)
private
FErrorcode: DWORD;
protected
property Errorcode: DWORD read FErrorcode;
public
constructor Create(const ErrMsg: string; Errcode: DWORD); overload;
constructor Create(errcode: DWORD); overload;
end;


{: Returns the public interface for the Wifi singleton. The object is
created on first access. It is thread-safe.}
function WiFi: IWiFi;

implementation

resourcestring
SNotSupported = ' not supported';
SWifiNotSupported = 'Wifi API is not supported on this machine';

const
WlanAPI = 'Wlanapi.dll';

{
DWORD WINAPI WlanOpenHandle(
__in DWORD dwClientVersion,
__reserved PVOID pReserved,
__out PDWORD pdwNegotiatedVersion,
__out PHANDLE phClientHandle
);
}
type
TWlanOpenHandle = function (dwClientVersion: DWORD; reserved: Pointer;
out dwNegotiatedVersion: DWORD; out hClientHandle: THandle): DWORD;
stdcall;
{
DWORD WINAPI WlanCloseHandle(
__in HANDLE hClientHandle,
__reserved PVOID pReserved
);
}
type
TWlanCloseHandle = function (hClientHandle: THandle; reserved:
Pointer): DWORD;
stdcall;

type
TWlanSupported = (wsUnknown, wsSupported, wsNotSupported);
TWiFi = class(TInterfacedObject, IWiFi)
private
FLibHandle: HMODULE;
FSupported: TWlanSupported;
FWlanCloseHandle: TWlanCloseHandle;
FWlanOpenHandle: TWlanOpenHandle;
procedure BindAPI;
procedure CloseHandle(aHandle: THandle);
function IsSupported: Boolean;
function OpenHandle(ClientVersion: DWORD; out NegotiatedVersion:
DWORD): THandle;
protected
procedure WlanCheck(Res: DWORD);
property LibHandle: HMODULE read FLibHandle;
public
procedure CheckSupport(var FuncAddress; const FuncName: string);
end;

{: This function allows us to set a pointer variable in a lock-free but
thread-safe fashion. It will only change the target variable if its
current value is equal to the Comperand. It returns the old value
of the Target. The whole operation is atomic. }
function InterlockedCompareExchangeObject(
var Target; Exchange, Comperand: TObject): TObject; stdcall;
{$IFDEF WIN64}
external kernel32 name 'InterlockedCompareExchangePointer';
{$ELSE}
external kernel32 name 'InterlockedCompareExchange';
{$ENDIF}

var
InternalWiFi: IWiFi = nil;

function WiFi: IWiFi;
var
P: TObject;
begin
if Assigned(InternalWiFi) then
Result := InternalWiFi
else begin
Result := TWiFi.Create;
Result._AddRef; // the call below does not increment the refcount!
P:= InterlockedCompareExchangeObject(InternalWiFi,
TObject(Result), nil);
if P <> nil then begin
Result._Release;
Result := InternalWiFi;
end; {if}
end; {else}
end; {WiFi}

{== TWiFi =============================================================}

procedure TWiFi.BindAPI;
var
hLib: HMODULE;
temp: TObject;
begin
hLib := LoadLibrary(WlanAPI);
if hLib = 0 then
FSupported := wsNotSupported
else begin
temp:= InterlockedCompareExchangeObject(FLibHandle, TObject(hLib),
nil);
FSupported := wsSupported;
if Assigned(temp) then
// some other thread got there first.
FreeLibrary(hLib);
end; {else}
end;

procedure TWiFi.CheckSupport(var FuncAddress; const FuncName: string);
var
P: Pointer;
begin
if not Assigned(Pointer(FuncAddress)) then begin
if not IsSupported then
raise EWiFiError.Create(SWifiNotSupported, ERROR_NOT_SUPPORTED);
P := GetProcAddress(LibHandle, Pchar(FuncName));
if not Assigned(P) then
raise EWifiError.Create(FuncName + SNotSupported,
ERROR_NOT_SUPPORTED);
InterlockedCompareExchangeObject(FuncAddress, TObject(P), nil);
end; {if}
end;

procedure TWiFi.CloseHandle(aHandle: THandle);
begin
CheckSupport(FWlanCloseHandle, 'WlanCloseHandle');
WLanCheck(FWlanCloseHandle(aHandle, nil));
end;

function TWiFi.IsSupported: Boolean;
begin
if FSupported = wsUnknown then
BindAPI;
Result := FSupported = wsSupported;
end;

function TWiFi.OpenHandle(ClientVersion: DWORD; out
NegotiatedVersion: DWORD): THandle;
begin
CheckSupport(FWlanOpenHandle, 'WlanOpenHandle');
WLanCheck(FWlanOpenHandle(ClientVersion, nil, NegotiatedVersion,
Result));
end;

procedure TWiFi.WlanCheck(Res: DWORD);
begin
if Res <> ERROR_SUCCESS then
raise EWiFiError.Create(Res);
end;

{== EWiFiError ========================================================}

constructor EWiFiError.Create(const ErrMsg: string; Errcode: DWORD);
begin
inherited Create(ErrMsg);
FErrorCode := Errcode;
end;

constructor EWiFiError.Create(errcode: DWORD);
begin
Create(SysErrorMessage(errcode), errcode);
end;


initialization
finalization
InternalWiFi := nil;
end.


--
Peter Below (TeamB)
Don't be a vampire (http://slash7.com/pages/vampires),
use the newsgroup archives :
http://www.tamaracka.com/search.htm
http://groups.google.com

Dolf van den Berg

unread,
Jun 25, 2008, 4:44:04 AM6/25/08
to
Hi Peter

Thanks for the help I am starting to get the picture.

I just have one more question.

How do you make a procedure a type.

here is the C header

DWORD WINAPI WlanGetAvailableNetworkList(
HANDLE hClientHandle,
const GUID* pInterfaceGuid,
DWORD dwFlags,
PVOID pReserved,
PWLAN_AVAILABLE_NETWORK_LIST* ppAvailableNetworkList
);

PWLAN_AVAILABLE_NETWORK_LIST is a pointer to a procedure called
WLAN_AVAILABLE_NETWORK_LIST how would is do this in Delphi?

Thanks for you help

"Peter Below (TeamB)" <no...@nomail.please> wrote in message
news:xn0frtlu...@newsgroups.borland.com...

Peter Below (TeamB)

unread,
Jun 25, 2008, 1:56:04 PM6/25/08
to
Dolf van den Berg wrote:

> Thanks for the help I am starting to get the picture.
>
> I just have one more question.
>
> How do you make a procedure a type.
>
> here is the C header
>
> DWORD WINAPI WlanGetAvailableNetworkList(
> HANDLE hClientHandle,
> const GUID* pInterfaceGuid,
> DWORD dwFlags,
> PVOID pReserved,
> PWLAN_AVAILABLE_NETWORK_LIST* ppAvailableNetworkList
> );
>
> PWLAN_AVAILABLE_NETWORK_LIST is a pointer to a procedure called
> WLAN_AVAILABLE_NETWORK_LIST how would is do this in Delphi?

The code I gave you does define procedural types (function pointer
types) for the API functions it wraps. To translate the above you need
the declaration of WLAN_AVAILABLE_NETWORK_LIST, which is in fact a
record type, not a procedure. And it is not a fixed-size record type
either,which makes it a bit difficult to declare properly in Delphi.
Fortunately you do not need to allocate such a record yourself, the
function will return a pointer to such a record to you and you just
have to pass it to WLanFreeMemory once you are done with it to prevent
a memory leak.

I would declare the record type as follows:

type
WLAN_AVAILABLE_NETWORK_LIST = record
dwNumberOfItems: DWORD;
dwIndex : DWORD;
Network : array [0..1023] of WLAN_AVAILABLE_NETWORK
end;
PWLAN_AVAILABLE_NETWORK_LIST = ^WLAN_AVAILABLE_NETWORK_LIST;

WLAN_AVAILABLE_NETWORK is another complex record type (containing
fields of other record types) which you have to translate. Fortunately
it is of fixed size.

With this WlanGetAvailableNetworkList would be translated to a function
pointer type of this form:

type
TWlanGetAvailableNetworkList = function (
hClientHandle: THandle;
pInterfaceGuid: PGUID;
dwFlags: DWORD;
pReserved: Pointer;
out ppAvailableNetworkList: PWLAN_AVAILABLE_NETWORK_LIST
): DWORD; stdcall;

To use this function you would declare a (local) variable of type

var
Networklist: PWLAN_AVAILABLE_NETWORK_LIST;

and pass this variable as last parameter to the API call. If the call
succeeds Networklist^.dwNumberOfItems gives you the number of records
the Networklist^.Network array actually contains. You must not index
past the last item in this array since there will mst likely be no
allocated memory beyond that item. With such dynamically sized records
the Delphi compiler is unable to do any range checking on the indices
you use to access the array. For the compiler the array contains 1024
elements since that is how the type is declared. But that is a blatent
lie <g>.

Dolf van den Berg

unread,
Jun 27, 2008, 3:19:08 AM6/27/08
to
Hi Peter

Can you please have a look at the following am I doing it correctly?

Just to explain what I am intending to do. I am writing a utility to test if
a wireless router is working. What I will do is change the SSID of the
router and restart the router. This is all done, but now I need to rescan
the wireless network to test if Windows can see the router on the wireless
network.

Thanks

Dolf

Type
DOT11_SSID = Record
uSSIDLength : ULONG;
ucSSID : String[DOT11_SSID_MAX_LENGTH];
end;
PDOT11_SSID = ^DOT11_SSID;

WLAN_RAW_DATA = Record
dwDataSize : DWORD;
DataBlob : BYTE;
end;
PWLAN_RAW_DATA = ^WLAN_RAW_DATA;

{
DWORD WINAPI WlanScan(
__in HANDLE hClientHandle,
__in const GUID *pInterfaceGuid,
__in_opt const PDOT11_SSID pDot11Ssid,
__in_opt const PWLAN_RAW_DATA pIeData,
__reserved PVOID pReserved
);
}

TWlanScan = function (hClientHandle : THandle; pInterfaceGuid : PGUID;
pDot11Ssid : PDOT11_SSID;pIeData : PWLAN_RAW_DATA; pReserved : Pointer)
: DWORD;
stdcall;

function ScanWNetwork(aHandle: THandle) : DWORD;

function TWiFi.ScanWNetwork(aHandle: THandle) : DWORD;
Var
pInterfaceGuid : PGUID;
myPDOT11_SSID : PDOT11_SSID;
pIeData : PWLAN_RAW_DATA;
begin
CheckSupport(FWlanOpenHandle, 'WlanOpenHandle');
WlanCheck(FTWlanScan(aHandle,pInterfaceGuid,myPDOT11_SSID,pIeData,Nil));
end;

"Peter Below (TeamB)" <no...@nomail.please> wrote in message

news:xn0fruy3...@newsgroups.borland.com...

Peter Below (TeamB)

unread,
Jun 27, 2008, 1:40:56 PM6/27/08
to
Dolf van den Berg wrote:

> TWlanScan = function (hClientHandle : THandle; pInterfaceGuid :
> PGUID; pDot11Ssid : PDOT11_SSID;pIeData : PWLAN_RAW_DATA; pReserved
> : Pointer) : DWORD; stdcall;
>
> function ScanWNetwork(aHandle: THandle) : DWORD;
>
> function TWiFi.ScanWNetwork(aHandle: THandle) : DWORD;
> Var
> pInterfaceGuid : PGUID;
> myPDOT11_SSID : PDOT11_SSID;
> pIeData : PWLAN_RAW_DATA;
> begin
> CheckSupport(FWlanOpenHandle, 'WlanOpenHandle');
>
> WlanCheck(FTWlanScan(aHandle,pInterfaceGuid,myPDOT11_SSID,pIeData,Nil)
> ); end;

You check the support for the wrong function here (it pays to switch on
the ol' brain before doing copy & paste) and of course you also do not
initialize the parameters you pass to FTWlanScan. Never ever pass an
uninitialized local variable to an API function! Random garbage can
make Windows do amazing things, none of them good.

I'm not actually familiar with the wifi API, by the way. I can only
give you the mechanism of translation to Delphi, not an explanation
about how to use this particular API correctly. You have to study the
API docs for that.

0 new messages