2011-09-02 12 views
5

Ho un errore di runtime che si verifica nel rtl Streaming di un modulo, causando un'eccezione EClassNotFound da sollevare mentre si esegue TReader.ReadRootComponent. Il particolare messaggio di errore è "Class not found TActionList".Cosa fa un EClassNotFound generato in fase di runtime significa veramente quando la classe in questione è presente al momento della compilazione e del collegamento, e c'è esplicitamente nel codice?

Che cosa è dispari è:

  1. La mia forma principale utilizza nell'elenco Azione.
  2. Per divertimento, ho aggiunto ActnList.pas (dalla cartella di origine VCL) al mio progetto, per provare a risolverlo.

Questo capita a me quando istanziare un modulo che ho lavorato fino a pochi minuti fa. La modifica che ho apportato era in qualche codice sub-frame: ho rimosso tutto il suo codice di sezione di implementazione con un marcatore ifdef, perché sto prendendo in giro alcuni frame, per test delle unità e prototipi.

Ho provato ad aggiungere la classe dell'elenco di azioni al progetto e ho provato con e senza varie opzioni di compilatore e collegamento e tuttavia ho ancora questa eccezione. Ovviamente qualcosa di strano è attivo. Ci deve essere un altro modo strano per ottenere questo problema.

In effetti, sembra che ci sia qualcosa di veramente strano in corso. Quando questo errore viene generato, ottengo il seguente stack di chiamate:

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) 

Sembra che il nil è intenzionale, in TReader.ReadDataInner (istanza: TComponent):

 while not EndOfList do ReadComponent(nil); 

Aggiornamento: Credo la risposta a questa domanda è comprendere i "contesti di serializzazione" come menzionato da Mason. Ed è tempo di ammettere la mia Stupidità: ho rimosso il genitore del frame dal progetto, non rendendomi conto che era il genitore dell'inquadratura. Ho risolto il problema perdendo la dichiarazione del tipo per TMyFrameParent come TMyFrameParent = class(TFrame) e questo a sua volta ha portato alla condizione in questione. Lascio la domanda qui perché penso che potrebbe essere davvero utile in futuro notare quando questa eccezione si verifica in casi arcani e come risolverlo. In particolare, Mason ha un po 'di informazioni interessanti sui "contesti di serializzazione" e su come si applicano alla ricerca del nome di classe.

+1

L'nil segnala a ReadComponent solo l'istanza di una nuova istanza anziché la lettura in una esistente. –

risposta

6

Significa che la classe non è stata trovata nel contesto di deserializzazione corrente. Non tutte le classi esistenti sono registrate per tutto il caricamento. Ogni classe di form ha RTTI contenente riferimenti ai componenti che utilizza. Per arrivare a questo lavoro, assicurarsi che il modulo (o telaio, se questo è un fotogramma) dichiara almeno una TActionList prima del tag privato:

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

Usa Classes.RegisterClass per registrare le classi che si desidera utilizzare con il sistema di streaming. Citazione dal doc

Le classi di modulo e le classi di componenti citate in una dichiarazione di modulo (variabili di istanza) vengono registrate automaticamente. Qualsiasi altra classe utilizzata da un'applicazione deve essere esplicitamente registrata chiamando RegisterClass se le istanze devono essere salvate. Una volta che le classi sono state registrate, possono essere caricate o salvate dal sistema di streaming dei componenti.

+0

Quindi nel caso di un frame che eredita da un altro frame, probabilmente questo spiega perché Classes.RegisterClass non è mai stato chiamato, perché il .dfm del frame contiene solo una voce "x ereditata", non la voce "oggetto x". –

+0

C'è qualcosa di strano ... se il progetto non contiene il frame principale, allora come mai il sistema di streaming ha cercato la classe TActionList? Perché il dfm per il frame discendente era vuoto (cioè non contiene l'ActionList), giusto? – ain

1

Sembra che questo accade quando si copia un frame da un progetto a un altro progetto, e che fanno da cornice eredita da qualcosa, e si falsa l'eredità, ma lasciare i "ereditati" descrizioni degli oggetti nella cornice DFM, articoli in questo modo:

inherited ActionList: TActionList 
    Left = 520 
    Top = 576 
end 

questo a sua volta nel "contesto attuale deserializzazione" che Mason ha parlato, non contiene la classe. Una correzione è di cambiare ereditato per oggetto in tutti i casi di cui sopra.