AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen Object-Pascal / Delphi-Language Delphi Zugriffverletzungen bei vererbten Eigenschaften.
Thema durchsuchen
Ansicht
Themen-Optionen

Zugriffverletzungen bei vererbten Eigenschaften.

Ein Thema von christian_r · begonnen am 30. Dez 2008 · letzter Beitrag vom 3. Jan 2009
Antwort Antwort
christian_r
(Gast)

n/a Beiträge
 
#1

Zugriffverletzungen bei vererbten Eigenschaften.

  Alt 30. Dez 2008, 01:18
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:
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.
oCommandWrapper.pas
Delphi-Quellcode:
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.
oCommandPluginWrapper.pas
Delphi-Quellcode:
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.
oCommandPluginProcess.pas
Delphi-Quellcode:
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.
Ich habe den eigentlichen Code auf das Wesentliche zusammengeschröpft und kommentiert. Hoffentlich gibt es bei Euch Interesse, da mal reinzuschauen.

Danke.
Angehängte Dateien
Dateityp: zip modules_111.zip (213,2 KB, 2x aufgerufen)
  Mit Zitat antworten Zitat
christian_r
(Gast)

n/a Beiträge
 
#2

Re: Zugriffverletzungen bei vererbten Eigenschaften.

  Alt 2. Jan 2009, 08:28
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.
  Mit Zitat antworten Zitat
Benutzerbild von sx2008
sx2008

Registriert seit: 15. Feb 2008
Ort: Baden-Württemberg
2.332 Beiträge
 
Delphi 2007 Professional
 
#3

Re: Zugriffverletzungen bei vererbten Eigenschaften.

  Alt 2. Jan 2009, 09:50
Du musst einfach deinen Werkzeugkasten für Debugging auspacken und den Fehler suchen:
Delphi-Quellcode:
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;
PS: deine Schreibweise der Klammern bei einer Funktion ist ungünstig.
Zwischen dem Methodenname und der öffnenden Klammer sollte kein Weißraum sein!
http://dn.codegear.com/article/10280#4.2.2
  Mit Zitat antworten Zitat
christian_r
(Gast)

n/a Beiträge
 
#4

Re: Zugriffverletzungen bei vererbten Eigenschaften.

  Alt 3. Jan 2009, 08:34
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.
  Mit Zitat antworten Zitat
mkinzler
(Moderator)

Registriert seit: 9. Dez 2005
Ort: Heilbronn
39.851 Beiträge
 
Delphi 11 Alexandria
 
#5

Re: Zugriffverletzungen bei vererbten Eigenschaften.

  Alt 3. Jan 2009, 08:42
protected wird beim Ableiten zu private
Markus Kinzler
  Mit Zitat antworten Zitat
christian_r
(Gast)

n/a Beiträge
 
#6

Re: Zugriffverletzungen bei vererbten Eigenschaften.

  Alt 3. Jan 2009, 09:04
public bringt leider keine Abhilfe.

Edit:

Bevor ich es vergesse, wurde ja nachgefragt.

Delphi-Quellcode:
type
  TAString = Array of String;
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe
Online

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.009 Beiträge
 
Delphi 12 Athens
 
#7

Re: Zugriffverletzungen bei vererbten Eigenschaften.

  Alt 3. Jan 2009, 12:35
Dein Fehler liegt an dieser Stelle:

Delphi-Quellcode:
var
var
  MethodCaller : TProcedure;
...
    MethodCaller := Self.MethodAddress( pParams[ 0 ] );
...
      MethodCaller;
...
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.

Folgende Änderungen sollten dir weiterhelfen:

Delphi-Quellcode:
...
type
  TClassMethod = procedure(Self: TObject);
...
var
  MethodCaller : TClassMethod;
...
      MethodCaller(Self);
...
Uwe Raabe
  Mit Zitat antworten Zitat
christian_r
(Gast)

n/a Beiträge
 
#8

Re: Zugriffverletzungen bei vererbten Eigenschaften.

  Alt 3. Jan 2009, 13:12
Hey, Danke!

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.
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:47 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz