Einzelnen Beitrag anzeigen

christian_r
(Gast)

n/a Beiträge
 
#1

Zugriffverletzungen bei vererbten Eigenschaften.

  Alt 30. Dez 2008, 02: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