![]() |
Re: Events in TThread
So, zunächst zu :
Zitat:
Jetzt zum eigentlichen Problem: Als ich versuchte, ein einfaches Programm nachzubauen und anstatt der Audiokomponente einen einfachen Timer einzusetzen, kam es - egal was ich machte - nicht mehr zu dem Fehler. (Sowas ärgerliches :wink: ). Also habe ich mich auf die Suche gemacht und bin (hoffentlich) fündig geworden: Die Audiokomponente erstellt ein CallBackWin (TWinControl), welches für die Verarbeitung der MM Messages verantwortlich ist.
Delphi-Quellcode:
Dieses wurde ursprünglich erzeugt mit:
TCallBackWin = Class(TWinControl)
private { Private declarations } AudioComponent : PAudioComp; procedure BufferFinished(var Msg: TMessage); message MM_WIM_DATA; procedure WaveOpenIn(var Msg: TMessage); message MM_WIM_OPEN; procedure WaveCloseIn(var Msg: TMessage); message MM_WIM_CLOSE; end;
Delphi-Quellcode:
Da ein Thread keinen TWinControl-Owner hat, habe ich das ganze abgewandelt in:
CallBackWin := TCallBackWinIn.CreateParented(TWinControl(Owner).Handle);
CallBackWin.Visible := false; CallBackWin.AudioComponent := @TS; iErr := WaveInOpen(@WaveHandle, FWaveDevice, @FWaveFmtEx, Integer(CallBackWin.Handle), 0, CALLBACK_WINDOW or WAVE_ALLOWSYNC );
Delphi-Quellcode:
Die WndProc war einfach leer. FWindowHandle := Classes.AllocateHWnd(WndProc); // <- Neu CallBackWin := TCallBackWinIn.CreateParented(FWindowHandle); // <- Geändert CallBackWin.Visible := false; CallBackWin.AudioComponent := @TS; iErr := WaveInOpen(@WaveHandle, FWaveDevice, @FWaveFmtEx, Integer(CallBackWin.Handle), 0, CALLBACK_WINDOW or WAVE_ALLOWSYNC ); Das Problem ist (war) jetzt wohl, dass überhaupt dieses CallBackWin, also ein TWinControl im TThread erzeugt ist und Messages verarbeitet (oder sonst was Problematisches vornimmt). Mir ist wohl das Problem bekannt, dass beim Erzeugen eines Formulars in einem Thread der gleiche Fehler auftritt: "Leinwand/Bild erlaubt kein Zeichnen". Deshalb dachte ich mir: Wozu überhaupt dieses CallBackWin, wenn ich ja jetzt eine WndProc-Prozedur habe. Gesagt, getan: CallBackWin entfernt und Ereignisbehandlung in WndProc gepackt:
Delphi-Quellcode:
FWindowHandle := Classes.AllocateHWnd(WndProc); // <- Neu
iErr := WaveInOpen(@WaveHandle, FWaveDevice, @FWaveFmtEx, Integer(FWindowHandle), // <- Geändert 0, CALLBACK_WINDOW or WAVE_ALLOWSYNC ); Jetzt geht's und der Fehler tritt nicht mehr auf. Allerdings verwende ich immer noch das Application.ProcessMessages in der Schleife des Threads. Scheint also kein Problem darzustellen. Meine Tests haben bisher auch ergeben, dass das im Thread aufgerufene Application.ProcessMessages auf den Hauptthread keine Auswirkungen hat. Vielleicht bedeutet das aber auch nur, dass die Messages für den Hauptthread verloren gehen? Mir fällt jedenfalls nichts auf. An dieser Stelle wäre nur noch das Problem zu klären: 1. Ob Application.ProcessMessages (im Thread aufgerufen) sich irgendwie schadhaft/problematisch auswirkt. 2. if 1 then :-D Wie können wir das Problem umgehen? Sprich: "Eigene" MessageBehandlung innerhalb des Threads ermöglichen. Das warten auf die Events in dem Thread läuft nämlich immer noch so:
Delphi-Quellcode:
while not Terminated do
begin sleep(100); Application.ProcessMessages; end; Vielleicht habt ihr ja noch eine Idee. Grüsse ...Doc |
Re: Events in TThread
Hallo,
Zitat:
Da die Audio-Komponente asynchron arbeitet, solltest Du diese im Hauptthread erstellen und nur das Verarbeiten des Puffers in einen Thread packen. Das wäre eine saubere Lösung. Zitat:
2: Wie gesagt, ich würde nur die reine Verarbeitung in einen Thread auslagern, dann stellt sich diese Frage nicht mehr. Gruß xaromz |
Re: Events in TThread
Mahlzeit,
Zitat:
Wird ein Sleep(10000) im Hauptthread ausgeführt (oder eine Operation, die solange dauert), wird erst im Anschluss daran, also nach 10 Sekunden das Ereignis "BufferFilled" im Hauptthread ausgeführt. Dann sind mir aber beim nächsten Event schon 9,9 Sekunden Puffer verloren gegangen. Stellt also keine wirkliche Alternative dar. Zitat:
Grüsse ...Doc |
Re: Events in TThread
Hallo,
Zitat:
Zitat:
Gruß xaromz |
Re: Events in TThread
Hallo,
ich habe jetzt nochmal ein bißchen rumprobiert und vertrete weiterhin die Meinung, dass der Thread nicht die Messages des Hauptthreads "bekommt". Dazu habe ich die MessageQueue des Hauptthreads "aufgefüllt" und mit sleep(x) die Verarbeitung angehalten, während im TThread weiter Application.ProcessMessages ausgeführt wurde. Nach dem Sleep habe ich die Messages in ein Memo schreiben lassen. Sie waren immer noch da. Dann habe ich die Messages des TThreads und vom Hauptthread in Memos schreiben lassen (zeitgleich) und siehe da, es waren unterschiedliche. Der TThread bekam ausschließlich MM_WIM_OPEN (1x) und MM_WIM_DATA (wiederkehrend) als Message. Der Hauptthread allerdings trotz sleep, alle möglichen. Das bedeutet, dass der TThread die Messages des Hauptthreads weder empfangen, noch abgearbeitet hat. Das Application.ProcessMessages macht im Prinzip (vereinfacht) nichts anderes als:
Delphi-Quellcode:
In der Hilfe ist zu finden:
while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do
begin TranslateMessage(Msg); DispatchMessage(Msg); end;
Delphi-Quellcode:
Merke: Es wird also als HWND Null übergeben.
BOOL PeekMessage(
LPMSG lpMsg, // pointer to structure for message HWND hWnd, // handle to window UINT wMsgFilterMin, // first message UINT wMsgFilterMax, // last message UINT wRemoveMsg // removal flags ); Unter Remarks: Zitat:
Zitat:
Oder seht ihr einen Denkfehler? Grüsse ...Doc |
Re: Events in TThread
Hallo DocE,
mag sein, dass ich falsch liege - ich würde Dir raten, einen Thread A zu erstellen, der intern die Audiokomponente hält (Create & Free zu den entsprechenden Zeitpunkten). Parallel dazu einen Thread B erstellen, der eine Behandlungsmethode für das Event der Audiokomponente enthält. Diese Behandlungsmethode würde ich auf das Event der Komponente in Thread A zuweisen. Thread A kann dann Warterunden in seiner Execute-Methode drehen und Thread B ebenso. Allerdings wird in Thread B dann die Execute-Methode von der Ausführung der Behandlungsmethode unterbrochen. Um in Deiner HauptVCL-Anwendung Dinge zu verändern, benötigst einen Synchronized-Aufruf in Dein Form. Diesen würde ich in Thread A in einer Methode implementieren und diese Methode durch einen Event von Thread B aus auslösen. Gruß, Christoph |
Re: Events in TThread
Hallo Christoph,
die Audioauswertung in zwei Threads auszulagern sehe ich nicht als besonder sinnvoll an. Es funktioniert ja in einem Thread bereits wunderbar. Das Problem war ja nur, dass der Thread in einer Schleife Application.ProcessMessages ausführen muss, damit das Ereignis überhaupt ausgelöst wird. Das müßte einer Deiner beiden Threads ja trotzdem machen, sonst würde er das Ereignis nicht bekommen oder "auslaufen" (-> Terminated). Momentan stellt sich eigentlich nur noch folgende Frage: In meinem letzten Post habe ich folgende These aufgestellt und begründet: Zitat:
![]() Die Frage ist jetzt nur, ob jemand meine begründete These widerlegen kann. Grüsse ...Doc |
Re: Events in TThread
Application.Processmessages ruft Prozessmessages in einer Schleife auf. ProcessMessages setzt wiederum eine private-Variable von TApplication. Das setzen dieser Variablen ist nicht mit CriticalSections abgesichert so das es dort krachen könnte. Allerdings hat dies nichts mit dem berichteten Fehler zu tun (Leinwandbild erlaubt....). Du greifst definitiv irgendwo auf grafische Elemente des Hautpthreads zu.
|
Re: Events in TThread
N'Abend SirThornberry,
das stimmt. Es gibt evtl. sogar noch ein paar weitere Punkte an denen es zu Komplikationen kommen könnte z.B. hier, falls irgendein Ereignis zugewiesen ist.
Delphi-Quellcode:
Aber man könnte sich ja das ProcessMessages für den TThread einfach selbst schreiben durch:
if Assigned(FOnMessage) then FOnMessage(Msg, Handled);
(verkürzt)
Delphi-Quellcode:
oder
while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do
begin TranslateMessage(Msg); DispatchMessage(Msg); end; (fast 1:1)
Delphi-Quellcode:
Ich denke, dann sollte es zu keinem Problem kommen.
procedure MyProcessMessageImThread(var Msg: TMsg);
begin while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do begin if Msg.Message <> WM_QUIT then // bekommt ein Thread vermutlich nie, aber gut begin // trifft alles für Threads normalerweise nicht zu {if not IsHintMsg(Msg) and not IsMDIMsg(Msg) and not IsKeyMsg(Msg) and not IsDlgMsg(Msg) then} TranslateMessage(Msg); DispatchMessage(Msg); end; end; end; Irgendwie kursiert wohl allgemein das Gerücht, dass ein TThread mit Application.ProcessMessages auch auf die Messages des Hauptthreads zugreift. Dies ist jedoch nicht richtig! Jeder Thread hat praktisch seine eigene MessageQueue. Mit diesem Vorurteil sollte einmal aufgeräumt werden. Grüsse ...Doc |
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:45 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 by Thomas Breitkreuz