![]() |
Thread sauber Beenden ? Handle ungültig.
Nächster Tag, nächste dumme Frage.
Ich bastel zur Zeit an einem Programm das mit diverse Threads arbeitet, in diesem Beispiel-Thread wird unter anderem ein DLL Funktion importiert, ferner hole ich mir ein Handel im Zusammenhang mit der PDH.DLL. Dieser Thread läuft in einer Endlos-Schleife (While not Terminated.....). Nun würde ich gerne beim Beenden des Hauptprogrammes, auch diesen Thread sauber beenden, d.h. meine DLL sowie mein Handle wieder freigeben. Hier die Thread Unit:
Delphi-Quellcode:
Dieser Thread wird im Hauptprogramm folgendermaßen gestartet:
unit PDHThread;
interface uses Classes, Windows, SysUtils; const PDH_NO_DATA = $800007D5; PDH_MEMORY_ALLOCATION_FAILURE = $C0000BBB; PDH_INVALID_HANDLE = $C0000BBC; PDH_INVALID_ARGUMENT = $C0000BBD; PDH_FMT_RAW = $00000010; PDH_FMT_ANSI = $00000020; PDH_FMT_UNICODE = $00000040; PDH_FMT_LONG = $00000100; PDH_FMT_DOUBLE = $00000200; PDH_FMT_LARGE = $00000400; PDH_FMT_NOSCALE = $00001000; PDH_FMT_1000 = $00002000; PDH_FMT_NODATA = $00004000; PDH_FMT_NOCAP100 = $00008000; type PQUERY = ^HQUERY; HQUERY = THandle; PCOUNTER = ^HCOUNTER; HCOUNTER = THandle; PDH_STATUS = Longint; PPDH_FMT_COUNTERVALUE = ^TPDH_FMT_COUNTERVALUE; _PDH_FMT_COUNTERVALUE = record CStatus : DWORD; longValue : Longint; doubleValue : double; largeValue : LONGLONG; AnsiStringValue : LPCSTR; WideStringValue : LPCWSTR; end; TPDH_FMT_COUNTERVALUE = _PDH_FMT_COUNTERVALUE; PDH_FMT_COUNTERVALUE = _PDH_FMT_COUNTERVALUE; type TPDHInfo = record Counter : HCounter; PDHLoad : PDH_FMT_COUNTERVALUE; end; type TPDHThread = class(TThread) private HQ : HQuery; PDHInfo : Array[1..4] of TPDHInfo; hPDH : THandle; procedure UpdatePDH; protected procedure Execute; override; public end; Var PdhOpenQuery : function(pReserved: Pointer; dwUserData: DWORD; phQuery: PQUERY): PDH_STATUS; stdcall; PdhCloseQuery : function(ahQuery: HQUERY): PDH_STATUS; stdcall; PdhAddCounter : function(ahQuery: HQUERY; szFullCounterPath: PChar; dwUserData: DWORD; phCounter: PCOUNTER ): PDH_STATUS; stdcall; PdhRemoveCounter : function( ahCounter: HCOUNTER ): PDH_STATUS; stdcall; PdhCollectQueryData : function( ahQuery: HQUERY ): PDH_STATUS; stdcall; PdhValidatePath : function( szFullCounterPath: PChar ): PDH_STATUS; stdcall; PdhGetFormattedCounterValue : function( ahCounter: HCOUNTER; dwFormat: DWORD; lpdwType: LPDWORD; pValue: PPDH_FMT_COUNTERVALUE): PDH_STATUS; stdcall; implementation uses Main; function GetNumberOfProcessors: Integer; var SystemInfo: TSystemInfo; begin GetSystemInfo(SystemInfo); Result:=SystemInfo.dwNumberOfProcessors; end; Function LoadPDH : THandle; Var H : THandle; Begin H := LoadLibrary('PDH.DLL'); If H <> 0 then Begin PdhOpenQuery := GetProcAddress(H, 'PdhOpenQuery'); PdhCloseQuery := GetProcAddress(H, 'PdhCloseQuery'); PdhAddCounter := GetProcAddress(H, 'PdhAddCounterA'); PdhRemoveCounter := GetProcAddress(H, 'PdhRemoveCounter'); PdhCollectQueryData := GetProcAddress(H, 'PdhCollectQueryData'); PdhValidatePath := GetProcAddress(H, 'PdhValidatePath'); PdhGetFormattedCounterValue := GetProcAddress(H, 'PdhGetFormattedCounterValue'); End; Result := H; End; procedure TPDHThread.Execute; var X, MaxX : Integer; dwctrType : DWord; begin hPDH := LoadPDH; If hPDH <> 0 then Begin If PDHOpenQuery(nil, 1 ,@HQ) = ERROR_SUCCESS then Begin MaxX := GetNumberOfProcessors; If MaxX > 4 then MaxX := 4; For X := 1 to MaxX do If PDHAddCounter(HQ, PChar('\Prozessor('+InttoStr(X-1)+')\Prozessorzeit (%)'), 1, @PDHInfo[X].Counter) <> ERROR_SUCCESS then PDHAddCounter(HQ, PChar('\Processor('+InttoStr(X-1)+')\% Processor Time'), 1, @PDHInfo[X].Counter); while not Terminated do Begin If PDHCollectQueryData(HQ) = ERROR_SUCCESS then For X := 1 to MaxX do PdhGetFormattedCounterValue(PDHInfo[X].Counter, PDH_FMT_DOUBLE, @dwctrType, @PDHInfo[X].PDHLoad); Synchronize(UpdatePDH); Sleep(1000); End; PdhCloseQuery(HQ); FreeLibrary(hPDH); End; End; end; procedure TPDHThread.UpdatePDH; begin MainForm.UpdatePDH(PDHInfo); end; end.
Delphi-Quellcode:
Beim Testen des Ganzen bin ich jedoch darauf gestoßen das weder PdhCloseQuery noch FreeLibrary ausgeführt werden.
// Thread zur Berechnung des CPULoad starten
PDHThread := TPDHThread.Create(True); PDHThread.Priority := tpNormal; PDHThread.FreeOnTerminate := True; PDHThread.Resume; Also hab ich das Ganze geändert, so:
Delphi-Quellcode:
Und habe im OnDestroy Event (wahlweise auch im OnCloseQuery) des Hauptprogrammes folgendes eingeführt:
// Thread zur Berechnung des CPULoad starten
PDHThread := TPDHThread.Create(True); PDHThread.Priority := tpNormal; PDHThread.FreeOnTerminate := False; PDHThread.Resume;
Delphi-Quellcode:
Nun werden PdhCloseQuery und FreeLibrary ausgeführt, allerdings fange ich mir Exceptions.
// Thread zur Berechnung des CPULoad beenden
PDHThread.Terminate; PDHThread.Waitfor; PDHThread.Free; Handle ist ungültig(6) oder Read of Address 0001. Wo liegt mein Fehler ? Wenn ich FreeOnterminate auf True setze ist es für mich noch verständlich, offensichtlich wird der Thread abgewürgt bevor PdhCloseQuery und FreeLibrary ausgeführt werden. Bei FreeOnterminate auf False, wenn ich den Thread also selber beende, müssten doch die Handles auf hPDH und HQ weiter gültig sein, zumindestens so lange bis ich sie selber frei gebe, oder PDHThread.Free aufrufe. ODER ? MfG El.Blindo |
Re: Thread sauber Beenden ? Handle ungültig.
Ich hab' nicht genau durchschaut, was Du da tust, mir ist aber etwas aufgefallen.
Im Thread deklarierst Du
Delphi-Quellcode:
Im execute rufst Du
private
PDHInfo : Array[1..4] of TPDHInfo;
Delphi-Quellcode:
auf. Das finde ich etwas komisch. Bei der Variante FreeOnTerminate machst Du PDHInfo platt, obwohl Du evtl. noch im synchronize() steckst, das ist riskant. Ob das auch ohne FreeOnTerminate so ist, kann ich nicht überblicken, aber generell: eine private-Variable eines nachgeordneten Thread an den Hauptthread zu übergeben ist mir unheimlich.
MainForm.UpdatePDH(PDHInfo);
Warum deklarierst Du PDHInfo nicht public im HauptThread? Den Aufruf von PDHCloseQuery etc. bekommst Du meiner Meinung nach hin, indem Du die while not-Schleife in eine try..finally-Struktur packst - jedenfalls mache ich das so. Dann kannst Du im finally reproduzierbar aufräumen. Grüße, Messie |
Re: Thread sauber Beenden ? Handle ungültig.
Danke, hast mir schon mal sehr geholfen.
Das Problem war das ich auch die Handles im Public erstellt habe und daher auch zu früh platt gemacht habe. Daran gedacht, die PDHInfo im Hauptprogramm Public zu erstellen habe ich auch schon, mir ist allerdings nicht klar wie ich dann von beiden Threads auf diese Variable zugreifen kann, ohne das es Probleme gibt. Also wie ich das dann Synchronisized hin bekomme. MfG El.Blindo |
Re: Thread sauber Beenden ? Handle ungültig.
Hi, so gäbe es etwas weniger Probleme mit der Sichtbarkeit usw... ist aber auch noch nicht so toll, weil sehr speziell.
Delphi-Quellcode:
unit PDHThread;
interface {...} TPDHThreadEvent = class(TObject) private FUsage: Double; FCPUIndex: Integer; public constructor Create(Info: TPDHInfo; CPUIndex: Integer); published property Usage: Double read FUsage write FUsage; property CPUIndex: Integer read FCPUIndex write FCPUIndex; end; {...} implementation {...} procedure TPDHThread.UpdatePDH; var I: Integer; Event: TPDHThreadEvent; begin for I := Low(PDHInfo) to High(PDHInfo) do begin try Event := TPDHThreadEvent.Create(PDHInfo[i], i); MainForm.UpdatePDH(Event); finally Event.Free; end; end; end; { TPDHThreadEvent } constructor TPDHThreadEvent.Create(Info: TPDHInfo; CPUIndex: Integer); begin inherited Create; FUsage := Info.PDHLoad.doubleValue; FCPUIndex := CPUIndex; end; end. |
Re: Thread sauber Beenden ? Handle ungültig.
Der Fehler ('Das Handle ist ungültig') tritt bei Threads bekanntermaßen dann auf, wenn die Classes-Unit finalisiert wurde und DANACH noch Threads freigegeben werden. TThread.Destroy verwenden Objekte aus der Classes-Unit.
Aber das scheint bei Dir nicht der Fall zu sein. step doch einfach mal den Thread durch (NICHT das Synchronize, das macht keinen Spass). Ich meine, das Dein Code sauber ist, vor allen Dingen mit der manuellen Freigabe. Erstelle Dir ein kleines Projekt mit zwei Buttons. Button1 = Thread-Instantiierung und -Start Button2 = Thread-Ende und Destroy. Klappt das dann auch? |
Re: Thread sauber Beenden ? Handle ungültig.
Ich hatte auch schon mal Probleme beim Ausführen von Code nach dem Beenden durch ein Terminate bzw. ein Free.
Mein Fazit:
Hier nochmal die Beschreibung aud Delphi: Mit WaitFor ermitteln Sie den Wert von ReturnValue, wenn die Ausführung des Thread beendet ist. WaitFor kehrt erst zurück, wenn der Thread beendet ist. Dazu muß der Thread entweder die Methode Execute abschließen oder die Beendigung einleiten, sobald die Eigenschaft Terminated den Wert True annimmt. Das fette ist der springende Punkt. Ich vermute mal bis die 1000ms Wartezeit vergehen, ist das Terminated Bit schon gesetzt und die internen Daten sind freigegeben. Dann ist auch Dein Handle nicht mehr gültig. Abhilfe: Ich würde ganz einfach mal:
Delphi-Quellcode:
in eine Prozedur verschieben, die Du in der Prozedur Create an OnTerminate hängst.
PdhCloseQuery(HQ);
FreeLibrary(hPDH); Und aus
Delphi-Quellcode:
würd ich auch ein Typ machen.
Array[1..4] of TPDHInfo
So lass ich zb. in einem Rechnungsprogramm verzögert die Daten aus einer Datenbank lesen und in ein Listview schreiben. Und der Benutzer kann jederzeit durch ESC o.ä. den Vorgang abbrechen. Andreas |
Re: Thread sauber Beenden ? Handle ungültig.
Zitat:
![]() |
Re: Thread sauber Beenden ? Handle ungültig.
Hier verdeutlicht der Ablauf:
Delphi-Quellcode:
PDHThread.Terminate;
// setzt Terminate im Thread, nun läuft die 1000ms Wartezeit ab // ... PDHThread.Waitfor; // Delphi wartet bis Terminated true wird oder Execute-Procedur beendet wird. // ... // Mittlerweile wird Terminated true // Nun Springt Delphi zum nächsten Befehl // Die Wartezeit ist noch nicht um PDHThread.Free; // jetzt wird alles freigegeben // Die Wartezeit ist noch nicht um // Der Thread ruft jetzt erst die zwei anderen Befehle auf. Und hier knallts. |
Re: Thread sauber Beenden ? Handle ungültig.
Achso, da hab ich dich falsch verstanden :stupid: . Allerdings stimmt das hier zumindest für Delphi 6 nicht, denn Terminated = true führt nicht dazu, dass Waitfor sofort zurückkehrt.
Delphi-Quellcode:
PDHThread.Waitfor;
// Delphi wartet bis Terminated true wird oder Execute-Procedur beendet wird. |
Re: Thread sauber Beenden ? Handle ungültig.
Ich schlage mich auch schon seit einiger Zeit mit der PDH.dll herum, bisher mit mäßigem Erfolg. :(
Beim Aufruf von PdhGetFormattedCounterValue() stürzt die Software ab, als ob stdcall nicht stimmt oder ein Varaiblentyp falsch deklariert ist. Zitat:
Zitat:
Delphi-Quellcode:
_PDH_FMT_COUNTERVALUE = record
CStatus : DWORD; case Integer of // !!! 0 : ( longValue : Longint ); 1 : ( doubleValue : double ); 2 : ( largeValue : LONGLONG ); 3 : ( AnsiStringValue : LPCSTR ); 4 : ( WideStringValue : LPCWSTR ); end; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 11:58 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