Passing String array Values from Java native interface to Delphi wrapper class - delphi

I am trying to pass the String array values from Java native interface to Delphi wrapper class.
But didn't getting any array values from JNI. How can declare Array String in Delphi Wrapper class.
How to convert JNI String array.
This is my Delphi wrapper class Code:
function Java_com_erm_controller_ARMReports_A3(PEnv: PJNIEnv; Obj: JObject;
ex_BSName_ALM: JStrings): Integer; stdcall; export;
var
objRpt: TARMReports;
begin
Result := objRpt.A3(aBSName_ALM);
JVM.Free;
objRpt.Free;
end;

Without knowing what JStrings is actually declared as (it is not defined in Delphi's JNI wrapper units), I can only guess that you need to do something like this:
function Java_com_erm_controller_ARMReports_A3(PEnv: PJNIEnv; Obj: JObject;
ex_BSName_ALM: JStrings): Integer; stdcall; export;
var
strs: array of String;
len, i: Integer;
el: JNIObject;
...
begin
...
len := PEnv.GetArrayLength(PEnv, ex_BSName_ALM);
SetLength(strs, len);
for i := 0 to len-1 do
begin
el := PEnv.GetObjectArrayElement(PEnv, ex_BSName_ALM, i);
strs[i] := JStringToString(JString(el));
end;
...
end;

Related

Converting a generic TArray into a varArray

I'm trying to convert an array of T into a Variant (varArray).
With not-generic types (i.e: Integer), I'm using the following function:
function ToVarArray(AValues : array of Integer) : Variant;
var
i : integer;
begin
Result := VarArrayCreate(
[Low(AValues), High(AValues)],
varInteger
);
for i := Low(AValues) to High(AValues) do
Result[i] := AValues[i];
end;
I'm having some problems while trying to do the same thing with a generic TArray:
uses
System.Generics.Collections;
type
TArray = class(System.Generics.Collections.TArray)
public
class function ToVarArray<T>(const AValues: array of T) : Variant; static;
end;
I've tried the following:
class function TArray.ToVarArray<T>(const AValues: array of T) : Variant;
var
i : integer;
Tmp : T;
begin
Result := Tmp;
Result := VarArrayCreate(
[Low(AValues), High(AValues)],
VarType(Result)
);
for i := Low(AValues) to High(AValues) do
Result[i] := AValues[i];
end;
But It produces the following compile error at each row where I'm assigning a T to a Variant:
[dcc32 Error] Unit1.pas(36): E2010 Incompatible types: 'Variant' and
'T'
The title of the question says that you'd like to process generic TArray<T>, but the first sentence say it's array of T. You might think that both terms refer to the same data structure (dynamic array), and most of the time they do, but they make difference if you use them in place of a procedure/function argument declaration.
Therefore the following methods have different signatures and accept different types of parameters:
class function TArray.ToVarArray<T>(const AValues: TArray<T>): Variant;
class function TArray.ToVarArray<T>(const AValues: array of T): Variant;
While the first invariant accepts true dynamic array as a parameter, the latter takes an open array. This unfortunate language design is regular source of confusion for Delphi developers.
Delphi RTL already contains function that converts dynamic array to variant array of appropriate type - DynArrayToVariant. It takes pointer to initial element of dynamic array and array's type information as its arguments. To make use of generics you would write:
uses
System.Variants;
class function TArray.DynArrayToVarArray<T>(const AValues: TArray<T>): Variant;
begin
DynArrayToVariant(Result, #AValues[0], TypeInfo(TArray<T>));
end;
Your code would fail to compile if you try to use this routine with a static array:
var
SA: array[0..2] of Integer;
begin
SA[0] := 0;
SA[1] := 1;
SA[2] := 2;
{ E2010 Incompatible types: 'System.TArray<System.Integer>' and 'array[0..2] of Integer' }
TArray.DynArrayToVarArray<Integer>(SA);
end.
Open array solves this issue, but you need to "convert" it to dynamic array in order to use it with RTL's DynArrayToVariant.
class function TArray.OpenArrayToVarArray<T>(const AValues: array of T): Variant;
var
LArray: TArray<T>;
Index: Integer;
begin
SetLength(LArray, Length(AValues));
for Index := Low(AValues) to High(AValues) do
LArray[Index] := AValues[index];
Result := DynArrayToVarArray<T>(LArray);
end;
var
DA: TArray<Integer>;
SA: array[0..2] of Integer;
begin
DA := [0, 1, 2];
SA[0] := 0;
SA[1] := 1;
SA[2] := 2;
{ these all work }
TArray.OpenArrayToVarArray<Integer>(DA); // dynamic array
TArray.OpenArrayToVarArray<Integer>(SA); // static array
TArray.OpenArrayToVarArray<Integer>([0, 1, 2]); // open array constructor
end.
The above OpenArrayToVarArray<T> routine is pretty ineffective both in terms of performance and memory usage, because it copies elements one by one from open array to dynamic array. If you really need to support open arrays you should probably write better implementation inspired by RTL's DynArrayToVariant, however I find that one to be sub-optimal too.

dynamic array as type in delphi/pascal object

I have a unit in which there are multiple variables that has to be same sized vectors.
However I do not know the length of this array before i parse the file.
So I want to have a dynamic array that is "global" for the whole unit and then i can
The code below shows the issue as well as the solution I have now. The solution I have now is to assign a maximum value to be the length of the array.
unit xyz;
interface
uses
abc
const
maxval=50;
type
vectorofdouble = [1...maxval] of double; // I want to change this to dynamic array
type
T_xyz = object
public
NP: integer;
private
var1: vectorofdouble;
var2: vectorofdouble;
public
number: integer;
var3: vectorofdouble;
private
procedure Create();
function func1(etc): integer;
public
procedure ReadFile(const FileName, inputs: string);
end;
implementation
procedure T_xyz.ReadFile();
////////
Read(F,np)
//SetLength(vectorofdouble, np) // DOES NOT WORK
for i := 0 to maxval // I DONT WANT TO LOOP UP TO MAXVAL
begin
var1[i] := 0
end;
procedure T_xyz.func1(etc);
////////
do stuff
for i := 0 to maxval // I DONT WANT TO LOOP UP TO MAXVAL
begin
var2[i] := 0
end;
end;
end.
You want to use a dynamic array instead of a fixed-length array. You do that by using
array of <Type>
instead of
array[<Low>..<High>] of <Type>
Then SetLength() will work, but you need to pass it a dynamic array variable instead of a type.
Try this:
unit xyz;
interface
uses
abc;
type
vectorofdouble = array of double;
type
T_xyz = object
public
NP: integer;
private
var1: vectorofdouble;
var2: vectorofdouble;
public
number: integer;
var3: vectorofdouble;
private
procedure Create();
function func1(etc): integer;
public
procedure ReadFile(const FileName, inputs: string);
end;
implementation
procedure T_xyz.ReadFile();
var
i: integer;
begin
Read(F, NP);
SetLength(var1, NP);
for i := 0 to NP-1 do
begin
var1[i] := 0;
end;
end;
procedure T_xyz.func1(etc);
begin
for i := Low(var2) to High(var2) do
begin
var2[i] := 0;
end;
end;
end.
You must pass the array to SetLength rather than the type. So instead of
SetLength(vectorofdouble, np)
you must use
SetLength(var1, np)

Using interfaces to pass data from exe to dll in Delphi

I'm trying to pass an array of strings from host EXE to DLL in a loop. The host EXE is an external app which supports scripts (compiled and executed by a delphi-like compiler with certain limitations). The DLL is written by me in Delphi 2010.
So the problem is - I can't get rid of access violations when I pass a Widestring array to DLL. Whatever I tried - I got AVs sooner or later. And even if not, my statically loaded DLL was locked by host app, I can't rename or delete the DLL, so something is wrong.
There's an example of code which describes how to do it using interfaces (new for me which I liked a lot). Works like a charm and the DLL file is not locked.
General definitions (DLL + EXE)
type
// Your "structure"
TSomething = record
A: Integer;
S: WideString;
end;
// Your "structure list"
TSomethingArray = array of TSomething;
// The DLL and the EXE exchange data via Interface
IArray = interface
['{EE7F1553-D21F-4E0E-A9DA-C08B01011DBE}'] // press Ctrl+Shift+G to generate id
// protected
function GetCount: Integer; safecall;
function GetItem(const AIndex: Integer): TSomething; safecall;
// public
property Count: Integer read GetCount;
property Items[const AIndex: Integer]: TSomething read GetItem; default;
end;
DLL
// DLL, auxilary code:
type
// IArray implementation, which will be passed from DLL to EXE
TBaseArray = class(TInterfacedObject, IArray)
protected
FArray: TSomethingArray;
function GetCount: Integer; safecall;
function GetItem(const AIndex: Integer): TSomething; safecall;
end;
// Copying an array - for static and open arrays
TArray = class(TBaseArray)
public
constructor Create(const AArray: array of TSomething);
end;
// We don't have to copy an array for dynamic arrays
TArrayRef = class(TBaseArray)
public
constructor Create(const AArray: TSomethingArray);
end;
// We could create 1 class instead of 3 (TArray),
// but with 2 constructors (CreateByArr, CreateByDynArr).
// Or even with 1 constructor, if you work with 1 type of array.
// But it doesn't really matter, it's a matter of convenience.
// What really matters is a class which implements an interface.
{ TBaseArray }
function TBaseArray.GetCount: Integer;
begin
Result := Length(FArray);
end;
function TBaseArray.GetItem(const AIndex: Integer): TSomething;
begin
Result := FArray[AIndex];
end;
{ TArray }
constructor TArray.Create(const AArray: array of TSomething);
var
ArrIndex: Integer;
begin
inherited Create;
SetLength(FArray, Length(AArray));
for ArrIndex := 0 to High(AArray) do
FArray[ArrIndex] := AArray[ArrIndex];
end;
{ TArrayRef }
constructor TArrayRef.Create(const AArray: TSomethingArray);
begin
inherited Create;
FArray := AArray;
end;
// DLL, applied code:
function DoSomething1: IArray; stdcall;
var
A: array[0..2] of TSomething;
begin
// Some operations with array...
A[0].A := 1;
A[0].S := 'S1';
A[1].A := 2;
A[1].S := 'S2';
A[2].A := 3;
A[2].S := 'S3';
// Returning result
Result := TArray.Create(A); // <- An array is copied here
end;
function DoSomething2: IArray; stdcall;
var
A: TSomethingArray;
begin
// Some operations with array...
SetLength(A, 3);
A[0].A := 1;
A[0].S := 'S1';
A[1].A := 2;
A[1].S := 'S2';
A[2].A := 3;
A[2].S := 'S3';
// Returning result
Result := TArrayRef.Create(A); // An array isn't copied here, only reference counting
// We could also write:
// Result := TArray.Create(A);
// but the array would be copied in this case
end;
exports
DoSomething1, DoSomething2;
EXE
function DoSomething1: IArray; stdcall; external 'Project2.dll';
function DoSomething2: IArray; stdcall; external 'Project2.dll';
procedure TForm1.Button1Click(Sender: TObject);
var
A: IArray;
X: Integer;
begin
A := DoSomething1; // or DoSomething2
for X := 0 to A.Count - 1 do
OutputDebugString(PChar(IntToStr(A[X].A) + ' ' + A[X].S));
end;
This code works fine with my host app (when all OOP logic is in DLL). But I need the data to be passed from EXE to DLL.
So I swapped the code and the EXE became the 'heavier' part, everything was fine too, but only if both DLL and EXE are written in Delphi 2010.
If I use my host app and a static array, the compiler in my host app reports 'incompatible types' error at string in DoSomething1:
Result := TArray.Create(A);
When I write
Result := TArray.Create(A) as IArray;
it compiles but the app crashes.
If I use dynamic array, the compiler reports "Access violation at address 0D92062B. Read of address FFFFFFF8" here:
function TBaseArray.GetItem(const AIndex: Integer): TSomething;
begin
Result := FArray[AIndex];
end;
The author of this code said that if we want to pass data from EXE to DLL we need to use callbacks. And I just swapped the code. Maybe this is the problem? If so, how should I use callbacks here?

Delphi generics TObjectList<T> inheritance

I want to create a TObjectList<T> descendant to handle common functionality between object lists in my app. Then I want to further descend from that new class to introduce additional functionality when needed. I cannot seem to get it working using more than 1 level of inheritance. I probably need to understand generics a little bit more, but I've search high and low for the correct way to do this without success. Here is my code so far:
unit edGenerics;
interface
uses
Generics.Collections;
type
TObjectBase = class
public
procedure SomeBaseFunction;
end;
TObjectBaseList<T: TObjectBase> = class(TObjectList<T>)
public
procedure SomeOtherBaseFunction;
end;
TIndexedObject = class(TObjectBase)
protected
FIndex: Integer;
public
property Index: Integer read FIndex write FIndex;
end;
TIndexedObjectList<T: TIndexedObject> = class(TObjectBaseList<T>)
private
function GetNextAutoIndex: Integer;
public
function Add(AObject: T): Integer;
function ItemByIndex(AIndex: Integer): T;
procedure Insert(AIndex: Integer; AObject: T);
end;
TCatalogueItem = class(TIndexedObject)
private
FID: integer;
public
property ID: integer read FId write FId;
end;
TCatalogueItemList = class(TIndexedObjectList<TCatalogueItem>)
public
function GetRowById(AId: Integer): Integer;
end;
implementation
uses
Math;
{ TObjectBase }
procedure TObjectBase.SomeBaseFunction;
begin
end;
{ TObjectBaseList<T> }
procedure TObjectBaseList<T>.SomeOtherBaseFunction;
begin
end;
{ TIndexedObjectList }
function TIndexedObjectList<T>.Add(AObject: T): Integer;
begin
AObject.Index := GetNextAutoIndex;
Result := inherited Add(AObject);
end;
procedure TIndexedObjectList<T>.Insert(AIndex: Integer; AObject: T);
begin
AObject.Index := GetNextAutoIndex;
inherited Insert(AIndex, AObject);
end;
function TIndexedObjectList<T>.ItemByIndex(AIndex: Integer): T;
var
I: Integer;
begin
Result := Default(T);
while (Count > 0) and (I < Count) and (Result = Default(T)) do
if Items[I].Index = AIndex then
Result := Items[I]
else
Inc(I);
end;
function TIndexedObjectList<T>.GetNextAutoIndex: Integer;
var
I: Integer;
begin
Result := 0;
for I := 0 to Count - 1 do
Result := Max(Result, Items[I].Index);
Inc(Result);
end;
{ TCatalogueItemList }
function TCatalogueItemList.GetRowById(AId: Integer): Integer;
var
I: Integer;
begin
Result := -1;
for I := 0 to Pred(Self.Count) do
if Self.Items[I].Id = AId then
begin
Result := I;
Break;
end;
end;
end.
/////// ERROR HAPPENS HERE ////// ???? why is beyond me
It appears that the following declaration:
>>> TCatalogueItemList = class(TIndexedObjectList<TCatalogueItem>) <<<<
causes the following compiler error:
[DCC Error] edGenerics.pas(106): E2010 Incompatible types:
'TCatalogueItem' and 'TIndexedObject'
However the compiler shows the error at the END of the compiled unit (line 106), not on the declaration itself, which does not make any sense to me...
Basically the idea is that I have a generic list descending from TObjectList that I can extend with new functionality on an as needs basis. Any help with this would be GREAT!!!
I should add, using Delphi 2010.
Thanks.
Your error is in the type casting, and the compiler error is OK (but it fails to locate the correct file in my Delphi XE3).
Your ItemByIndex method is declared:
TIndexedObjectList<T>.ItemByIndex(AIndex: Integer): T;
But then you have the line:
Result := TIndexedObject(nil);
This is fine for the parent class TIndexedObjectList, where the result of the function is of type TIndexedObject, but is not OK for the descendant class TCatalogueItemList, where the result of the function is of the type TCatalogueItem.
As you may know, a TCatalogueItem instance is assignment compatible with a TIndexedObject variable, but the opposite is not true. It translates to something like this:
function TCatalogueItemList.ItemByIndex(AIndex: Integer): TCatalogueItem;
begin
Result := TIndexedObject(nil); //did you see the problem now?
To initialize the result to a nil value, you can call the Default() pseudo-function, like this:
Result := Default(T);
In Delphi XE or greater, the solution is also generic. Rather than type-casting the result as a fixed TIndexedObjectList class, you apply a generic type casting use the T type
Result := T(nil);
//or
Result := T(SomeOtherValue);
But, in this specific case, type-casting a nil constant is not needed, since nil is a special value that is assignment compatible with any reference, so you just have to replace the line with:
Result := nil;
And it will compile, and hopefully work as you expect.

How to set value to a dynamic array using the rtti? [duplicate]

I have a question. I am a newbie with Run Time Type Information from Delphi 2010. I need to set length to a dynamic array into a TValue. You can see the code.
Type TMyArray = array of integer;
TMyClass = class
publihed
function Do:TMyArray;
end;
function TMyClass.Do:TMyArray;
begin
SetLength(Result,5);
for i:=0 to 4 Result[i]=3;
end;
.......
.......
......
y:TValue;
Param:array of TValue;
.........
y=Methods[i].Invoke(Obj,Param);//delphi give me a DynArray type kind, is working, Param works to any functions.
if Method[i].ReturnType.TypeKind = tkDynArray then//is working...
begin
I want to set length for y to 10000//i don't know how to write.
end;
I don't like Generics Collections.
TValue wasn't designed for arbitrary manipulation of its contents (it would have more helpers for e.g. setting record fields etc. if so), but rather for transporting values between concrete static types and dynamic RTTI. In this respect, TValue.SetArrayElement is an anomaly, and in hindsight, perhaps should not have been included. However, what you ask is possible:
uses Rtti;
type
TMyArray = array of Integer;
TMyClass = class
function Go: TMyArray;
end;
function TMyClass.Go: TMyArray;
var
i: Integer;
begin
SetLength(Result, 5);
for i := 0 to 4 do
Result[i] := 3;
end;
procedure P;
var
ctx: TRttiContext;
v: TValue;
len: Longint;
i: Integer;
begin
v := ctx.GetType(TMyClass).GetMethod('Go').Invoke(TMyClass.Create, []);
Writeln(v.ToString);
len := 10;
DynArraySetLength(PPointer(v.GetReferenceToRawData)^, v.TypeInfo, 1, #len);
Writeln(v.GetArrayLength);
for i := 0 to v.GetArrayLength - 1 do
Writeln(v.GetArrayElement(i).ToString);
end;
begin
P;
end.

Resources