I have a C based DLL that exports a function that has char*** as an argument, this is a pointer to a pointer to a pointer of char (Non-unicode) My question is, after much frustration, what is the equivalent declaration at the Delphi end?
I've tried for example:
// C Method declaration
void method (char*** arg)
TArrayOfPAnsiChar = array of PAnsiChar;
PArrayOfPAnsiChar = ^TArrayOfPAnsiChar;
PPArrayOfPAnsiChar = ^PArrayOfPAnsiChar;
// Delphi declaration
procedure method (var p : PPArrayOfPAnsiChar); cdecl;
p : PPArrayOfAnsiChar;
method (p)
But I'm not getting anything sensible back. Any suggestions? The var in the declaration is of course already a pointer so perhaps that's where I am going wrong. I've tried many variants, written down on paper what the structure is and reverse engineered a Delphi declration but to no avail. This DLL is used in other C based applications so I assume its functioning correctly.
Using XE6
Don't use array of, because that declares a dynamic array, and use AnsiChar rather than Char, since the latter is two bytes in Delphi.
PPAnsiChar = ^PAnsiChar;
PPPAnsiChar = ^PPAnsiChar;
procedure Method(Arg: PPPAnsiChar); cdecl;
or
procedure Method(var Arg: PPAnsiChar); cdecl;
If you want to index into the variable like an array, add {$POINTERMATH ON} before declaring the types
In this case char*** is a pointer to a pointer that points to an array where each element points to a string.
This implies that the callee is returning data to the caller. So you want to use:
procedure method(out Arg: PPAnsiChar); cdecl; external dllname;
where PPAnsiChar is ^PAnsiChar. Note that PPAnsiChar is defined in the System unit.
The C code has three levels of indirection. The outermost is how function passses data from callee to caller. That's represented as a Delphi out parameter. The remaining two levels of indirection are captured in PPAnsiChar. Finally C's char is an 8 bit type on Windows and so maps to AnsiChar.
You will also need to iterate over the array when the function returns. Like this:
var
StrArr: PPAnsiChar;
Str: string;
....
method(StrArr);
while StrArr^ <> nil do
begin
Str := StrArr^;
Inc(StrArr);
end;
I'm assuming that the length of the array is indicated by a terminating null pointer. If instead you are told the length, you use a for loop as should be obvious.
Related
I have a C++ DLL:
int __stdcall Hello(int numFiles, char **inFiles)
and I don't know how to translate this char **.
I tried:
function Hello(numFiles: Integer; InFiles: PPChar): Integer; stdcall; external 'dll2.dll';
and then:
Files: array of PChar;
begin
SetLength(Files, 2);
Files[0] := PChar('siema.jpg');
Files[1] := PChar('siema2.jpg');
c := Hello(2, #Files[0]);
but all I get is "Access violation"
On the face of it, given the information in the question, your code seems basically fine. However, a rookie mistake, made by many beginners with interop, is to believe that a function's signature is enough to define the interface. It is not. For instance, a parameter of type char** could be used for many different things. So, to specify the information you must also specify the semantics of the parameters, not just the syntax.
Given
int __stdcall Hello(int numFiles, char **inFiles)
let us assume the following:
The return value is an error code, with 0 indicating success.
The first parameter is the number of files.
The second parameter is an array of files, of length specified by the first parameter. Each element of the array is a C string, a pointer to a null-terminated array of 8 bit characters.
With those assumptions in place, I would write the code like this:
function Hello(numFiles: Integer; InFiles: PPAnsiChar): Integer; stdcall;
external 'dll2.dll';
The function would be called like this:
var
Files: array of PAnsiChar;
retval: Integer;
....
SetLength(Files, 2);
Files[0] := PAnsiChar('siema.jpg');
Files[1] := PAnsiChar('siema2.jpg');
retval := Hello(Length(Files), PPAnsiChar(Files));
if retval <> 0 then
... handle error
You might prefer to write the final parameter as #Files[0] if you prefer. I prefer the cast because it allows me to pass an empty array even when range checking is enabled.
Note that I used PAnsiChar to match with char*. The PChar type is an alias to either PAnsiChar or PWideChar depending on the Delphi version. It is better to be explicit.
Of course, my assumptions may be wrong. You should confirm them by consulting the library's documentation.
I am Delphi 6 developer. And trying to call C++ dll function from delphi.
Dll written in C++ and the have Char pointer as out parameter.
Eg.
Function doGetStatus(Var szStatusData : Char) : Integer; cdecl; external 'WhizCal.dll';
When I call function from DLL I send char variable 'szStatusData' and return value should be 'Down' or 'Live' but it retuens only 'D' or 'L' this is because I have passed Char variable reference.
My question is, what can I do for getting return data as 'Down' or 'Live' without changing datatype.
A char is just a single character. So you cannot expect more.
Most likely the C++ code expects to be passed a pointer to an array of characters. Perhaps it should be like this:
function doGetStatus(szStatusData: PAnsiChar): Integer; cdecl; external 'WhizCal.dll';
And you might call it like this:
var
StatusData: array [0..255] of AnsiChar;
RetVal: Integer;
....
RetVal := doGetStatus(StatusData);
// check RetVal
We cannot tell you what exactly your code should be because we don't have any details of the specified interface. Unless the caller also passes the length of the array, such a function is bait for buffer overrun errors. As written above, this is the case. How can the caller know how much space to allocate. Well designed functions also allow the caller to pass the length of the array that they supplied.
My general advice to you is that interop is a topic that demands precision and detail, and knowledge of both sides of the boundary. You need to find out that detail.
I have problem with delphi code. I want to call the function in delphi to process the fortran function, but I have transferred to DLL. Here is code Fortran
SUBROUTINE c_zsn(m,d,k,f,zsn,nf)
! Specify that the routine name is to be made available to callers of the
! DLL and that the external name should not have any prefix or suffix
!MS$ ATTRIBUTES DLLEXPORT :: c_zsn
!MS$ ATTRIBUTES ALIAS:'c_zsn' :: c_zsn
!MS$ ATTRIBUTES VALUE :: m,d,k,nf
!MS$ ATTRIBUTES REFERENCE :: f,zsn
IMPLICIT NONE
INTEGER :: nf,i
REAL(KIND(0.D0)) :: m,d,k,f(0:(nf-1)),zsn(0:(nf-1)),om,pi
COMPLEX(KIND(0.D0)) :: j
j = (0.d0, 1.d0)
pi = 4.d0 * datan(1.d0)
do i=0,nf-1
om = 2.d0*pi*f(i)
zsn(i) = abs(-om**2*m-j*om*d+k)
end do
END SUBROUTINE
and here is code for the Delphi that I used
procedure TForm1.Button2Click(Sender: TObject);
type tarray=array[0..10]of double;
var a:thandle;
fcn:function(s,d,f:double;var g,h:tarray;n:integer):double;
e,f,d,g,h,i,j:double;
k:tarray;
l,o:tarray;
n,m:integer;
begin
a:=LoadLibrary('dllsub.dll');
if (A=0) then
begin
Application.MessageBox('Failed to open library','Error', MB_OK or MB_ICONEXCLAMATION);
exit;
end;
#fcn:=GetProcAddress(a, 'c_zsn');
if #b=nil then
begin
ShowMessage('Failed to open function');
exit;
end;
e:=2;
f:=200;
d:=0.01;
n:=10;
for m:=0 to n do
l[m]:=m;
fcn(e,d,f,l,o,n); // this is the problem
FreeLibrary(a);
end;
I cannot call the function (the bold one).
I would declare the function like this:
procedure c_zsn(
m: Double;
d: Double;
k: double;
f: PDouble;
zsn: PDouble;
n: Integer
); stdcall; external 'dllsub.dll';
You do need to specify the calling convention. You omitted that which meant that your code used the default register calling convention which is private to Delphi. I'm guessing that the calling convention is stdcall but it may be cdecl. Check the compiler documentation to be sure.
And it is not at all obvious to me why you declared a function that returns a double. The Fortran does not do that.
Other than that I changed the parameter names to match the Fortran code. I also switched to load time linking which is easier to code against. You can skip the calls to LoadLibrary and GetProcAddress and let the loader resolve the linkage.
Finally, I think the two arrays are better passed as PDouble (that is pointer to Double) rather than committing at compile time to fixed size arrays.
You can call the function like this:
c_zsn(e,d,f,#l[0],#o[0],n);
Do note that you have declared arrays of length 11 rather than length 10. Did you mean to do that? I think you should declare the arrays like this:
var
l, o: array [0..9] of Double;
One final point is that the Fortran code is very simple. It would be very easy indeed to translate it into Delphi.
Delphi has:
var : pass by reference; parameter is both input and output.
out : pass by reference; parameter is output only.
const: pass by ..... well it depends; parameter is input only.
in : pass by reference; parameter is input only and will not be changed there is no "in".
I don't mind that there is no spoon, but I miss in; considering the following code, is there a cleaner way of doing this?
type TFastDiv = record
strict private
FBuffer: Int64;
other fields
....
//Must be `var` because `const` would pass a Int64 by value
// |||
// VVV
function DivideFixedI32(var Buffer: Int64; x: integer): integer;
asm
mov r9,rcx
imul dword ptr[rcx] // do stuff with the buffer
..
mov ecx, [r9+4] // do other stuff with the rest of the buffer
{Changing the code to imul ecx;...;shr r9,32;mov ecx,r9d would allow pass by value, but let's assume the code must not be changed.}
class operator TFastDiv.IntDivide(x:integer; const buffer:TFastDiv):integer;
begin
Result:= DivideFixedI32(Int64((#buffer.FBuffer)^), abs(x)); <<-- Ugly
if (x < 0) then Result:= - Result;
end;
DivideFixed will never change the buffer. The whole point of the routine is that buffer is a precalculated value that does not change.
In the class operator I declare buffer as const, because the record must not change.
The question is:
If I insist on declaring the buffer parameter in IntDivide as const is there a cleaner way of coding or am I stuck in the pointer_to/points_to hack?
Newer compiler versions (from XE3 onwards) support the [Ref] decorator:
procedure Foo(const [Ref] Arg1: Integer; [Ref] const Arg2: Byte);
Example adapted from the documentation, which emphasises the [Ref] can go either before or after the const keyword.
The only option (pre Delphi XE3) if you want to ensure pass-by-reference, is to pass something big.
i.e. bigger than sizeof(pointer)
type TFastDiv = record
strict private
FBuffer: Int64; <<-- make sure this is the first member
other fields
....
function DivideFixedI32(const Buffer: TFastDiv; x: integer): integer;
...
class operator TFastDiv.IntDivide(x:integer; const buffer:TFastDiv):integer;
begin
Result:= DivideFixedI32(buffer, abs(x));
This line in the Delphi help file:
Sets, records, and static arrays of 1, 2, or 4 bytes are passed as 8-bit, 16-bit, and 32bit values. Larger sets, records, and static arrays are passed as 32-bit pointers to the value. An exception to this rule is that records are always passed directly on the stack under the cdecl, stdcall, and safecall conventions; the size of a record passed this way is rounded upward to the nearest double-word boundary.
is misleading and should be changed to/read as:
Sets, records, and static arrays up to SizeOf(pointer) are passed as 8-bit, 16-bit, and 32bit values (64 bit values on x64). Sets, records, and static arrays larger than SizeOf(Pointer) are passed as pointers to the value. An exception to this rule is that records are always passed directly on the stack under the cdecl, stdcall, and safecall conventions; the size of a record passed this way is rounded upward to the nearest SizeOf(pointer) boundary.
I'm working on c++ language bindings for our game engine (made with Delphi XE). How would I translate the array of OleVariant and array of const params to work properly in C++ side?
function DLM_CallRoutine(const aFullname: PWideChar;
const aParamList: array of OleVariant): OleVariant; stdcall;
function DLM_CreateObject(const aClassName: PWideChar;
const aParamList: array of const): Integer; stdcall;
Thanks.
Delphi has two completely separate array semantics that both use the same array of code syntax for different purposes.
When array of is used to declare a data type or variable, a Dynamic Array is being used, eg:
type
TIntegerArray: array of Integer;
var
Array1: TIntegerArray;
Array2: array of Integer;
In C++, these correspond to the DynamicArray<T> template class, eg:
typedef DynamicArray<int> TIntegerArray;
TIntegerArray Array1;
DynamicArray<int> Array2;
On the other hand, when array of is used in a function parameter directly (ie, without using a typedef), then an Open Array is being used instead, ie:
procedure DoSomething(Arr: array of Integer);
procedure DoSomethingElse(Arr: array of const);
Values passed to an Open Array parameter are passed by the compiler using two separate parameters - a pointer to the actual array, and the index of the last element in the array. Delphi hides this fact so the coder only sees one parameter, and provides a simple syntax for specifying the parameter values:
DoSomething([12345, 67890]);
DoSomethingElse(['Hello', 12345, True]);
In C++, however, the two parameters used for the array are explicitally declared, and values are typically specified using the OPENARRAY() and ARRAYOFCONST() macros, eg:
// despite their names, the Size parameters are actually indexes.
// This misnaming has already been slated to be fixed in a future
// C++Builder release...
void __fastcall DoSomething(int const *Arr, const int Arr_Size);
void __fastcall DoSomethingElse(TVarRec const *Arr, const int Arr_Size);
DoSomething(OPENARRAY(int, (( 12345, 67890 )) );
DoSomethingElse(ARRAYOFCONST(( "Hello", 12345, true )) );
When creating code with interfaces to other languages it is wise to avoid Delphi specific types and conventions. Where you know your code will be interfacing with Delphi code, you may choose to provide Delphi-friendly interfaces, and "wrappers" to map Delphi-friendly types and mechanisms onto more portable ones for those other languages.
So....
Array of OLEVariant
Since you are passing an array of variants, clearly your C++ code is variant aware/capable, in which case I would pass these values in a variant array themselves (passed as a Variant itself), preserving the dynamic nature of the array, but eliminating any concerns over Delphi RTL specific "magic" types (dynamic arrays).
So you may have a Fn(array of OLEVariant) for your Delphi code, which internally re-packages the array into a Variant Array of Variant before passing the call on to the actual API code (psuedo-code):
Fn(array of OLEVariant)
begin
arr := VarArrayCreate(...);
try
// ... init arr from array of OLEVariant parameter
// call actual API fn:
APIFn(arr);
finally
// Dispose of variant array
end;
end;
Array of Const
The values end up being passed in an array of TVarRec (not directly connected to variants, tho with a similar intention). Again this is a "magic" Delphi type - you will need to research the TVarRec type and map it to some equivalent C++ type.
In this case I would determine exactly what you need to pass in the params list and adopt a mechanism that is more portable between the two languages. Perhaps a simple string containing a name/value pair delimited string of parameter names and values?
In this case, providing a Delphi friendly wrapper around the API call would involve a slightly different approach from that which you are currently using (array of const), given that array of const does not provide for named entries in the array. Or you might choose simply to provide a delimited set of type/values, encoded in a string, in which case you could continue to use array of const for the Delphi side, with a wrapper function that processes the TVarRec array to format a string approriately for the API function.
If I remember correctly, dynamic arrays in Delphi store the size of the array in the first few bytes. You would have to declare your interface function as taking a pointer, plus a size:
function DLM_CallRoutine(const aFullname: PWideChar;
aParamList: POleVariant; numParams :integer): OleVariant; stdcall;
Callers would have to pass the address of the first actual element and the number of elements, #A[0] and Length(A) in Delphi.
Beware: There are memmory management/garbage collection issues wherever OleVariant and other OLE types are involved. You have to make sure that reference counts are incremented and decremented appropiately.
Delphi Dynarrays
This is correctly documented somewhere in the Delphi help, and can probably be learned from looking at the source code of System.pas and SysUtils.pas. What follows is from memory (sorry in advance for being of such little help).
A Delphi dynarray is a record:
type
TDynArray = record
refcount :integer;
size: :integer;
content :array[size*elementSize] of byte;
end;
#A[0] is equivalent is the same as #content. To get to the address that includes the refcount you'd have to do some casting.
Having C call Delphi
You could have the clients written in C manipulate the structures for Delphi dynarrays, But why impose Delphi implementation semantics on them? If C is calling into your code, then by all means use the C way of doing it, which is:
A pointer to an array of structures.
A number-of-records parameter.
The obligation of your API to copy what was passed as parameters so the caller can free what it likes right after the call. The C code owns what it passes in parameters.
The API exposed to C can be easily implemented with calls to the existing API.
I hope that helps.