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])
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.
The TStream class contains many overloads of WriteData that are of this form:
function WriteData(const Buffer: Int32; Count: Longint): Longint; overload;
There are overloads for all the usual suspects, AnsiChar, Char, UInt32, Double and so on. Similarly for ReadData. I'm trying to understand what purpose the Count parameter serves. The implementation of the overload mentioned above is as follows:
function TStream.Skip(Amount: Integer): Integer;
var
P: Integer;
begin
P := Position;
Result := Seek(Amount, soCurrent) - P;
end;
function TStream.WriteData(const Buffer: Int32; Count: Longint): Longint;
const
BufSize = SizeOf(Buffer);
begin
if Count > BufSize then
Result := Write(Buffer, BufSize) + Skip(Count - BufSize)
else
Result := Write(Buffer, Count)
end;
I can obviously see what this code does, but I cannot understand why it would make sense to perform a partial write. Why would it ever make sense to call this function with Count < BufSize? The behaviour then is very odd.
Does anyone know why these overloads were added and what purpose they are intended for? Naturally I've looked at the documentation which has nothing to say about these methods.
As an aside I will submit bug report concerning this line:
Result := Write(Buffer, BufSize) + Skip(Count - BufSize);
It is a mistake to assume that the call to Write will occur before the call to Skip. The evaluation order of the operands to the + operator is not defined. This code should rather be written like this:
Result := Write(Buffer, BufSize);
inc(Result, Skip(Count - BufSize));
Theory crafting
if TStream predate the introduction of the overload keyword (Delphi 3 IIRC), they probably introduced a single method to write integer that was probably int32. When calling the function with a "byte" variable, it would get passed to the function as Integer, and then the Count parameter would only allow to write a single byte. Now they support this for backward compatibility purpose.
In some cases(like next one), supporting Count < Bufsize is indeed especially silly :
function WriteData(const Buffer: Int8; Count: Longint): Longint; overload;
Another justification would be in the next situation when a variable only need to be saved to stream as an Int8 but is worked on as a Int32 during program execution (because it is passed to a function that only takes a var : Int32 as parameter).
procedure SomeProc(var MyInt : Integer);
procedure DoSomeStream;
var
iVal : Integer;
// bVal : ShortInt;
begin
SomeProc(iVal);
Stream.WriteData(iVal, SizeOf(Byte));
//Instead of
// SomeProc(iVal);
// bVal := iVal;
// Stream.WriteData(bVal)
end;
I'm not saying it's required (can be worked around) but in some corner case situation, it could be useful.
For me it seems that this code enables you to write some data and than skip to a position far behind the data.
e.g. you have a stream containing multiple integers and you want to overwrite every 5th, you can do it with:
mData := 15;
WriteData(mData, SizeOf(mData) * 5);
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'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;