Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Überschreiben von TThread.Terminate (https://www.delphipraxis.net/152790-ueberschreiben-von-tthread-terminate.html)

MatthiasR 7. Jul 2010 07:31

Delphi-Version: 2005

Überschreiben von TThread.Terminate
 
Ich stehe gerade vor folgendem "Problem". Ein wirkliches Problem ist es nicht, da ich Alternativen habe, es zu umgehen, trotzdem würde ich gerne wissen, warum mein erster Ansatz so nicht funktioniert.

Ich habe eine eigene Thread-Klasse geschrieben, die (logischerweise) von TThread abgeleitet ist. Ich möchte gerne die Methode TThread.Terminate aus dem public-Bereich überschreiben, weil bei deren Aufruf intern in meiner eigenen Thread-Klasse noch etwas mehr passieren soll, als nur das FTerminated-Feld auf True zu setzen. Aber auch das soll natürlich weiterhin passieren. Daher habe ich TThread.Terminate folgendermaßen überschrieben:
Delphi-Quellcode:
interface

type
  TMeinThread = class(TThread)
  private
    ...
  public
    procedure Terminate; // override hier ja nicht nötig, da nicht virtual
  end;

implementation

procedure TMeinThread.Terminate;
begin
  // TThread.Terminate aufrufen, um FTerminated auf True zu setzen
  inherited;

  // anschließend noch alles weitere machen
  ...
end;

end.
Das Problem ist, dass TThread.Terminate scheinbar übersprungen wird und von dem inherited nicht aufgerufen. Ein Breakpoint innerhalb von TThread.Terminate wird grün dargestellt und deshalb beim Debuggen auch nie erreicht. Woran liegt das? Kann man TThread.Terminate nicht überschreiben und falls nicht, warum?

Danke für die Aufklärung :)

Stevie 7. Jul 2010 07:36

AW: Überschreiben von TThread.Terminate
 
Die Antwort hast du schon selber gegeben durch deinen Kommentar hinter Terminate.
Wenn intern Terminate aufgerufen wird, dann wird das TThread.Terminate aufgerufen und nicht TMeinThread.Terminate.
Dazu müsste die Methode in TThread schon virtual sein.

himitsu 7. Jul 2010 07:37

AW: Überschreiben von TThread.Terminate
 
Delphi-Quellcode:
// override hier ja nicht nötig, da nicht virtual
Genau falsch ... es muß heißen
"override hier ja nicht möglich, da nicht virtual"

Heißt also, du kannst es nicht überschreiben
Und TThread ruft nur das ihm bekannte Terminate auf (deines kennt er nicht).

Wie wäre es mit einem Eventhandler für OnTerminate?

sirius 7. Jul 2010 07:45

AW: Überschreiben von TThread.Terminate
 
Zitat:

Zitat von himitsu (Beitrag 1033915)
Delphi-Quellcode:
// override hier ja nicht nötig, da nicht virtual
Genau falsch ... es muß heißen
"override hier ja nicht möglich, da nicht virtual"

Heißt also, du kannst es nicht überschreiben
Und TThread ruft nur das ihm bekannte Terminate auf (deines kennt er nicht).

Ganz richtig wäre: TThread ruft Terminate nie auf, deswegen muss es auch nicht virtual sein. Ein simples verdecken (reintroduce) reicht hier völlig aus.

Virtuelle Methoden sind ja nur wichtig, wenn von deiner Elternklasse in eine deiner Methoden gesprungen werden soll. Aber Terminate ruft man ja nur von außen auf, deswegen einfach verdecken, fertsch.

Stevie 7. Jul 2010 07:49

AW: Überschreiben von TThread.Terminate
 
Zitat:

Zitat von sirius (Beitrag 1033919)
Ganz richtig wäre: TThread ruft Terminate nie auf, deswegen muss es auch nicht virtual sein. Ein simples verdecken (reintroduce) reicht hier völlig aus.

Schonmal in den Destructor von TThread geguckt?

MatthiasR 7. Jul 2010 07:49

AW: Überschreiben von TThread.Terminate
 
Zitat:

Zitat von himitsu (Beitrag 1033915)
Delphi-Quellcode:
// override hier ja nicht nötig, da nicht virtual
Genau falsch ... es muß heißen
"override hier ja nicht möglich, da nicht virtual"

Ob du's glaubst oder nicht, ich habe relativ lange überlegt, ob ich "nötig" oder "möglich" schreiben soll, ersteres kam mir aber in meinem Zusammenhang sinnvoller vor.
Zitat:

Zitat von himitsu (Beitrag 1033915)
Heißt also, du kannst es nicht überschreiben
Und TThread ruft nur das ihm bekannte Terminate auf (deines kennt er nicht).

Heißt also, ich habe keine Möglichkeit, innerhalb von TMeinThread.Terminate das TThread.Terminate aufzurufen? Ist das allgemein bei jeder nicht-virtuellen Methode so, wenn man in einer abgeleiteten Klasse eine Methode genauso benennt? Ab da kennt die abgeleitete Klasse die geerbte Methode gleichen Namens nicht mehr?
Zitat:

Zitat von himitsu (Beitrag 1033915)
Wie wäre es mit einem Eventhandler für OnTerminate?

Jo, das wäre eine der Alternativen, die mir im Kopf rumschwebt :) . Wobei ich dann ja nicht verhindern kann, dass jemand, der meine neue TMeinThread-Klasse verwendet, dem OnTerminate-Ereignis einen anderen Handler zuordnet und meiner dadurch abgehängt wird?

mkinzler 7. Jul 2010 07:53

AW: Überschreiben von TThread.Terminate
 
Wenn nich virtuell dann gibt es auch keine VMT

sirius 7. Jul 2010 08:07

AW: Überschreiben von TThread.Terminate
 
Zitat:

Zitat von Stevie (Beitrag 1033922)
Zitat:

Zitat von sirius (Beitrag 1033919)
Ganz richtig wäre: TThread ruft Terminate nie auf, deswegen muss es auch nicht virtual sein. Ein simples verdecken (reintroduce) reicht hier völlig aus.

Schonmal in den Destructor von TThread geguckt?

Ui, korrekterweise wäre dann ein Virtual sinnvoll, weil man sich sonst nirgendwo ins Terminate reinklinken kann.
Aber ein kurzes Üüberlegen, wann der Fall eintritt, sagt mir, dass man dann den Thread während des Betriebes von außen abschießen muss. Böse Sache, dass.

jfheins 7. Jul 2010 08:09

AW: Überschreiben von TThread.Terminate
 
Zitat:

Zitat von sirius (Beitrag 1033919)
Ganz richtig wäre: TThread ruft Terminate nie auf, deswegen muss es auch nicht virtual sein. Ein simples verdecken (reintroduce) reicht hier völlig aus.

Virtuelle Methoden sind ja nur wichtig, wenn von deiner Elternklasse in eine deiner Methoden gesprungen werden soll. Aber Terminate ruft man ja nur von außen auf, deswegen einfach verdecken, fertsch.

Wette ich um ein virtuelles Bier dagegen ;)

Wenn man "von Außen" eine TThread Variable hat, und diese dann aber mit einem TmeinThread belegt und .terminate aufruft, wird trotzdem nur TThread.Terminate aufgerufen.

MatthiasR 7. Jul 2010 08:14

AW: Überschreiben von TThread.Terminate
 
Andere Frage: wieso kann ich in TThread.Terminate auf der (einzigen) Zeile
Delphi-Quellcode:
FTerminated := True;
keinen Breakpoint setzen, der auch angesteuert wird?

sirius 7. Jul 2010 08:32

AW: Überschreiben von TThread.Terminate
 
@jfheins: Mist, naja virtuelle Biere sind ja recht günstig.
Aber dann hat TThread echt einen Designfehler (also noch einen mehr). Denn wo kann ich sonst zum Terminate noch etwas anfügen? Ich benutze diese Methode (hatte mich anfangs auch gewundert warum nicht virtuell, und mir dann meine, diese, Erklärung zurechtgelegt) recht häufg, wenn zum Beispiel mein Thread schläft auf Grund von:
  • waitforsingleobject (o.ä)
  • Winsock.recv
  • ReadFile (bei Pipe)
  • Warten auf Datenbank-Event
  • ...
Wie macht man das dann?

@MatthiasR
Menu - Projektoptionen -> Debug-DCUs verwenden

himitsu 7. Jul 2010 09:10

AW: Überschreiben von TThread.Terminate
 
Die Prozedur Terminate setzt nur .Terminated auf true.
Ob und wie der Thread das nun intern beachtet ist egal.

Terminate/Terminated beendet die Threadverarbeitung nicht.
Terminated sagt nur "Hallo lieber Thread, würdest du dich bitte mal beenden?"

Also eigentlich sehe ich demnach auch keinen Grund, warum dieses überschreibar sein soll, da man es ja Threadintern dennoch anders behandeln kann (z.B. einfach ignorieren oder mit was Anderem verknüpfen).

Es muß ja nicht sein, daß man alles virtuell macht und so die VMT unnütz vergrößert.



- bei WaitForSingleObject und Co. läßt sich auch noch eine weitere Abbruchbedingung einbauen.
- ReadFile ließe sich Asynchron verarbeiten
- usw.

MatthiasR 7. Jul 2010 09:21

AW: Überschreiben von TThread.Terminate
 
Zitat:

Zitat von sirius (Beitrag 1033938)
@MatthiasR
Menu - Projektoptionen -> Debug-DCUs verwenden

Bei mir unter D7 wäre das dann: Projekt -> Optionen -> Compiler -> Mit Debug-DCUs. Nur bringt das bei mir irgendwie nichts. Der Breakpoint innerhalb TThread.Terminate ist immernoch durchgestrichen und die Zeile grün, statt rot.

Zitat:

Zitat von himitsu (Beitrag 1033959)
Also eigentlich sehe ich demnach auch keinen Grund, warum dieses überschreibar sein soll, da man es ja Threadintern dennoch anders behandeln kann (z.B. einfach ignorieren oder mit was Anderem verknüpfen).

Innerhalb meines TMeinThread.Execute wird ein mitunter sehr lange dauernder Download getätigt, der beim Aufruf von Terminate abgebrochen werden soll, damit überhaupt die Chance besteht, das Terminated-Flag auszuwerten und den Thread zu beenden. Das liebste wäre mir gewesen, wenn ein Aufruf von Terminate von außen ausreicht und das Unterbrechen des Downloads intern automatisch durchgeführt wird. Aber ich kann aus dem einen Terminate-Aufruf auch zwei Aufrufe machen. Terminate und anschließend eine zweite Methode "BrecheLaufendeDownloadsAb" oder so ähnlich.

Stevie 7. Jul 2010 09:38

AW: Überschreiben von TThread.Terminate
 
Zitat:

Zitat von himitsu (Beitrag 1033959)
Die Prozedur Terminate setzt nur .Terminated auf true.
Ob und wie der Thread das nun intern beachtet ist egal.

Terminate/Terminated beendet die Threadverarbeitung nicht.
Terminated sagt nur "Hallo lieber Thread, würdest du dich bitte mal beenden?"

Also eigentlich sehe ich demnach auch keinen Grund, warum dieses überschreibar sein soll, da man es ja Threadintern dennoch anders behandeln kann (z.B. einfach ignorieren oder mit was Anderem verknüpfen).

Wie "gut", dass FTerminated private und die property Terminated, die gottseidank protected ist nur read only ist. /irony=off

Zum Glück hat ein Thread ein OnTerminate Event, wie schade nur, dass das erst aufgerufen wird, nachdem der Thread terminated wurde, und nicht zu dem Zeitpunkt, wo Terminate aufgerufen wurde.

Wie man es auch dreht und wendet, es ist schlecht, dass Terminate nicht virtual ist.

mkinzler 7. Jul 2010 09:41

AW: Überschreiben von TThread.Terminate
 
Es ging Frank um die Tatsache, dass die Methode nur das Feld setzt und nicht den Thread beendet. Diese Funktionalität gehört in den Threadcode ( Reagieren auf die Änderung von .Terminated)

sirius 7. Jul 2010 09:46

AW: Überschreiben von TThread.Terminate
 
Zitat:

Zitat von MatthiasR (Beitrag 1033961)
Bei mir unter D7 wäre das dann: Projekt -> Optionen -> Compiler -> Mit Debug-DCUs. Nur bringt das bei mir irgendwie nichts. Der Breakpoint innerhalb TThread.Terminate ist immernoch durchgestrichen und die Zeile grün, statt rot.

HAst du das Projekt auch neu erzeugt oder nur gestartet (letzteres reicht nicht immer aus)?

Zitat:

Aber ich kann aus dem einen Terminate-Aufruf auch zwei Aufrufe machen. Terminate und anschließend eine zweite Methode "BrecheLaufendeDownloadsAb" oder so ähnlich.
Du kannst auch einfach Terminate verdecken (also ohne override), wenn du (wie ich jetzt begriffen habe) DeineKlasse.Terminate direkt von außen aufrufst. Und wenn du eh eine spezielle Methode einbauen musst, ist das ja auch gegeben.

sirius 7. Jul 2010 09:50

AW: Überschreiben von TThread.Terminate
 
@himi
Steve hat ja schon alles gesagt, noch Ergänzungen:

Zitat:

Zitat von himitsu (Beitrag 1033959)
Es muß ja nicht sein, daß man alles virtuell macht und so die VMT unnütz vergrößert.

Dann eben in die DMT.


Zitat:

- bei WaitForSingleObject und Co. läßt sich auch noch eine weitere Abbruchbedingung einbauen.
Und wo löst du die zweite Abbruchbedingung aus (ich nutze dann auch meist Waitformultipleobjects)? Richtig in Terminate!
Zitat:

- ReadFile ließe sich Asynchron verarbeiten
Pipes gehen nicht asynchron (zumindest bis XP)
Zitat:

- usw.
eben nicht usw.

Ich nutze es ständig weil es eben nicht anders geht, ansonsten kann ich auch gleich jedwedes Threading mir sparen.

himitsu 7. Jul 2010 09:55

AW: Überschreiben von TThread.Terminate
 
Zitat:

Zitat von sirius (Beitrag 1033968)
Du kannst auch einfach Terminate verdecken (also ohne override), wenn du (wie ich jetzt begriffen habe) DeineKlasse.Terminate direkt von außen aufrufst.

Aber nicht vergessen daß die Thread-Variable dann auch mit TMeinThread deklariert sein muß.
Bei einer TThread-Variable würde wieder das "Alte" aufgerufen.

Zitat:

Zitat von sirius (Beitrag 1033969)
Dann eben in die DMT.

:stupid:

Zitat:

Zitat von sirius (Beitrag 1033969)
Pipes gehen nicht asynchron (zumindest bis XP)

Kann mich nicht erinnern mal was davon irgendwo gelesen zu haben, daß es irgendwo nicht geht, aber hab mich auch noch nicht so mit Pipes beschäftigt und dieses bisher nur für "normale" Dateien verwendet.


Wie, womit wird denn die Datei runtergeladen?
Viele der Wege bieten es ja an, daß man wärend des Downloads Ereignisse bekommt oder wo man stückchenweise in einen Puffer lesen kann und dann dazwischen die Abbuchbedingung mit einbindet.

MatthiasR 7. Jul 2010 10:08

AW: Überschreiben von TThread.Terminate
 
Zitat:

Zitat von sirius (Beitrag 1033968)
Zitat:

Zitat von MatthiasR (Beitrag 1033961)
Bei mir unter D7 wäre das dann: Projekt -> Optionen -> Compiler -> Mit Debug-DCUs. Nur bringt das bei mir irgendwie nichts. Der Breakpoint innerhalb TThread.Terminate ist immernoch durchgestrichen und die Zeile grün, statt rot.

HAst du das Projekt auch neu erzeugt oder nur gestartet (letzteres reicht nicht immer aus)?

Ja, ich habe das Projekt komplett neu erzeugt, nicht nur compiliert!
Zitat:

Zitat von sirius (Beitrag 1033968)
Zitat:

Aber ich kann aus dem einen Terminate-Aufruf auch zwei Aufrufe machen. Terminate und anschließend eine zweite Methode "BrecheLaufendeDownloadsAb" oder so ähnlich.
Du kannst auch einfach Terminate verdecken (also ohne override), wenn du (wie ich jetzt begriffen habe) DeineKlasse.Terminate direkt von außen aufrufst. Und wenn du eh eine spezielle Methode einbauen musst, ist das ja auch gegeben.

Eben das ist doch das hüpfende Komma. Ich habe Terminate anfangs einfach nur verdeckt, sprich in meiner eigenen Thread-Klasse nochmals deklariert. Nur: wie kann ich dann in "meinem" Terminate zu Beginn noch das geerbte Terminated-Flag auf True setzen? TThread.Terminate kenn meine Klasse nicht mehr. Auf das Feld FTerminated hat es keinen Zugriff und die property Terminated ist nur lesbar :| . IMO darf Terminate also überhaupt nicht verdeckt werden, weil man sonst von außen überhaupt keine Möglichkeit mehr hat, das Terminated-Flag zu setzen. Was wieder zu der These führt, die Methode Terminate hätte als virtual deklariert werden müssen.

sirius 7. Jul 2010 10:16

AW: Überschreiben von TThread.Terminate
 
Du kannst nach wie vor die Vorgängermethode aufrufen. Mach ich ja, wie gesagt, auch ständig.

Du kannst dir ja mal bspw. mein DosCommand ansehen:
http://forum.delphi-treff.de/showthr...783#post201783

MatthiasR 7. Jul 2010 10:17

AW: Überschreiben von TThread.Terminate
 
Und wie geht das? Mit inherited?

himitsu 7. Jul 2010 10:23

AW: Überschreiben von TThread.Terminate
 
Zitat:

Zitat von MatthiasR (Beitrag 1033974)
Der Breakpoint innerhalb TThread.Terminate ist immernoch durchgestrichen und die Zeile grün, statt rot.

dieses Terminate wird auch verwendet?
Was nicht einkompiliert ist (weil es z.B. nicht verwendet wird), das kann auch nicht im Debugger mit einem Haltepunkt versehn werden.

Sind denn in dieser Procedur die kleinen blauen Pünktchen zu sehn?
(oder überhaupt irgendwo in der ganzen Unit)

sirius 7. Jul 2010 10:25

AW: Überschreiben von TThread.Terminate
 
Zitat:

Zitat von MatthiasR (Beitrag 1033976)
Und wie geht das? Mit inherited?

ja, ein Beispiel aus obiger verlinkter Unit:
Delphi-Quellcode:
constructor TReadPipe.Create(Aread_stdout,Awrite_stdout:THandle);
begin
  inherited Create(false);
  FEvent:=TEvent.Create(nil,false,false,'');
  FSyncString:=TSyncString.Create;
  Fread_stdout:=Aread_stdout;
  Fwrite_stdout:=Awrite_stdout;
  freeonterminate:=true;
end;

destructor TReadPipe.Destroy;
begin
  FEvent.Free;
  FSyncString.Free;
  inherited;
end;

procedure TReadPipe.Execute;
var Buf:array[0..1023] of byte;
    bread:Cardinal;
    s:string;
begin
  repeat
    ReadFile(Fread_stdout, Buf, length(buf), bread, nil); //wait for input
    if terminated then exit;
    setlength(s,bread);
    move(buf[0],s[1],bread);
    FSyncstring.Add(s);
    FEvent.SetEvent;
  until terminated;
end;

procedure TReadPipe.Terminate;
const fin='fin';
var bwrite:Cardinal;
begin
  inherited;
  WriteFile(Fwrite_stdout,fin,length(fin),bwrite,nil);
end;
Es gibt da auch noch ein größeres Beispiel.

MatthiasR 7. Jul 2010 10:36

AW: Überschreiben von TThread.Terminate
 
Zitat:

Zitat von himitsu (Beitrag 1033980)
Zitat:

Zitat von MatthiasR (Beitrag 1033974)
Der Breakpoint innerhalb TThread.Terminate ist immernoch durchgestrichen und die Zeile grün, statt rot.

dieses Terminate wird auch verwendet?
Was nicht einkompiliert ist (weil es z.B. nicht verwendet wird), das kann auch nicht im Debugger mit einem Haltepunkt versehn werden.

Sind denn in dieser Procedur die kleinen blauen Pünktchen zu sehn?
(oder überhaupt irgendwo in der ganzen Unit)

Ja natürlich wird das TThread.Terminate ebenfalls verwendet, wie soll ich sonst die property Terminated setzen?

Und nein, in der ganzen Classes-Unit werden keine blauen Pünktchen angezeigt, auch wenn die Debug-DCUs aktiviert sind.

Zitat:

Zitat von sirius (Beitrag 1033982)
Zitat:

Zitat von MatthiasR (Beitrag 1033976)
Und wie geht das? Mit inherited?

ja, ein Beispiel aus obiger verlinkter Unit

OK, hätte mich auch schwer gewundert, weil ich ja auch schon zigmal einen constructor Create (ebenfalls nicht virtual deklariert) auf diese Weise verdeckt habe und intern über inherited trotzdem aufrufen konnte. Dann klappte das in meinem Code-Schnipsel aus Beitrag 1 die ganze Zeit, nur hat es mich irritiert (und es irritiert mich weiterhin), dass ich in TThread.Terminate keinen Breakpoint setzen kann. Dann ist das also mein eigentliches Problem, trotz der nun aktivierten Debug-DCUs.

Stevie 7. Jul 2010 11:08

AW: Überschreiben von TThread.Terminate
 
Du hast nicht zufällig "build with runtime packages" an, oder?

MatthiasR 7. Jul 2010 11:26

AW: Überschreiben von TThread.Terminate
 
Zitat:

Zitat von Stevie (Beitrag 1033993)
Du hast nicht zufällig "build with runtime packages" an, oder?

Doch, habe ich. Habe die Option nun mal ausgeschaltet, allerdings beschwert sich der Compiler dann in einer Unit des ImageEn-Komponentenpakets über folgendes:
Zitat:

[Fataler Fehler] Previews.pas(37): Unit IEMIO wurde mit einer unterschiedlichen Version von IOPreviews.TfIOPreviews compiliert

Stevie 7. Jul 2010 11:47

AW: Überschreiben von TThread.Terminate
 
Ok, das ist nun ein Problem, was mit der Ursprungsfrage nix mehr zu tun hat.
Ich kann nur sagen: dass du nicht in die Classes.pas debuggen kannst, hat mit den Runtime Packages zu tun, die sind nämlich Release und keine Debug Builds. Es gibt wohl ne Möglichkeit, sich Debug Builds der Delphi eigenen Runtime Packages zu basteln, aber das ist recht kompliziert, wenn ich mich recht erinnere. Wenn du also dein Projekt ohne Packages kompilierst, und debug dcus anhakst, kannst du auch durch die ganzen rtl/vcl sourcen debuggen.

Wenn du also auf eine Variable von TMeinThread Terminate aufrufst wird mit Sicherheit über inherited die verdeckte Terminate Methode von TThread aufgerufen. Du darfst nur keine Variable vom Typ TThread haben, dann würde nur die von dort aufgerufen.

MatthiasR 7. Jul 2010 13:24

AW: Überschreiben von TThread.Terminate
 
Ja, das stimmt, das ist wieder ein anderes Problem und mein ursprüngliches wäre nun geklärt. Danke für die ausführliche Erklärung! Klar, wenn in den Runtime Packages keine Debug Symbole sind, und ich das Projekt aktuell OHNE Runtime Packages nicht compilieren kann, dann wird das schwierig mit dem Debuggen der Delphi-eigenen Klassen.

Bummi 7. Jul 2010 13:56

AW: Überschreiben von TThread.Terminate
 
@Stevie
debuggen in classes ...
wenn Du classes.pas zu debuggen in Dein Projektverzeichnis kopierst.

himitsu 7. Jul 2010 14:09

AW: Überschreiben von TThread.Terminate
 
Zitat:

Zitat von Bummi (Beitrag 1034036)
@Stevie
debuggen in classes ...
wenn Du classes.pas zu debuggen in Dein Projektverzeichnis kopierst.

Dann hat er die Classes.pas doppelt im Code.
Einmal das Selbsteingebundene/Kopierte und dann verwenden die restlichen Delphi/VCL-Units dennoch die originale Classes.pas.

Heißt also, daß beim Debuggen in diesem Fall eher mit Problemem zu rechnen ist, da Einiges die Originale und Anderes die Kopie verwendet.


Alle Zeitangaben in WEZ +1. Es ist jetzt 23:21 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