Pointer typecasting in Delphi - delphi

How can I correctly typecast to a structure in Delphi? This does not work exactly like in C++ where one would just pass a &Data according to the MSDN documentation.
program Project1;
uses
System.SysUtils,
Winapi.Windows,
Winapi.Winsock2;
function WSAStartup(wVersionRequired: WORD; out lpWSAData: LPWSADATA): Integer; WINAPI; external 'ws2_32.dll';
var
Data: WSADATA;
begin
WSAStartup(WINSOCK_VERSION, LPWSADATA(#Data)); // E2197 Constant object cannot be passed as var parameter
ReadLn;
end.

I guess that you've translated the function from the MSDN documentation which reads:
int WSAStartup(
_In_ WORD wVersionRequested,
_Out_ LPWSADATA lpWSAData
);
The confusion stems from the use of the _Out_ annotation. That is a macro that expands to nothing. It is used to convey intent to tools that, for instance, convert the header file declaration to different languages. More information can be found here:
What is the meaning of _In_ and _Out_ in documentation of any function?
What is the purpose of __in __out __in_opt __allowed(), how do they work? Should I use similar constructs in my own code?
You've erroneously translated _Out_ to the Delphi out keyword. You could simply remove that keyword and your declaration would be correct:
function WSAStartup(wVersionRequired: WORD; lpWSAData: LPWSADATA): Integer;
WINAPI; external 'ws2_32.dll';
Then your call would be:
WSAStartup(WINSOCK_VERSION, #Data);
Alternatively, since this parameter is not optional, you could translate it like this:
function WSAStartup(wVersionRequired: WORD; out lpWSAData: WSADATA): Integer;
WINAPI; external 'ws2_32.dll';
You would then call like this:
WSAStartup(WINSOCK_VERSION, Data);
You should however, use the declaration of the function that can be found in Winapi.Winsock2 and so avoid risking making such mistakes. That is, assuming that Embarcadero have not made mistakes in translation, which does sometimes happen.
Finally, it would be remiss of me were I not to chide you, at least mildly, for ignoring the return value of the function call.

Related

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.

Is the [Ref] attribute for const record parameters useful?

With the latest Delphi version (Berlin/10.1/24), is the [Ref] attribute really necessary?
I ask this because the online doc says:
Constant parameters may be passed to the function by value or by
reference, depending on the specific compiler used. To force the
compiler to pass a constant parameter by reference, you can use the
[Ref] decorator with the const keyword.
It's pretty much as described by the documentation. You'd use [ref] if you had a reason to enforce the argument to be passed by reference. One example that I can think of is for interop. Imagine you are calling an API function that is defined like this:
typedef struct {
int foo;
} INFO;
int DoStuff(const INFO *lpInfo);
In Pascal you might wish to import it like this:
type
TInfo = record
foo: Integer;
end;
function DoStuff(const Info: TInfo): Integer; cdecl; external libname;
But because TInfo is small, the compiler might choose to pass the structure by value. So you can annotate with [ref] to force the compiler to pass the parameter as a reference.
function DoStuff(const [ref] Info: TInfo): Integer; cdecl; external libname;
Another example is the new FreeAndNil procedure declaration in Delphi 10.4 in SysUtils.pas, which now finally ensures that only TObject descendants can be used with FreeAndNil. In previous Delphi versions, you could pass anything to this function, even if it didn't make sense.

What is exact signature of System.Assign (for use in procedural expression)?

Unfortunately, exact Assign signature is not available in the RTL source, and my attempts to guess, like:
const
Assign: procedure (var F; const FileName: string) = System.Assign;
{ or }
Assign: function (var F; const FileName: string): Integer = System.Assign;
{ also I tried "internal" one from _AssignFile }
didn't yield any positive results, and compiler refuses to treat this contant expression as procedural and complains about right-value (E2029 '(' expected but ';' found)).
So, which type should I use to match Delphi RTL exactly?
Assign is a language remnant of Delphi tracing its origins to the original Turbo Pascal syntax. Long before function overloading was added to the language syntax, long before the RTL's text file and "file of type" internal data structures were documented, there was Assign.
Assign is a simple enough procedure to use, but when you look at what it has to do you'll see that it's pretty much impossible to implement without some sort of compiler magic. File of TFoo creates a file type that is distinct and incompatible with File of Integer. And yet, both can be passed as the first parameter to Assign.
Today, you could probably implement Assign using a generic type param for the file parameter. The generic type would conform to the type of the variable passed in. That's great and all, but we needed a way to associate a typed file variable with a string filename 25 years before generics were added to the mix.
Assign is a compiler intrinsic function. That means it lives outside of the Delphi/Pascal syntax space. Assign is essentially a type conforming procedure, which can't be represented in the strongly typed Delphi/Pascal language (without modern and complex language extensions such as generic types). It's not typeless, and it's not dynamically typed because the actual parameter type is fully determined at compile time.
You can't take the address of the Assign function. Even if you could, because it is amorphous, you won't be able to define a procedure type that accurately represents all of its possible call signatures.
Compiler magic functions exist outside the language to take care of tasks that could not be easily represented in the syntax available at the time. Assign is one example. Writeln() is another.
System.Assign is an intrinsic function. The official documentation of it is completely hopeless. It says:
function Assign(var F: File; FileName: String; [CodePage: Word]): Integer; overload;
function Assign(var F: File; FileName: String; [CodePage: Word]): Integer; overload;
function Assign(var F: File; FileName: String; [CodePage: Word]): Integer; overload;
I can only guess as to why the documentation generator cannot cope with this function. But the three identical overloads are clearly bogus. And it's not a function, rather it is a procedure.
No matter. Because it is an intrinsic, you cannot assign it to a function pointer. The solution is to wrap it up in a function of your own.
procedure MyAssign(var F: File; const FileName: string);
begin
Result := System.Assign(F, FileName);
end;
You can then assign that function to a variable or constant of procedural type.
const
AssignProc: procedure(var F: File; const FileName: string) = MyAssign;
The other variant of Assign takes a third parameter that specifies the code page, passed as a Word. You can only call that function if the first argument to Assign is a TextFile.
So, intrinsics are really a law unto themselves.
Note that the documentation does state that Assign should no longer be used. Instead you should use AssignFile. The documentation is no better there mind you!
If to open System.pas file and read it, in XE2 you have those internal procedures:
function _AssignFile(var t: TFileRec; const s: PChar): Integer;
function _AssignText(var t: TTextRec; const s: PChar; const CP: word): Integer;
And i don't think you have any global stable procedure w/o underscore in names - those listed are Delphi compiler internals, like invisible procedures, that are covertly called when you declare string or dynarray variable.
http://en.wikipedia.org/wiki/Intrinsic_function
Like a lot of procedures like _Write0LString which are implementing usual WriteLn calls.
So you would have to make a wrapper function that would call AssignFile and put reference to your wrapper into your variable instead;
However you did not put tag with your Delphi version, and different vesions may have different declarations for intrinsics. So look into sources of your particular Delphi.
Also, why would you need it ? I remember i did such a thing in TurboPascal 5.5 to make string-based file - make WriteLn act like C sprintf. But why would you need it in modern Delphi, when you can use TStream instead?

Access violation at address 003B0E8E in module 'MpLib.dll'. Read of address 00120BF4

I've searched around Stackoverflow for the issue I was having, but they were all very specific for that particular access violation.
The piece of code I believe I am having trouble with is a function called MpqExtractFile, stored in a DLL.
Function:
function MpqExtractFile(var hMPQ: Integer; szToExtract, szExtracted: AnsiString): Boolean; stdcall; external 'MpqLib.dll' name 'B2';
Call:
if MpqExtractFile(hMPQ, 'war3map.j', AnsiString(tempDir+'\war3map.j')) = True
then ShowMessage('Success.')
else ShowMessage('Failed.');
Upon execution of this code my application throws the access violation error so the ShowMessage is not displayed at all.
I am unsure if the above is adequate to even estimate what the problem could be, but if there is anything else I should please tell me.
Edit
This is an extract of the VB.NET source code that I have been interpreting:
Declare Function MpqExtractFile Lib "MpqLib.dll" Alias "B2" (ByVal hMPQ As Integer, ByVal szToExtract As String, ByVal szExtracted As String) As Boolean
I am obviously not familiar with declarations in other languages, but I have found the below function in the same VB file where the VB functions were declared.
BOOL WINAPI MpqExtractFile(HANDLE hMpq, const char * szToExtract, const char * szExtracted);
Thank you in advanced!
AnsiString is the completely wrong type to use, unless the DLL was writting in Delphi/C++Builder and actually used AnsiString in its parameters. If that were the case, you would need to know which version of Delphi/C++Builder the DLL was written in, because the memory layout of AnsiString was changed in 2009.
In any case, it is very dangerous to pass non-POD data across DLL boundaries, so most DLLs do not do that. The parameters in question are most likely PAnsiChar instead, eg:
function MpqExtractFile(var hMPQ: Integer; szToExtract, szExtracted: PAnsiChar): Boolean; stdcall; external 'MpqLib.dll' name 'B2';
.
if MpqExtractFile(hMPQ, 'war3map.j', PAnsiChar(AnsiString(tempDir+'\war3map.j'))) then
ShowMessage('Success.')
else
ShowMessage('Failed.');
Other points to consider:
1) not all DLLs use the stdcall calling convention. It is not uncommon for DLLs written in C, like many open-source libraries are, to use the cdecl calling convention instead:
function MpqExtractFile(var hMPQ: Integer; szToExtract, szExtracted: PAnsiChar): Boolean; cdecl; external 'MpqLib.dll' name 'B2';
2) C does not have a true Boolean data type like Delphi and C++ do. It is not uncommon for C code to use Byte or even Integer to mimic a Boolean.
In order to use a DLL in Delphi, you really need to know the actual proper declaration of its exported functions. This is less of an issue in C/C++ because most DLLs have an accompanied .h file that provides the declarations. Do you have such a .h file? If so, post it here so someone can verify your translation to Delphi.
Update:
Based on new information, the correct Delphi declaration is this:
function MpqExtractFile(hMpq: THandle; const szToExtract, szExtracted: PAnsiChar): BOOL; stdcall; external 'MpqLib.dll' name 'B2';
Your VB.net declaration is:
Declare Function MpqExtractFile Lib "MpqLib.dll" Alias "B2" (
ByVal hMPQ As Integer, ByVal szToExtract As String,
ByVal szExtracted As String) As Boolean
The equivalent Delphi import would be:
function MpqExtractFile(MpqExtractFile: Integer;
szToExtract, szExtracted: PAnsiChar): BOOL;
stdcall; external 'MpqLib.dll' name 'B2';
Delphi string types should not be used for interop. The p/invoke marshaller maps String to C++ char* which is PAnsiChar in Delphi.
This sort of task really should be carried out with the C++ header file. You say you have not got that. If the DLL is written in C++ then the header file surely exists. It would pay to track it down and work from that as your source.

"Handle" Data Type Equivalent

I am reading the documentation for MoPaQ and trying to convert the functions over to Delphi because some of the data types mentioned in the documentation do not directly match the ones in Delphi.
This is the function I am having troubles with:
HANDLE WINAPI MpqOpenArchiveForUpdate(LPCSTR lpFileName, DWORD dwCreationDisposition, DWORD dwHashTableSize);
In Delphi, I have converted it as:
function MpqOpenArchiveForUpdate(lpFileName: Char, CreationDisposition, dwHashTableSize: LongWord); external 'lmpqapi.dll';
I am missing the return type for my function which I believe is HANDLE (according to the documentation). If that is the case, what is the Delphi equivalent for the HANDLE data type?
The equivalent would be something along these lines:
function MpqOpenArchiveForUpdate(lpFileName: LPCSTR;
dwCreattionDisposition,
dwHasTableSize: DWord): THandle; stdcall; external 'lmpqapi.dll';
Note that you'll have to find out if MoPacQ is Unicode-aware or not; if it's not, change the definition of lpFileName to PAnsiChar instead.
Remy points out in his comment below that Delphi maps LPCSTR to the proper PAnsiChar type on all versions, so you should use it that way.

Resources