Jump to content
aehimself

Component property disappears from DFM

Recommended Posts

Hello all,

 

I have an issue with some of our components in the application we support where an event handler simply disappears (gets unlinked, code stays in .pas) when a frame is opened.

The main container is a frame which is the 3rd or 4th descendant of a simple TFrame (with 2-3 levels of visual inheritance included). The event handler is the OnDataChange event of a custom TDataSource descendant component. This frame has ~50 of these datasources, but only 2 are affected. No other datasources / frames have the same issue (which we have hundreds, if not thousands).

 

When you open the file in Delphi, the tab indicator already shows that it is modified and can be saved. Upon saving the event handler is simply unlinked, leaving the actual code in the .pas file. Restoration is easy, just double-click the handler in object inspector so it gets linked back which you can save perfectly... until next time someone forgets to check the modifications and roll back the removals; which already happened at least 5 times in the past half a year.

inherited frmCustom: TfrmCustom
  [...]
  object dtsMyDTS: TCustomDataSource
    [...]
    OnDataChange = dtsMyDTSDataChange   <-- this line disappears
    [...]
  end
  [...]
end

I bet it will be some rare edge case but it already caused lots of headaches and it would be a blessing if it could be fixed. I am not able to share the sources as they are proprietary plus you'd need the whole custom component package as well to be able to open it 🙂 I'm simply looking for indicators, what I can try, what I can check to find the root cause.

 

Anyone saw something like this?

 

Cheers!

Share this post


Link to post

I think I have seen this but it's been a while, like maybe in an older version of Delphi?  In what version is this happening?

 

It may be that while Delphi is loading the DFM, it can't find the source for the component so it thinks it's an invalid or old property and removes it--somewhat like loading a form that has been descended from a custom base form but the base form is not part of the project. You do mention descendants, so maybe include all the ancestor frames in the project if they aren't already?

Share this post


Link to post
1 hour ago, corneliusdavid said:

I think I have seen this but it's been a while, like maybe in an older version of Delphi?  In what version is this happening?

 

It may be that while Delphi is loading the DFM, it can't find the source for the component so it thinks it's an invalid or old property and removes it--somewhat like loading a form that has been descended from a custom base form but the base form is not part of the project. You do mention descendants, so maybe include all the ancestor frames in the project if they aren't already?

Unfortunately I do not remember the 10.4 era, but this definitely happened under Delphi 11.2 and now Delphi 12. The project (and probably this frame) originally was created with Delphi 7 but was slowly upgraded to the most recent version.

 

The source of said component is in Browsing path, Library path points to the precompiled DCU. All frame files (including the first one inherited from TFrame) is a part of the project.

 

We have ~1500 frames with at least 20-50 of these custom datasources on each and this problem only occures on one frame, two datasources (always the same 2). This makes me believe that fixing it won't be something generic - e.g. on component or project level.

Share this post


Link to post
Just now, aehimself said:

We have ~1500 frames with at least 20-50 of these custom datasources on each and this problem only occures on one frame, two datasources (always the same 2). This makes me believe that fixing it won't be something generic - e.g. on component or project level.

Oh, yeah, you mentioned this in the first post and I just skipped right over that--you're right, it'll be something specific about these two datasource components.

 

So, then what is different about these datasources from all the rest?  Are they hooked to a different type of dataset or have any other properties set uniquely?  Does changing the creation order in the DFM have any effect?

  • Like 1

Share this post


Link to post
41 minutes ago, corneliusdavid said:

Oh, yeah, you mentioned this in the first post and I just skipped right over that--you're right, it'll be something specific about these two datasource components.

 

So, then what is different about these datasources from all the rest?  Are they hooked to a different type of dataset or have any other properties set uniquely?  Does changing the creation order in the DFM have any effect?

By the first look... nothing seems different, that's my issue. The create order is an excellent idea, I'll doublecheck that tomorrow!

Share this post


Link to post

Does baseform have the onMyEvent assigned to empty event simply a frameclick(Sender:TObject)begin..end?  

 

doing that allows the inherited form a stub to hook up its event.  

Share this post


Link to post
Just now, Pat Foley said:

Does baseform have the onMyEvent assigned to empty event simply a frameclick(Sender:TObject)begin..end?  

 

doing that allows the inherited form a stub to hook up its event.  

The datasource is on the last frame not on an ascendant (object dtsMyDTS instead of inherited dtsMyDTS). Inheritance only plays a role here if Delphi bugs out due to the long frame chain I think.

Share this post


Link to post
19 hours ago, aehimself said:

We have ~1500 frames with at least 20-50 of these custom datasources on each and this problem only occures on one frame, two datasources (always the same 2). This makes me believe that fixing it won't be something generic - e.g. on component or project level.

In this case just override the Loaded method of the frame and link the event handler in code there, after calling the inherited method first. That will fix the issue even if you cannot figure out why it happens in the first place. Not really satisfactory, of course, but the only reason I can think of would be if somebody cleared the Name property of the dataset in question or moved the handler method from published to public scope by accident.

Share this post


Link to post

Long story short, the issue was in the custom component itself. Someone wanted to have a constant OnDataChange code to run every single time and - as TDataSource is an INSANELY bad component with no overridable methods whatsoever - this is how they solved it:

  TCustomDataSource = class(TDataSource)
  protected
    procedure Loaded; override;
  private
    FOnDataChangeDesigned: TDataChangeEvent;
  end;

procedure TCustomDataSource.Loaded;
begin
  inherited;

  FOnDataChangeDesigned := OnDataChange;
  OnDataChange := DataChangeDef;
end;

In the moment the component was successfully streamed (run- or design time) the event handler was "saved" and reset, only to be called from the internal method. But since it's a private method the Object Inspector could not access it, therefore clearing the event handler alltogether. Saving only wrote this inconsistent state back to the disk.

 

And yes, it seems all of the TCustomDataSources were affected, the event handler just disappeared so long ago that noone seemed to realize it.

 

After fixing it and relinking all DataChange events found in the entire application... it seems to be working properly now.

Edited by aehimself

Share this post


Link to post

Hey,

 

Here is a script to find those events

 

# !!! Be careful with scripts, always make sure to back up your work or experiment with a copy. !!!

# Finding abandoned events v3

for i in `find . -not -path '*/.*' -name "*.pas" -type f`; do if [ -f ${i%.*}.dfm ]; then cat $i | \
sed -nr "/=(\s)*class/I,/(private|protected|public|published|automated)/I{p}" | \
sed -nr "/procedure/Ip" | \
sed -e "s/\(.*\)\ \(.*\)(\(.*\)/\2/g" | \
xargs -n 1 | \
xargs -I @@ sh -c "echo -n \"${i%.*}.dfm->@@: \"; grep -i -s -c @@ ${i%.*}.dfm" | \
grep ": 0"; fi; done

run from wsl

results are <event> <occurence> 

where 0 means missing from dfm (could be assigned by code)

 

 

 

 

 

Share this post


Link to post
56 minutes ago, aehimself said:

Long story short, the issue was in the custom component itself.

Interesting. Thanks for letting us all know what happened.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×