Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

In this Discussion

Assigned function on 64 bits

I have a problem with a number of VCL components that are using the Assigned function in 64 bits. A few months ago I opened an issue about this problem in component TTimer (and a workarround to solve it in that case), but now I have the same problem with other components. The link of the previous item is:

https://forum.lmd.de/discussion/992/error-adding-ttimer-to-a-form-only-in-64-bits#latest

The problem is the next:

- A component has an event assigned
- The code of the component is using Assigned function to detect if the event is assigned or not
- The code of the component is not making any kind of checking about if the component is in DesingState or not to fire the event (it is only checking if the event is Assigned)
- In 64 bits, in design mode, Assiged function is returning true and the component tries to fire the event (in desing mode), generating an exception.

This problem was first time detected in TTimer tick, but now I have the same problem with TPageControl (OnChage event). The implementation of this event in VCL is the following one:


procedure TPageControl.Change;
var
  Form: TCustomForm;
begin
  if TabIndex >= 0 then
    UpdateActivePage;
  if csDesigning in ComponentState then
  begin
    Form := GetParentForm(Self);
    if (Form <> nil) and (Form.Designer <> nil) then
      Form.Designer.Modified;
  end;
  inherited Change;
end;

The invocation to the base is clase is made even if the component is in designe state. The implementation in the base class is:

procedure TCustomTabControl.Change;
begin
  if Assigned(FOnChange) then FOnChange(Self);
end;

In 64 bits mode, even in design time, the OnChague event is fired, and an exception is generated.

Do you have any solution to this problem? It affects to most of the VCL component and makes to work in 64 bits mode almost impossible.



Comments

  • 9 Comments sorted by Votes Date Added
  • Hi,

    We already discussed previously that, unfortunately, there no solution for this issue. 

    One possible workaround will be to implement and use descendants of the standard controls, like:

    type
      TMyPageControl = class(TPageControl)
      protected
        procedure Change; override;
      end;

    procedure TMyPageControl.Change; 
    begin
      if AssignedEx(@OnChange) then OnChange(Self);
    end;

    And I can provide the implementation of AssignedEx, if you interested in this way.


  • Hello Eugene, 

    this is the solution I have implemented in this case. But the problem is that I don't know when another component will fail. Now, our product is used by hundreds of customers and each time they notify me a problem I have to modify the control that they are using. It is a problem for us. 

    This problem could not be solved modifing the way as the event handled is stored in desing mode? it seems that the problem is that during design time the event handler is already assigned, allowing the component to fire it. If the event handled was not assigned to the component during design time, and making the assigment when the controlgoes to runtime mode I think the problem could be solved.
  • Can you send me an implementation of AssignedEx function?
  • function AssignedEx(P: Pointer): Boolean;
    const
      MASK = {$IFDEF CPUX64}$FFFFFFFFFFFF0000{$ELSE}$FFFF0000{$ENDIF};
    begin
      Result := (NativeUInt(P) and MASK) <> 0;
    end;


  • Hello Eugene,

    I have made some modifications on LMDDsgModule.pas to solve this problem. The general idea is to have all event handlers detached during designer-time and only attach them once the form pass to runtime. To do this, Inhave made the following modifications:

    Use a TDicionary to store the real association between event handler and property

    In the original code, the association of the event handler is made in the event handler property itself. During design-time, event handlers are identified by an index and that index is stored as  pointer in the property. This is the source of the problem, since Assigned function detects that the event handler is assigned and it tries to fire it (but it is not actually a method, it is just an index).

    I define a TDictionary (in TLMDModule) to store this association and do not use the property. 

    FEventHandlersByObject:       TDictionary<TComponent, TDictionary<TLMDString, TObject>>;

    This dictionary stores the TEventHandler as objects and provide a fast access by the component and the property name.

    I have also added some aux method to manage this dictionary (also in TLMDModule)

        - These methods allows to remove associations by index or by component and property name
        procedure DeleteEventHandlerAssociationByIndex(AIndex: Integer);
        procedure DeleteEventHandlerAssociation(AComponent : TComponent; APropName : TLMDString);

        - These methods allows to get or set the associations
        function GetStoredEventProp(AInstance: TPersistent; APropInfo: TLMDPropInfo): TObject;
        procedure SetStoredEventProp(AInstance: TPersistent; AName: TLMDString; AEventHandler: TObject);

    Modify TWin32Traits to use the new association mechanims

    GetEventProp and SetEventProp  methods of this class are the ones that are "translating" from handle index to event handles. Now, they only call to the new GetStoredEventProp and SetStoredEventProp to use the new association method


    "Fix" event handler for serialization

    This is one of the main problems. During deserialization (read), it is not possible identify when an event handler is read. It is possible to detect that an event handle is read (using the FindEventHandler) but at that point we don't know the name of the propoerty. To solve this problem, after read all components, I iterate through all components to detach all event handles and store them in the dictionary in TLMDModule (method FixAllComponentEventHandlers), with this, after read all component, no event handlers are assigned to any TEventHandler.

    For serialization I hace implemented the reverse operation. Before start the write process, I iterate through all assigned event handlers attaching them to its real TEventHandle (ReverseFixComponentEventHandlers). Once the write is completed, I have to detach all event handlers again to ensure that in design time is still working.

    Attached you can find the file with the modifications. Probably it could be improve by your team. Consider to include it the next release.




    zip
    zip
    LMDDsgModule.zip
    15K
  • Hi,

    I'll look at your code. However, I've had such an idea previously, don't remeber now, why exactly I rejected it.

    >  This is the source of the problem, since Assigned function detects that the event handler is assigned and it tries to fire it (but it is not actually a method, it is just an index).

    This is, actually, how Delphi works itself. But, Delphi is a 32 bit application. So, Assigned function is compiled differently in 32 and 64 bit platforms.

  • Hello Eugene,

    I think the problem is not the assign function. Obiously, it seems the Assign function is implemented in a different way in 32 and 64 bits, but this implementation does not affect to the Delphi's IDE, and it should not affect to any LMD-based IDE.

    I don't know if the proposed solution has any problem in you whole system, but I also think that it must have a solution to solve this problem. Modifying an VCL component each time this problem appears is not a solution. It was a workarround when only one component was affected (TTimer), but now it is severe problem.
  • You right, of course. As I mentioned, I'll look at you code. However, we have to remeber that there no x64 design-time in Delphi world, so, some other incompatibilities may also exist.
  • Hello Eugene,

    I'm also cheking my code since I have found a problem. Some events are fired before the rest of the components have been completly read. For example, OnShow event is fired before "AfterRead" in LMDModule is invoqued. That means that those event will still fail since their events are still pointing to a bad address (are pointing to an index).

    I'm also trying to find a solution for this point
Sign In or Register to comment.