Using interfaces to pass data from exe to dll in Delphi - 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?

Related

Delphi TThread descendant return result

SITUATION. I have created an unit with some classes to solve algebra stuff (congruences and systems), I am showing you the code:
type
TCongrError = class(Exception)
end;
type
TCongruence = class(TComponent)
//code stuff
constructor Create(a, b, n: integer); virtual;
end;
type
TCongrSystem = array of TCongruence;
type
TCongruenceSystem = class(TThread)
private
resInner: integer;
FData: TCongrSystem;
function modinv(u, v: integer): integer; //not relevant
protected
procedure Execute; override;
public
constructor Create(data: TCongrSystem; var result: integer; hasClass: boolean);
end;
I have decided to use TThread because this class has an Execute method that could take some time to finish due to the length of the parameters passed to the constructor. Here's the implementation:
constructor TCongruenceSystem.Create(data: TCongrSystem; var result: integer; hasClass: boolean);
begin
inherited Create(True);
FreeOnTerminate := true;
FData := data;
setClass := hasClass;
resInner := result;
end;
procedure TCongruenceSystem.Execute;
var sysResult, i, n, t: integer;
begin
sysResult := 0;
n := 1;
//computation
Queue( procedure
begin
ShowMessage('r = ' + sysResult.ToString);
resInner := sysResult;
end );
end;
PROBLEM
If you look at the Queue you see that I am using (just as test) the ShowMessage and it is showing the correct value of sysResult. The second line by the way has some problems that I cannot understand.
The constructor has var result: integer so I can have side-effect from the passed variable and then I can assign resInner := result;. At the end (in the Queue) I am giving resInner the value of sysResult and I expect result to be updated too due to the side effect of var. Why doesn't this happen?
I have made another test changing the constructor like this:
constructor TCongruenceSystem.Create(data: TCongrSystem; result: TMemo; hasClass: boolean);
//now of course I have resInner: TMemo
And changing the Queue to this:
Queue( procedure
begin
ShowMessage('r = ' + sysResult.ToString);
resInner.Lines.Add(sysResult.ToString);
end ); //this code now works properly in both cases! (showmessage and memo)
In the constructor I am passing TMemo which is a reference and ok, but isn't the original var result: integer passed as reference too? Why then it doesn't work?
I want to do this because I'd like to do something like this:
//I put var a: integer; inside the public part of the TForm
test := TCongruenceSystem.Create(..., a, true);
test.OnTerminate := giveMeSolution;
test.Start;
test := nil;
Where giveMeSolution is just a simple procedure that uses the variable a containing the result of the system. If this is not possible what could I do? Basically the result at the end of Execute is just an integer number that has to be passed to the main thread.
I have read about ReturnValue but I am not sure how to use it.
Basically the result at the end of Execute is just an integer number that has to be passed to the main thread.
I have read about ReturnValue but I am not sure how to use it.
Using the ReturnValue property is very easy:
type
TCongruenceSystem = class(TThread)
...
protected
procedure Execute; override;
public
property ReturnValue; // protected by default
end;
procedure TCongruenceSystem.Execute;
var
...
begin
// computation
ReturnValue := ...;
end;
test := TCongruenceSystem.Create(...);
test.OnTerminate := giveMeSolution;
test.Start;
....
procedure TMyForm.giveMeSolution(Sender: TObject);
var
Result: Integer;
begin
Result := TCongruenceSystem(Sender).ReturnValue;
...
end;
Let's assume a class field FFoo : integer; ;
procedure TFoo.Foo(var x : integer);
begin
FFoo := x;
end;
Here what you are doing is assigning the value of x to FFoo. Inside the method Foo you are free to modify the value of the variable passed in as x but integers are otherwise value types that are copied on assignment. If you want to keep a reference to an external integer variable you would need to declare FFoo (or, in your case, resInner) as a PInteger (pointer to an integer). For example (simplifying) :
TCongruenceSystem = class(TThread)
private
resInner: PInteger;
protected
procedure Execute; override;
public
constructor Create(result: PInteger);
end;
where
constructor TCongruenceSystem.Create(result: PInteger);
begin
inherited Create(True);
FreeOnTerminate := true;
resInner := result;
end;
which you would call as test := TCongruenceSystem.Create(#a); and assign:
{ ** See the bottom of this answer for why NOT to use }
{ Queue with FreeOnTerminate = true ** }
Queue( procedure
begin
ShowMessage('r = ' + sysResult.ToString);
resInner^ := sysResult;
end );
The reason it works with TMemo is that classes are reference types - their variables do not hold values but rather point to the address of the object in memory. When you copy a class variable you are only copying a reference (ie: a pointer) whereas for value types the contents of the variable are copied on assignment.
With that said, there's nothing stopping you from keeping the argument typed as var x : integer and taking a reference in your constructor :
constructor TCongruenceSystem.Create(var result: Integer);
begin
inherited Create(True);
FreeOnTerminate := true;
resInner := #result; {take the reference here}
end;
but this gives the caller the impression that once the constructor is complete that you have made any modifications to the variable you intend to and they are free to dispose of the integer. Passing explicitly as PInteger gives the caller a hint that your object will keep a reference to the integer they provide and that need to ensure the underlying variable remains valid while your class is alive.
And... with all that said, I still fundamentally don't like this idea. By taking in a variable reference like this you are offloading an atypical lifetime management issue to the caller. Passing pointers is best done in place where they are used at the point of transfer only. Holding onto a foreign pointer is messy and it's too easy for mistakes to happen. A far better approach here would be to provide a completion event and have the consumer of your class attach a handler.
For example :
{ define a suitable callback signature }
TOnCalcComplete = procedure(AResult : integer) of object;
TCongruenceSystem = class(TThread)
private
Fx, Fy : integer;
FOnCalcComplete : TOnCalcComplete;
protected
procedure Execute; override;
public
constructor Create(x,y: integer);
property OnCalcComplete : TOnCalcComplete read FOnCalcComplete write FOnCalcComplete;
end;
constructor TCongruenceSystem.Create(x: Integer; y: Integer);
begin
inherited Create(true);
FreeOnTerminate := true;
Fx := x;
Fy := y;
end;
procedure TCongruenceSystem.Execute;
var
sumOfxy : integer;
begin
sumOfxy := Fx + Fy;
sleep(3000); {take some time...}
if Assigned(FOnCalcComplete) then
Synchronize(procedure
begin
FOnCalcComplete(sumOfxy);
end);
end;
Which you would then call as :
{ implement an event handler ... }
procedure TForm1.CalcComplete(AResult: Integer);
begin
ShowMessage(IntToStr(AResult));
end;
procedure TForm1.Button1Click(Sender: TObject);
var
LCongruenceSystem : TCongruenceSystem;
begin
LCongruenceSystem := TCongruenceSystem.Create(5, 2);
LCongruenceSystem.OnCalcComplete := CalcComplete; { attach the handler }
LCongruenceSystem.Start;
end;
You'll also notice that I used Synchronize here instead of Queue. On this topic, please have a read of this question (I'll quote Remy...):
Ensure all TThread.Queue methods complete before thread self-destructs
Setting FreeOnTerminate := True in a queued method is asking for a memory leak.

Reference to interfaced object in two pleaces

unit example;
interface
type
ILettersSettings = interface
function Letters: String;
end;
INumbersSettings = interface
function Numbers: String;
end;
TSettings = class(TInterfacedObject, ILettersSettings, INumbersSettings)
private
fLoadedLetters: String;
fLoadedNumbers: String;
public
procedure LoadFromFile;
private {ILettersSettings}
function Letters: String;
private {INumbersSettings}
function Numbers: String;
end;
TNumbers = class
private
fNumbers: String;
public
constructor Create(settings: INumbersSettings);
end;
TLetters = class
private
fLetters: String;
public
constructor Create(settings: ILettersSettings);
end;
implementation
{ TSettings }
procedure TSettings.LoadFromFile;
begin
fLoadedLetters := 'abc';
fLoadedNumbers := '123';
end;
function TSettings.Letters: String;
begin
result := fLoadedLetters;
end;
function TSettings.Numbers: String;
begin
result := fLoadedNumbers;
end;
{ TNumbers }
constructor TNumbers.Create(settings: INumbersSettings);
begin
fNumbers := settings.Numbers;
end;
{ TLetters }
constructor TLetters.Create(settings: ILettersSettings);
begin
fLetters := settings.Letters;
end;
var
settings: TSettings;
letters: TLetters;
numbers: TNumbers;
begin
settings := TSettings.Create;
settings.LoadFromFile;
letters := TLetters.Create(settings);
numbers := TNumbers.Create(settings);
end.
I have object with settings for whole project.
settings := TSettings.Create;
settings.LoadFromFile;
I use this object to create two objects: numbers and letters, by inject it by constructor.
letters := TLetters.Create(settings);
numbers := TNumbers.Create(settings);
But I dont assign it to any variable inside constructor, just use it.
{ TNumbers }
constructor TNumbers.Create(settings: INumbersSettings);
begin
fNumbers := settings.Numbers;
end;
{ TLetters }
constructor TLetters.Create(settings: ILettersSettings);
begin
fLetters := settings.Letters;
end;
So at the begin of constructor there is made reference count = 1, and on the end of constructor reference count is decreace to 0, and object is destroyed.
So in line:
numbers := TNumbers.Create(settings);
There is inject nil and Runtime Error is raised.
How fix it?
The problem is that you are mixing two different approaches to lifetime management. You have a mix of reference counted lifetime management, and programmer controlled lifetime management.
Your variable settings is declared to be of type TSettings. Although you did not show that declaration, we know this to be so because you are able to call LoadFromFile. That's only possible if settings is declared to be of type TSettings.
Because settings is a class, this means that your code is responsible for its lifetime. As such, the compiler does not emit reference counting code when you assign to settings.
However, when you call TLetters.Create and TNumbers.Create, you pass interface references, to ILetters and INumbers respectively. For this code, the compiler does emit reference counting code. The reference count goes up to 1 when you obtain an interface reference, and then down to zero when that reference leaves scope. At which point the implementing object is destroyed.
The fundamental problem in all of this is that you have broken the lifetime management rules. You must not mix the two different approaches as you have done.
The usual policy that people adopt is to either use programmer controlled management always, or reference counted management always. The choice is yours.
If you wish to use reference counted management exclusively then you would need to ensure that all functionality of your settings class was available via interfaces. That would mean making sure that LoadFromFile could be called via an interface. Or perhaps arranging for it to be called by the constructor.
Alternatively you could switch to programmer controlled management. In that case you must not derive from TInterfacedObject. You might instead derive from a class like this:
type
TInterfacedObjectWithoutReferenceCounting = class(TObject, IInterface)
protected
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
end;
function TInterfacedObjectWithoutReferenceCounting.QueryInterface(const IID: TGUID;
out Obj): HResult;
begin
if GetInterface(IID, Obj) then begin
Result := S_OK;
end else begin
Result := E_NOINTERFACE;
end;
end;
function TInterfacedObjectWithoutReferenceCounting._AddRef: Integer;
begin
Result := -1;
end;
function TInterfacedObjectWithoutReferenceCounting._Release: Integer;
begin
Result := -1;
end;
But that comes with its own risks. You must make sure that you do not hold any references to the object after the object has been destroyed.
There are many ways to fix that... The simplest would probably be to have TSettings inherit from TComponent instead of TInterfacedObject.
TComponent implements IInterface but doesn't not implement the reference counting by default, so when the refcount is decremented, the object won't be destroyed. That also means you have to destroy it yourself.
TSettings = class(TComponent, ILettersSettings, INumbersSettings)
[...]
settings := TSettings.Create;
try
settings.LoadFromFile;
letters := TLetters.Create(settings);
numbers := TNumbers.Create(settings);
finally
Settings.Free;
end;

Implement stack of function pointers in Delphi

We have declared a type which can be used as a progress callback (such as loading every 10,000 lines from a gigantic log file):
// Declared in some base unit
TProcedureCallback = procedure() of object;
// Declared in the class that loads the events
procedure ReadEvents(callback: TProcedureCallback);
// Implementation of above method
procedure TEvents.ReadEvents(callback: TProcedureCallback);
var
nEvents: Integer;
begin
nEvents := 0;
// Read some events...
Inc(nEvents);
// ...and repeat until end of log file
// Every 10,000 events, let the caller know (so they update
// something like a progress bar)
if ((nEvents mod 10000) = 0) then
callback();
end;
// And the caller uses it like this
public
procedure EventsLoadCallBack();
// Implementation of callback
procedure TfrmLoadEvents.EventsLoadCallBack();
begin
// Update some GUI control...
end;
// And the events are loaded like this
events.ReadEvents(EventsLoadCallBack);
This all works very well...but I'd like to extend this to the TObjectStack container so that we can implement an automatic log off feature. The idea is that as each form is created, it registers a callback (ie. pushes it onto some system-wide stack). And when the form is destroyed, it pops the callback off the stack. If the auto log off occurs, you just unwind the stack and return the user to the main form and then do the rest of work associated with an automatic log off.
But, I cannot get it working...when I try and push a TProcedureCallback object onto the stack I get compiler errors:
// Using generic containers unit from Delphi 7
uses
Contnrs;
// Declare stack
stackAutoLogOff: TObjectStack;
// Initialise stack
stackAutoLogOff := TObjectStack.Create();
// Attempt to use stack
stackAutoLogOff.Push(callback);
stackAutoLogOff.Push(TObject(callback));
// Clean up...
stackstackAutoLogOff.Free();
The 1st returns Incompatible types and the 2nd Invalid typecast. What is the correct way to implement a stack of function pointers?
The problem is that TObjectStack expects to contain objects of type TObject and your callback is a TMethod type, which is a record containing two pointers.
If you are using a modern version of Delphi a simple solution is to use generics. For example:
TObjectProc = procedure of object;
TMyCallbackStack = TStack<TObjectProc>;
Without generics, you would need to build your own stack class to manage storage of the callbacks. This is a reasonably simple class to build and, at its most basic, might look something like this :
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
TMyClass = class
procedure foo;
end;
TObjProc = procedure of object;
TObjProcStack = class(TObject)
private
FList: array of TObjProc;
public
function Count: Integer;
procedure Push(AItem: TObjProc);
function Pop: TObjProc; inline;
function Peek: TObjProc; inline;
end;
function TObjProcStack.Peek: TObjProc;
begin
Result := FList[Length(FList)-1];
end;
function TObjProcStack.Pop: TObjProc;
begin
Result := Peek();
SetLength(FList, Length(FList) - 1);
end;
procedure TObjProcStack.Push(AItem: TObjProc);
begin
SetLength(FList, Length(FList) + 1);
FList[Length(FList)-1] := AItem;
end;
function TObjProcStack.Count: Integer;
begin
Result := Length(FList);
end;
{TMyClass}
procedure TMyClass.Foo;
begin
WriteLn('foo');
end;
var
LMyClass : TMyClass;
LStack : TObjProcStack;
begin
LStack := TObjProcStack.Create;
LMyClass := TMyClass.Create;
try
LStack.Push(LMyClass.foo);
LStack.Pop; {executes TMyClass.Foo - outputs 'foo' to console}
finally
LStack.Free;
LMyClass.Free;
end;
ReadLn;
end.
You can wrap the callback in an object and then use the standard Stack options. By wrapping that in your own class, you have a complete solution, like this:
unit UnitCallbackStack;
interface
uses
Contnrs;
type
TProcedureCallback = procedure() of object;
type
TMyCallbackObject = class // wrapper for callback
private
FCallBack : TProcedureCallback;
protected
public
constructor Create( ACallback : TProcedureCallback ); reintroduce;
property CallBack : TProcedureCallback
read FCallBack;
end;
type
TCallBackStack = class( TObjectStack)
private
public
function Push(ACallback: TProcedureCallback): TProcedureCallback; reintroduce;
function Pop: TProcedureCallback; reintroduce;
function Peek: TProcedureCallback; reintroduce;
end;
implementation
{ TCallBackStack }
function TCallBackStack.Peek: TProcedureCallback;
var
iObject : TMyCallbackObject;
begin
iObject := inherited Peek as TMyCallbackObject;
if assigned( iObject ) then
begin
Result := iObject.CallBack; // no delete here as reference not removed
end
else
begin
Result := nil;
end;
end;
function TCallBackStack.Pop: TProcedureCallback;
var
iObject : TMyCallbackObject;
begin
iObject := inherited Pop as TMyCallbackObject;
if assigned( iObject ) then
begin
Result := iObject.CallBack;
iObject.Free; // popped, so no longer needed
end
else
begin
Result := nil;
end;
end;
function TCallBackStack.Push(ACallback: TProcedureCallback): TProcedureCallback;
begin
inherited Push( TMyCallbackObject.Create( ACallBack ));
end;
{ TMyCallbackObject }
constructor TMyCallbackObject.Create(ACallback: TProcedureCallback);
begin
inherited Create;
fCallBack := ACallBack;
end;
end.
You can then use TCallBackStack the way you are trying to use TStack.

Is it possible to get a hash of an anonymous routine's implementation?

procedure DoSomething;
var
MyAnonymousProcedure : TProc;
begin
//assign an anonymous procedure to a variable.
MyAnonymousProcedure := procedure
begin
Foo;
end;
MyAnonymousProcedure(); //Call the newly assigned procedure.
// do the same thing again but with a different anonymous method.
MyAnonymousProcedure := procedure
begin
Bar;
end;
MyAnonymousProcedure();
end;
In the above code there are two anonymous procedures. They are assigned to the same TProc variable in turn. The code in each anonymous procedure is clearly different. Is there a way to find the executable code that the MyAnonymousProcedure variable references? I guess that would be a memory location. From there is it possible to then calculate the hash of the executable code found at that memory location?
Is there a way to find the executable code that the
MyAnonymousProcedure variable references?
There is always "a way" but it is tricky in this case.
First an anonymous method can be treated as a reference to interface with a single Invoke method as explained by Barry Kelly.
Applying the idea to your code we get:
procedure MethRefToProcPtr(const MethRef; var ProcPtr);
type
TVtable = array[0..3] of Pointer;
PVtable = ^TVtable;
PPVtable = ^PVtable;
begin
// 3 is offset of Invoke, after QI, AddRef, Release
TMethod(ProcPtr).Code := PPVtable(MethRef)^^[3];
end;
Unfortunately the ProcPtr value returned is not what you probably want - it is an address of a stub code that fixes an interface reference (converts an interface reference to an object reference) and jumps to the address we are looking for. If you trace the code pointed by ProcPtr you will find something like this (Delphi XE, 32-bits):
add eax,-$10
jmp FooBar
and at the FooBar address you will find
call Foo
or
call Bar
dependent of the current value of your anonymous method.
I guess the only way to get the FooBar address now is to parse the assembler jmp instruction.
Here is the code I used for my experiments:
procedure Foo;
begin
Writeln('Foo');
end;
procedure Bar;
begin
Writeln('Bar');
end;
procedure MethRefToProcPtr(const MethRef; var ProcPtr);
type
TVtable = array[0..3] of Pointer;
PVtable = ^TVtable;
PPVtable = ^PVtable;
begin
// 3 is offset of Invoke, after QI, AddRef, Release
TMethod(ProcPtr).Code := PPVtable(MethRef)^^[3];
end;
procedure DoSomething;
var
MyAnonymousProcedure : TProc;
MyProc : procedure;
begin
//assign an anonymous procedure to a variable.
MyAnonymousProcedure := procedure
begin
Foo;
end;
// MyAnonymousProcedure(); //Call the newly assigned procedure.
MethRefToProcPtr(MyAnonymousProcedure, MyProc);
Writeln(Format('%p', [#MyProc]));
Writeln(Format('%p', [#Foo]));
MyProc;
// do the same thing again but with a different anonymous method.
MyAnonymousProcedure := procedure
begin
Bar;
end;
// MyAnonymousProcedure();
MethRefToProcPtr(MyAnonymousProcedure, MyProc);
Writeln(Format('%p', [#MyProc]));
Writeln(Format('%p', [#Bar]));
MyProc;
end;
In addition to the other answer here is a routine that converts the compiler generated method stub that fixes the eax to the "real" method of the compiler generated class for the anonymous method.
procedure MethodStubToMethod(const Method; var Result);
var
offset: ShortInt;
begin
offset := PByte(TMethod(Method).Code)[2];
TMethod(Result).Code := PByte(TMethod(Method).Code) + 3;
TMethod(Result).Data := PByte(TMethod(Method).Data) + offset;
end;
It's a simple and naive implementation that assumes that the offset will never get bigger than one byte (which only would happen if you have hundred different anonymous methods within the same routine (like you have 2 in the original source in the question).
It assumes the layout of the stub is like this (which it for anonymous methods afaik)
add eax, offset
jmp address
Then you can write:
procedure MethRefToProcPtr(const MethRef; var ProcPtr);
type
TVtable = array[0..3] of Pointer;
PVtable = ^TVtable;
PPVtable = ^PVtable;
begin
// 3 is offset of Invoke, after QI, AddRef, Release
TMethod(ProcPtr).Code := PPVtable(MethRef)^^[3];
TMethod(ProcPtr).Data := Pointer(MethRef);
end;
procedure DoSomething;
var
MyAnonymousProcedure: TProc;
Method: procedure of object;
begin
//assign an anonymous procedure to a variable.
MyAnonymousProcedure := procedure
begin
Foo;
end;
MyAnonymousProcedure(); //Call the newly assigned procedure.
MethRefToProcPtr(MyAnonymousProcedure, Method); //
Method(); //same as calling the anonymous method
MethodStubToMethod(Method, Method)
Method(); // now we are calling the method directly on the object
end;

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.

Resources