Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Published interface properties bug and workarounds

I wrote a set of components that link to each other via published interface properties. They are registered and installed in a design package.

Using published interface properties is not that common in Delphi, and thus, unsurprisingly, doesn't seem to work that well.

It works fine when components reside on the same form, however interface property links between components on different forms cause issues.

Unlike object links to components on another form, interface links don't seem to be recognized by IDE. What I mean is best described by an example, when you have 2 forms open in IDE, and have links between components on them, then trying to switch to form view as text (Alt+F12) would cause IDE to correctly complain that:

Module 'UnitXXX.pas' has open descendents or linked modules. Cannot close.

But if the property is an interface then this does not happen, what happens instead is that the link is severed (and that's the best case scenario when you use Notification mechanism to clear references, otherwise you're left with an invalid pointer)

Another problem, likely as a consequence of the same bug is that when you open a project in IDE, the order in which forms will be reopened is undefined, so IDE can try to open a form that contains components that have interface links to components on another form, but that other form is not recreated yet. So this effectively results in either AV or severed links.

Back in 90s while I used Datasets and Datasources I remember similar issues with links between forms disappearing, so this is somewhat similar.

As a temp workaround I added duplicate published properties, for each Interface property I added another that is declared as TComponent. This makes Delphi aware there is a link between forms, but is an ugly workaround to say the least.

So I wonder if there is something I can do to fix this issue ? It's an IDE bug and likely not fixable directly, but perhaps I can override something or otherwise hook in to streaming mechanism to more effectively workaround this bug.

I haven't ever gone so deep into streaming mechanism, but I suspect the Fixup mechanism is supposed to deal with this. There is a csFixups TComponentState so I hope a workaround is possible.

Edit: Using D2007.

Update:

New updated reproducible example uploaded to http://www.filedropper.com/fixupbugproject2

Added property ComponentReference: TComponent so that it's easy to compare and trace interface vs component streaming.

I narrowed the problem down to assembler level which is a bit out of my depth.

In procedure GlobalFixupReferences in classes unit it calls:

(GetOrdProp(FInstance, FPropInfo) <> 0)

which eventually executes:

function TInterfacedComponent.GetInterfaceReference: IInterface;
begin
// uncomment the code bellow to avoid exception
{  if (csLoading in ComponentState) and (FInterfaceReference = nil) then
  // leave result unassigned to avoid exception
  else
}
    result := FInterfaceReference; // <----- Exception happens here
end;

As you can see from the comment, the only way I found to avoid the exception is to leave the result unassigned, but that breaks the functionality since comparison above in GlobalFixupReferences fails due to GetOrdProp <> 0, which severes the link.

tracing deeper the more exact location of exception is in

procedure _IntfCopy(var Dest: IInterface; const Source: IInterface); in system unit

This line in particular raises an read of address 0x80000000

{   Now we're into the less common cases.  }
@@NilSource:
        MOV     ECX, [EAX]      // get current value

So, why MOV fails and what's wrong with ECX or EAX I have no idea.

like image 481
Daniel Maurić Avatar asked Apr 26 '13 21:04

Daniel Maurić


People also ask

Can an interface have a read-only property?

Defining a default implementation for a property in an interface is rare because interfaces may not define instance data fields. In this example, the interface IEmployee has a read-write property, Name, and a read-only property, Counter. The class Employee implements the IEmployee interface and uses these two properties.

What is an interface property without a body?

Interface properties typically don't have a body. The accessors indicate whether the property is read-write, read-only, or write-only. Unlike in classes and structs, declaring the accessors without a body doesn't declare an auto-implemented property. Beginning with C# 8.0, an interface may define a default implementation for members, ...

Can an interface have a default implementation for a property?

Beginning with C# 8.0, an interface may define a default implementation for members, including properties. Defining a default implementation for a property in an interface is rare because interfaces may not define instance data fields.

How do you declare an interface property?

Interface Properties (C# Programming Guide) Properties can be declared on an interface. The following is an example of an interface property accessor: public interface ISampleInterface { // Property declaration: string Name { get; set; } } The accessor of an interface property does not have a body.


1 Answers

To summarize, the problem happens only with published interface properties that have a getter method, and the property points to component on another form/module (and that form/module is not recreated yet). In such case restoring form DFM causes an AV.

I'm pretty sure the bug is in the ASM code in GetOrdProp, but it's beyond my ability to fix, so the easiest workaround is to use a Field instead of a getter method and read it directly in the property. This is, fortunately good enough for my case currently.

Alternatively, you can declare the property as TComponent instead of interface, then write a TComponentProperty descendant, override ComponentMayBeSetTo to filter component that don't support the required interface. And of course register it using RegisterPropertyEditor

like image 114
Daniel Maurić Avatar answered Oct 16 '22 23:10

Daniel Maurić