![]() |
Delphi-Version: 10 Berlin
[VCL] TCustomControl, Welche Methode für Komponente um empfangsbereit zu sein?
Liste der Anhänge anzeigen (Anzahl: 1)
Guten morgen zusammen,
Ich habe ne Art Logging Komponente, die Thread-safe Log-Ereignisse empfangen kann, in einer oder mehreren Dateien hinterlegen kann, und an mehreren Stellen anzeigen kann. Die Komponente, die die Ereignisse Live anzeigt, bereitet mir im Moment ein paar Kopfschmerzen. Und Zwar dann, wenn Ereignisse zur Create Phase der Anwendung passieren. Ich habe mal eine Demo erstellt (siehe Anhang) Aber Grundsätzlicht, habe ich eine TCustomPanel, auf der Eine Listbox erstellt wird, und dort werden per AddLog Meldungen eingetragen. im Addlog habe ich eine Sperre, das erst Meldungen angenommen werden, wenn die Komponente dazu bereit ist, weil ohne Sperre kommen sehr merkwürdige Windows Fehler. Mein Aktueller "Workarround" ist es, erst nach dem Aufruf vom Paint, Meldungen zu erlauben (FAddAllowed=true). Ich würde euch jetzt gerne Fragen, wie ich am besten die Sperre aufhebe, sozusagen, wann kann gefahrlos eingetragen werden? Und wann kann sicher SendMessage() aufgerufen werden für den Autoscroll? (Es geht gar nicht darum das evtl. Daten verloren gehen, in meiner eigentlichen Komponente wird das berücksichtigt.
Delphi-Quellcode:
unit uFMain;
interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls,SyncObjs, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, JvExStdCtrls, JvListBox; type TTestLog=class(TCustomPanel) private FAddAllowed:boolean; FListBox:TListBox; FCS_Add:TCriticalSection; FWorkaroundActive: boolean; protected procedure Paint; override; public property WorkaroundActive:boolean read FWorkaroundActive write FWorkaroundActive; procedure AddLog(Amessage:string); constructor Create(AOwner: TComponent); override; destructor Destroy; override; end; TTestThread=class(TThread) private FLog:TTestLog; protected procedure Execute; override; public constructor Create(ALog:TTestLog); destructor Destroy; override; class procedure SicherFreigeben(var threadObjekt: TTestThread); static; end; TFMain = class(TForm) pb1: TPanel; btnStart: TButton; btnStop: TButton; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure btnStartClick(Sender: TObject); procedure btnStopClick(Sender: TObject); private { Private-Deklarationen } public FLog:TTestLog; FT:array[0..100] of TTestThread; { Public-Deklarationen } end; var FMain: TFMain; CONST MAXTHREADS=10; implementation {$R *.dfm} { TTestLog } procedure TTestLog.AddLog(Amessage: string); begin FCS_Add.Enter; try if FAddAllowed or not FWorkaroundActive then begin FListBox.Items.Add(Amessage); SendMessage(FListBox.Handle, LB_SETTOPINDEX, FListBox.Items.Count-1, 0); FListBox.Update; self.Update; end; finally FCS_Add.Leave; end; end; constructor TTestLog.Create(AOwner: TComponent); begin inherited; FCS_Add:=TCriticalSection.Create; FListBox:=TListBox.Create(self); FListBox.Parent:=self; FListBox.Align:=alClient; FListBox.Color:=clBlack; FListBox.Font.Color:=clWhite; end; destructor TTestLog.Destroy; begin FCS_Add.Free; FListBox.Free; inherited; end; procedure TTestLog.Paint; begin inherited; if FWorkaroundActive then FAddAllowed:=true; end; { TTestThread } constructor TTestThread.Create(ALog: TTestLog); begin inherited Create(false); FLog:=ALog; end; destructor TTestThread.Destroy; begin FLog:=nil; inherited; end; procedure TTestThread.Execute; begin while not Terminated do begin if Assigned(FLog) then FLog.AddLog('THREAD: '+inttostr(ThreadID)+' TESTLOG'); sleep(100); end; end; class procedure TTestThread.SicherFreigeben(var threadObjekt: TTestThread); begin if assigned(threadObjekt) then begin threadObjekt.Terminate; threadObjekt.WaitFor; threadObjekt.free; threadObjekt:=nil; end; end; procedure TFMain.btnStartClick(Sender: TObject); var I: Integer; begin for I := low(ft) to MAXTHREADS-1 do FT[i]:=TTestThread.Create(FLog); end; procedure TFMain.btnStopClick(Sender: TObject); var I: Integer; begin for I := low(ft) to MAXTHREADS-1 do begin FT[i].SicherFreigeben(FT[i]); end; end; procedure TFMain.FormCreate(Sender: TObject); begin // FLog:=TTestLog.Create(self); FLog.Parent:=self; FLog.Align:=alClient; //Hier ist mein aktueller Workaround: //Wenn True, darf erst was hinzugefügt werden, wenn Paint aufgerufen wurde //auf False setzen um die Windows Fehler zu sehen ;) FLog.WorkaroundActive:=true; btnStartClick(self); end; procedure TFMain.FormDestroy(Sender: TObject); begin btnStopClick(self); end; end. |
AW: [VCL] TCustomControl, Welche Methode für Komponente um empfangsbereit zu sein?
Delphi-Quellcode:
Probiere mal so und rufe zum Loggen nur AddLogThreadSafe auf.
procedure TTestLog.AddLog(const AMessage: string);
begin FListBox.Items.Add(AMessage); SendMessage(FListBox.Handle, LB_SETTOPINDEX, FListBox.Items.Count-1, 0); FListBox.Update; self.Update; end; procedure TTestLog.AddLogThreadSafe(const AMessage: string); begin if TThread.CurrentThread.ThreadID = MainThreadID then begin AddLog(AMessage) end else begin TThread.Queue(nil, procedure begin AddLog(AMessage); end); end; end; |
AW: [VCL] TCustomControl, Welche Methode für Komponente um empfangsbereit zu sein?
Danke für die schnelle Antwort.
Klappt. Aber, mich würde trotzdem noch interessieren, wann bei meiner Methode, die Komponente einsatzbereit wäre. Z.b. wegen Abwärtskompatibilität (D7 usw). |
AW: [VCL] TCustomControl, Welche Methode für Komponente um empfangsbereit zu sein?
Na ja, der Ansatz ist halt schon etwas auf dünnen Eis gebaut.
Einfach mal das Forum nach dem Stichwörtern Mainthread, VCL und threadsafe abklappern, zum Beispiel hier: ![]() ![]() Für D7 kannste auch GetCurrentThreadId aus der Windows.pas nehmen. |
AW: [VCL] TCustomControl, Welche Methode für Komponente um empfangsbereit zu sein?
Zitat:
Ich bin bei der Komponenten Programmierung einfach nicht tief genug drin ;) |
AW: [VCL] TCustomControl, Welche Methode für Komponente um empfangsbereit zu sein?
Hab jetzt noch nen paar themen, und auch eine links dazu gelesen, und du hast recht es
ist einfach nur sinvoll wenn man es per: Antwort: Queue oder Synchronize benutzen macht, für D7 geht das hier dann: ![]() |
AW: [VCL] TCustomControl, Welche Methode für Komponente um empfangsbereit zu sein?
Es wäre eigentlich am gescheitesten, wenn du ein Logging unabhängig von einer Komponente entwickelst!
Irgendeine globale Logging-Instanz, die dann wahlweise in einer VCL-Komponente, als Textdatei, ins Windows-Eventlog, über das Internet zu einen Server oder oder oder...dir die Log-Einträge wegschreibt/zur Anzeige bringt. Oft braucht man den Zugriff auf die Einträge visuell direkt im Programm und dann später als Textdatei zum analysieren. Da würde es sich anbieten einfach beide Wege gleichzeitig bedienen zu können. Stell dir die Logging-Instanz als Singleton vor, an dem sich verschiedene Listener anmelden können, die dann den jeweiligen Weg entsprechend implementieren. |
AW: [VCL] TCustomControl, Welche Methode für Komponente um empfangsbereit zu sein?
"merkwürdig": auf nichtexistierende Objekte und dann auch unsyncronisiert (deine CriticalSection kennt die VCL nicht, also mußt DU dich mit Ihr synchronisieren) zuzugreifen ist nunmal ungünstig, aber das wurde ja schon behoben.
Und wenn du dennoch auch vorher Loggen willst, dann solltest du eben in eine Liste Loggen und die Anzeige holt sich dann später die Einträge da raus. * wenn sie existiert und bereit ist * und du kannst dir dann auch die Synchronisiation mit der VCL sparen (also beim Loggen ... beim Auslesen auch nur noch mit deiner CS synchronisieren) * entweder schaut der VCL-Log regelmäßig (pollen) nach, ob es was Neues gibt, oder es wird z.B. eine Message gesendet (PostMessage), dass es nun was zum Abholen gibt Also entsprechend dem Vorschlag von TiGü. |
AW: [VCL] TCustomControl, Welche Methode für Komponente um empfangsbereit zu sein?
@TiGü
Grundsätzlich ist es nicht abhängig von irgendeiner Komponente, Es handelt sich aber auch um eine Firmeninternes Logging. Es basiert auf der LogEngine, die unterschiedliche Storages erlaubt (Text Datei, Datenbank) Und beliebig viele unterschiedliche Anzeigen. Erweitert da ist es im Prinzip ein Grid, und Einfache Darstellung, dort ist es die Listbox mit der Problematik. Die Engine ansich habe ich bewusst nicht als Singleton gemacht, bzw gibt es eine abgeleitete Variante die Singleton ist. Aber danke für deine Tips ;) @himitsu Syncronisieren per Liste + CS + Message, hatte ich auch dran gedacht, war ich aber wohl zu faul für ;) Und da ich keinerlei Probleme hatte, wenn die Anwendung läuft, nur im Create Bereich kam es dann zu den Problemen. Im Endeffekt finde ich die Lösung per Thread.Queue auch am einfachsten :) |
Alle Zeitangaben in WEZ +1. Es ist jetzt 23:54 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