Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Die Delphi-IDE (https://www.delphipraxis.net/62-die-delphi-ide/)
-   -   Überwachen von Objekteigenschaften (https://www.delphipraxis.net/156285-ueberwachen-von-objekteigenschaften.html)

noisy_master 26. Nov 2010 10:45

Überwachen von Objekteigenschaften
 
Hallo Gemeinde,

gibt es in Delphi die Möglichkeit eine Art Breakpoint/TraceTrigger zu setzen wenn die Eigenschaft eines Objekts geändert wird?
Hintergrund der Frage: Ich erwarte z.B. dass ein Button disabled ist, er ist aber doch leider Enabled.
Nun würde ich gerne herausfinden welche der ~500 Stellen die den Button enablen können denn nun fälschlicherweise zuschlägt(ohne 500 breakpoints zu setzen zu müssen)

Danke im voraus für eure Hilfe.

mleyen 26. Nov 2010 10:58

AW: Überwachen von Objekteigenschaften
 
Delphi-Quellcode:

/// Ganz oben
type
  TButton = class(StdCtrls.TButton)
    procedure SetEnabled(Value: Boolean); override;
  end;

/// ...
/// Die eigentliche Unit...
/// ...

/// Vor end. dann:
{ TButton }

procedure TButton.SetEnabled(Value: Boolean);
begin
  inherited;
  if Self.Name = 'btnMeinBtnDerEnabledWird' then
    MessageDlg('Debugpoint hier setzen. :)', mtInformation, [mbOK], 0);
end;
tadaa 8-)

DeddyH 26. Nov 2010 11:02

AW: Überwachen von Objekteigenschaften
 
Was mir jetzt spontan einfällt: die Message CM_ENABLEDCHANGED abzufangen.
Delphi-Quellcode:
type
  TButton = class(StdCtrls.TButton)
  private
    FOnEnabledChanged: TNotifyEvent;
    procedure CMEnabledChanged(var Message: TMessage); message CM_ENABLEDCHANGED;
  public
    property OnEnabledChanged: TNotifyEvent read FOnEnabledChanged write FOnEnabledChanged;
  end;

  TFormTest = class(TForm)
  ...
  private
    { Private-Deklarationen }
    procedure DoOnChange(Sender: TObject);
  ...  


procedure TButton.CMEnabledChanged(var Message: TMessage);
begin
  inherited;
  if Assigned(FOnEnabledChanged) then
    FOnEnabledChanged(Self);
end;

procedure TFormTest.DoOnChange(Sender: TObject);
begin
  if Sender is TButton then
    ShowMessage(TButton(Sender).Name + ' hat sich geändert');
end;

procedure TFormTest.FormCreate(Sender: TObject);
begin
  Button1.OnEnabledChanged := DoOnChange;
end;

noisy_master 26. Nov 2010 11:07

AW: Überwachen von Objekteigenschaften
 
Danke, kling schon mal echt gut und dürfte für den Einzelfall das Mittel der Wahl sein.

Schön wäre es natürlich nun, wenn man nicht jede einzelne Eigenschaft für jede einzlen Klasse, die man überwachen möchte überschreiben müsste, sondern in der Art z.B. eines ExceptionHandlers einfach zu überwachende Eigenschaften auswählen könnte.
Kennt jemand dafür ein geeignetes Tooling/Expert/wizard?

Zitat:

Zitat von mleyen (Beitrag 1064236)
Delphi-Quellcode:

/// Ganz oben
type
  TButton = class(StdCtrls.TButton)
    procedure SetEnabled(Value: Boolean); override;
  end;

/// ...
/// Die eigentliche Unit...
/// ...

/// Vor end. dann:
{ TButton }

procedure TButton.SetEnabled(Value: Boolean);
begin
  inherited;
  if Self.Name = 'btnMeinBtnDerEnabledWird' then
    MessageDlg('Debugpoint hier setzen. :)', mtInformation, [mbOK], 0);
end;
tadaa 8-)


stahli 26. Nov 2010 11:13

AW: Überwachen von Objekteigenschaften
 
Das stimmt, ich hätte mir einen solchen Breakpint auch schon oft gewünscht.
Das würde die Laufzeit natürlich sehr ausbremsen, aber in bestimmten Fällen würde ich das gern mal in Kauf nehmen - natürlich nicht auf Dauer.
Der Debuger müsste an jeder Stelle (die einen Breakpoint erhalten KÖNNTE), die Erfüllung einer Bedingung prüfen und dann ggf. anhalten.

Bummi 26. Nov 2010 11:18

AW: Überwachen von Objekteigenschaften
 
Zitat:

Hintergrund der Frage: Ich erwarte z.B. dass ein Button disabled ist, er ist aber doch leider Enabled.
Nun würde ich gerne herausfinden welche der ~500 Stellen die den Button enablen können denn nun fälschlicherweise zuschlägt(ohne 500 breakpoints zu setzen zu müssen)
arbeitest Du nicht mit Actions ??

himitsu 26. Nov 2010 11:26

AW: Überwachen von Objekteigenschaften
 
Man könnte sich eine Klasse zusammenschreiben, welche die virtuellen Methoden, hier am Beispiel SetEnabled oder WndProc (die zentrale Nachrichtenbehandlung der Komponenten), zur Laufzeit "überschreibt", seinen eigenen Prüfcode einschleust und so leichter Debuggen kann.

Dieser Komponente wurde man dann sagen "Hooke mir die und die Methode jener Klasse".


evtl. gibt's sowas aber auch schon :stupid:

(über die neue 2010er/XE-RTTI würde sowas bestimmt ein Leichtes sein
und davor nur einen Hauch umständlicher/aufwändiger)


[add]
- "mit Debug-DCUs" in den Projektoptionen aktivieren (neuere Delphiversionen)
- in SetEnabled einen Haltepunkt sezten (wirkt natürlich nur, wenn die Änderung direkt über .Enabled reinkommt)
- die Bedingung für den Haltepunkt auf "Name = 'name der komponente'"
- und nun nur noch warten

Nicht wundern, aber dieses könnte das Programm ein bissl ausbremsen, da dieses natürlich von jedem kleinen Komponentchen aufgerufen wird.

noisy_master 26. Nov 2010 11:27

AW: Überwachen von Objekteigenschaften
 
Jupp ist richtig, aber wenn man sich solche "Komponenten" wie z.B. Eureka anschaut ist eine solche Funktion ganz hilfreich.

Zitat:

Zitat von stahli (Beitrag 1064245)
Das stimmt, ich hätte mir einen solchen Breakpint auch schon oft gewünscht.
Das würde die Laufzeit natürlich sehr ausbremsen, aber in bestimmten Fällen würde ich das gern mal in Kauf nehmen - natürlich nicht auf Dauer.
Der Debuger müsste an jeder Stelle (die einen Breakpoint erhalten KÖNNTE), die Erfüllung einer Bedingung prüfen und dann ggf. anhalten.


noisy_master 26. Nov 2010 11:28

AW: Überwachen von Objekteigenschaften
 
Ich muss zu meiner Schande gestehen: NEIN!
Aber wie sollte das bei diesen Probleme helfen?


Zitat:

Zitat von Bummi (Beitrag 1064247)
Zitat:

Hintergrund der Frage: Ich erwarte z.B. dass ein Button disabled ist, er ist aber doch leider Enabled.
Nun würde ich gerne herausfinden welche der ~500 Stellen die den Button enablen können denn nun fälschlicherweise zuschlägt(ohne 500 breakpoints zu setzen zu müssen)
arbeitest Du nicht mit Actions ??


noisy_master 26. Nov 2010 11:30

AW: Überwachen von Objekteigenschaften
 
Ja klingt plausibel.

Genau dieses "EVTL" suche ich :-D



Zitat:

Zitat von himitsu (Beitrag 1064249)
Man könnte sich eine Klasse zusammenschreiben, welche die virtuellen Methoden, hier am Beispiel SetEnabled oder WndProc (die zentrale Nachrichtenbehandlung der Komponenten), zur Laufzeit "überschreibt", seinen eigenen Prüfcode einschleust und so leichter Debuggen kann.

Dieser Komponente wurde man dann sagen "Hooke mir die und die Methode jener Klasse".


evtl. gibt's sowas aber auch schon :stupid:

(über die neue 2010er/XE-RTTI würde sowas bestimmt ein Leichtes sein
und davor nur einen Hauch umständlicher/aufwändiger)


himitsu 26. Nov 2010 11:37

AW: Überwachen von Objekteigenschaften
 
Bitte zitiere nicht jeden Beitrag einzeln ... ein Beitrag hätte auch gereicht.
Mehrfachposts wären hier nicht nötig gewesen. :warn:
Und komplette Beiträge zu zitieren ebenso wenig.

PS: Ich hatte oben, in meinem letzen Beitrag, noch was dazugeschrieben.
(ohne Garantie, ob's so funktioniert)

shmia 26. Nov 2010 16:52

AW: Überwachen von Objekteigenschaften
 
Manche Programmierer verbringen viel Zeit mir ihrem Debugger; Andere versuchen lieber ihre Software "wartungsfreundlich" und sauber zu designen.

Wenn ich einen Button en/disable dann achte ich darauf wie oft ich das tue.
Beim ersten Mal schreibe ich direkt hin
Delphi-Quellcode:
BtnImport.Enabled := False;
.
Spätestens aber beim 3. Mal mache ich mir Gedanken und zentralisere die Geschichte.
In folgendem Szenario gibt es z.B. 3 Buttons - Importieren, Stop und Schliesen.
Delphi-Quellcode:
procedure TImportDataForm.SetRunningState(running:Boolean);
begin
   BtnImport.enabled := not running;
   BtnStop.Enabled  := running;
   BtnClose.Enabled := not running;
   LblZeitdauer.Visible := running;
end;
Das ist die einzige Stelle im ganzen Programm, an der diese Buttons manipuliert werden.
Was könnte einfacher sein als jetzt einen Breakpoint zu setzen?

Ich würde auch nie auf die Idee kommt z.B. folgendes zu Schreiben:
Delphi-Quellcode:
// Beispiel für ganz schlechten Programmierstil
Form5.Button11.Enabled := False;
Form5.Button3.Enabled := True;
Form5.ShowModal;
Sobald man zwei Punkte auf der linke Seite braucht (also Formular.Komponente.Property := Irgendwas) ist was faul im Staate Dänemark!


Also ich verzichte gerne auf einen Debugger der alles kann und investiere meine Zeit in sauberen Sourcecode.

stahli 26. Nov 2010 17:18

AW: Überwachen von Objekteigenschaften
 
@shima
Häng Dich doch mal nicht an dem Button auf...
Es kann doch auch um andere Variablen oder Eigenschaften gehen, deren Werte "irgendwo" unerwartet verändert werden.
Wenn Du keine Fehler in der Programmentwicklung machst, dann gehe ich mal bei Dir in Schulung :wink:
Als Option fände ich eine solche Debug-Option durchaus nützlich. Man müsste sie ja nicht nutzen.

Bummi 26. Nov 2010 17:19

AW: Überwachen von Objekteigenschaften
 
Darf ich bei diesem Punkt nochmals meine vormals gestellte Frage in den Raum werfen, warum keine Actions?

Sir Rufo 26. Nov 2010 21:59

AW: Überwachen von Objekteigenschaften
 
Nepp, hier sind definitiv die Actions erste Wahl.

Vor allem das Event TAction.OnUpdate würde hier helfen diesen Bug in den Griff zu bekommen.

Innerhalb des Events prüft man alle möglichen Statuswerte, die für die Action relevant sind und setzt dann die Eigenschaften (Enabled, Visible, Caption, etc.) für die Action, und der Drops ist gelutscht.

Nun setzt keiner mehr die Eigenschaft des Buttons, sondern der Button passt sich von selbst an ;)

OldGrumpy 27. Nov 2010 03:53

AW: Überwachen von Objekteigenschaften
 
Das geht auch mit den Bordmitteln des Debuggers, ohne sich Code zu schreiben. Am Beispiel von TButton erklärt:

- Breakpoint in der Applikation an einer Stelle setzen wo das Ziel-Control schon existiert und inspiziert werden kann. Z.B. für einen Button im OnActivate des jeweiligen Forms, die Möglichkeiten sind zahlreich. Rechtsklick auf den Namen des Controls im Source und das Control inspizieren. Ganz oben im Inspektorfenster steht jetzt beispielsweise:

Code:
btn1: TButton $123456 : $789ABC
Die hintere der beiden Zahlen notieren, die brauchen wir später noch.

Nun auf den Reiter "Methoden" klicken, und dort - um im obigen Beispiel zu bleiben - "SetEnabled" suchen. Die Zeile sieht bei TButton z.B. so aus:

Code:
SetEnabled   Controls.TControl.SetEnabled($480834)
Jetzt haben wir alles zusammen. Nun setzen wir einen Breakpoint an der Adresse $00480834 und geben als Bedingung ein:
Code:
EAX=$00789ABC
Der Wert für EAX ist die oben notierte Zahl.

Nun kann man den oben zuerst gesetzten Breakpoint wieder entfernen und das Programm laufen lassen. Immer wenn das Property Enabled des oben herausgesuchten TButton gesetzt wird, triggert nun der Breakpoint. Das lässt sich sicherlich auch in einen Experten gießen, aber wenn man das ein paar Mal gemacht hat, geht das so flott von der Hand dass ich bisher noch nicht weiter drüber nachgedacht hab, diese vier Schritte noch weiter zu automatisieren. Und es geht auf jeden Fall schneller als sich erst jedes Mal einen Class Helper zu schreiben.

Nachtrag: Diese Methode funktioniert recht universell für alle möglichen Objekte (nicht nur Controls) und deren Properties, geht also in den Debuggingmöglichkeiten noch viel weiter als die Methode mit den Actions.

hoika 27. Nov 2010 06:49

AW: Überwachen von Objekteigenschaften
 
Hallo,

OldGrumpy:
interessanter Aspekt des Debuggens,
kannte ich noch nicht ... ;)

Aber:
Zitat:

Es kann doch auch um andere Variablen oder Eigenschaften gehen, deren Werte "irgendwo" unerwartet verändert werden
Es gibt kein "irgendwo".
Verändere ich an zwei unterschiedlichen Stellen eine Variable direkt (auch ein Property eines fremdem Objektes),
habe ich was was falsch gemacht.
-> Methode schreiben.


Heiko

OldGrumpy 27. Nov 2010 07:05

AW: Überwachen von Objekteigenschaften
 
Zitat:

Zitat von hoika (Beitrag 1064381)
Zitat:

Es kann doch auch um andere Variablen oder Eigenschaften gehen, deren Werte "irgendwo" unerwartet verändert werden
Es gibt kein "irgendwo".
Verändere ich an zwei unterschiedlichen Stellen eine Variable direkt (auch ein Property eines fremdem Objektes),
habe ich was was falsch gemacht.
-> Methode schreiben.

Doch, "irgendwo" gibt es sehr wohl. Sollte zwar nicht vorkommen, passiert aber durchaus mal. Um solche "wilden" Zugriffe einzukreisen, gibt es Data Breakpoints. Die greifen halt nicht, wenn Code an einer bestimmten Adresse ausgeführt wird, sondern wenn eine bestimmte Adresse gelesen und/oder geschrieben wird (je nach Wunsch.) Damit bekommt man heraus, warum z.B. eine Variable auf einmal einen völlig falschen Wert hat. Tritt gerne auf wenn man mit Pointern oder Arrays arbeitet :) Natürlich ist das im Endeffekt ein Fehler, man hat also was falsch gemacht, aber eine Methode zu schreiben bringt einen dem Fehler keinen Schritt näher in diesem Fall. Ein Objekt kann sich ja (okay, Massnahmen aus dem Bereich Software Protection mal außen vor gelassen) gar nicht gegen Schreibzugriffe auf seinen Speicherbereich "von außen" wehren, es bekommt davon ja gar nichts mit.

noisy_master 29. Nov 2010 09:05

AW: Überwachen von Objekteigenschaften
 
Hallo liebe Delphi Gemeinde,

ich denke wir sollten den Thread an dieser Stelle beenden, weil doch viele hilfreiche Anregungen gekommen sind. Leider war nicht die "ultimative Lösung mit der alles von selbst geht:wink:" bei, aber dennoch vielen Dank an alle, die wieder einmal konstruktiv zu dem Problem beigetragen haben!

DANKE!!!

noisy_master 29. Nov 2010 09:19

AW: Überwachen von Objekteigenschaften
 
Das ist wirklich DIE Antwort, die dem gewünschten am nähesten kommt!

Kannte ich bisher noch nicht und daher: Super Antwort:thumb:

Schönen Dank auch nochmal für das hilfreich in Die Seite treten bzgl. "Irgendwo im Code gibt es nicht"
Hast recht ist zwar kein gutes Design, manchmal muss es aber halt sein.

Luckie 29. Nov 2010 09:27

AW: Überwachen von Objekteigenschaften
 
Zitat:

Zitat von noisy_master (Beitrag 1064729)
Hast recht ist zwar kein gutes Design, manchmal muss es aber halt sein.

Es gibt keinen guten Grund für schlechtes Design. Wenn man aus Zeitdruck mal schlechten Code schreiben muss OK, aber das Design macht man ja schon vorher.

DeddyH 29. Nov 2010 09:29

AW: Überwachen von Objekteigenschaften
 
Wenn man Projekte von anderen übernimmt, muss man wohl oder übel auch das ggf. miese Design übernehmen, es sei denn, man hat sehr viel Zeit (wer hat die schon?).

Luckie 29. Nov 2010 09:36

AW: Überwachen von Objekteigenschaften
 
Das ist was anderes. Aber offensichtlich handelt es sich um sein eigenes Projekt. Aber auch Refactoring kann Zeit sparen. Ich habe mal Code vom Kollegen bekommen, den er so runtergetippt hatte. Dieser Code sollte jetzt erweitert werden. Ich glaube es waren sechs Stunden veranschlagt. Ohne Refactoring hätte ich das in den sechs Stunden nicht geschafft. Weil keine Struktur drin war. Und beim verstehen ist das Refactoring fast von alleine gegangen. Vom ursprünglichen Code ist eigentlich nur der rein funktionale Code übrig geblieben.

shmia 29. Nov 2010 17:13

AW: Überwachen von Objekteigenschaften
 
Zitat:

Zitat von stahli (Beitrag 1064328)
@shima
Häng Dich doch mal nicht an dem Button auf...
Es kann doch auch um andere Variablen oder Eigenschaften gehen, deren Werte "irgendwo" unerwartet verändert werden.

Ok, es gibt Klassen, Properties, Proceduren und Funktionen in die man nicht hineindebuggen kann; also kurz gesagt Code, der nicht unter der eigenen vollständigen Kontrolle steht.
Dazu gehört z.B. die Windows API, ActiveX und Komponenten aus der VCL.
Ja, man kann mit Debug-DCUs kompilieren aber es handelt sich doch um fremden Code.

Im Laufe der Jahre habe ich gelernt mit diesem "Fremdcode" umzugehen.
Falls nötig baue ich um diesen Fremdcode eine Schicht, die meistens aber nur hauchdünn ist.
Es ist wichtig zu erkennen, wann man die Schicht braucht und wann nicht.
Die Schicht kann manchmal auch nur eine Funktion sein, die ein Argument 1 zu 1 weiterleitet.
Delphi-Quellcode:
Beispiel
procedure TForm1.ShowError(const msg:string);
begin
  ShowMessage(msg);
  // oder
  // Statusbar1.SimpleText := msg;
end;
Diese Schicht ist sehr dünn, aber ich kann einen Breakpoint setzen oder die Fehlermeldung auf eine andere Weise anzeigen.

Für den Zugriff auf Komponenten habe ich meistens keine Zwischenschicht.
Nur falls nötig oder falls ein Spareffekt (z.B. mehrere Buttons auf einen Rutsch dis/enablen) eintritt würde ich eine Procedure/Funktion als Zwischenschicht einführen.

Ganz anderst sieht das mit Handles und Zeigern der Windows API aus.
Hier kapsele ich grundsätzlich immer mit einer Klasse, Procedure oder Funktion.
Stellt euch vor es gäbe die Klassen TFont, TCanvas, TPen und TBrush nicht.
Was wäre das für eine Qual irgendetwas zu zeichnen.

Ähnlich sieht das bei Zeigern und Strukturen aus der Windows API aus.
Ich würde z.B. niemals die Windows API-Funktion MSDN-Library durchsuchenGetComputerName() direkt in meiner Anwendung aufrufen.
Nein, dieser Aufrauf braucht eine kleine Zwischenschicht:
Delphi-Quellcode:
function GetLocalComputerName: string;
var
  Count: DWORD;
begin
  Count := MAX_COMPUTERNAME_LENGTH + 1;
  SetLength(Result, Count);
  if GetComputerName(PChar(Result), Count) then
    StrResetLength(Result)
  else
    Result := '';
end;
Ich würde auch nie auf die Idee kommen meine Anwendung direkt mit WinSock-Funktionen (socket(),bind(),listen(),..) oder Funktionen für die serielle Schnittstelle reden zu lassen.
Hier braucht es unbedingt eine Klasse, die den Zugriff kapselt.
Automatisch habe ich dadurch Code auf den ich Breakpoints setzen kann.

Fazit: um Code, der nicht unter der eigenen Kontrolle steht sollte man (bei Bedarf) eine Zugriffsschicht legen.
Die dünnstmögliche Schicht ist die 1:1 Weiterleitung einer Funktion/Procedure


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