How to use "Native Wifi API" Windows API functions with Delphi - delphi

Im trying to use a function from Windows API on delphi, functions for Windows Wlanapi.dll (Native WIFI API)
WlanOpenHandle
DWORD WINAPI WlanOpenHandle(
__in DWORD dwClientVersion,
__reserved PVOID pReserved,
__out PDWORD pdwNegotiatedVersion,
__out PHANDLE phClientHandle
);
WlanHostedNetworkQueryProperty
DWORD WINAPI WlanHostedNetworkQueryProperty(
__in HANDLE hClientHandle,
__in WLAN_HOSTED_NETWORK_OPCODE OpCode,
__out PDWORD pdwDataSize,
__out PVOID *ppvData,
__out PWLAN_OPCODE_VALUE_TYPE *pWlanOpcodeValueType,
__reserved PVOID pvReserved
);
I trying to use this functions and others for hours, reading the MSDN references and others sites, but I just can't get this working.
My attempt
type
TWlanOpenHandle = function( dwClientVersion:DWORD;
pReserved:Pointer;
pdwNegotiatedVersion:PDWORD;
phClientHandle:PHANDLE
):DWORD; stdcall;
function apiWlanOpenHandle( dwClientVersion:DWORD;
pReserved:Pointer;
pdwNegotiatedVersion:PDWORD;
phClientHandle:PHANDLE
):DWORD;
implementation
function apiWlanOpenHandle ( dwClientVersion:DWORD; pReserved:Pointer; pdwNegotiatedVersion:PDWORD; phClientHandle:PHANDLE ):DWORD;
var
WlanOpenHandle: TWlanOpenHandle;
DLL: Cardinal;
begin
DLL:=LoadLibrary('Wlanapi.dll');
WlanOpenHandle := GetProcAddress(DLL, 'WlanOpenHandle');
if Assigned(WlanOpenHandle) then
begin
WlanOpenHandle(dwClientVersion, pReserved, pdwNegotiatedVersion, phClientHandle);
end
else begin
ShowMessage('Function not found');
end;
end;
I'm trying to translate this API, seems a lot of work, and I'm just a beginner in delphi, I read a lot of stuff on the web, how do I deal with this OpCode parameter, seems a C Struct with constants, and PWLAN_OPCODE_VALUE_TYPE?
http://msdn.microsoft.com/en-us/library/windows/desktop/dd439502(v=vs.85).aspx

You didn't actually show how you called apiWlanOpenHandle which would, I think, explain what the problem is. However, there's one very common mistake that is most likely what is confusing you.
Consider the C declaration of the API:
DWORD WINAPI WlanOpenHandle(
__in DWORD dwClientVersion,
__reserved PVOID pReserved,
__out PDWORD pdwNegotiatedVersion,
__out PHANDLE phClientHandle
);
The parameters that I suspect are causing you problems are the final two. Let us consider pdwNegotiatedVersion. This is a pointer to a DWORD. Because this is an out parameter you must supply a pointer to valid memory. I suspect you are just declaring a variable of type PDWORD and passing that.
var
NegotiatedVersionPtr: PDWORD;
begin
WlanOpenHandle(...., NegotiatedVersionPtr, ...);
The function WlanOpenHandle then de-references that pointer and tries to write to the memory. If you have not given a valid pointer then this will fail.
The naive solution is to change the calling code to look like this:
var
NegotiatedVersion: DWORD;
NegotiatedVersionPtr: PDWORD;
begin
NegotiatedVersionPtr := #NegotiatedVersion;
WlanOpenHandle(...., NegotiatedVersionPtr, ...);
This will work but there is a much cleaner way. Declare the API import like this:
function WlanOpenHandle(
dwClientVersion: DWORD;
pReserved: Pointer;
out NegotiatedVersion: DWORD;
out ClientHandle: THandle
): DWORD; stdcall; external 'Wlanapi.dll';
An out parameter of type DWORD is actually passed as a pointer to the DWORD that you supply as the argument to the function call. You can then change your calling code to look like this:
var
ReturnValue: DWORD;
NegotiatedVersion: DWORD;
ClientHandle: THandle;
begin
ReturnValue := WlanOpenHandle(2, nil, NegotiatedVersion, ClientHandle);
if ReturnValue<>ERROR_SUCCESS then
//respond to error
Note that I have also added some error checking which you really ought to be doing.
The reason that the Windows API function is declared using pointers is that the C language only supports parameter passing by value. It simply does not have pass-by-reference, i.e. out or var in Delphi terms. Languages that do support pass-by-reference should make use of them when they can.
Some Windows API functions have optional parameters declared as pointers. When this is the case passing NULL as the pointer is the way to signal that you do not wish to pass a parameter. Translating those APIs to Delphi is more complex. You need to implement a version using pointers to allow callers to opt-out of supplying the parameter. But it can be helpful to supply an overloaded version that uses out or var for convenience to the caller. The Delphi Windows unit contains many such examples.
As for WlanHostedNetworkQueryProperty, I would declare it like this:
const
// WLAN_HOSTED_NETWORK_OPCODE constants
wlan_hosted_network_opcode_connection_settings = 0;
wlan_hosted_network_opcode_security_settings = 1;
wlan_hosted_network_opcode_station_profile = 2;
wlan_hosted_network_opcode_enable = 3;
// WLAN_OPCODE_VALUE_TYPE constants
wlan_opcode_value_type_query_only = 0;
wlan_opcode_value_type_set_by_group_policy = 1;
wlan_opcode_value_type_set_by_user = 2;
wlan_opcode_value_type_invalid = 3;
function WlanHostedNetworkQueryProperty(
hClientHandle: THandle;
OpCode: Integer;
out DataSize: DWORD;
out Data: Pointer;
out WlanOpcodeValueType: Integer;
Reserved: Pointer
): DWORD; external 'Wlanapi.dll' delayed;
I have used the delayed facility because this is a Windows 7 and up API. You will presumably want your program to run on older versions of Windows and so delay loading is needed. For more information on delay loading in Delphi, see this answer, and particularly the onward links.
Note that the documentation in the MSDN topic to which you link is incorrect. The pWlanOpcodeValueType parameter is declared incorrectly in the MSDN topic. The correct definition, the one to be found in wlanpi.h is this:
__out PWLAN_OPCODE_VALUE_TYPE pWlanOpcodeValueType,

Related

Delphi - using C DLL with int* parameter

I'm having trouble using a DLL function with an int* parameter.
DLL supplier information:
The function is declared in the DLL as:
int sendQuoGetInfDstn(char* nomed, int *rigd)
I have imported this into Delphi 11 using:
const
QUODLL = 'PcQuoDllNoWrap.dll';
implementation
function sendQuoGetInfDstn(Name: PAnsiChar; Count: PInteger): integer; stdcall; external QUODLL;
This compiles fine.
My question is, how do I call this function from my Delphi program?
I have tried all sorts of things, but I get Access violation errors or program crash.
For example, I have made this wrapper:
function TPCQuo.GetWorklistInfoTest(Name: String; Count: integer): integer;
begin
Result := sendQuoGetInfDstn(PAnsiChar(Ansistring(Name)), #Count); {I have also tried PInteger(Count)}
end;
And I call the wrapper like this:
procedure TForm1.Button4Click(Sender: TObject);
var
name: String;
count: integer;
begin
if QUO.GetWorklistInfoTest(name, count) <> 0 then
ShowMessage('No worklist available ')
else
ShowMessage('Worklist available ' + name + ' number of lines: ' + count.ToString );
end;
So, how should I call this function?
There are a few possible issues here:
The function may be cdecl rather than stdcall. It's not possible to say for sure but you'd need to look more closely at the supplied header file.
The first argument char *nomed might be expecting a modifiable character array. Perhaps the function does attempt to modify the text, or perhaps the author made a mistake and should have written const char *nomed. With the information provided here we cannot tell.
The encoding of the text could be ANSI, or it could be some other 8-bit encoding such as UTF-8. With the information provided here we cannot tell what it is.
The second argument, int *rigd, could be either a pointer to a single integer, or it could be a pointer to an array. With the information provided here we cannot tell.
In summary, the only potential mistake in your translation is item 1. But getting the arguments, types and calling convention correct is only the first step. You also need to know the semantics of how to call a function. My best guess is that the issues are in that area, which will require information that you have, but we do not.

Convert variable arguments in C stdcall interface method to Delphi

I am converting the ICallFrame interface to Delphi and I am not sure how to handle the ICallFrame::Invoke method.
This is the definition:
HRESULT Invoke(
void *pvReceiver,
...
);
Per documentation the varargs directive works only with external routines and only with the cdecl calling convention.
It's a bit strange as Invoke is declared with STDMETHODCALLTYPE thus __stdcall, which is a callee-clean convention. But a variadic function cannot be callee-clean since the callee does not know how many parameters were passed.
Is there some kind of compiler magic if you do this with Visual Studio?
Even if compiler magic uses cdecl, the Delphi compiler doesn't accept this (because it's not external) E2070 Unknown directive: 'varargs'
ICallFrame = interface(IUnknown)
['{D573B4B0-894E-11d2-B8B6-00C04FB9618A}']
// other methods left out
function Invoke(pvReceiver: PVOID): HRESULT; cdecl; varargs;
end;
Based on Rudy's answer it seems this could work:
type
TfnInvoke = function(this: Pointer; pvReceiver: Pointer): HRESULT; cdecl varargs;
implementation
function GetInterfaceMethod(const intf; methodIndex: dword) : pointer;
begin
Result := Pointer(pointer(dword_ptr(pointer(intf)^) + methodIndex * SizeOf(Pointer))^);
end;
...
var
Invoker: TfnInvoke;
...
// Don't forget IUnknown methods when counting!
Invoker := GetInterfaceMethod(myIntf, 21);
Invoker(myIntf, FObject, 11, 17.3);
I will test with this and report back...
EDIT: tested and works.
Stdcall cannot use variable arguments. Any C or C++ function taking variable arguments must be __cdecl, because only in that calling convention, the caller (code), which knows how many parameters were passed, cleans up the stack. Even if the default calling convention is stdcall, var args functions will be cdecl.
Delphi can not generate functions like that, but it can consume existing external C functions (in a DLL or .obj file) using the varargs directive.
So a C (or C++) function like
HRESULT Invoke(void *pvReceiver, ...);
should be converted as:
function Invoke(pvReceiver: Pointer); cdecl; varargs;
Note that you leave out the ... part in the Delphi declaration. It is assumed when you use the varargs directive.
And that allows you to use it in Delphi like in C or C++:
Invoke(someReceiver, 17, 1.456);
Another example: for instance the well known C function printf():
int printf(const char *format, ...);
is declared as
function printf(format: PAnsiChar {args}): Integer; cdecl; varargs;
in System.Win.Crtl.pas.
Update
This seems to be a method of an interface.
Not sure if that works, but I would try:
type
TInvoke = function(Intf: IInterface; pvReceiver: Pointer): HRESULT; cdecl varargs;
// No semicolon between cdecl and varargs.
and use it as:
MyResult := TInvoke(#myIntf.Invoke)(myIntf, someReceiver, 11, 17.3);
This method may have been declared as __stdcall (through the STDMETHODCALLTYPE macro), but even then, if it is variadic, the (VC++) compiler will generate __cdecl, as Remy Lebeau found in Raymond Chen's blog.

Should I close a handle with IcmpCloseHandle?

I try to write a ping thread searching for connected devices to a local network. I need to avoid indy because it works on an administration account in windows.
I translated c++ example of msdn. Should I use IcmpCloseHandle? If so why should I close the handle which is just a cardinal variable. If not so what's the method for?
function IcmpSendEcho(ICMPHandle: Cardinal; DestinationAddress: Integer; RequestData: Pointer; RequestSize: Word; RequestOptions: Pointer; ReplyBuffer: Pointer; ReplySize: Cardinal; TimeOut: Cardinal): Cardinal; stdcall; external 'icmp.dll';
function TForm1.Ping(IPAddress: string; TimeOut: Integer): Integer;
var
ICMPHandle: Cardinal;
DestinationAddress: Integer;
ReplyBuffer: pICMPEchoReply;
RequestData: array[0..31] of AnsiChar;
ReplySize: Cardinal;
begin
ICMPHandle := IcmpCreateFile;
DestinationAddress := inetaddr(pansichar(AnsiString(IPAddress)));
RequestData := 'data buffer';
ReplySize := SizeOf(ticmpechoreply) + SizeOf(RequestData);
ReplyBuffer := AllocMem(ReplySize);
IcmpSendEcho(ICMPHandle, DestinationAddress, #RequestData, SizeOf(requestdata), nil, ReplyBuffer, ReplySize, TimeOut);
Result := replybuffer.Status;
FreeMem(ReplyBuffer);
end;
The documentation for IcmpCreateFile is rather weak. It should make the point that an handle successfully returned by IcmpCreateFile should be closed with a call to IcmpCloseHandle when it is no longer needed. So yes, you do need a call to IcmpCloseHandle.
You are mistaken in thinking that IcmpCreateFile returns a Cardinal. A Cardinal is a 32 bit type, but IcmpCreateFile returns HANDLE which is a pointer sized type. Your code will fail when compiled on 64 bit. Some of the other types that you use in your IcmpSendEcho header translation look dubious. If I were you I would either bone up on how to translate Win32 header files, and find and use trustworthy header translations.
I would also like to stress that your code makes no attempt whatsoever to handle errors. You are playing with fire here. We see this so many times here on SO, and I am sure I have personally told you this very thing on a number of early occasions. So, read the documentation for each API function that you call, and handle errors appropriately.
Unless you are an expert in Win32 API header translations and use, then you are likely to produce code that has defects. It might be prudent to use a third party header translation from, for instance, JEDI.

How to call GetProcessAffinityMask from Delphi XE5

I tried to use the below code snippet to call GetProcessAffinityMask in the windows api.
var
procaffmask,
sysaffmask : DWord;
begin
GetProcessAffinityMask(GetCurrentProcess, procaffmask, sysaffmask);
end;
Upon compilation I got the following error message......
[dcc32 Error] UnitfrmMain.pas(54): E2033 Types of actual and formal var parameters must be identical
The C++ syntax for the API call is below:
BOOL WINAPI GetProcessAffinityMask(
_In_ HANDLE hProcess,
_Out_ PDWORD_PTR lpProcessAffinityMask,
_Out_ PDWORD_PTR lpSystemAffinityMask
);
So then I changed the DWORD to a PDWORD but that did not fix it.
Can anybody tell me how to fix this? I see Delphi code samples around the internet that do not use pointers.
Here is the declaration of GetProcessAffinityMask() in Delphi's Winapi.Windows unit:
function GetProcessAffinityMask(hProcess: THandle;
var lpProcessAffinityMask, lpSystemAffinityMask: DWORD_PTR): BOOL; stdcall;
DWORD and DWORD_PTR are different data types. DWORD_PTR is not a "pointer to a DWORD", it is actually a "pointer-sized DWORD" (4 bytes on a 32bit system, 8 bytes on a 64bit system). Whereas DWORD is always 4 bytes on both 32bit and 64bit systems. Microsoft uses the P prefix to indicate a pointer to a type, and uses the _PTR suffix to indicate the type itself is dependent on the byte size of a pointer.
The Delphi declaration is using var parameters, so you have to match the data type exactly:
var
procaffmask,
sysaffmask : DWORD_PTR;
begin
GetProcessAffinityMask(GetCurrentProcess, procaffmask, sysaffmask);
end;
Even your quote of the C++ declaration shows that the parameters are PDWORD_PTR (pointer to DWORD_PTR).
instead of DWORD use SIZE_T, there will be no error

How to declare an array of C strings? (needed for a WinAPI function)

I want to use HttpOpenRequest to download a file from internet using GET. I don't know how to declare the AcceptType parameter. The MS documentations says that it is an array of strings. So I declare it like this:
CONST
AcceptType: packed array[0..1] of LPWSTR = (PChar('*/*'), nil);
I have done something wrong? LPWSTR is a pointer to a string, however, the documentation says that I need a string. How do I declare a matrix of strings that are compatible with C++ ?
procedure THTTPGetThread.Execute;
CONST
AcceptType: packed array[0..1] of LPWSTR = (PChar('*/*'), nil); // Originally was: AcceptType:= PWideChar('Accept: ' + FTAcceptTypes);
VAR
hConnect: hInternet;
FileName: String;
Data: Array[0..1024] of Char;
TempStr: PAnsiChar;
RequestMethod: PChar;
InternetFlag: DWord;
begin
...
hRequest:= HttpOpenRequest(hConnect, RequestMethod, PChar(FileName), PChar('HTTP/1.0'), PChar(FTReferer), #AcceptType, InternetFlag, 0);
...
end;
I use Delphi XE.
The MS documentation is here: http://msdn.microsoft.com/en-us/library/windows/desktop/aa384233(v=vs.85).aspx
My function is multi-threaded so it won't block the program while it downloads. Single threaded functions won't work for me.
You are almost there. You just need to pass a pointer to the first element of the array:
#AcceptType[0]
In fact, as #Serg points out, this is equivalent to your existing code. So it seems that, as you have commented below, the issue you are facing is unrelated to the passing of this parameter.
As an aside, I think I would use PWideChar rather than LPWSTR, but that's not the issue here since they are equivalent.

Resources