I have a dll library. I have excluded memory unit for delphi types.
In that way, what would be the appropriate Boolean type for function declaration?
Is it BOOL or something else?
The problem is that in the method signature:
function Test(Param1: BOOL; Param2: BOOL; docContent: PCharArray): Integer;
I get AV when program leaves that function.
I assume that it is the problem with the data type of these two first parameters.
BOOL is fine for Boolean types. It's a Windows type, so it's what you'll see in all the functions in Windows.pas.
Access violations upon return from a DLL function often indicate that you have the calling convention wrong — the default calling convention is register, but you probably need stdcall or cdecl. Add it at the end of the declaration:
function Test(Param1: BOOL; Param2: BOOL; docContent: PCharArray): Integer; stdcall;
Related
Data block:
PMyDataBlock = ^MyDataBlock;
MyDataBlock = record
// .............
end;
Is a following definition:
function MyFunction(const pstSettings: MyDataBlock): HRESULT; stdcall; external 'MyLib.dll' name 'MyFunction';
a full equivalent of this?:
function MyFunction(pstSettings: PMyDataBlock): HRESULT; stdcall; external 'MyLib.dll' name 'MyFunction';
The short answer is "No, it is not"
In your case, your record may or may not be passed by reference. The size of the record is 1 of the factor I know of that affect that behavior. If your record is 4 bytes or less, I believe it will be passed by value, otherwise, it will be passed by reference. I don't believe this behavior is contractual (In other word, Embarcadero is free to change it at any time in the future). In other words, it's a bad idea to use const parameter to call an external function.
If you want to pass your record by reference, the proper way to do would be to declare it var
function MyFunction(var pstSettings: MyDataBlock): HRESULT; stdcall; external 'MyLib.dll' name 'MyFunction';
or pass it as a pointer.
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.
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.
I have converted all of the VB.NET DLL declarations
Declare Function SFileOpenArchive Lib "Storm.dll" Alias "#266" (ByVal lpFileName As String, ByVal dwPriority As Integer, ByVal dwFlags As Integer, ByRef hMPQ As Integer) As Boolean
over into Delphi.
function SFileOpenArchive(lpFileName: String; dwPriority, dwFlags, hMPQ: Integer): Boolean; stdcall; external 'Storm.dll';
However, when I call the functions and compile the program I get the following error:
The procedure entry point SFileOpenArchive could not be located in the dynamic link library Storm.dll.
I have ensured that:
the DLL is stored in the same directory as the application
the functions I have declared exist (I was given the source code which was coded in VB.NET)
What can I do to fix this error?
Thank you in advanced.
You have the following problems that I can see:
The function has been exported by ordinal rather than name. You need to specify the ordinal in your Delphi code.
The first parameter should be PAnsiChar rather than a Delphi string. The VB code uses string to instruct the marshaller to pass a null-terminated string. As a rule of thumb, it's almost always a mistake to include a Delphi managed stype like string in a DLL import or export.
The final parameter is passed by reference. That's a Delphi var parameter.
The return value should be BOOL, I think, but that probably won't be causing you trouble.
Put it all together like this:
function SFileOpenArchive(lpFileName: PAnsiChar;
dwPriority, dwFlags: Integer; var hMPQ: Integer): BOOL;
stdcall; external 'Storm.dll' index 266;
Note that I'm not 100% sure that the text encoding is ANSI. However, the main blockage for you was the ordinal import. I hope I've cleared that for you. I trust you can resolve the remaining details.
I need to wrap some legacy code in Delphi 7 for use within Delphi XE2. My question seems simple, but I tried ALL the examples I could find and they all fail. Basically, I need to be able to pass strings between D7 and DXE2, and as far as I can figure out, the safest approach is to use pchar (since I do not want to ship the borlandmm dll).
So DLL written in D7, to be called by Delphi XE2
My interface needs to be
IN My DLL:
function d7zipFile(pFatFile,pThinFile : PChar) : integer; stdCall;
function d7unzipfile(pThinFile,pFatFile : PChar) : integer; stdCall;
I need to pass BACK the pFatFile name in the unzipfile function.
In My calling code:
function d7zipFile(pFatFile,pThinFile : PChar) : integer; external 'd7b64zip.dll';
function d7unzipfile(pThinFile,pFatFile : PChar) : integer; external 'd7b64zip.dll';
Could someone please assist with the best way to implement these?
Obviously I am not looking for the actual zip/unzip code - I have that working fine within D7. I want to know how to declare and work with the string / pchar params, since the various types I tried (PWideChar, WideString, ShortString etc) all give errors.
So I would be happy to simply be able to do a showMessage in the d7zipFile function for both filenames.
And then be able to do a showMessage in delphiXE2 on the pFatFile variable, which means the strings went both ways OK?
By far the easiest way to do this is to use WideString. This is the Delphi wrapper around the COM BSTR type. Dynamic allocation of the string payload is done using the shared COM allocator. Since the Delphi RTL manages that, it is transparent to you.
In the Delphi 7 code you declare your functions like this:
function d7zipFile(const FatFile, ThinFile: WideString): integer; stdcall;
function d7unzipfile(const ThinFile: WideString; var FatFile: WideString):
integer; stdcall;
In your calling code you declare the functions like this:
function d7zipFile(const FatFile, ThinFile: WideString): integer; stdcall;
external 'd7b64zip.dll';
function d7unzipfile(const ThinFile: WideString; var FatFile: WideString):
integer; stdcall; external 'd7b64zip.dll';
The alternative to this approach is to use PAnsiChar or PWideChar. Note that you cannot use PChar because that alias refers to different types depending on which version of Delphi you use. In Delphi 7 PChar is an alias for PAnsiChar, and in XE2 it is an alias for PWideChar.
The big downside of using PAnsiChar, say, is that the caller needs to allocate the string which is returned from the DLL. But typically the caller does not know how large that string needs to be. There are a variety of solutions to the problem but the neatest approach is always to use a shared allocator. You state that you do not want to rely on borlandmm.dll and so the next most obvious common allocator is the COM allocator. And that's why WideString is attractive.