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.