What does a EClassNotFound raised at runtime really mean when the class in question is there at compile and link time, and there explicitly in code?

StackOverflow https://stackoverflow.com/questions/7287204

Question

I have a runtime error happening in the rtl Streaming in of a form, causing an exception EClassNotFound to be raised, while doing TReader.ReadRootComponent. The particular error message is "Class not found TActionList".

What is odd is:

  1. My main form uses Action list.
  2. For fun, I added ActnList.pas (from the VCL source folder) to my project, to try to fix it.

This happens to me when instantiating a form that I had working until a few minutes ago. The change that I made was in some sub-frame code: I removed all its implementation section code with an ifdef marker, because I am mocking up some frames, for unit testing and prototypes.

I tried adding the action list class to the project, and I tried with and without various compiler and link options, and yet, I still get this exception. Obviously something weird is up. There must be another weird way to get this problem.

In fact, it seems there is something really weird going on. When this error is raised, I get the following call stack:

rtl.Classes.ClassNotFound('TActionList')
rtl.Classes.TReader.FindComponentClass(???)
rtl.Classes.FindExistingComponent
rtl.Classes.TReader.ReadComponent(nil)       /// NIL!? WHAT!!!!!
rtl.Classes.TReader.ReadDataInner(???)
rtl.Classes.TReader.ReadData(???)
rtl.Classes.TComponent.ReadState(???)
vcl.Controls.TControl.ReadState(???)
vcl.Controls.TWinControl.ReadState($60B9CF0)
vcl.Forms.TCustomForm.ReadState(???)
rtl.Classes.TReader.ReadRootComponent($606EB90)
rtl.Classes.TStream.ReadComponent($606EB90)
rtl.Classes.InternalReadComponentRes(???,???,$606EB90)
rtl.Classes.InitComponent(TComplexFormContainingFrames)

It seems the nil is intentional, in TReader.ReadDataInner(Instance:TComponent):

      while not EndOfList do ReadComponent(nil);

Update: I believe the answer to this question is to understand "serialization contexts" as Mason has mentioned. And, it's time to admit my own Stupidity: I removed the parent of the frame from the project, not realizing it was the parent of the frame. I worked around it being missing by stubbing the type declaration for TMyFrameParent as TMyFrameParent = class(TFrame), and this in turn lead to the condition in question. I am leaving the question here because I think it might be really handy in future to note when this exception occurs in arcane cases, and how to fix it. In particular, Mason has a really interesting bit of information about "serialization contexts" and how they apply to class-name-finding.

Was it helpful?

Solution

It means that the class wasn't found in the current deserialization context. Not all existing classes are registered for all loading. Each form class has RTTI containing references to the components it uses. To get this to work, make sure that your form (or frame, if this is a frame) declares at least one TActionList before the private tag:

TMyForm = class(TForm)
  ActionList: TActionList;
  OtherComponent: TSomeComponent;
private
  //whatever
public
  //whatever
end;

OTHER TIPS

Use Classes.RegisterClass to register classes you want to use with the streaming system. Quote from the doc

Form classes and component classes that are referenced in a form declaration (instance variables) are automatically registered. Any other classes used by an application must be explicitly registered by calling RegisterClass if instances are to be saved. Once classes are registered, they can be loaded or saved by the component streaming system.

It seems that this happens when you copy a frame from one project to another project, and that frame inherits from something, and you fake the inheritance, but leave the "inherited" item descriptions in the frame dfm, items like this:

inherited ActionList: TActionList
  Left = 520
  Top = 576
end

This in turn results in the "current deserialization context" that Mason talked about, not containing the class. One fix is to change Inherited to object in all the above cases.

I got a "EClassNotFound" error when I had a TLabel declaration present in my DFM file but there was no declaration for it in the corresponding PAS file. Somehow the form editor screwed up.

The error was not visible until I deleted all labels from my form except that particular "corrupted" one. It was difficult to hunt it down because that label was hidden under a panel.

One easy fix is to cut (ctrl+x) that label (once you find it) from the form and paste it back. This time the form editor will correctly insert a declaration for it in the PAS file.

There is another way to get this error: put 'public' at the top of a form definition class. By default class members are 'published'. I accidentally added 'public' to the top of a form declaration and it produces multiple 'Class not found' exceptions at run-time.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top