When I want to export a class within a DLL, is it the right approach to derive it from an interface and return that interface by an exported function?
//exported dll function, which is used in the exe.
function MyClass_Create: IMyClass;
begin
result := TMyClass.Create;
end;
What about the memory management? Can I than pass in/out different interfaces and strings without worries and crashes?
IMyClass = interface
procedure SetString(aMsg: string);
function GetString: string;
procedure SetClass(aClass: ITestClass);
function GetClass: ITestClass;
end;
Interface references are orthogonal to memory management. Usually you export a function that returns interface reference from dll, and don't care about memory management. With reference counting interfaces you can be sure that object instance that implements interface will be freed in dll too.
Strings are different. It does not matter whether you export interface or export flat functions - the same restrictions applies.
BTW your question title is incorrect, there are no 'interface instances' in Delphi.
Using interfaces like this will ensure that the object implementing the interface will be created and freed on the same heap.
However, this will not solve the problem of dynamic string types being allocated and deallocated on different heaps. There are many possible solutions to this, but in my view the best approach is to use WideString across the module boundary.
The WideString type is a wrapper around the COM BSTR and is allocated on the shared COM heap. You only need to use WideString for the interface. The internals of the implementing classes can use native Delphi strings.
Just as strings present problems so do dynamic arrays. Attempting to pas dynamic arrays across module boundaries is not safe. There is no solution analagous to WideString that is as convenient. You can use variant arrays but that's pretty clunky in comparison to WideString.
Related
Can anyone point me toward tutorials or best practice for using object interfaces in a TList?
I started implementing this model to take advantage of the reference counting as an alternative to TObjectList (that I use extensively). While I had no problems implementing the list it appears that doing this adds complexity in other ways.
Specifically because the list is only holding an interface I don't have access to the class fields (obvious). So I end up littering the code with "MyInterface as TMyClass" or creating a lot of functions to access the fields. Is there some trick that I am not aware of that makes an interface container more usable?
If you need to access certain methods, why are you hiding them with interfaces? An interface is just that: A description of the ways that others can interact [interface] with that object: If you want other classes to interact with your class as an interfaced object, those methods should be part of the interface.
Using an interface as a shortcut to simplify finalization and then hacking it with casting violates the entire concept of interfaces, and can also cause memory leaks and hard to track down AV's, because of the way you are mixing your references: sometimes interface, sometimes TObject.
Can anyone point me toward tutorials or best practice for using object
interfaces in a TList?
Your problem has nothing to do with TList. All TList does is hold references to your objects, however they are designed - it knows nothing about the specifics of your object's interface or implementation. Your problem is that you are not designing your classes and interfaces correctly, as #ArnaudBouchez has pointed out in the comments.
I started implementing this model to take advantage of the reference
counting as an alternative to TObjectList (that I use extensively)
Why is that necessary? To simplify finalization? TObjectList owns its objects by default. (And you can also set the OwnsObjects property ) . You don't need to use interfaces to automate your clean-up if you're using TObjectList. You just have to free the TObjectList instance, just as you have to free a TList. Unless you need interfaces for a specific reason, using Objects and TObjectList is arguably preferable, since as opposed to interfaces, your finalization is deterministic.
From XE Help:
Create TObjectList instance.
This overloaded method creates a TObjectList instance.
The OwnsObjects parameter is a boolean that indicates whether object
entries are owned by the list. If the object is owned, when the entry
is removed from the list, the object is freed. The OwnsObjects
property is set from the value of this parameter. The default is true.
When you free TObjectList, the objects are also deallocated when OwnObjects=True. See:
Clear a TList or a TObjectList (accepted answer there)
Know: Normally there is no reason for you as a Delphi developer to get involved at all with details of reference counting. If you feel you need to do so, something is wrong with your design at the fundamental level. Reference counting is implemented in TInterfacedObject, and works transparently. All of your Delphi interfaced objects should be derived from TInterfacedObject (or one of its sibling or derivative classes), as follows:
interface
...
type
IMyInterface=interface (IUnknown)
[GUID]
function GetValue:integer;
end;
TMyInterface=class(TInterfacedObject,IMyInterface)
protected
function GetValue:integer;
end;
implementation
...
TMyInterface.GetValue:integer;
begin
result := fValue;
end;
end.
Then use it like this:
procedure useValue;
var im:IMyInterface;
x:integer;
begin
im:=TMyInterface.create;
x:=im.GetValue;
showMessage(inttstr(x));
end;
When useValue returns, im is cleaned up - you don't have to do anything or worry about reference counting. TInterfacedObject takes care of all that.
Note that in useValue, you declare the reference as IMyInterface but call the class constructor for TMyInterface - that is how you must initialize the interface - an interface has no constructor. However, since your reference type is IMyInterface, that is how it is treated by the compiler - only methods exposed through IMyInterface will be accessible.
I need to be able to pass the same set of structures (basically arrays of different records) over two different interfaces
The first (legacy) which is working requires a pointer to a record and the record size
The second, which I am attempting to develop, is type-safe and requires individual fields to be set using Get/Set methods for each field
Existing code uses records (probably around 100 or so) with memory management being handled in a 3rd party DLL (i.e. we pass the record pointer and size to it and it deals with memory management of new records).
My original thought was to bring the memory management into my app and then copy over the data on the API call. This would be easy enough with the old interface, as I just need to be able to access SizeOf() and the pointer to the record structure held in my internal TList. The problem comes when writing the adapter for the new type-safe interface
As these records are reliant on having a known size, there is heavy use of array 0..n of char static arrays, however as soon as I try to access these via 2010-flavour RTTI I get error messages stating 'Insufficient RTTI information available to support this operation'. Standard Delphi strings work, but old short-strings don't. Unfortunately, fixing string lengths is important for the old-style interface to work properly. I've had a look at 3rd party solutions such as SuperObject and the streaming in MorMot, though they can't do anything out of the box which doesn't give me too much hope of a solution not needing significant re-work.
What I want to be able to do is something like the following (don't have access to my Delphi VM at the moment, so not perfect code, but hopefully you get the gist):
type
RTestRec = record
a : array [0..5] of char;
b : integer;
end;
// hopefully this would be handled by generic <T = record> or passing instance as a pointer
procedure PassToAPI(TypeInfo: (old or new RTTI info); instance: TestRec)
var
Field: RTTIField;
begin
for Field in TypeInfo.Fields do
begin
case Field.FieldType of
ftArray: APICallArray(Field.FieldName, Field.Value);
ftInteger: APICallInteger(Field.FieldName, Field.Value.AsInteger);
...
end;
end;
Called as:
var
MyTestRec: RTestRec;
begin
MyTestRec.a := 'TEST';
MyTestRec.b := 5;
PassToAPI(TypeInfo(TestRec), MyTestRec);
end;
Can the lack of RTTI be forced by a Compiler flag or similar (wishful thinking I feel!)
Can a mixture of old-style and new-style RTTI help?
Can I declare the arrays differently to give RTTI but still having the size constraints needed for old-style streaming?
Would moving from Records to Classes help? (I think I'd need to write my own streaming to an ArrayOfByte to handle the old interface)
Could a hacky solution using Attributes help? Maybe storing some of the missing RTTI information there? Feels like a bit of a long-term maintenance issue, though.
This Embarcadero article discussing memory issues for the XE7 IDE contains the following:
Be aware of the “Growth by Generics”
Another scenario that might depend on your application code and cause an increase of the memory used by the compiler and the debugger relates with the way generic data types are used. The way the Object Pascal compiler works can cause the generation of many different types based on the same generic definition, at times even totally identical types that are compiled in different modules. While we won’t certainly suggest removing generics, quite the contrary, there are a few options to consider:
Try to avoid circular unit references for units defining core generic types
Define and use the same concrete type definitions when possible
If possible, refactor generics to share code in base classes, from which a generic class inherits
The last item I understand. The first two I am less clear on.
Do these issues affect only IDE performance, or is there an impact on the size of the compiled code?
For instance, considering the second item, if I declare TList<Integer> in two separate units, will I get two separate chunks of code in each of those units in my executable? I certainly hope not!
Point 2. This refers to instantiating same generic type where possible. For instance using TList<Integer> in all places instead of having two generic types TList<Integer> and TList<SmallInt>.
Declaring and using TList<Integer> in several units will only include single copy of TList<Integer> in exe file. Also, declaring TIntegerList = TList<Integer> will result with same.
Generic bloat people are referring to relates to having complete TList<T> copy for each specific type you use even though underlying generated code is the same.
For instance: TList<TObject> and TList<TPersistent> will include two separate copies of TList<T> even though generated code could be folded to single one.
That moves us to Point 3. where using base class for common class code and then using generic classes on top of that to get type safety, can save you memory both during compilation and in exe file.
For example, building generic class on top of non generic TObjectList will only include thin generic layer for each specific type instead of complete TObjectList functionality. Reported as QC 108966
TXObjectList<T: class, constructor> = class(TObjectList)
protected
function GetItem(index: Integer): T;
procedure SetItem(index: Integer; const Value: T);
public
function Add: T;
property Items[index: Integer]: T read GetItem write SetItem; default;
end;
function TXObjectList<T>.GetItem(index: Integer): T;
begin
Result := T( inherited GetItem(index));
end;
procedure TXObjectList<T>.SetItem(index: Integer; const Value: T);
begin
inherited SetItem(index, Value);
end;
function TXObjectList<T>.Add: T;
begin
Result := T.Create;
inherited Add(Result);
end;
The code bloat they are talking about in the article (as it is about the out of memory issue in the IDE) is related to the generated DCUs and all the meta information that is held in the IDE. Every DCU contains all the used generics. Only when compiling your binary the linker will remove duplicates.
That means if you have Unit1.pas and Unit2.pas and both are using TList<Integer> both Unit1.dcu and Unit2.dcu have the binary code for TList<Integer> compiled in.
If you declare TIntegerList = TList<Integer> in Unit3 and use that in Unit1 and Unit2 you might think this would only include the compiled TList<Integer> in Unit3.dcu but not in the other two. But unfortunately that is not the case.
I'm trying to figure out a safe way of passing objects from a main app to a DLL (for a plugin system).
The idea is to use the main app's TZConnection (database access from Zeos Lib) in the dll's.
I'd rather not use Runtime Packages, and DLL must be the way to go (I don't need BPL's need to recompile each time, and have no idea of how to use COM).
I've been told it's possible to do it with Interfaces.
I've never used them before, but been messing around with it, and managed to do it... But, I don't know if I did it right (as in, safe).
Here's my Interface unit.
unit PluginIntf;
interface
uses
ZConnection, ZDataSet;
type
IQueryResult = interface ['{743AB77E-7897-403E-A0D9-4D02748E565D}']
function GetRecordCount: Integer;
function GetDataSet: TZQuery;
end;
IPluginHost = interface ['{A5A416B4-437E-4A1E-B228-0F94D54840B0}']
function RunSql(const SQLString:WideString): IQueryResult;
end;
IPlugin = interface ['{8D9591C3-5949-4F0A-883E-6ABD02597846}']
function GetCaption: WideString;
end;
implementation
end.
In IQueryResult, I'm passing a TZQuery. It works, but... Is it safe?
Is there any other way to wrap it in the Interface?
Thank you
Nuno Picado
TZQuery is a class. Therefore it is not safe to pass one across the module boundary unless you use runtime packages. Use a class with DLLs and you actually have two separate types, one in each module.
You are correct that interfaces are safe for this but you need to restrict yourself to DLL interop safe types. That is simple types, records, pointers to arrays, interfaces, or combinations of these types.
You need to wrap TZQuery with an interface that exposes it's functionality.
Does anyone here know how Delphi represents a reference to procedure?
for example
var
proc: TProc;
...
proc = procedure begin beep end;
What do we got in "proc"?
I know that for "method variable" the memory representation is 4 bytes for the "procedure address" followed by 4 bytes for the "object address", but for "reference to procedure" is somewhat different and I cannot quite figure it out.
The reason I want this is because I have some legacy code that I want to make it work with references.
Does anyone know something about it?
Method references are implemented as a COM-style interface with a single method called Invoke, which has the same signature as the method reference.
So TProc looks like this:
type
TProc = interface(IInterface) // so inherits QI, AddRef, Release
procedure Invoke;
end;
It's a valid question to ask, as Delphi has interoperability with the C++ product. By using a pre-existing reference-counted type and idiom (COM lifetime rules), interop with C++ at the method reference level is possible.
Anonymous methods generate a hidden class which implements an interface isomorphic to the method reference interface, i.e. exactly the same shape, but not with the same symbolic identity. The hidden class doesn't implement the method reference interface directly because it may need to implement the interface multiple times (a single block may contain multiple anonymous methods all assigned to locations of the same method reference type).