Calling C DLL from Delphi program - delphi

In my Delphi code I have to call a DLL's function (written in Visual C) with the following prototype:
int PWFunc(LPCSTR szName, int nWidth, int nHeight, LPCSTR szFileName)
How can I convert Delphi AnsiString variables (for Name and FileName) into right type parameters (LPCSTR szName and szFileName) of function call ?
I know that VC LPCSTR type corresponds to Delphi PAnsiChar type, but what is the right procedure to convert AnsiString to PAnsiChar ?

LPCSTR and LPSTR correspond to PAnsiChar, so that is what you use:
function PWFunc(szName: PAnsiChar; nWidth, nHeight: Longint;
szFileName: PAnsiChar): Longint; cdecl { or stdcall, see documentation };
external 'somedll.dll' name 'PWFunc';
You call it like:
X := PWFunc(PAnsiChar(AnsiString(SomeName)), 17, 33,
PAnsiChar(AnsiString(SomeFileName)));
Whether your function is stdcall or dcecl depends on compiler settings. Read the documentation. If in doubt, try both. It looks like cdecl to me, so start with that.

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.

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

Delphi 10 Seattle changes to Win32 GetPath and redundant TPoint and _POINTL record types

I am trying to port some code that works in Delphi XE8 to Delphi 10 Seattle. This code calls the GetPath function in Winapi.Windows.
The new Win32 API function signature is:
function GetPath(DC: HDC; Points: PPointL; Types: PByte; nSize: Integer): Integer; stdcall;
In XE8, previously the function had "var Points,Types" which is known commonly as a "var untyped" parameter.
Fixing the code to work with Delphi 10 Seattle means "unifying" the arbitrary types in the application code to use exactly the types declared in the unit itself. However what is confusing me is that there are two types, PPointL, and TPoint, and when I get the GetPath function working, the data it populates is populated into an array of _POINTL records, declared thus in Winapi.Windows:
type
_POINTL = record { ptl }
x: Longint;
y: Longint;
end;
{$EXTERNALSYM _POINTL}
PPointL = ^TPointL;
TPointL = _POINTL;
However, there is also another type TPoint, declared in System.Types:
TPoint = record
X: FixedInt;
Y: FixedInt;
public
Elsewhere, FixedInt is aliased to Longint for both 32 bit and 64 bit Windows, and so TPoint and _POINTL are equivalent, as far as I can tell, on the Windows platform at least.
If existing application component code is all using a type named TPoint, like this:
procedure AddPoint(const P:TPoint);
... What sense am I to make of the situation on the ground inside the RTL sources in Delphi 10? What should my approach to fixing this be? Alias TPoint to _POINTL at the unit level?
How do I fix this and proceed? Since this code is a commercial component, I'm thinking I'll wait until the vendor fixes this, but then, I think that understanding the _POINTL and TPoint in the RTL, and why these structures are redundantly/duplicated in definition, would help others porting low level Win32 Code from Delphi XE8 to Delphi 10 Seattle.
Update: As a workaround, I find I can re-declare an import of the function GetPath, and have it remain as var untyped in my own private unit implementation area import, and continue:
{$ifdef D23}
{$POINTERMATH ON}
// Delphi 10 Seattle: function GetPath(DC: HDC; Points: PPointL; Types: PByte; nSize: Integer): Integer; stdcall;
// previously had "var Points,Types" untyped,
const
gdi32 = 'gdi32.dll';
{$EXTERNALSYM GetPath}
function GetPath(DC: HDC; var Points, Types; nSize: Integer): Integer; stdcall; external gdi32 name 'GetPath';
{$endif}
There's not much to be said about this, beyond the fact that the change to Winapi.Windows.GetPath in DX Seattle is wrong. I mean, technically it will work, but it leaves any code that uses GetPath in an isolated silo.
This TPointL type is not new, but it is the wrong type for GetPath. The Win32 API function is:
int GetPath(
_In_ HDC hdc,
_Out_ LPPOINT lpPoints,
_Out_ LPBYTE lpTypes,
_In_ int nSize
);
And LPPOINT is POINT* and POINT maps to TPoint. There are some Win32 API functions that use POINTL, but the majority use POINT. Of course, Microsoft are not helping by having declared two identical types when one would suffice.
Very hard to see how the Embarcadero developer has managed to come up with POINTL in the new GetPath, but there you go. In my view you should submit a QP report and request that the declaration is changed from PPointL to PPoint.
In the meantime a simple cast will suffice because these two types are binary compatible. You wish to pass a PPoint, but the compiler wants PPointL. So pass PPointL(...) where ... is the expression that yields a PPoint.

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.

Call Delphi DLL from C++\CLI with many parameters

I have Delphi 2010 built DLL with two methods:
function Foo1(a, b: Integer):PChar; export; stdcall;
function Foo2(a, b, c:Integer):PChar; export; stdcall;
exports Foo1, Foo2;
Each of them returns Result := PChar('Test') .
My C++\CLI code
in header
typedef const wchar_t* (*pFUNC1)(int a, int b);
pFUNC1 TestFoo1;
typedef const wchar_t* (*pFUNC2)(int a, int b, int c);
pFUNC2 TestFoo2;
Initialize by LoadLibrary and GetProcAddress functions.
Usage: TestFoo1(0,0) and TestFoo2(0,0,0);
Both works in Release mode.
But in Debug mode Foo2 is being aborted.
Please advise what is wrong.
Most likely you have calling convention mismatch. Change the stdcall in the Delphi to cdecl to match your C++/CLI code.
As an aside, you will need to be careful with the lifetime of your strings if ever you attempt to return a value from the DLL that is not a literal stored in read-only memory in the data segment. But that's not the problem here because PChar('Test') has the same lifetime as the DLL.

Resources