Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Routine mit Namen aufrufen (https://www.delphipraxis.net/184033-routine-mit-namen-aufrufen.html)

Sigi55 20. Feb 2015 15:56

Routine mit Namen aufrufen
 
Hallo,

ich nutze diese kleine Routine um Prozeduren mit Namen zu starten,
das klappt auch alles ganz wunderbar solange sich diese Prozedure
in der gleichen Form befindet ...

Delphi-Quellcode:
type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    // Your routines (that you'll run by name) must be here
    procedure Hello_World(Sender: TObject);
  private
    procedure ExecuteRoutine(Instance: TObject; Name: string);
  end;

var
  Form1: TForm1;

type
  TExecute = procedure of object;

procedure TForm1.ExecuteRoutine(Instance: TObject; Name: string);
var
  Routine: TMethod;
  Execute: TExecute;
begin
  Routine.Data := Pointer(Instance);
  // Returns the address of a published method.
  Routine.Code := Instance.MethodAddress(Name);
  if Routine.Code = nil then Exit;
  Execute := TExecute(Routine);
  Execute;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ExecuteRoutine(Form1, 'Hello_World');
end;

procedure TForm1.Hello_World(Sender: TObject);
begin
  ShowMessage('This is a test');
end;
Jetzt möchte ich von der Hauptform eine Routine (Prozedure) aus einer
anderen unit aufrufen.
Und das bekomm ich nicht gebacken ..., da fehlt's mir echt an den
Grundlagen :-(

Kann mit jemand weiterhelfen ?

Danke schonmal.

Gruß

Sigi

BadenPower 20. Feb 2015 16:09

AW: Routine mit Namen aufrufen
 
Zitat:

Zitat von Sigi55 (Beitrag 1290778)
Jetzt möchte ich von der Hauptform eine Routine (Prozedure) aus einer
anderen unit aufrufen.

Dazu musst Du die andere Unit in die Uses-Klausel eintragen.

Zitat:

Zitat von Sigi55 (Beitrag 1290778)
Und das bekomm ich nicht gebacken ..., da fehlt's mir echt an den Grundlagen :-(

Dir fehlt es an Grundlagen und möchtest eine Methode über ihren Namen ansprechen?

Warum nicht den normalen Weg?
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
begin
  Hello_World(Self);
end;

Medium 20. Feb 2015 16:26

AW: Routine mit Namen aufrufen
 
Das hier ist der wichtige Teil:
Zitat:

Zitat von Sigi55 (Beitrag 1290778)
Delphi-Quellcode:
  // Returns the address of a published method.

Die RTTI (zumindest die nicht erweiterte, die es bei Delphi 5 auch lange noch nicht gab) ist nur bei published Feldern behilflich, alles was nicht published ist, kennt sie einfach nicht. Daher wird der Aufruf auf diese Weise nicht gehen, auch nicht mit Tricks. (Der Teil direkt unter einer Klassendeklaration ausserhalb der "private, protected, public, published"-Sektionen ist implizit published.)

stahli 20. Feb 2015 16:51

AW: Routine mit Namen aufrufen
 
Nenne am besten erst mal konkret Deine Delphi-Version. Delphi5?

Und was willst Du mit der Funktionalität erreichen bzw. was willst Du dem User ermöglichen? Vielleicht gibt es ja bessere Alternativen.

Methoden nach Namen ermitteln und ausführen ist ab D2010 einfacher - aber immer noch recht komplex.

Evtl. kannst Du das auch einfacher lösen:

Delphi-Quellcode:
procedure TForm1.ExecuteRoutine(Instance: TObject; Name: string);
begin
  if (Name = "DoA") and (Instance is TMyClass) then
  begin
    (Instance as TMyClass).DoA;
    Exit;
  end;
  if (Name = "DoB") and (Instance is TOtherClass) then
  begin
    (Instance as TOtherClass).DoB;
    Exit;
  end;
end;
Das geht natürlich nur, wenn die möglichen Klassen und Methoden relativ übersichtlich sind.

himitsu 20. Feb 2015 16:53

AW: Routine mit Namen aufrufen
 
Zitat:

Zitat von Medium (Beitrag 1290784)
(Der Teil direkt unter einer Klassendeklaration ausserhalb der "private, protected, public, published"-Sektionen ist implizit published.)

Nein und vielleicht auch ja.

Der Standard ist "public", außer die Klasse oder ein Vorfahre ist mit
Delphi-Quellcode:
{$M+}
compiliert, wie z.B. TPersistent/TComponent, wo es dann "published" wird.

BadenPower 20. Feb 2015 17:48

AW: Routine mit Namen aufrufen
 
Der Code funktioniert auch mit 2 Units.

Aber für was er das benötigt ist noch nicht offensichtlich.


Hier mal ein Beispiel mit 2 Units:

1. Unit mit 2 Buttons auf der Form

Delphi-Quellcode:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Hello_World(Sender: TObject);
  private
    procedure ExecuteRoutine(Instance: TObject; Name: string);
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses
  unit2;

type
  TExecute = procedure of object;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ExecuteRoutine(Form1, 'Hello_World');
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  ExecuteRoutine(Form2, 'Hello_World');
end;

procedure TForm1.ExecuteRoutine(Instance: TObject; Name: string);
var
  Routine: TMethod;
  Execute: TExecute;
begin
  Routine.Data := Pointer(Instance);
  // Returns the address of a published method.
  Routine.Code := Instance.MethodAddress(Name);
  if Routine.Code = nil then Exit;
  Execute := TExecute(Routine);
  Execute;
end;


procedure TForm1.Hello_World(Sender: TObject);
begin
  ShowMessage('Form1.Hello_World');
end;


end.
Und die 2. Unit

Delphi-Quellcode:
unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs;

type
  TForm2 = class(TForm)
    procedure Hello_World(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

procedure TForm2.Hello_World(Sender: TObject);
begin
  ShowMessage('Form2.Hello_World');
end;

end.

Medium 20. Feb 2015 18:56

AW: Routine mit Namen aufrufen
 
Zitat:

Zitat von himitsu (Beitrag 1290789)
Zitat:

Zitat von Medium (Beitrag 1290784)
(Der Teil direkt unter einer Klassendeklaration ausserhalb der "private, protected, public, published"-Sektionen ist implizit published.)

Nein und vielleicht auch ja.

Der Standard ist "public", außer die Klasse oder ein Vorfahre ist mit
Delphi-Quellcode:
{$M+}
compiliert, wie z.B. TPersistent/TComponent, wo es dann "published" wird.

Da hatter Recht, über diese Falle bin ich sogar auch schon mal gestolpert, wenn auch in einem anderen Zusammenhang.

himitsu 20. Feb 2015 21:52

AW: Routine mit Namen aufrufen
 
Delphi-Quellcode:
procedure TForm1.Hello_World(Sender: TObject);    

TExecute = procedure of object;
Und schon hat jemand Mist gebaut, denn die Signaturen stimmen nicht überein.

Zum Glück liegt dieser Parameter zufällig in den Registern und nicht auf'm Stack,
so steht da nur Schrott drin und es knallt nicht gleich.

Sigi55 21. Feb 2015 06:21

AW: Routine mit Namen aufrufen
 
Moin erstmal,

also, wozu braucht er das ?

Ich hab mir so eine Art Taskplaner, unix-cron, etc.. geschrieben.
Die jobs lese ich aus einer DB-Tabelle und arbeite sie zeitgesteuert ab.
Der "JobName" ist die Bezeichnung der aufzurufen Procedure den ich per Variable
übergebe.

Das klappt auch alles wunderbar, nur liegen nicht alle Proceduren auf der
Hauptform ...

z.Z. mache ich es über den Umweg

Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
begin
  ExecuteRoutine(Form1, Datensicherung);
end;

procedure TForm1.Datensicherung(Sender: TObject);
begin
  DatenSicherungStarten;
end;
... und ich hätte gern etwas inder Art:

Delphi-Quellcode:
ExecuteRoutine(Unit??? oder wo auch immer, Datensicherung);

Das muss zwar alles nicht sein, ist auch kein Akt wenn es so nicht
zu machen ist, wäre nur schöner :-D

Gruß

Sigi

hanvas 21. Feb 2015 08:55

AW: Routine mit Namen aufrufen
 
Zitat:

Zitat von Sigi55 (Beitrag 1290823)
Moin erstmal,

Ich hab mir so eine Art Taskplaner, unix-cron, etc.. geschrieben.
Die jobs lese ich aus einer DB-Tabelle und arbeite sie zeitgesteuert ab.
Der "JobName" ist die Bezeichnung der aufzurufen Procedure den ich per Variable
übergebe.

Sigi

Wenn ich das richtig verstehen dann gibt es keine "unbekannten" Prozeduren und Methoden sondern lediglich Objekte/Klassen die sich auf mehrere Units verteilen und die Du einheitlich aufrufen willst, Du weisst aber wo (in welcher Unit/Klasse) diese Funktionen ursprünglich definiert sind. Da du alle Methoden in Deinem Beispiel ohne Parameter aufrufen wolltest gehe ich davon aus das alle aufzurufenden Methoden paramterlos sind.

In jedem Fall würde ich dafür sorgen das sich die aufzurufenden Objekte/Methoden selbst bei einer zentralen Instanz "registrieren".

Delphi-Quellcode:
 

unit register;

....

type TJobRegister = class(TObject)
     private
       FRegister : TStringList;
     public
       procedure registerCall (const registeredName : String; m : pMethod);
       procedure Call(const registeredName : String);      
     end;

function RegisteredJobs : TJobRegister; // singleton für arme

implementation

procedure TJobRegister.registerCall (const registeredName : String; m : pMethod);
begin
 if (FRegister.IndexOf(registeredName)<0) then
     FRegister.AddObject(registeredName,m);
end;

procedure TJobRegister.Call(const registeredName : String);      
var idx : Integer;
begin
 idx := FRegister.IndexOf(registeredName);
 if (idx >= 0 ) then begin
                         TExecute(FRegister.Objects[idx]^).Execute;
                     end
                else begin
                       .....
                     end;
end;
Die Klassen deren Methoden aufgerufen werden sollen müssten dann die Unit Register verwenden und sich dort anmelden, beispielsweise so

Delphi-Quellcode:
 

uses Register;

type TUseable = class(TObject)
     public
      constructor Create;
      procedure TuWas;
     end;

constructor TUseable.Create;
var m : pMethod;
begin
 New(m);
 m.Data := self;
 m.Code := @TuWas;
 register.registeredJobs.registerCall('Irgendwas',m);
end;
Das hätte auch den Vorteil das Du beliebige Namen verwenden kannst. Die Registerklasse müsste sich freilich noch um die Freigabe der pMethod Variablen kümmern, eine Deregistrierung wäre angeraten wenn es passieren kann das Instanzen während der Laufzeit des Programmes zerstört werden etc. Aber wenn Du das so machst, dann kannst Du es auch gleich richtig machen und entweder Interfaces verwenden oder Dir einen Wrapper je aufzurufender Klasse schreiben und dessen Instanzen / Objekte registrieren.

cu Ha-Jö

Sigi55 21. Feb 2015 09:23

AW: Routine mit Namen aufrufen
 
Moin Ha-Jö

Zitat:

Wenn ich das richtig verstehen dann gibt es keine "unbekannten" Prozeduren und Methoden sondern lediglich Objekte/Klassen die sich auf mehrere Units verteilen und die Du einheitlich aufrufen willst, Du weisst aber wo (in welcher Unit/Klasse) diese Funktionen ursprünglich definiert sind. Da du alle Methoden in Deinem Beispiel ohne Parameter aufrufen wolltest gehe ich davon aus das alle aufzurufenden Methoden paramterlos sind.
Richtig :-)

Zitat:

... machst, dann kannst Du es auch gleich richtig machen und entweder Interfaces verwenden oder Dir einen Wrapper je aufzurufender Klasse schreiben und dessen Instanzen / Objekte registrieren.
Interfaces, Wrapper, ?, da hört es bei mir auf, das kann ich nicht :-(

Ich glaub ich mach das mit den Fähnchen ...(Sparkasse) :-D

Ich bin eher so der "Auto-Zusammen-Schrauber", sprich, fertige Komponenten
zusammenbauen, aber wenn es dann (für mich) ins "Eingemachte" geht muss
ich passen.

Aber vielen Dank für Deine Mühe !

cu

Sigi

stahli 21. Feb 2015 10:19

AW: Routine mit Namen aufrufen
 
So wie ich das verstehe, liegt Dein Problem nicht bei "anderen Units" sondern bei "anderen Instanzen".
Zeig doch mal noch einen Auszug aus Deiner Datenbank, wie die Jobs definiert sind.

Wenn Du als JobNamen jetzt "DatensicherungStarten" ermittelst und dann in einem Objekt der Klasse TForm1 interpretierst um eine Methode "DatensicherungStarten" zu finden funktioniert das´, da Du Dich auf das Objekt "Self" beziehst.

Wenn Du aber mit einem anderen Objekt arbeiten willst, musst Du dessen Instanz mitgeben. Da reicht es nicht, eine Unit zu kennen.

Also ich denke, Du hast da noch einen Ansatz, der nicht ganz passt.

Versuch doch mal meinen oben genannten Ansatz. Offenbar hast Du ja nicht viele in Frage kommenden Möglichkeiten, welche Jobs aufgerufen werden können.
Dann könnte so ein "Übersetzer" schon Sinn machen. Du übergibst eine Objektinstanz und den Methodennamen und führst dann etwas passendes dazu aus.

Vom Prinzip könntest Du dann auch statt dem Methodennamen einen Code verwalten (42 = "DatensicherungStarten").

Dann siehst Du schon mal, dass Du eine Objektinstanz kennen musst, damit das funktionieren kann.

Ich denke, dass es derzeit bei Dir daran scheitert.


Zeig einfach noch ein bissl mehr, dann können wir das auch besser nachvollziehen.

BadenPower 21. Feb 2015 10:28

AW: Routine mit Namen aufrufen
 
Zitat:

Zitat von Sigi55 (Beitrag 1290823)
Moin erstmal,
Der "JobName" ist die Bezeichnung der aufzurufen Procedure den ich per Variable
übergebe.

Das klappt auch alles wunderbar, nur liegen nicht alle Proceduren auf der
Hauptform ...

Wenn Du das unbedingt so machen möchtest und 2 Variablen übergeben kannst und die Proceduren der 2. Unit auch in einer Form stecken dann kannst Du auch folgendes machen:

Delphi-Quellcode:
procedure TForm1.Button4Click(Sender: TObject);
begin
  ExecuteRoutine(Application.FindComponent('Form2'), 'Hello_World');
end;

Sigi55 21. Feb 2015 11:39

AW: Routine mit Namen aufrufen
 
Hallo Stali,

Zitat:

Zitat von stahli (Beitrag 1290841)
Zeig einfach noch ein bissl mehr, dann können wir das auch besser nachvollziehen.

Da löppelt einfach nur ne Schleife ...

Delphi-Quellcode:
while not eof do
begin
  ExecuteRoutine(Form1,dmLOG.tbl_JobsLinkedPreUpdate.FieldByName('Command').AsString);
  next;
end;
Das können ein- oder mehrere Jobs sein die nacheinander abgearbeitet
werden, z.B.: Angebote ausdrucken und versenden, Datenbanken abgleichen, etc.

Das ist auch nicht wirklich ein Problem, so wie es jetzt ist, ist es eigentlich
OK, aber ich dachte halt, ich könnte mir den quasi "doppelt gemoppelten" Aufruf
einer Procedure sparen, wenn die Procedure in irgendeiner anderen unit liegt.

Gruß

Sigi

Sigi55 21. Feb 2015 11:46

AW: Routine mit Namen aufrufen
 
Hallo BadenPower,

Zitat:

Zitat von BadenPower (Beitrag 1290844)

... und die Proceduren der 2. Unit auch in einer Form stecken dann kannst Du auch folgendes machen:

Delphi-Quellcode:
procedure TForm1.Button4Click(Sender: TObject);
begin
  ExecuteRoutine(Application.FindComponent('Form2'), 'Hello_World');
end;

Da liegt ja das Problem, die Procedure die ich aufrufen möchte liegt
in einer "formlosen" unit.

Vielen Dank und Gruß

Sigi

BadenPower 21. Feb 2015 12:20

AW: Routine mit Namen aufrufen
 
Zitat:

Zitat von Sigi55 (Beitrag 1290850)
Da liegt ja das Problem, die Procedure die ich aufrufen möchte liegt
in einer "formlosen" unit.

Dann zeig einmal die "formlose" Unit.
Vielleicht lässt sich das einfach umbiegen.

BadenPower 21. Feb 2015 12:31

AW: Routine mit Namen aufrufen
 
Oder Du machst aus der anderen Unit einfach eine "Form-Unit", welche Du nicht einmal anzeigen lassen musst.


kleines Copy-und-Paste Beispiel

MainForm
Delphi-Quellcode:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Buttons, Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    procedure Job1();
    procedure Job2();
    procedure Job3();
    procedure Button1Click(Sender: TObject);
  private
    procedure ExecuteJobs();
    procedure ExecuteRoutine(AInstance: TObject; AName: string);
  public
    Memo1: TMemo;
    Button1: TButton;
    ListBox1: TListBox;
    constructor Create(AOwner: TComponent); override;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

type
  TExecute = procedure of object;

constructor TForm1.Create(AOwner: TComponent);
begin
  inherited;
  Position := poScreenCenter;
  Height := 500;
  Width := 600;
  Memo1 := TMemo.Create(Self);
  Memo1.Parent := Self;
  Memo1.Left := 300;
  Memo1.Top := 10;
  Memo1.Width := 280;
  Memo1.Height := 400;
  Button1 := TButton.Create(Self);
  Button1.Parent := Self;
  Button1.Left := 100;
  Button1.Top := 420;
  Button1.Width := 100;
  Button1.Height := 25;
  Button1.Caption := 'Jobs starten';
  Button1.OnClick := Button1Click;
  ListBox1 := TListBox.Create(Self);
  ListBox1.Parent := Self;
  ListBox1.Left := 10;
  ListBox1.Top := 10;
  ListBox1.Width := 280;
  ListBox1.Height := 400;
  ListBox1.Items.Add('Form1/Job1');
  ListBox1.Items.Add('Form1/Job2');
  ListBox1.Items.Add('Form1/Job3');
  ListBox1.Items.Add('Form2/Job1');
  ListBox1.Items.Add('Form2/Job2');
  ListBox1.Items.Add('Form2/Job3');
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ExecuteJobs();
end;

procedure TForm1.ExecuteJobs();
var
  liZ1: Integer;
  lObjectName: String;
  lProcedureName: String;
  lPos: Integer;
begin

  for liZ1 := 0 to ListBox1.Count - 1 do
   begin
    if ListBox1.Items[liZ1] <> '' then
     begin
      lObjectName := '';
      lProcedureName := '';
      lPos := Pos('/', ListBox1.Items[liZ1]);
      lObjectName := Copy(ListBox1.Items[liZ1], 1, lPos - 1);
      lProcedureName := Copy(ListBox1.Items[liZ1], lPos + 1, MaxInt);
      if ((lObjectName <> '') and (lProcedureName <> '')) then
       begin
         Memo1.Lines.Add('Methode ' + lProcedureName + ' in ' + lObjectName + ' starten.');
         ExecuteRoutine(Application.FindComponent(lObjectName), lProcedureName);
         Memo1.Lines.Add('Methode ' + lProcedureName + ' in ' + lObjectName + ' beendet.');
       end;
     end;
   end;

end;

procedure TForm1.ExecuteRoutine(AInstance: TObject; AName: string);
var
  lRoutine: TMethod;
  lExecute: TExecute;
begin
  lRoutine.Data := Pointer(AInstance);
  lRoutine.Code := AInstance.MethodAddress(AName);
  if (lRoutine.Code = nil) then Exit;
  lExecute := TExecute(lRoutine);
  lExecute;
end;

procedure TForm1.Job1();
begin
  ShowMessage('Form1 - Job1');
end;

procedure TForm1.Job2();
begin
  ShowMessage('Form1 - Job2');
end;

procedure TForm1.Job3();
begin
  ShowMessage('Form1 - Job3');
end;

end.
Unit2
Delphi-Quellcode:
unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs;

type
  TForm2 = class(TForm)
    procedure Job1();
    procedure Job2();
    procedure Job3();
  private
  public
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

procedure TForm2.Job1();
begin
  ShowMessage('Form2 - Job1');
end;

procedure TForm2.Job2();
begin
  ShowMessage('Form2 - Job2');
end;

procedure TForm2.Job3();
begin
  ShowMessage('Form2 - Job3');
end;

end.

Sigi55 21. Feb 2015 13:01

AW: Routine mit Namen aufrufen
 
Hallo BadenPower,

Zitat:

Zitat von BadenPower (Beitrag 1290852)
Oder Du machst aus der anderen Unit einfach eine "Form-Unit", welche Du nicht einmal anzeigen lassen musst.

Mensch, geil, klappt wie blöd :-)

Vielen Dank für Deine Mühe, euch anderen auch.
Hier wird man echt geholfen.

Meistens kann ich ja meine Wissenslücken in Bezug auf Delphi über
Google oder durch Suche hier im Forum auffüllen, aber in diesem Fall
....

Danke nochmal und

Gruß

Sigi

himitsu 21. Feb 2015 13:08

AW: Routine mit Namen aufrufen
 
Zitat:

Zitat von BadenPower (Beitrag 1290852)
Oder Du machst aus der anderen Unit einfach eine "Form-Unit", welche Du nicht einmal anzeigen lassen musst.

Ich brauch nur eine String-Variable und nehm dafür ein TRichEdit? :wall:

Es wurde doch genau gesagt, was man braucht.
> Published und/oder {$M+} (also TPersistent/TComponent)

BadenPower 21. Feb 2015 13:15

AW: Routine mit Namen aufrufen
 
Zitat:

Zitat von himitsu (Beitrag 1290855)
Ich brauch nur eine String-Variable und nehm dafür ein TRichEdit? :wall:

Mach einmal bitte eine Unit2 mit Deinem Ansatz.
Ich steh' grad' auf dem Schlauch.

himitsu 21. Feb 2015 15:00

AW: Routine mit Namen aufrufen
 
Man kann natürlich aus einer Mücke einen Elefanten machen.

Statt
Delphi-Quellcode:
var s: string;
nimmt man ein TRichEdit,
bzw. statt einer TStringList nimmt man ein TMemo
und statt TPersistent eine ganze Form, (wenn schon, dann maximal ein TDataModul)
statt TBitmap/TPicture nimmst du ein TImage,
statt mit dem Smart machst du deinen Freitagseinkauf mit einem 80-Tonner
usw.

BadenPower 21. Feb 2015 15:27

AW: Routine mit Namen aufrufen
 
Zitat:

Zitat von himitsu (Beitrag 1290870)
(wenn schon, dann maximal ein TDataModul)

Fuss von Schlauch hochheb.

Ja klar, ein Datenmodul geht ja auch.

Es wird beim Programmstart auch automatisch erzeugt und es muss auch in Form1 nie etwas geändert werden, da ja die Units mit den Funktionen nicht in Form1 bekannt gemacht werden müssen.

Bei Erweiterungen des Programms entweder ein Datenmodul oder eben eine Form, wenn die Funktionen in einem Fenster enthalten sind, welches sowieso benutzt wird.

Würde der TE etwas anderes benutzen, dann müsste er immer selbst dafür sorgen, dass der entsprechende Container auch erzeugt wird.

So kann er einfach ein neues Fenster oder Datenmodul erstellen und braucht an keiner anderen Stelle etwas zu ändern.

Unit3 als Datenmodul:
Delphi-Quellcode:
unit Unit3;

interface

uses
  SysUtils, Classes, Dialogs;

type
  TDataModule3 = class(TDataModule)
    procedure Job1();
    procedure Job2();
    procedure Job3();
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  DataModule3: TDataModule3;

implementation

{$R *.dfm}

procedure TDataModule3.Job1();
begin
  ShowMessage('DataModule3 - Job1');
end;

procedure TDataModule3.Job2();
begin
  ShowMessage('DataModule3 - Job2');
end;

procedure TDataModule3.Job3();
begin
  ShowMessage('DataModule3 - Job3');
end;

end.

zum Test dieses noch in den Constructor Form1.Create der Form1 hinzufügen

Delphi-Quellcode:
  ListBox1.Items.Add('DataModule3/Job1');
  ListBox1.Items.Add('DataModule3/Job2');
  ListBox1.Items.Add('DataModule3/Job3');
und man sieht, es geht selbstverständlich auch.

Sigi55 21. Feb 2015 16:20

AW: Routine mit Namen aufrufen
 
Zitat:

Zitat von BadenPower (Beitrag 1290873)
Zitat:

Zitat von himitsu (Beitrag 1290870)
(wenn schon, dann maximal ein TDataModul)


und man sieht, es geht selbstverständlich auch.

Hi,
und wie das geht, ist das jetzt der Ansatz den himitsu meinte ??

Gruß

Sigi

BadenPower 21. Feb 2015 16:46

AW: Routine mit Namen aufrufen
 
Zitat:

Zitat von Sigi55 (Beitrag 1290877)
und wie das geht, ist das jetzt der Ansatz den himitsu meinte ??

himitsu's Ansatz wäre noch weniger Speicherplatz zu verbrauchen, also mit der Klasse zu arbeiten, welche selbst am wenigsten Speicher belegt.

Hätte allerdings einen erhöhten Programmieraufwand zur Folge und dies immer dann, wenn Du auf Funktionen in einer neuen Klasse zugreifen wolltest. Und ob dies dann wirklich weniger Speicher benötigt ist eher ungewiss.

Mit dem Datenmodul ist es ein sehr guter Kompromiss, da das DM weniger Speicher belegt, als eine extra Form, wenn Du die Form selbst eigentlich nicht benötigst.

Wie gesagt: einfach neues Datenmodul oder neue Form mit den Proceduren anlegen und fertig.

Sigi55 21. Feb 2015 17:26

AW: Routine mit Namen aufrufen
 
Zitat:

Zitat von BadenPower (Beitrag 1290881)
Mit dem Datenmodul ist es ein sehr guter Kompromiss, da das DM weniger Speicher belegt, als eine extra Form, wenn Du die Form selbst eigentlich nicht benötigst.

Wie gesagt: einfach neues Datenmodul oder neue Form mit den Proceduren anlegen und fertig.

Hi,

seh ich auch so, hab ich getest und löppelt super :-)
Für mich als "DummProgger" gerade richtig :-)

Dann werd ich mal an's umbauen gehn ...

Danke nochmal.

Sigi

BUG 21. Feb 2015 19:58

AW: Routine mit Namen aufrufen
 
Kann man über diesen Weg auch z.B. Destroy aufrufen*? Das konnte spaßig sein :angel2:

* Sry, kein Delphi zur Hand.

BadenPower 21. Feb 2015 20:42

AW: Routine mit Namen aufrufen
 
Zitat:

Zitat von BUG (Beitrag 1290901)
Kann man über diesen Weg auch z.B. Destroy aufrufen*? Das konnte spaßig sein :angel2:
[/SIZE]

Standardmäßig nicht, da der Destructor nur "public" ist.


Wenn Du das aber möchtest dann so:

Delphi-Quellcode:
type
  TForm1 = class(TForm)
    procedure Job1();
    procedure Button1Click(Sender: TObject);
  private
    procedure ExecuteJobs();
    procedure ExecuteRoutine(AInstance: TObject; AName: string);
  public
  published
    procedure Destroy;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

type
  TExecute = procedure of object;

procedure TForm1.Destroy;
begin
  inherited Destroy;
end;

Dejan Vu 22. Feb 2015 07:44

AW: Routine mit Namen aufrufen
 
Das Problem bei Ansätzen dieser Art(*) erblickt in seiner ganzen Wucht in dem Augenblick das Licht der Welt, in dem die Namen der aufzurufenden Prozeduren abgespeichert werden und anschließend ein Refactoring der Prozedurnamen erfolgt (= Methode umbenennen). Dann funktioniert nämlich gar nichts mehr.

Angenommen im Beispiel des TE kommt jemand auf die Idee, das '_' in Prozedurnamen ein NoGo sind (was nicht abwegig ist, denn sie sind es :stupid:). Eben wurde gespeichert, das 'Hello_World' aufgerufen werden soll, dann wird umbenannt und dann geht gar nichts mehr und im ungünstigsten Fall werden die abgespeicherten Aufrufe (als String) komplett unbrauchbar.

Alternativen? Z.B. Methodenattribute einführen, d.h. 'Hello_World' wird mit dem Attribute 'HELLOWORLD' dekoriert. Dann ist das Verfahren immun gegen Refactorings. Natürlich: Wer Attributparameter verändert, hat selbst schuld.

Mit Sicherheit gibt es bessere Ideen für eine robustere Umsetzung der Anforderung.

(*) Ein anderer Ansatz der gleichen Art: 'FindComponent' :wall:

hanvas 22. Feb 2015 10:59

AW: Routine mit Namen aufrufen
 
Zitat:

Zitat von Dejan Vu (Beitrag 1290925)

Mit Sicherheit gibt es bessere Ideen für eine robustere Umsetzung der Anforderung.

Das war es doch was ich schon ansatzweise beschrieben (und den größten Teil des Überbaus gleich mitgeliefert) habe, indem man das Objekt welches die später aufzurufende(n) Methoden bereistellt diese selbst bei einer zentralen Instanz registrieren (und gff. deregistrieren) lässt, kann man die Methoden unter einem beliebigen Namen aufrufen, die Methode ist immun gegen Refactorings, relativ einfach und benötigt außer einer Liste keine weiteren Ressourcen, außerdem ist es nicht nötig das man die Objekte die Methoden bereitstellen von TForm oder TDataModule ableitet, möglich ist es natürlich trotzdem.

cu Ha-Jö

Dejan Vu 22. Feb 2015 11:51

AW: Routine mit Namen aufrufen
 
:thumb: Oder so. Hab ich doch glatt überlesen.

Sigi55 22. Feb 2015 20:47

AW: Routine mit Namen aufrufen
 
Hallo,

Mann-O-Mann, da hab ich ja was losgetreten ...

Ich würd ja gern den Ansatz von Ha-Jö testen, aber ich kann das
nicht ausprogrammieren ..., das ist für mich zu weit im Eingemachten.

Mit dem Ansatz von BadenPower komm ich klar.

Vielleicht hat ja jemand Lust, Zeit, Ruhe und Muße die bessere Lösung
als fertiges Beispiel zu bauen ??

Gruß

Sigi

hanvas 23. Feb 2015 15:57

AW: Routine mit Namen aufrufen
 
Zitat:

Zitat von Sigi55 (Beitrag 1291030)

Ich würd ja gern den Ansatz von Ha-Jö testen, aber ich kann das
nicht ausprogrammieren ..., das ist für mich zu weit im Eingemachten.

Ich hatte Dir doch schon fast alles geliefert. Hier nochmal die Komfortvariante.

Delphi-Quellcode:

unit jRegister;
interface
uses classes;

type TJobRegister = class(TComponent)
     private
      FRegister : TStringList;
     public
      Constructor Create(aOwner : TComponent);                                 override;
      Destructor Destroy;                                                     override;

      procedure  RegisterObjectMethods
       (Instance : TObject; const methods : TStrings);                         virtual;

      procedure  RegisterObjectMethodByName
       (const registeredName : String; Instance : TObject; const method : String);
                                                                                virtual;
      procedure  RegisterObjectMethod
        (const registeredName : String; instance : TObject; addr : Pointer);   virtual;
      procedure  RegisterCall  (const registeredName : String; m : pMethod); virtual;
      procedure  UnRegisterCall (const registeredName : String);              virtual;
      procedure  Call(const aFunctionToCall : String);                        virtual;
     end;

     TExecute = procedure of object;

function jobRegister : TJobRegister;

implementation
uses sysUtils;

var reg : TJobRegister = nil;

function jobRegister : TJobRegister;
begin
 if not Assigned(reg) then
    reg := TJobRegister.Create(Application);
 result := reg;
end;

Constructor TJobRegister.Create(aOwner : TComponent);
begin
 inherited Create(aOwner);
 FRegister := TStringList.Create;
 FRegister.Sorted := True;
end;

Destructor TJobRegister.Destroy;
var i : Integer;
    p : pMethod;
begin
 for i := 0 to FRegister.Count-1 do
  begin
    p := pMethod(FRegister.Objects[i]);
    FreeMem(p);
  end;
end;

procedure  TJobRegister.RegisterObjectMethods
       (Instance : TObject; const methods : TStrings);
var i : Integer;
  n,v : String;
begin
 for i := 0 to methods.count-1 do
  begin
    n := methods.Names[i];
    v := methods.Values[n];
    if (n<>'') and
       (v<>'') then
         RegisterObjectMethodByName(n,instance,v);
  end;
end;

procedure  TJobRegister.RegisterObjectMethodByName
       (const registeredName : String; Instance : TObject; const method : String);
var p : Pointer;
begin
 if Assigned(instance) then
  begin
   p := Instance.MethodAddress(method);
   if (Assigned(p)) then
       RegisterObjectMethod(registeredName,Instance,p);
  end;
end;


procedure  TJobRegister.RegisterObjectMethod
        (const registeredName : String; instance : TObject; addr : Pointer);
var p : pMethod;
begin
 GetMem(p,SizeOf(TMethod));
 p.Data := instance;
 p.Code := addr;
 RegisterCall(registeredName,p);
end;

procedure  TJobRegister.RegisterCall (const registeredName : String; m : pMethod);
begin
    if (FRegister.IndexOf(registeredName)<0) then
        FRegister.AddObject(registeredName,Pointer(m))
    else raise Exception.Create('Funktion [' + registeredName + '] bereits registriert');
end;

procedure  TJobRegister.UnRegisterCall (const registeredName : String);
var idx : Integer;
    p  : pMethod;
begin
 idx := FRegister.IndexOf(registeredName);
 if (idx >= 0 ) then begin
                       p := pMethod(FRegister.Objects[idx]);
                       FreeMem(p);
                     end
                else raise Exception.Create('Ungültiger Versuch [' + registeredName + '] freizugeben');
end;

procedure  TJobRegister.Call(const aFunctionToCall : String);
var idx : Integer;
    p  : pMethod;
    e  : TExecute;
begin
 idx := FRegister.IndexOf(aFunctionToCall);
 if (idx >= 0 ) then begin
                         p := Pointer(FRegister.Objects[idx]);
                         e := TExecute(p^);
                         e();
                     end
                else begin
                       raise Exception.Create('Funtkion [' + aFunctionToCall + '] nicht vorhanden');
                     end;
end;
Die aufzurufenden Routinen musst Du aber selbst verpacken. Das geht aber ziemlich einfach. Das nachfolgende Beispiel zeigt wie.


Delphi-Quellcode:

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
  public
    { Public-Deklarationen }
  published
    procedure execute1;
    procedure execute2;
    procedure execute3;
    procedure execute4;
  end;

var
  Form1: TForm1;

implementation
uses jRegister;

{$R *.dfm}

procedure TForm1.execute1;
begin
 MessageDlg('Test', mtWarning, [mbOK], 0);
end;

procedure TForm1.execute2;
begin
 MessageDlg('Test 2', mtWarning, [mbOK], 0);
end;

procedure TForm1.execute3;
begin
 MessageDlg('Test 3', mtWarning, [mbOK], 0);
end;

procedure TForm1.execute4;
begin
 MessageDlg('Test 4', mtWarning, [mbOK], 0);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
 reg := TJobRegister.Create(self);
end;

procedure TForm1.Button1Click(Sender: TObject);
var s : TStringList;
begin
  s := TStringList.Create;
  s.Add('Test=execute1');
  s.Add('Test2=execute2');
 { methoden müssen published sein, Instance muss von TPersistent
   oder TComponent abgeleitet sein }
  jobRegister.RegisterObjectMethods(self,s);
  s.free;

  jobRegister.Call('Test');
  jobRegister.Call('Test2');

 { Instanzen können beliebige Objekte sein, Methoden müssen nicht published sein }
  jobRegister.RegisterObjectMethod('Test 3', self, @TForm1.execute3);
  jobRegister.Call('Test 3');

  { methoden müssen published sein, Instance muss von TPersistent
   oder TComponent abgeleitet sein }
  jobRegister.RegisterObjectMethodByName('Test 4', self, 'execute4' );
  jobRegister.Call('Test 4');
end;

end.
Die Funktion jobregister ist zentral definiert und sollte nur einmal vorhanden. Die Methoden die verpackt werden sollen dürfen in beliebigen anderen Objekten sein, die nicht zwingend von TComponent oder TPersistent abgeleitet sein müssen. Das Registrieren und Deregistrieren von Methoden wäre am besten im Constructor bzw. Destrukter der Objekte untergebracht die diese Methoden aufrufen - das Beispiel ist aber so das Du es verstehen solltest und ähnlich wie das bis jetzt von Dir verwendete.

Schön ist aber trotzdem anders.

hanvas 23. Feb 2015 19:43

AW: Routine mit Namen aufrufen
 
Habe leider zwei Fehler eingebaut. Bei Destroy fehlt der geerbte Destructor also bitte lieber so:

Delphi-Quellcode:


Destructor TJobRegister.Destroy;
var i : Integer;
    p : pMethod;
begin
 for i := 0 to FRegister.Count-1 do
  begin
    p := pMethod(FRegister.Objects[i]);
    FreeMem(p);
  end;
 inherited Destroy;
end;

und im Beispiel gehört sich FormCreate gestrichen.

Sigi55 24. Feb 2015 23:15

AW: Routine mit Namen aufrufen
 
Hallo Ha-Jö !!

ist ja der Wahnsinn, was für ne Arbeit :-)

Zitat:

Zitat von hanvas (Beitrag 1291175)
Delphi-Quellcode:
Destructor TJobRegister.Destroy;
var i : Integer;
    p : pMethod; // Mag er nicht, Undeclared Indentifier ????
begin
 for i := 0 to FRegister.Count-1 do
  begin
    p := pMethod(FRegister.Objects[i]);
    FreeMem(p);
  end;
 inherited Destroy;
end;

Sonst sieht alles gut aus, aber ich hab keine Ahnung was ich mit "pMethod"
machen soll ...

Danke für die Mühe, jetzt soll's aber auch laufen !

Gruß

Sigi

P.S.: ist D2010

DeddyH 25. Feb 2015 07:03

AW: Routine mit Namen aufrufen
 
Ich rate mal:
Delphi-Quellcode:
type
  pMethod = ^TMethod;

Sigi55 25. Feb 2015 16:46

AW: Routine mit Namen aufrufen
 
Moin,

mir sagt das Alles garnichts :-) und wird es auch wohl nie ...

Delphi-Quellcode:
  procedure RegisterCall (const registeredName : String; m : pMethod); virtual;
                                                             ^^^^^^^
// Mag er nicht, Undeclared Indentifier ???

Ich kann mir da leider nicht ansatzweise weiterhelfen, da hört's
echt auf.
Ich bin mehr so der Datenschauffler im kfm. Bereich, so von einer DB
in die andere, Plausibilitätsprüfungen, etc.

Aber das ist nicht meine Welt ... sry.

@DaddyH, das war es leider nicht :-(

Gruß

Sigi

hanvas 25. Feb 2015 17:28

AW: Routine mit Namen aufrufen
 
Zitat:

Zitat von Sigi55 (Beitrag 1291521)
Moin,

mir sagt das Alles garnichts :-) und wird es auch wohl nie ...

Delphi-Quellcode:
  procedure RegisterCall (const registeredName : String; m : pMethod); virtual;
                                                             ^^^^^^^
// Mag er nicht, Undeclared Indentifier ???

@DaddyH, das war es leider nicht :-(

Gruß

Sigi

Doch natürlich war es das! Aber eigentlich sollte pMethod in der Unit system ohnehin deklariert sein, möglicherweise ist das von Version zu Version unterschiedlich.

Wo hast Du das

Delphi-Quellcode:
  type pMethod = TMethod;
denn hingeschrieben. Dir Typdeklaration gehört vor die erste Verwendung also

Delphi-Quellcode:

unit jRegister;
interface
uses classes;

type pMethod = TMethod;

     TJobRegister = class(TComponent)
 .....
cu Ha-Jö

Sigi55 25. Feb 2015 18:43

AW: Routine mit Namen aufrufen
 
Hi,

Zitat:

... denn hingeschrieben. Dir Typdeklaration gehört vor die erste Verwendung also
Delphi-Quellcode:

unit jRegister;
interface
uses classes;

type pMethod = TMethod;

     TJobRegister = class(TComponent)
 .....
Hatte ich auch so gemacht, nur hatte ich "type pMethod = ^TMethod" wie DaddyH
es geschrieben hatte ...

Jetzt, ohne "^" ist es OK, nur eines noch ..

Delphi-Quellcode:
function jobRegister : TJobRegister;
begin
 if not Assigned(reg) then
    reg := TJobRegister.Create(Application);
 result := reg;            // ^^^^^^^^^^^
end;
Fehler: Application? Undeclared Indentifier ???

Das ist jetzt aber der Letzte ...

Und nochmal für Blö..., wo genau an welcher Stelle soll das
"FormCreate" gestrichen werden ??

Danke :-)

Sigi

himitsu 25. Feb 2015 19:19

AW: Routine mit Namen aufrufen
 
Zitat:

Zitat von Sigi55 (Beitrag 1291543)
Fehler: Application? Undeclared Indentifier ???

[F1] kennst du aber?

Delphi-Referenz durchsuchenApplication -> http://docwiki.embarcadero.com/Libra...ms.Application
In der Hilfe steht, in welcher Unit etwas steht.

Aber man muß hier nicht Application verwenden, denn dem Delphi-Referenz durchsuchenTComponent.Create gibt man einen Owner mit, also den, welcher sich um das Freigeben kümmert.
Meistens nimmt man die Form, aber es kann jeder TComponent-Nachfahre sein, oder wenn man sich definitiv immer selber um die Freigabe kümmert, dann kann man auch nil nehmen.

hanvas 25. Feb 2015 19:37

AW: Routine mit Namen aufrufen
 
Zitat:


Hatte ich auch so gemacht, nur hatte ich "type pMethod = ^TMethod" wie DaddyH
es geschrieben hatte

Es gehört sich mit ^, das Fehlen war Prellen meiner Tastatur.


Delphi-Quellcode:
function jobRegister : TJobRegister;
begin
 if not Assigned(reg) then
    reg := TJobRegister.Create(Application);
 result := reg;            // ^^^^^^^^^^^
end;
Zitat:


Fehler: Application? Undeclared Indentifier ???

Du musst die Unit Forms einbinden wenn Du Application verwenden willst.

Zitat:


Und nochmal für Blö..., wo genau an welcher Stelle soll das
"FormCreate" gestrichen werden ??

Mein ursprüngliches Posting bestand aus einer Unit jRegister und einer in der ein Formular zu Testzwecken verwendet wurde. In der Unit der ich das Formular zum Testen hatte, hatte ich auch "Quickn Dirty" enen Aufruf/Implementierung von FormCreate der nicht reingehört bzw nicht mehr notwendig ist. Wenn Du dieses Testformular nicht nachbaust und nur die andere Unit verwendest dann gibt es kein auch kein FormCreate.

Entschuldige die Fehler die beim Copy / Paste entstanden sind (bzw. das Fehlen von PMethod ist bei meinem Delphi kein Fehler)

cu Ha-Joe


Alle Zeitangaben in WEZ +1. Es ist jetzt 14:24 Uhr.
Seite 1 von 2  1 2      

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