How to pass interface type/GUID reference to an automation method in Delphi - delphi

In Delphi you can pass class references around to compare the types of objects, and to instantiate them. Can you do the same with interface references being passed to a COM automation server?
For example, you can define a method taking a GUID parameter using the type library editor:
function ChildNodesOfType(NodeType: TGUID): IMBNode; safecall;
In this function I would like to return automation types that support the interface specified by NodeType, e.g.
if Supports(SomeNode, NodeType) then
result := SomeNode;
But the Supports call always fails, I tried passing in the Guids defined in the type library but none of the different types (Ixxx, Class_xxxx, IId_Ixxxx) seem to work.

The SysUtils unit comes with at least five overloads of Supports, and they all accept a TGUID value for their second parameters.
You can indeed pass interface types as parameters, but they're really just GUIDs. That is, when a function expects a TGUID argument, you can pass it the interface type identifier, such as IMBNode or IUnknown. For that to work, though, the interface type needs to include a GUID in its declaration, like this:
type
IMBNode = interface
['{GUID-goes-here}']
// methods and properties
end;
When the first parameter to Supports is an interface reference, the function calls its QueryInterface method. If it returns S_OK, then Supports return true; otherwise, it returns false. When the first parameter is an object reference, then it first calls the object's GetInterface method to get its IUnknown interface, and calls Supports on that like before. If it doesn't work that way, then it falls back to asking for the requested interface directly from GetInterface. If you've implemented QueryInterface correctly on your object, or if you've used the default implementation from TInterfacedObject, then everything should work fine.
If Supports never returns true for you, then you should revisit some assumptions. Are you sure your node really supports the interface you're requesting? Go make sure the class declaration includes that interface. Make sure QueryInterface is implemented properly. And make sure SomeNode actually refers to the node you're expecting it to.

Related

Casting a JObject to a JList

To cast a JObject to a JList (or anything else, it doesn't matter, this is just an example), is just doing JList(MyJobject) a good way? I don't receive any error, but I'm not sure if it's the correct way to go.
When casting between different object types, you cannot use a plain type-cast. You must cast the JObject to ILocalObject and call its GetObjectID() method, and then pass that result to the Wrap() method of the destination class type, in this case TJList, eg:
For example:
var
MyJobject: JObject;
MyJList: JList;
MyJobject := ...;
MyJList := TJList.Wrap((MyJobject as ILocalObject).GetObjectID);
Or simpler (which is just a wrapper for the above):
var
MyJobject: JObject;
MyJList: JList;
MyJobject := ...;
MyJList := TJList.Wrap(MyJobject);
See What the purpose of doing (MyJobject as ILocalObject).GetObjectID
There are two possible problems with using plain typecast.
First, if particular Java class has not been initialized with previous Delphi code, its VMT table will not be initialized. Next, references returned by JNI calls are local references and they are only valid for the duration of particular native method.
JNI tips
Every argument passed to a native method, and almost every object
returned by a JNI function is a "local reference". This means that
it's valid for the duration of the current native method in the
current thread. Even if the object itself continues to live on after
the native method returns, the reference is not valid.
This applies to all sub-classes of jobject, including jclass, jstring,
and jarray. (The runtime will warn you about most reference mis-uses
when extended JNI checks are enabled.)
The only way to get non-local references is via the functions
NewGlobalRef and NewWeakGlobalRef.
If you want to hold on to a reference for a longer period, you must
use a "global" reference. The NewGlobalRef function takes the local
reference as an argument and returns a global one. The global
reference is guaranteed to be valid until you call DeleteGlobalRef.
Wrap solves both issues. It initializes Java class VMT if not already initialized and converts local JObject reference to global one.
Plain typecast can only work if class is initialized by some previous code and the local reference is not used outside native (Delphi) method that retrieved said reference.
That is why plain typecast used in JStringToString(JString(PurchaseDataList.get(I))) can work properly. JObject reference returned by get is immediately converted to Delphi string and JString VMT is already initialized at that point, being commonly used Java class.
When in doubt, using Wrap is safer, but it also takes more time than plain typecast.

A common ancestor for of object delegates in Delphi

Is there some common ancestor for Delphi delegates which are declared with of object clause?
I need to find a common ancestor for TNotifyEvent and my custom delegate:
TMyEvent = procedure(Sender: TObject; msg: stringh); of object;
to make an universal method for firing these events.
Should I use Pointer? or TObject?
You need to get down and dirty with the implementation details for method pointers. These are stored as a so-called double pointer value. One pointer for the subject of the method call (the instance) and one pointer for the method itself (the code).
You can use the type TMethod from the System unit to represent method pointers. Its declaration looks like so (with the comparison operators removed for simplicity):
type
TMethod = record
Code, Data: Pointer;
end;
You need to use a typecast to make assignments between these types:
uses
System.Classes;
var
Event: TNotifyEvent;
Method: TMethod;
begin
Method := TMethod(Event);
TMethod(Event) := Method;
end.
Obviously none of this is type-safe so you need to ensure correctness. The compiler cannot help you. There is nothing like the checked type conversion operator, as, to work with method pointers. That is, it is up to you that when you cast from TMethod to a specific method pointer type, you have to make sure that the TMethod instance really is an instance of the method pointer type to which you cast. Think of this whole process as being analogous to casting from a typed pointer to an untyped pointer, and then back again.
Now, if you are going to store arbitrary method pointers into TMethod instances, that's fine. But what happens when you subsequently need to fire these methods. You need to know which type of method pointer is really behind each TMethod instance. So that you know how to cast it, what arguments it needs, and so how to call it. That's going to mean you have to store extra information about the true type of the method, alongside the raw method itself.
So, I think that I have perhaps answered the question that you asked, but I'm not sure it's going to be of much use to you. To understand that I think we'd really need to know more about what you are trying to achieve, and what information you have, when.
For instance, if you know the arguments that are to be passed to the method at the time you need to store it away, you could use variable capture and wrap it in an anonymous method. That would allow you to retain type-safety and avoid any of the rather dubious casts that I demonstrate above. Perhaps you need partial application, as a means of adapting your non-homogeneous method pointers to have the same interface. In which case again anonymous methods can help.

Casting TList<T:class> to TList<W:class>

I have a list of type TList<TForm>. I need to cast it and use it as TList<TObject> like this:
procedure mainForm.testCast;
var
listT: TList<TForm>;
listW: TList<TObject>;
obj: TObject;
begin
listT := TList<TForm>.create;
listT.add(form1);
listT.add(form2);
listW := TList<TObject>(listT); // Casting is OK
// This works, but is this fine?
for obj in listW do
memo1.lines.add(obj.className);
end;
The sample works as expected, but is it ok to cast like this between generic lists? Will this cause some data structure corruption etc?
I use it only for looping (DoGetEnumerator) purposes and some string checks i.e. I'll not add/remove items.
The real function is little more complicated. It gets reference to listT using RTTI in a TValue.
The main goal is not to link FMX.Forms in my unit.
Update:
Why are TGeneric<Base> and TGeneric<Descendant> incompatible types?
Well, your code will work, but it somewhat dubious in my view. Simply put the cast is not legal because
TList<TForm>.InheritsFrom(TList<TObject>)
is false. So a TList<TForm> object is not a TList<TObject>. If it were, then the cast would not be needed.
That this is so is because Delphi's generic types are invariant. More details can be found here:
Why is a class implementing an interface not compatible with the interface type when used in generics?
If you have any difficulty understanding why the designers made generic types invariant, consider for a moment the effect of writing listW.Add(TObject.Create) in your code. Think what it means to the true underlying object of type TList<TForm>.
So the language promises you nothing. You are venturing outside its guarantees. It so happens that the implementation of these two un-related types is compatible enough for your code to work. But that is really just an accident of implementation.
Since you are already using RTTI, then I suggest that you iterate over the list with RTTI. You can call GetEnumerator and so on using RTTI. That way you will call the actual methods of the object.

Internal (memory) representation of TProc and references at all

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).

How do I determine the type of the implementing object of an interface

I'm attempting to write a unit test for a simple factory class that creates one of several possible implementing objects and returns it as an interface reference.
DUnit has a built in procedure, CheckIs(AObject: TObject; AClass: TClass; msg: string), that based on its name and the parameters it accepts should fail the test if the object's class type doesn't match the expected one. The only problem is it requires an object reference not an interface reference.
So I'm trying to use CheckTrue and perform the comparison in the body of the test but I'm not as familiar with Delphi's type checking support as I am with C#'s.
I know the is operator is out of the question since it only works with object references.
CheckTrue(LMyInterfaceReference {comparison here} TMyClass);
Any suggestions?
BTW, I'm using Delphi 2009 so I don't have access to the new RTTI support added in 2010+.
I'm wondering why you MUST have to test this... maybe you really don't have to.
But if knowing the underlying object of a Interface is a must, you have two choices:
Add a method to the interface which returns the underlying object, just a TObject, and implement this in each class just by returning self.
Hack a bit, for example using this Interface to object routine.
If you don't like hacks and don't feel like upgrading to Delphi 2010+ you may use an interface like this:
IImplementingObjectInterface = interface
function GetImplementingObject: TObject;
end;
Make sure your objects also implement this interface and use it to extract the implementing object. If you need to do this for a lot of objects you can define your own TInterfacedObject derivate that already implements this so you can simply change your inheritance and be done.
Barry Kelly (one of the main Embarcadero Delphi Compiler Engineers) wrote a nice An ugly alternative to interface to object casting this week.
It answers your question.
The fun is that Hallvard Vassbotn wrote a very similar piece of code back in 2004.
From Delphi 2010 on, you can just use an is check or as cast to go back from interface references to object references.
--jeroen

Resources