Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   In welcher Methode bin ich? (https://www.delphipraxis.net/97781-welcher-methode-bin-ich.html)

Luckie 16. Aug 2007 09:18


In welcher Methode bin ich?
 
Ich muss in meinen Klassen Exceptions auslösen. Jetzt wäre es schön, wenn ich den Namen der Methode und der Klasse herausfinden könnte, wo diese Exception ausgelöst wurde, um mir die Fehlersuche zu erleichtern. Ich weiß, dass es dazu umfangreiche Komponenten-Sammlungen gibt, wie zum Beispiel Mad-Except usw. Aber das ist mir etwas zu viel Overhead. Ich bräuchte nur den Methodennamen und den Klassennamen, der ist aber nicht so wichtig.

Phoenix 16. Aug 2007 09:23

Re: In welcher Methode bin ich?
 
Es mag Overhead sein, die entsprechenden Units z.B. aus der JCL zu benutzen - aber warum guckst Du da nicht mal kurz in die entsprechende Unit um rauszufinden, wie die den Namen rausfinden?

Ich habs auch mal gesucht und nach knapp 10 Minuten gefunden. Du bist da wahrscheinlich sogar schneller als ich, weil ich sie erst wieder runterladen müsste ;-)

Relicted 16. Aug 2007 09:25

Re: In welcher Methode bin ich?
 
Moin Michael,

Soweit ich weiß ist das ganze nicht so einfach den methodennamen irgendwie heraus zu bekommen.
daher biete ich dir mal das an wie ich es immer mache. einfach und über die macros recht schnell :-)

Delphi-Quellcode:

implementation

uses
  Windows.....;


const
  // PVCS Header
  csUnitVersion = '$Workfile:  Brabelschneck.pas $ ' +
                  '$Revision:  1.8  $ ' +
                  '$Modtime:  10 May 2007 13:53:44  $ ' +
                  '$Author:  Relicted $';
Delphi-Quellcode:
procedure TBrabelschneck.Test(Sender: TObject);
const
  csFuncName = 'TBrabelschneck.Test';
begin
  try
    //do something
  except
    on E : Exception do
      HandleException( E, csFuncName, csUnitVersion, E.Message );
  end;
end;

hier noch die standardmäßige implementierung bei forms die ne exception werfen..

Delphi-Quellcode:
procedure TExceptionForm.HandleException( coException : Exception;
                                        sFuncName : String;
                                        sUnitVersion : String;
                                        sMessage : String );
begin
  if Assigned( OnPrintException ) then
  begin
    OnPrintException( Format( csPrtException,
                              [ sFuncName,
                                DelCharFromStr( [ #13, #10 ], coException.Message ),
                                UnitVersionToStr( sUnitVersion ) ] ) );
  end;
  raise Exception.Create( Format( csFmtException, [ sFuncName, sMessage ] ) );
end;

vielleicht kannst damit ja was anfangen.

gruß
reli

Luckie 16. Aug 2007 09:27

Re: In welcher Methode bin ich?
 
Zitat:

Zitat von Phoenix
Es mag Overhead sein, die entsprechenden Units z.B. aus der JCL zu benutzen - aber warum guckst Du da nicht mal kurz in die entsprechende Unit um rauszufinden, wie die den Namen rausfinden?

Verdammt, an die Jedis habe ich gar nicht gedacht. :wall: Da ich die sowieso schon benutze, ist das kein größeres Problem.

_frank_ 16. Aug 2007 10:38

Re: In welcher Methode bin ich?
 
gibt es vielleicht eine Möglichkeit, ohne die Jedi's zu nutzen?

Gruß Frank

s-off 16. Aug 2007 10:47

Re: In welcher Methode bin ich?
 
Zitat:

Zitat von _frank_
gibt es vielleicht eine Möglichkeit, ohne die Jedi's zu nutzen?

Gruß Frank

Zitat:

Zitat von Phoenix
Es mag Overhead sein, die entsprechenden Units z.B. aus der JCL zu benutzen - aber warum guckst Du da nicht mal kurz in die entsprechende Unit um rauszufinden, wie die den Namen rausfinden?

Edit: die Betonung liegt auf dem Teil nach dem Bindestrich...

Luckie 16. Aug 2007 10:52

Re: In welcher Methode bin ich?
 
Mit den Jedis geht es so:
In den Jedi-Optionen beim Debuginfo Converter den debug Experten aktivieren und die Debugdaten in die Exe schreiben lassen. Dann im Code die Unit JCLDebug hinzufügen und über die Funktion ProcByLevel bekommt man dann Unit, Klasse und Methode in folgender Form als Zeichenkette: "Unit.Klasse.Methode".

negaH 16. Aug 2007 11:54

Re: In welcher Methode bin ich?
 
@Luckie,

es gibt Wege den Unit Namen und Methoden Namen auch zur Laufzeit zu ermitteln, ohne Debug Infos. Aber das sind tiefste Tricks die auf die RTTI aufbauen und nicht mit jeder Methode funktionieren. So bald eine Methode published ist speichert der Compiler in der RTTI diese Informationen, eben Name und Aufrufparameter. Den Unit Namen kann man ermitteln relativ zum Codesegement in dem man sich befindet. Der Compiler speichert die RTTI's immer zum Codesegement das diese Unit belegt. Bei Klassen/Interface-RTTIs steht der Unit-Name mit in dieser RTTI. Dann speichert der Compiler auch noch die Einsprungspunkte in die Initialization/Finalization Sektionen der Units und über diese kann man in einer speziellen Resource den Unit Namen ermitteln, das trifft besonderst bei Packages zu bzw. dafür wurde es auch konstruiert.

Gruß Hagen

Luckie 16. Aug 2007 12:03

Re: In welcher Methode bin ich?
 
Ich meine natürlich die JCL und nicht die Jedis. ;?

@Hagen: Ich habe in meiner Klasse keine published Properties, nur private und public. ;)

sirius 16. Aug 2007 16:51

Re: In welcher Methode bin ich?
 
Es geht doch nicht um published Properties, sondern um published Methods :P

Ganz ohne Zusatzinfos nicht ganz einfach (wie Hagen schon sagte). Ich habe noch eine "unsichere" Möglichkeit (auch gleich getestet). Im Prinzip bekommst du aus einer Exception als wichtige Infos die Addresse des Auftretens der Exception und den Zustand aller Register zu diesem Zeitpunkt. Dummerweise funkt dir als erstes schonmal Delphi dazwischen der bei "Except" gleich einen Handler aufruft, welcher den Stack durcheinanderwirbelt ($38 Bytes untendran hängt).
Man könnte jetzt über die Adresse versuchen die RTTI zu durchlaufen um eine published Methode zu finden. Ich bin allerdings noch zusätzlich davon ausgegangen, dass Delphi den self-Pointer meist in ebx legt. Einfacher wärs, wenn man die Methoden mit stdcall aufrufen würde, dann könnte man sich relativ zu ebp bewegen.
Also: Ich versuche nach der Exception das Register ebx zu "retten" und mir die Adresse des Auftretens der Exception zu holen. Das sieht bei mir so aus:
Delphi-Quellcode:
function getMethod:TMethod;
const cDelphiException   = $0EEDFADE; //from Unit System
asm
  //clear
  xor ecx,ecx
  xor edx,edx

  //push result
  push eax

  //try (Exception-Handling of getMethod)
  push ebp
  push offset @Exc
  push fs:[edx]
  mov fs:[edx],esp

  //get pointer to Exception-Context-Structure
  //[esp + $c + $38]
  //$38 because of system._HandleAnyException
  mov edx,[esp+$44]

  //old ebx is in Context.structure + $A4
  mov eax,[edx+$A4]

  //get pointer to Exception-Structure
  //[esp + $4 + $38]
  //$38 because of system._HandleAnyException
  mov edx,[esp+$3C]

  //is Delphi Exception (=Exception created by "Raise Exception.create...")
  cmp [edx],cDelphiException
  je @@1
    //non Delphi Exception
    mov ecx,[edx+$0C] //get Exception-Address from Exception-Structure
  jmp @@2
 @@1:
    //Delphi Exception
    mov ecx,[edx+$14] //get Exception-Adress from data of Exception-Structure
 @@2:

  //test if eax is self-pointer
  mov edx,[eax]
  cmp edx,[edx+vmtSelfPtr]
  jz @mbclass
    xor eax,eax //if not then clear eax
 @mbclass:


  //clear own Exception-Handling
  xor edx,edx
  pop fs:[edx]
  pop edx
  pop edx
  jmp @fend
 @Exc:
  //handle own Exception (+ clear eax)
  mov esp,[esp+8]
  pop edx
  pop edx
  pop ebp
  xor eax,eax


 @fend:
  pop edx //pop result
  mov [edx],ecx //TMethod.code <-- ecx
  mov [edx+4],eax //TMethod.data <-- eax


  //call getMethod (if TMethod.data<>nil)
  test eax,eax
  mov eax,edx
  jz @end
    call getMethodCode
 @end:
end;
Die Funktion speichert (bis zum aufruf von getMethodCode) die oben genannten Werte in TMethod. Der self-Pointer, der mutmaßlich in ebx lag, wird in TMethod.Data gelegt und die Adresse des Auftretens des Fehler in TMethod.Code. Letzteres ist allerdings noch nicht die Anfangsadresse der Methode. Dazu gehe ich in die RTTI und such nach dem Einsprungpunkt einer published Method, welcher vor Method.code liegt:
Delphi-Quellcode:
procedure getMethodCode(var Method:TMethod);
var mt:pointer;
    size:Pword;
    count:Pword;
    i,j,p:integer;
    adr:cardinal;
    adrlist:array of cardinal;
    found:boolean;
begin

  if cardinal(Method.code)>=cardinal(getprocaddress(getmodulehandle('kernel32'),'RaiseException'))
    then exit;


  mt:=ppointer(integer(method.data^)+vmtMethodtable)^;
  if mt<>nil then
  begin
    count:=mt;
    mt:=pointer(cardinal(mt)+2);
    setlength(adrlist,count^);
    for i:=0 to count^-1 do adrlist[i]:=0;
    for i:=1 to count^ do
    begin
      size:=mt;
      adr:=pcardinal(cardinal(mt)+2)^;

      j:=count^-1;
      while adrlist[j] > adr do dec(j);
      if j>=0 then for p:=0 to j do adrlist[p]:=adrlist[p+1];
      adrlist[j]:=adr;

      mt:=pointer(cardinal(mt)+size^);
    end;

    if cardinal(method.Code)>adrlist[0] then
    begin
      found:=false;
      for i:=1 to count^-1 do
      begin
        if cardinal(method.Code)<adrlist[i] then
        begin
          method.Code:=pointer(adrlist[i-1]);
          found:=true;
          break;
        end;
      end;
      if not found then method.Code:=pointer(adrlist[count^-1]);
    end
    else
      method.Code:=nil;
  end;
end;
Soweit funktioniert es bei mir. Dies alles unter den Vorraussetzungen, dass:
1. getMethod direkt nach "Except" aufgerufen wird
2. HandleAnyException nicht noch mehr "Unfug" macht (Exception-Klasse suchen, etc.), oder anderen als bei mir
3. Der self-Pointer zum Zeitpunkt der Excepton in ebx lag

Punkt 1 kann man ja recht gut sicherstellen. Für Punkt 2 hilft ein eigener Exceptionhandler (in ASM) oder die Hoffnung das HandleAnyException nicht anderes reagiert und zu 3. hilft wahrscheinlich stdcall bei den Methoden, da dann der self-Pointer auf dem Stack übergeben wird.


Delphi-Quellcode:
var Method:TMethod
    obj:Tobject;
begin

  try
   //...
  except
    Method:=getMethod;
    obj:=TObject(Method.Data);
    if obj is TObject then
    begin
      edit1.text:=obj.ClassName+' '+ obj.MethodName(method.Code);
    end
    else
      edit1.Text:='Kenne ich nicht';
  end;
end;

Edit: Bei stdcall für deine Methoden kannst du die Zeile
Delphi-Quellcode:
//old ebx is in Context.structure + $A4
  mov eax,[edx+$A4]
durch das hier ersetzen:
Delphi-Quellcode:
//old ebp is in Context.structure + $B4
  mov eax,[edx+$B4]
  mov eax,[eax+$08] //=self
.
Das dürfte etwas "sicherer" sein, als sich auf ebx zu verlassen.

negaH 16. Aug 2007 20:56

Re: In welcher Methode bin ich?
 
@Sirius, ja das ist schon kompliziert besonders weil die Addresse an der die Exception ausgelösst wurde durch Delphi manches mal falsch berechnet wird, statt +4 Offset mit +5 Offset.

Am sichersten ist die Methode der JCL, auch wenn sie sagen wir mal von Hinten durch die Brust in's Auge ist. Borland hätte einfach seine vom Turbo Debugger bekannten TD Files als eigenes Segment weiterhin in die EXE einlinken lassen sollen. Leider hat Borland dieses sehr gute Features (zu DOS Zeiten einfach spitze) nicht konsequent weiterverfolgt. Mit der Übernahme des Intel Compilers ab Version 5 glaube ich hat sich das eh erledigt.

Gruß hagen

Olli 16. Aug 2007 21:34

Re: In welcher Methode bin ich?
 
Tscha, da sieht man doch wiedermal die Vorteile eines Präprozessors :mrgreen:


... ich verbleibe mit einem herzlichen __FUNCTION__ , :zwinker:

negaH 16. Aug 2007 23:03

Re: In welcher Methode bin ich?
 
@Olli, das musst du mir erklären ? Was hat der Präprozessor damit zu tun, bzw. was kann er zum Problem beitragen ?

Hm, es sei denn du überschreibst den Funktionsbegriff per Präprozessor mit eigener DEFINE um zusätzliche Infos irgendwo abzulegen. Allerdings frage ich mich wie das aussehen soll, benötigt der Präprozessor doch Zugriff auf den Linker um das Namemangeling auswerten zu können. Übersehe ich da was ?

Gruß Hagen

DGL-luke 16. Aug 2007 23:27

Re: In welcher Methode bin ich?
 
Der Pärprozessor ersetzt __FUNCTION__ durch einen literalen string, der der bezeichnung der aktuellen methode entspricht. D.h. globale Variable "string CurrFunc = "";", in jeder Methode "CurrFunc = __FUNCTION__", bei Exceptions o.ä. CurrFunc ausgeben.

negaH 17. Aug 2007 00:38

Re: In welcher Methode bin ich?
 
haste ein Link zu einem Beispiel ?

[edit]
vergiß es, habs schon gefunden. Man lernt nie aus.

Wobei das ja nicht zwangsläufig einen Präprozessor benötigt. Ein Compiler-Magic in Delphi würde ja auch gehen, wenn wir die Sourcen von Borland bekämen ;)
[/edit]

Gruß hagen

Olli 17. Aug 2007 00:45

Re: In welcher Methode bin ich?
 
Bsp. mein DDKWizard:

Code:
#if DBG
   #ifdef ADVANCED_DEBUG
   #define DebugPrint DbgPrint("[%s] %s (line: %d)\n", __##FILE##__, __##FUNCTION##__, __##LINE##__); DbgPrint
   #else
   #define DebugPrint DbgPrint
   #endif
#else
#define DebugPrint /##/DbgPrint
#endif
Was macht das (Annahme ADVANCED_DEBUG=1 und DBG=1)? Bei jeder Erwaehnung von "DebugPrint(parameter)" wird ein DbgPrint davorgestellt, weshalb es nach dem Einfuegen so aussieht:

Code:
DbgPrint("[%s] %s (line: %d)\n", __FILE__, __FUNCTION__, __LINE__); DbgPrint(parameter)
Somit wird das beim eigentlichen finalen Durchlauf des Praeprozessors als, bspw.:

Code:
DbgPrint("[%s] %s (line: %d)\n", "bla.cpp", "CMeineKlasse::MeineMethode", 37); DbgPrint(parameter)
... interpretiert.

Ziemlich bequem ;)


... dank Praeprozessor leiste ich mir sowas dann auch nur im Debug-Build, womit die Performanceeinbussen auch nicht spuerbar sind, der Nutzen beim Debuggen allerdings schon.

negaH 17. Aug 2007 00:49

Re: In welcher Methode bin ich?
 
Mittlerweile klar, nächstes mal bitte nicht so trödeln ;) (habe ich dich zu so später Zeit aus der Kiste geholt? bin beim Warten auf deine Antwort fast eingepennt )

Allerdings bin ich mir noch nicht ganz im klaren ob das der Präprozessor macht oder der Compiler.
Die http://www.cs.cmu.edu/cgi-bin/info2www?(gcc.info)Function%20Names behaupten das der Compiler das macht.
Ok, macht aber keinen großen Unterschied für mich ob der Spaten aus Plastik oder Stahl ist, hauptsache er gräbt ;)
Ich arbeite nun schon einige Zeit mit C und ich kannte eben diese Direktiven noch nicht, deshalb meine verdutzte Frage.

Gruß Hagen

Olli 17. Aug 2007 00:59

Re: In welcher Methode bin ich?
 
Zitat:

Zitat von negaH
Ok, macht aber keinen großen Unterschied für mich ob der Spaten aus Plastik oder Stahl ist, hauptsache er gräbt ;)

Waere tatseachlich egal. Ich benutzte es bisher nur als Praeprozessor-Symbol und in meinen C-Buechern war es auch als solches ausgegeben.


Einerlei. Nein, hast mich nicht geweckt. Da Island, 2 Stunden Zeitverschiebung. Hier ist's gerade ~0:00 wenn ich das abschicke.

Dezipaitor 17. Aug 2007 12:17

Re: In welcher Methode bin ich?
 
Zitat:

Zitat von Olli
Zitat:

Zitat von negaH
Ok, macht aber keinen großen Unterschied für mich ob der Spaten aus Plastik oder Stahl ist, hauptsache er gräbt ;)

Waere tatseachlich egal. Ich benutzte es bisher nur als Praeprozessor-Symbol und in meinen C-Buechern war es auch als solches ausgegeben.

Mit einem Spaten aus Plastik hat man jedoch nur kurze Zeit freude oder bei leichtem Sand. Tief wird man nicht kommen :D

sirius 17. Aug 2007 13:03

Re: In welcher Methode bin ich?
 
Zitat:

Zitat von negaH
@Sirius, ja das ist schon kompliziert besonders weil die Addresse an der die Exception ausgelösst wurde durch Delphi manches mal falsch berechnet wird, statt +4 Offset mit +5 Offset.

:gruebel: Das ist mir noch nicht aufgefallen. Wo denn? Der nimmt doch einfach die Rücksprungadresse nach "Raise" (bzw. RaiseExcept).
Mein größtest Problem sind eher HandleAnyException und wo self liegt. Aber wenn man jetzt in den Methoden anfängt extra Infos zu hinterlegen, dann kann man doch auch gleich an die Exception-Klasse eigene Eigenschaften mitgeben.


(Unter TP war ich noch nicht so tief in die Materie eingestiegen.)

s-off 17. Aug 2007 13:17

Re: In welcher Methode bin ich?
 
Zitat:

Zitat von DGL-luke
Der Pärprozessor ersetzt __FUNCTION__ durch einen literalen string, der der bezeichnung der aktuellen methode entspricht. D.h. globale Variable "string CurrFunc = "";", in jeder Methode "CurrFunc = __FUNCTION__", bei Exceptions o.ä. CurrFunc ausgeben.

Könnte ich nicht genau so gut in jeder Methode der globalen Variable direkt den Methodennamen zuweisen?
__FUNCTION__ hätte doch nur den Vorteil, dass ich ggf. Änderungen am Methodennamen nicht nachziehen müsste, oder?

Khabarakh 17. Aug 2007 13:34

Re: In welcher Methode bin ich?
 
Schau dir doch mal Ollis Nutzung von __FUNCTION__ an, das ergibt schon ein wenig mehr Sinn ;) .
Interessant wäre doch eine DLangExtension oder Ähnliches, die jeden raise-Aufruf mit solchen Informationen spickt, da würde nicht einmal das fehlende CodeInsight stören.
Ansonsten, jo... System.Exception.StackTrace ftw :duck: .

negaH 17. Aug 2007 14:18

Re: In welcher Methode bin ich?
 
Zitat:

Könnte ich nicht genau so gut in jeder Methode der globalen Variable direkt den Methodennamen zuweisen?
__FUNCTION__ hätte doch nur den Vorteil, dass ich ggf. Änderungen am Methodennamen nicht nachziehen müsste, oder?
Ja du hast schon Recht. In Delphi muß man es so machen für die gleiche Funktionalität die per Präprozessor eben in C geht. Der Präprozessor ist ja qausi nur ein Text-/Makroparser der dynamisch den Source ergänzt. Technisch gesehen entstehen die gleichen Vor- und Nachteile dieser Methode.

Gruß Hagen

negaH 17. Aug 2007 14:22

Re: In welcher Methode bin ich?
 
Zitat:

Zitat von sirius
Zitat:

Zitat von negaH
@Sirius, ja das ist schon kompliziert besonders weil die Addresse an der die Exception ausgelösst wurde durch Delphi manches mal falsch berechnet wird, statt +4 Offset mit +5 Offset.

:gruebel: Das ist mir noch nicht aufgefallen. Wo denn? Der nimmt doch einfach die Rücksprungadresse nach "Raise" (bzw. RaiseExcept).
Mein größtest Problem sind eher HandleAnyException und wo self liegt. Aber wenn man jetzt in den Methoden anfängt extra Infos zu hinterlegen, dann kann man doch auch gleich an die Exception-Klasse eigene Eigenschaften mitgeben.


(Unter TP war ich noch nicht so tief in die Materie eingestiegen.)

Das bezieht sich auf das "AT" Postfix einer Exception.

Delphi-Quellcode:
raise Exception.Create('Test') at @MeineMethode.
Nachvollziehen konnte ich diesen Fehler in Delphi 5, später habe ich das nicht mehr benutzt noch getestet.

Gruß Hagen

Muetze1 17. Aug 2007 14:43

Re: In welcher Methode bin ich?
 
Zitat:

Zitat von s-off
Zitat:

Zitat von DGL-luke
Der Pärprozessor ersetzt __FUNCTION__ durch einen literalen string, der der bezeichnung der aktuellen methode entspricht. D.h. globale Variable "string CurrFunc = "";", in jeder Methode "CurrFunc = __FUNCTION__", bei Exceptions o.ä. CurrFunc ausgeben.

Könnte ich nicht genau so gut in jeder Methode der globalen Variable direkt den Methodennamen zuweisen?
__FUNCTION__ hätte doch nur den Vorteil, dass ich ggf. Änderungen am Methodennamen nicht nachziehen müsste, oder?

Deutlich mehr Arbeit: theoretisches Beispiel:

- 2 Funktionen. Func1 und Func2
- Grundlage: Func1 ruft Func2 auf; globale Variable __FUNCTION__ vorhanden
- Abflauf:

* Func1 angesprungen, setzt globale Variable auf "Func1"
* Func1 ruft Func2 auf
* Func2 angesprungen, setzt globale Variable auf "Func2"
* Func2 kehrt zurück
* Func1 logt __FUNCTION__ wird in der Log zu "Func2"

Ergo: Leider müsstest du dann ganz gewissenhaft nach eigentlich jedem Funktions-/Methodenaufruf die Variable erneut setzen.

Und zur Frage ob Präprozessor oder Compiler: Ich habe auch schon beide angesprochene Möglichkeiten gelesen (Compiler kümmert sich um __FUNCTION__ genauso wie Prärprozessor...) von daher scheint es eher davon abzuhängen wer wie die Compiler/Präprozessor nach eigenem Ermessen gebaut hat.

Hawkeye219 17. Aug 2007 15:07

Re: In welcher Methode bin ich?
 
Hallo,

Zitat:

Zitat von Muetze1
Ergo: Leider müsstest du dann ganz gewissenhaft nach eigentlich jedem Funktions-/Methodenaufruf die Variable erneut setzen.

Die Alternative wäre ein (globaler) Stack, der zu Beginn und am Ende jeder Routine aktualisiert wird. Dies hätte den Vorteil, im Fehlerfall eine Aufrufreihenfolge "frei Haus" zu bekommen, erfordert aber auch etwas Disziplin (Klammerung durch TRY..FINALLY).

Gruß Hawkeye

sirius 17. Aug 2007 15:41

Re: In welcher Methode bin ich?
 
@Hawkeye Man kann so einiges machen. Aber ein allgemeine Lösung gibt es (anscheinend) unter Delphi nicht. Also muss man spezielle Sachen finden. Dazu bräuchte man aber mehr Infos zum Problem...

Zitat:

Zitat von negaH
Nachvollziehen konnte ich diesen Fehler in Delphi 5, später habe ich das nicht mehr benutzt noch getestet.

Sehe ich nicht (D7). Egal.


Alle Zeitangaben in WEZ +1. Es ist jetzt 01:24 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz