![]() |
Zugriffverletzungen bei vererbten Eigenschaften.
Liste der Anhänge anzeigen (Anzahl: 1)
Guten Morgen.
Ich entwickle quasi gerade ein Kommando-MVC für eine Remote-GUI. Meine Zielsetzung: Über das Eingabefeld des Formulars werden Konsolenkommandos nach dem Schema 'Klasse-Methode-Parameter' eingegeben. (Bestätigung der Eingabe mit Return.) Diese Kommandos werden an den CommandWrapper weitergegeben. Dort wird in der Methode 'GetPlugin' mittels 'GetClass( )' die generische Klasse ermittelt und eine Instanz davon erzeugt und zurückgegeben. Somit kann man das ganze System um weitere Klassen wie die Klasse 'TOCommandPluginProcess' in der Unit 'oCommandPluginProcess.pas' erweitern, ohne den Code des Wrappers erst erweitern zu müssen. Alle Klassen 'TOCommandPluginXXX' müssen mindestens von der Klasse 'TOCommandPluginWrapper' abgeleitet werden und müssen mit 'RegisterClass( )' verfügbar gemacht werden. Wurde die Klasse gefunden und instanziert, dann wird deren virtuelle Methode 'Execute( )' in 'TOCommandPluginWrapper' aufgerufen, welcher die verbliebenen Parameter ohne die Angabe der Klasse übergeben werden. (Es ist dann nur noch das Kommando 'Methode-Parameter' übrig.) In dieser Methode wird versucht die Methode mit 'MethodAddress( )' zu ermitteln. Wurde sie gefunden, dann wird Sie aufgerufen. Nun bin ich an der Fehlerquelle angelangt. In der Klasse 'TOCommandPluginWrapper' ist eine Eigenschaft 'Params : TAString' als 'protected' deklariert. Diese ist dann eigentlich in allen davon abgeleiteten Klassen verfügbar. Nachdem nun die Methode mit 'MethodAddress( )' ermittelt wurde, werden alle verbliebenen Parameter ohne den Methodenbezeichner in der Eigenschaft 'Params' gespeichert, da ich diese über 'MethodAddress( )' nicht übergeben kann. Anschließend wird die gefundene Methode in über die Variable 'MethodCaller' aufgerufen. Der Aufruf der Default-Eingabe bei Programmstart funktioniert - die Testausgabe muss 'Inside Test: 0' lauten. Es gibt zwei Auslöser für eine Exception. Entfernt man die Kommentierung von 'ShowMessage' vor dem Aufruf der Methode mittels 'MethodCall', dann löst die Testausgabe in 'TOCommandPluginProcess.Test( )' eine Exception aus. Lässt man den Kommentar bestehen und fügt hinter der Standardeingabe 'process test' noch einen oder mehrere fiktive Parameter an, dann löst die Testausgabe ebenfalls eine Exception aus. Es liegt definitiv am Zugriff auf 'Self.Params'. Ich verstehe dieses Problem leider nicht, weil es "eigentlich" nicht auftreten dürfte. Aber es muss ja einen sinnvollen Grund dafür geben. Ich weiß nicht, ob es am Aufruf der Methoden mittels 'MethodAddress( )', oder evtl. an den generischen Klassen liegt. Hier nun die Quellcodes, einmal gepostet und einmal das Projekt zum direkten Testen als Angang. Entscheidend sind die Units 'oCommandPluginWrapper' (dort die Methode 'Execute') und 'oCommandPluginProcess' (dort die Methode 'Test'). Ich hoffe, ich habe das Problem verstädnlich und ausführlich genug beschrieben. uFormModules.pas
Delphi-Quellcode:
oCommandWrapper.pas
unit uFormModules;
interface uses Windows, Forms, Classes, Dialogs, StdCtrls, Controls; type TFormModules = Class( TForm ) BxGrpConsole : TGroupBox; EdtConsole : TEdit; procedure EdtConsoleKeyDown ( Sender : TObject; var Key : Word; Shift: TShiftState ); private procedure ExecuteCommand ( const pCommand : String ); virtual; end; var FormModules : TFormModules; implementation uses SysUtils, oCommandWrapper, XtSystem; {$R *.dfm} // Private procedure TFormModules.ExecuteCommand ( const pCommand : String ); var CommandWrapper : TOCommandWrapper; Command : String; begin CommandWrapper := TOCommandWrapper.Create( Self ); Command := pCommand; ShowMessage( CommandWrapper.Execute( Command ) ); CommandWrapper.Free( ); end; procedure TFormModules.EdtConsoleKeyDown ( Sender : TObject; var Key : Word; Shift : TShiftState ); begin if Key = VK_RETURN then begin Self.ExecuteCommand( Self.EdtConsole.Text ); Self.EdtConsole.Clear( ); end; end; end.
Delphi-Quellcode:
oCommandPluginWrapper.pas
unit oCommandWrapper;
interface uses Classes, oCommandPluginWrapper, XtSystem; type TOCommandWrapper = Class( TComponent ) private function GetPlugin ( const pClassName : String ) : TOCommandPluginWrapper; virtual; procedure AddParam ( var pParams : TAString; const pParam : String ); virtual; public function Execute ( pCommand : String ) : String; virtual; end; implementation uses SysUtils; // Private function TOCommandWrapper.GetPlugin ( const pClassName : String ) : TOCommandPluginWrapper; var CommandPlugin : TCCommandPluginWrapper; begin CommandPlugin := TCCommandPluginWrapper( GetClass( pClassName ) ); if Assigned( CommandPlugin ) then Result := CommandPlugin.Create( ) else Result := nil; end; procedure TOCommandWrapper.AddParam ( var pParams : TAString; const pParam : String ); begin SetLength( pParams, Length( pParams ) + 1 ); pParams[ High( pParams ) ] := pParam; end; // Public function TOCommandWrapper.Execute ( pCommand : String ) : String; var CommandPlugin : TOCommandPluginWrapper; Command : String; Params : TAString; ParamPos : Integer; begin // Parameter als dyn. Array (Typ: TAString) zusammenstellen pCommand := Trim( pCommand ); Params := nil; ParamPos := Pos( #32, pCommand ); if ParamPos = 0 then Command := pCommand else begin Command := Copy( pCommand, 1, ParamPos - 1 ); Delete( pCommand, 1, ParamPos ); while Length( pCommand ) > 0 do begin ParamPos := Pos( #32, pCommand ); if ParamPos = 0 then begin Self.AddParam( Params, pCommand ); pCommand := ''; end else begin Self.AddParam( Params, Copy( pCommand, 1 , ParamPos - 1 ) ); Delete( pCommand, 1, ParamPos ); end; end; end; // ermitteln der Klasse und Aufruf derer virtuellen Methode 'Execute' bzw. Abbruch mit Fehlermeldung CommandPlugin := Self.getPlugin( 'TOCommandPlugin' + Command ); if not Assigned( CommandPlugin ) then begin Result := 'Unknown command!' + #10 + Command; Exit; end else begin Result := CommandPlugin.Execute( Params ); CommandPlugin.Free( ); end; end; end.
Delphi-Quellcode:
oCommandPluginProcess.pas
unit oCommandPluginWrapper;
interface uses Classes, XtSystem; type TOCommandPluginWrapper = Class( TPersistent ) protected Params : TAString; public function Execute ( const pParams : TAString ) : String; virtual; end; TCCommandPluginWrapper = Class of TOCommandPluginWrapper; implementation uses dialogs, SysUtils; // Public function TOCommandPluginWrapper.Execute ( const pParams : TAString ) : String; var MethodCaller : TProcedure; begin if Length( pParams ) = 0 then Result := '' else begin MethodCaller := Self.MethodAddress( pParams[ 0 ] ); if Assigned( MethodCaller ) then begin Self.Params := Copy( pParams, 1, Length( pParams ) ); ShowMessage( 'Before: ' + IntToStr( Length( Self.Params ) ) ); MethodCaller; end else Result := 'Unknown method!' + #10 + pParams[ 0 ]; end; end; end.
Delphi-Quellcode:
Ich habe den eigentlichen Code auf das Wesentliche zusammengeschröpft und kommentiert. Hoffentlich gibt es bei Euch Interesse, da mal reinzuschauen.
unit oCommandPluginProcess;
interface uses Classes, XtSystem, oCommandPluginWrapper; type TOCommandPluginProcess = Class( TOCommandPluginWrapper ) published procedure Test ( ); virtual; end; implementation uses dialogs, sysutils; procedure TOCommandPluginProcess.Test ( ); begin // hier wird die Exception ausgelöst ShowMessage( 'Inside ''Test'': ' + IntToStr( Length( Self.Params ) ) ); end; begin RegisterClass( TOCommandPluginProcess ); end. Danke. |
Re: Zugriffverletzungen bei vererbten Eigenschaften.
Jetzt muss ich mal nachfragen. Liegt es am Jahreswechsel, oder doch an einer unklaren Problemformulierung bzw. zu viel Quellcode? Ich hoffe immer noch, dass mir jemand bei diesem Problem helfen kann.
Gesundes Neues. :party: :cheer: |
Re: Zugriffverletzungen bei vererbten Eigenschaften.
Du musst einfach deinen Werkzeugkasten für Debugging auspacken und den Fehler suchen:
Delphi-Quellcode:
PS: deine Schreibweise der Klammern bei einer Funktion ist ungünstig.
procedure TOCommandPluginProcess.Test( );
begin // 1. Self muss ungleich nil sein Assert(Assigned(self)); // 2. Self muss die richtige Klasse haben // es ist selten, kann aber vorkommen, dass self auf ein Objekt der falschen Klasse zeigt Assert(self is TOCommandPluginProcess); // 3. ist TAString eine Klasse ? // Falls ja, ist auch noch folgende Bedingung nötig Assert(Assigned(self.Params)); // hier wird die Exception ausgelöst (jetzt vielleicht nicht mehr) ShowMessage( 'Inside TOCommandPluginProcess.Test: ' + IntToStr( Length( Self.Params ) ) ); end; Zwischen dem Methodenname und der öffnenden Klammer sollte kein Weißraum sein! ![]() |
Re: Zugriffverletzungen bei vererbten Eigenschaften.
Leider ohne Erfolg. Das Objekt ist instanziert und die Klasse stimmt auch. Das habe ich alles schon durch. Self.Params kann nicht gelesen werden.
Datei: uCommandPluginWrapper.pas Methode: TOCommandPluginWrapper.Execute( ) In dieser Methode mal "ShowMessage('Before:'+IntToStr(Length(Self.Params )));" auskommentieren. Es wird keine Exception in TOCommandPluginProcess.Test() ausgelöst. Kommentar wieder entfernen und die Exception wird ausgelöst. Oder: Betreffende Zeile auskommentieren, Programm starten und an das Kommando einen Parameter anhängen. Exception wird in TOCommandPluginProcess.Test() ausgelöst. Immer beim Zugriff auf Self.Params in TOCommandPluginProcess.Test(). Ich verstehe nur nicht den Zusammenhang, zwischen dem angehängten Parameter und der Exception, oder zwischen dem Zugriff auf Self.Params in TOCommandPluginWrapper.Execute() und der daraus resultierenden Exception beim Zugriff auf Self.Params in TOCommandPluginProcess.Test(). --- Ich könnte jedes Kommando in eine eigene Klasse packen, und diese ebenfalls über getClass instanzieren. Aber ich finde das unnötig kompliziert. |
Re: Zugriffverletzungen bei vererbten Eigenschaften.
protected wird beim Ableiten zu private
|
Re: Zugriffverletzungen bei vererbten Eigenschaften.
public bringt leider keine Abhilfe.
Edit: Bevor ich es vergesse, wurde ja nachgefragt.
Delphi-Quellcode:
type
TAString = Array of String; |
Re: Zugriffverletzungen bei vererbten Eigenschaften.
Dein Fehler liegt an dieser Stelle:
Delphi-Quellcode:
Self.MethodAddress gibt dir, wie der Name schon sagt, einen Pointer auf eine Klassenmethode zurück, keine normale procedure! Daher fehlt beim nachfolgenden Aufruf der implizite Self-Parameter für diese Methode und jedwede Referenz auf Self landet im Nirwana.
var
var MethodCaller : TProcedure; ... MethodCaller := Self.MethodAddress( pParams[ 0 ] ); ... MethodCaller; ... Folgende Änderungen sollten dir weiterhelfen:
Delphi-Quellcode:
...
type TClassMethod = procedure(Self: TObject); ... var MethodCaller : TClassMethod; ... MethodCaller(Self); ... |
Re: Zugriffverletzungen bei vererbten Eigenschaften.
Hey, Danke! :cheers:
Damit ist das Problem gelöst. Nun weiß ich wenigstens, dass ich auch hier zwischen Prozeduren und Methoden unterscheiden muss. Nachtrag: Der Vorteil ist, dass ich das ganze sogar auf Funktionsmethoden erweitern kann, und somit über die Methode einen Rückgabewert liefern kann.
Delphi-Quellcode:
// TClassMEthod für Funktions-Methoden typisieren
type TClassMethod = function( Self : TObject ) : String; function TOCommandPluginWrapper.Execute ( const pParams : TAString ) : String; var MethodCaller : TClassMethod; begin // ... MethodCaller := Self.MethodAddress( pParams[ 0 ] ); if Assigned( MethodCaller ) then begin Self.Params := Copy( pParams, 1, Length( pParams ) ); Result := MethodCaller( Self ); end else Result := 'Unknown method!' + #10 + pParams[ 0 ]; end; P.S.: Weils es mehrfach im Forum angemerkt wurde, dass ich die Konventionen für die Schreibweise von Quellcode nicht einhalte. Ich finde den Code auf Dauer lesbarer, wenn ich zusätzliche Zeilenumbrüche und Leerzeichen in den Quellcode einfüge. Wenn ich den ganzen Tag am Rechner sitze, ist diese Schreibweise meiner Meinung nach viel entspannter für das Gehirn, als wenn es eine Zeile Quellcode ohne Leerzeichen erst selbst in seine Bestandteile zerlegen muss. Durch die Leerzeichen ist der Prozess im Kopf schneller erledigt. Ansonsten, Probleme mit dem Compiler hatte ich deshalb nie. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:59 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz