I'm stuck with calling an external DLL and passing a function (pointer) as parameter.
I've recently had different problem of passing some arguments to DLL and you helped.
Hope, someone know how to do this as well....
Here's function declaration in DLL (cpp) that needs to be called from Delphi:
typedef void (*PTR_Allocate)(char**, unsigned long*);
typedef void (*PTR_Deallocate)(char*);
extern "C" export_dll_function void SetAllocateFunction(PTR_Allocate);
extern "C" export_dll_function void SetDeallocateFunction(PTR_Deallocate);
void Allocate(char** pbuffer, unsigned long* psize)
{
*psize = *psize * 2;
*pbuffer = new char[*psize];
}
void Deallocate(char* buffer)
{
delete[] buffer;
}
Could you please be so kind to help me rewrite this in Delphi (7) ?
Here's what I've tried and it throws an exception ("External exception"):
type
PByte = ^TByte;
TByte = array of byte;
TFunc = function(var pbuffer: PByte; var psize: Cardinal): integer; cdecl;
Procedure _SetAllocateFunction(var f: TFunc); cdecl;
implementation
function Allocate(var pbuffer: PByte; var psize: Cardinal): Integer; cdecl;
begin
psize := psize * 2;
GetMem(pbuffer, psize);
end;
var Func: TFunc;
Func := #Allocate;
_SetAllocateFunction(Func);
Thank you very much !
If you're not sure what you're doing, always start with the most literal translation. The function prototype says it receives a pointer to a pointer to a char, so that's what you should use:
type
PTR_Allocate = procedure(param1: ^^Char; param2: ^LongWord); cdecl;
Once you're sure it's right, then start replacing things with their more Delphi-like equivalents. If you skip this first step, you might never get it right because you'll just keep making changes to something that started out wrong.
So, are you sure the above is right? Not quite. Char in Delphi can have different meanings depending on the product version. You're using Delphi 7, but you might upgrade, so you might share this code with someone else, so you should be explicit about what size Char you want. Use AnsiChar when you need a one-byte type.
type
PTR_Allocate = procedure(param1: ^^AnsiChar; param2: ^LongWord); cdecl;
Now we can start making it look more like Delphi. One level of pointer parameter can be replaced with a "var" or "out" directive. Do that to each parameter:
type
PTR_Allocate = procedure(out param1: ^AnsiChar; var param2: LongWord); cdecl;
Pointer-to-AnsiChar is such a common type that Delphi already has a name for it: PAnsiChar. Use the idiomatic name:
type
PTR_Allocate = procedure(out param1: PAnsiChar; var param2: LongWord); cdecl;
And finally, you might wish to take some liberty with the whole notion that there are characters involved at all. You're clearly allocating memory for arbitrary byte buffers, so Byte is probably a better choice than any character type. Recent Delphi versions declare a pointer-to-byte type, so use that:
type
PTR_Allocate = procedure(out param1: PByte; var param2: LongWord); cdecl;
Now on to SetAllocateFunction. It says it receives a PTR_Allocate parameter, which is a pointer to a function. Delphi's procedure types are implicitly pointers, so the type we've declared above is already exactly right for the Delphi equivalent. Don't pass it by reference with an extra "var" directive or you will have the problems you've seen, even before your program attempts to allocate any memory. This is something the other answers have overlooked.
procedure SetAllocateFunction(param: PTR_Allocate); cdecl;
Don't add an underscore to the start of the name, either, unless you want to make it inconvenient to call in your own code. If it's exported from the DLL using a different name, then use a "name" clause when you write the function's implementation:
procedure SetAllocateFunction; extern 'foo.dll' name '_SetAllocateFunction';
Finally, how to implement the allocation function. Start with something that matches the signature for PTR_Allocate, and then go ahead and implement it using as literal a translation as possible from the original C++ code.
procedure Allocate(out pbuffer: PByte; var psize: LongWord; cdecl;
begin
psize := psize * 2;
GetMem(pbuffer, psize);
end;
You can set it with the function from before:
SetAllocateFunction(Allocate);
Notice I didn't need a separate variable and I haven't used the # operator. If you need to use the # operator to mention a function pointer, in most cases, you're doing it wrong. You usually don't need it. Using that operator can hide errors in your program, such as signature mismatches, because the default setting is for the # operator to be untyped. Using it removes the type from the function pointer, and untyped pointers are compatible with everything in Delphi, so they fit with any other function-pointer type, including the ones with wrong signatures.
Only use # on a function pointer when the compiler has already indicated that it has tried to call the function, such as by mentioning how you don't have enough parameters or by mentioning the function's return type.
I foresee a problem with the declaration of TByte as a Dynamic Array type. A dynamic array is itself a pointer.
Either declare it as an array
type
PByte = ^TByte;
TByte = Array[0..1.) of Byte;
or remove the PByte declaration
type
TByte = Array of Byte;
TFunc = function(var buffer:TByte; var pSize: Cardinal): Integer; cdecl
function Allocate(var buffer:TByte; Var pSize: Cardinal): Integer; cdecl;
begin
SetLength(buffer,pSize*2);
end;
As shunty already pointed out, both Allocate and Deallocate should be procedures in delphi. Furthermore, the psize parameter should be a pointer like the c declaration states.
And remember you must implement both functions to get this working properly or one memory manager will allocate while the other will deallocate the same memory, so the implementation shown in your example will keep failing even when the implementation of Allocate is correct.
You only need to change the type of the pointer to PByte = ^byte;
type
PByte = ^byte;
TByte = array of byte;
TFunc = function(var pbuffer: PByte; var psize: Cardinal): integer; cdecl;
Procedure _SetAllocateFunction(var f: TFunc); cdecl;
implementation
function Allocate(var pbuffer: PByte; var psize: Cardinal): Integer; cdecl;
begin
psize := psize * 2;
GetMem(pbuffer, psize);
end;
Related
I am trying to get a c-based DLL working in Delphi. I thought I'd try to get one of the functions to work before I set in on all the others, and I'm stymied. Here's the C call:
int FindCityCounty(char *zip, char cityname[28], char state[2], char countyname[25],
char countynum[3]);
I'm confused by the notion of char cityname[25]. Is that:
array[0..24] of Char/AnsiChar;
?
What's the proper way to translate these constructs? All my attempts have ended in jibberish.
UPDATE:
Here's an example of the function in question being called:
#include <stdio.h>
#include <Windows.h>
#include <srv.h> /* for CALLBACK, HINSTANCE */
#include <direct.h> /* for _getdrive and _getdcwd */
#define cpy_blank(word,len) memset(word,' ',len)
#define cpy_word strncpy
typedef int (CALLBACK* FindCityCounty)(
char ZIP[5],char cityname[28],char state[2],char countyname[25],char countynum[3]);
void runCA(FindCityCounty pCA)
{
char ZIP[5], cityname[28], state[2], countyname[25], countynum[3];
int rc;
printf("CorrectAddress C API Demonstration\n\n");
printf("FindCityCounty() function call\n");
printf("==================================\n");
/* set input parameters */
cpy_word(ZIP,"10601",5);
printf("Input ZIP code: %.5s\n", ZIP);
/* call function */
rc = pCA(ZIP, cityname, state, countyname, countynum);
/*** Now output results ***/
printf("Output city: %.28s\n",cityname);
printf("Output state: %.2s\n",state);
printf("Output county name: %.25s\n",countyname);
printf("Output county number: %.3s\n",countynum);
printf("\n\n");
printf("=====================================\n");
printf("(c) Intelligent Search Technology Ltd\n");
printf("=====================================\n");
}
int main()
{
HINSTANCE hDLL;
FindCityCounty pCA;
/* Load DLL and get function location */
hDLL = LoadLibrary("CorrectA.dll");
pCA = (FindCityCounty)GetProcAddress(hDLL,"FindCityCounty");
runCA(pCA);
getch();
return 0;
}
Delphi Code:
unit CorrectAdressAPI;
interface
type
TZip = array[0..4] of AnsiChar;
TCityName = array[0..27] of AnsiChar;
TState = array[0..1] of AnsiChar;
TCountyName = array[0..24] of AnsiChar;
TCountyNum = array[0..2] of AnsiChar;
PCityName = ^TCityName;
PState = ^TState;
PCountyName = ^TCountyName;
PCountyNum = ^TCountyNum;
type
TFindCityCounty = function(aZip: PAnsiChar;
var aCity: TCityName;
var aState: TState;
var aCounty: TCountyName;
var aCountyNum: TCountyNum): Integer; stdcall;
function Load(const AFileName : string) : Boolean;
function Unload : Boolean;
function FindCityCounty(aZip: PANSIChar;
var aCity: TCityName;
var aState: TState;
var aCounty: TCountyName;
var aCountyNum: TCountyNum): Integer;
implementation
uses
Winapi.Windows, dialogs, SysUtils;
var
hDll : THandle;
PFindCityCounty: TFindCityCounty;
function Load(const AFileName : string) : Boolean;
begin
hDll := LoadLibrary(PChar(AFileName));
Result := hDll <> 0;
if Result then
begin
PFindCityCounty := GetProcAddress(hDLL, PAnsiChar('FindCityCounty'));
Assert(Assigned(PFindCityCounty));
end;
end;
function Unload : Boolean;
begin
Result := (hDll <> 0) and FreeLibrary(hDll);
end;
function FindCityCounty(aZip: PANSIChar;
var aCity: TCityName;
var aState: TState;
var aCounty: TCountyName;
var aCountyNum: TCountyNum): Integer;
begin
Result := PFindCityCounty(aZip, aCity, aState, aCounty, aCountyNum);
end;
end.
As Mason says, arrays passed as parameters in 'C' are always passed as a pointer to the declared array type. However, a char array of fixed size is not certain to be a null terminated array, so using a PChar, PWideChar or PANSIChar would be potentially wrong.
To answer your specific question regarding the dimensions of the arrays, you appear to have mixed up your city and county names in your example, but essentially you are correct. C arrays are 0-based so the C declaration:
char name[N]
is equivalent to the Pascal:
name: array[0..N-1] of Char;
But getting the correct number of elements in the array is not the end of the story. You also need to be careful to ensure your array element type is declared correctly.
In the case of the char type you need to be careful that 'Char' is the correct type for the corresponding C environment when converting any C code. In Delphi 1-2007 'Char' is a single byte ANSIChar. In Delphi 2009 onwards 'Char' is a 2-byte WideChar.
In most (if not all) C implementations, certainly for Windows, char is an 8-bit/1-byte value, so an ANSIChar is most likely to be the appropriate Pascal type and ensures a 1-byte char on any Delphi version.
Returning to your specific example, the fact that explicitly dimensioned arrays are involved, rather than explicitly (or implicitly) null terminated strings, I think it may be advisable in this particular case to declare types for each of the required array types, with corresponding pointer types:
type
TCityName = array[0..27] of ANSIChar;
TState = array[0..1] of ANSIChar;
TCountyName = array[0..24] of ANSIChar;
TCountyNum = array[0..2] of ANSIChar;
PCityName = ^TCityName;
PState = ^TState;
PCountyName = ^TCountyName;
PCountyNum = ^TCountyNum;
You then have two choices in your Pascal version of the C function declaration. You could explicitly use the implicit pointer types that the C code produces 'under the hood':
function FindCityCounty(aZip: PANSIChar;
aCity: PCityName;
aState: PState;
aCounty: PCountyName;
aCountyNum: PCountyNum): Integer; cdecl;
Or you could use the array types themselves, but declare them as var parameters to co-erce the Pascal compiler to pass them by reference, thus producing implied pointers, as per the C code:
function FindCityCounty(aZip: PANSIChar;
var aCity: TCityName;
var aState: TState;
var aCounty: TCountyName;
var aCountyNum: TCountyNum): Integer; cdecl;
The effect is essentially the same in both cases, though personally I would favour the explicit pointers since the use of var parameters overtly suggests that the parameter values may/will be modified by the function call which is almost certainly not the case.
Note that the zip parameter is already being passed explicitly as a pointer to a char in the C code, so using the PANSIChar pointer type here is appropriate for this parameter in either case.
If this was a struct, you would be correct. But in C, arrays as function parameters are always pointers. According to this tutorial, this is still the case even in an array parameter with an explicit size given. So this should be translated as PChar or PAnsiChar, as appropriate.
Is there some trick how to get pointer of a member function in Lazarus / delphi?
I have this code which won't compile.... Error is
in Delphi:
variable required
in Lazarus:
Error: Incompatible types: got "<procedure variable type of function(Byte):LongInt of object;StdCall>" expected "Pointer"
The code:
TClassA = class
public
function ImportantFunc(AParameter: byte): integer; stdcall;
end;
TClassB = class
public
ObjectA: TClassA;
ImportantPtr: pointer;
procedure WorkerFunc;
end;
function TClassA.ImportantFunc(AParameter: byte): integer; stdcall;
begin
// some important stuff
end;
procedure TClassB.WorkerFunc;
begin
ImportantPtr := #ObjectA.ImportantFunc; // <-- ERROR HERE
end;
Thanks!
A member function cannot be represented by a single pointer. It needs two pointers, one for the instance and one for the code. But that's implementation detail and you just need to use a method type:
type
TImportantFunc = function(AParameter: byte): integer of object; stdcall;
You can then assign ImportantFunc to a variable of this type.
Since you are using stdcall I suspect you are trying to use this as a Windows callback. That's not possible for a member function. You need a function with global scope, or a static function.
type
TImportantFunc = function(AParameter: byte): integer of object;stdcall;
ImportantPtr: TImportantFunc;
procedure TClassB.WorkerFunc;
begin
ImportantPtr := ObjectA.ImportantFunc; // <-- OK HERE
end;
ObjectA.ImportantFunc is not a memory location, so address operator # can't be applied to it - hence compiler error. It is 2 pointers, #TClassA.ImportantFunc (method code) and ObjectA (Self argument). An answer to your question depends on what you really need - code pointer, Self, both or none.
If you need just to scope a function name use static class method
TClassA = class
public
class function ImportantFunc(Instance: TClassA; AParameter: byte): integer;
stdcall; static;
end;
In the Delphi code I am looking at I've found the following set of lines:
const
function1: function(const S: String): String = SomeVariable1;
function2: function(const S: String): String = SomeVariable2;
What is this doing? I mean, not the actual code within the functions, but what does it do to declare a function inside the const section and compare(?) it with a variable value? I'm assuming the single equals is a comparison since that's what it is everywhere else in Delphi.
Thank you.
No, the equals is an assignment, as this is how constants are assigned. Consider, for example,
const Pi = 3.1415;
or
const s = 'This is an example';
There are also 'typed constants':
const Pi: extended = 3.1415;
In your snippet above, we define a typed constant that holds a function of signature function(const S: String): String. And we assign the (compatible) function SomeVariable1 to it.
SomVariable1 has to be defined earlier in the code, for instance, as
function SomeVariable1(const S: String): String;
begin
result := S + '!';
end;
Consider the following example:
function SomeVariable1(const S: String): String;
begin
result := S + '!';
end;
const
function1: function(const S: String): String = SomeVariable1;
procedure TForm1.FormCreate(Sender: TObject);
begin
caption := function1('test');
end;
Andreas's answer covers the technical bits very well, but I'd like to provide an answer to this part:
What is this doing?
More along the lines of Why use this weired-looking construct? I can think of two reasons:
The code is written with {$J+} (assignable typed constants), and the "constant" is assigned a different value at some point. If function1 were declared as a variable, the initialization would need to be done in the initialization section of the unit, and that might be too late (if some other unit's initialization section runs before this one and attempts calling the function1 "function")
Used if the function name was changed from function1 to SomeVariable1 and there's 3rd party code that can't easily be changed. This provides a one-line way of declaring the alias.
I am new to Delphi. I have a DLL with the following exported function in it:
bool __stdcall MyFunction(char * name, int * index)
This code which calls this DLL function in C++ works perfectly:
typedef void (WINAPI * MyFunction_t)(char *, int *);
void main()
{
HMODULE mydll = LoadLibrary(L"C:\\mydll.dll");
MyFunction_t MyFunction = (MyFunction_t)GetProcAddress(mydll, "MyFunction");
int index = 0;
MyFunction("MyString", &index);
}
I need to do the same in Delphi. Here is my code, which is not working (MyFunction gets called but the index variable doesn't receive the appropriate value). This is a code excerpt so please ignore disorder. Any input would be much appreciated!
type
TMyFunction= function(name: PChar; var index_ptr: Integer): Boolean; stdcall;
var
fMyFunction : TMyFunction;
i : Integer;
h: THandle;
begin
Result := 0;
h := LoadLibrary('c:\\mydll.dll');
fMyFunction := GetProcAddress(h, 'MyFunction');
if #fMyFunction <> nil then
begin
fMyFunction('MyString', i);
Result := i;
end;
FreeLibrary(h);
end;
First of all I am assuming that you are using C linkage with extern "C" in case this function is defined in a C++ translation unit.
If you are using Delphi 2009 or later, you need to be aware that PChar is a pointer to a null-terminated wide character string.
To interop with your ANSI C function you need to use:
type
TMyFunction= function(name: PAnsiChar; var index: Integer): Boolean; stdcall;
The C bool type is probably best mapped to LongBool since it's not quite the same as a Delphi Boolean:
type
TMyFunction= function(name: PAnsiChar; var index: Integer): LongBool; stdcall;
You don't need to escape \ in strings so you can write:
h := LoadLibrary('c:\mydll.dll');
You probably ought to check for errors on the call to LoadLibrary and, technically, h is an HMODULE rather than a THandle, although that won't cause you any problems.
Idiomatic Delphi would be to write:
if Assigned(fMyFunction) then
fMyFunction('MyString', Result);
Basically it looks reasonable to me but I'm most suspicious of the character width.
Hope that helps.
Try not using STDCALL in your TMyFunction type.
I have an external function like this:
extern "C" __declspec(dllexport) int __cdecl Identify(BSTR* bstrTemplates, __int64 lCount, __int64* lIndex, __int64* lRetCode)
The bstrTemplates should be a string array.
How should my function look like in D7, and how to pass a string array to the external function. Can't get my head around right now.
A BSTR is a WideString in Delphi, and a pointer to a BSTR is also a pointer to a WideString in Delphi, but in terms of C-code, it is most likely an array reference. A typical way to handle such arrays, and I'm going to assume this is how it's done here, is to use a null-terminated array.
So, we need to declare an array of WideString's in Delphi, and leave the last element as null, or nil in Delphi:
var
templates : array of WideString;
begin
SetLength(templates, 3); // 2 template names + 1 nil
templates[0] := 'template1';
templates[1] := 'template2';
templates[2] := nil;
Identify(#templates[0], ....); // pass it as a pointer to the first element
I'm not guaranteeing this will work. I'm guessing here, and haven't tried it (which would involve creating a C project and testing) so this might fail horribly. Worth a shot though.
Finally solved the problem. It was the dynamic array. Looks like it can't be used as C-style array. Looks like the length prefix confused the c dll. For the records here the prototype and usage:
Type
type
TArrayOfWideString= array[0..999] of WideString;
Declaration
function Identify(var ATemplates: TArrayOfWideString; ATemplatesCount: int64; var ATemplateIndex: int64; var ARetCode: int64): Integer; cdecl; external 'Identify.dll';
Usage
var
templateIndex, retCode: int64;
templates: TArrayOfWideString;
retval: integer;
//TODO: range checking for TArrayOfWideString needed
templates[0] := 'template1';
templates[1] := 'template2';
retVal := Identify(templates, 2, scanIndex, retCode);
BSTR* is a pointer to a BSTR (in Delphi BSTR is a WideString).
EDIT: To make the answer complete (and make Rob Kennedy happy :-) ):
Most literal translation:
function Identify(bstrTemplates: PWideString; lCount: int64; lIndex: PInt64; lRetCode: PInt64): Integer; cdecl external 'mydll.dll';
or more the Delphi way:
function Identify(bstrTemplates: PWideString; lCount: int64; var lIndex: Int64; var lRetCode: Int64): Integer; cdecl external 'mydll.dll';
or even (but this depends if the bstrTemplates can be nil):
function Identify(var bstrTemplates: WideString; lCount: int64; var lIndex: Int64; var lRetCode: Int64): Integer; cdecl external 'mydll.dll';
Use the first element in the array when you pass bstrTemplates (eg #MyArray[0])