Hallo zusammen.
Ich habe das Problem, dass mein Programm nach mehreren Stunden kommentarlos zugeht. Nach Beseitigung div. technischer Probleme, die für den Thread irrelevant sind, konnte ich das Programm nun endlich in Delphi / mit Debugger lange genug laufen lassen, um den Fehler angezeigt zu bekommen. Leider war das Programm incl. Delphi soweit aufgehängt, dass das Kopieren der Nachricht nicht möglich war. Das wichtigste konnte ich aber gerade noch ablesen: Es war -in diesem konkreten Fall- eine EOutOfResources
Exception, die auslösende Prozedur konnte er mir auch gerade noch anzeigen. Falls dies in allen Fällen, in den das Programm kommentarlos zugegangen ist, die Ursache war, besteht ja Chance auf Besserung
.
Der Fehler konnte auch schnell gefunden werden; durch eine falsche Verschachtelung wurde ein mit GetDC erhaltenes
Handle nicht wieder freigegeben, und diese Prozedur wird aktuell ~4x Sekunde aufgerufen, also kommen pro Stunde knapp 10'000 neue Handles dazu, die nicht freigegeben werden - klar, dass das System irgendwann streikt. Ich glaube, auch ein zweites TBitmap wurde nicht wieder freigegeben.
Was mich nun akut irritiert:
-Im Taskmanager waren CPU (hierfür eh irrelevant) und
RAM auf Bestwerten. Das Programm verbraucht je nach Arbeitsschritt mal mehr, mal weniger
RAM, pendelt sich aber immer wieder bei ~15 MB ein. Also ist die Resource "
RAM" schon mal nicht aufgebraucht.
-Ich hatte bis dato immer regelmäßig
Delphi-Quellcode:
CoFreeUnusedLibraries;
SetProcessWorkingSetSize(GetCurrentProcess, $FFFFFFFF, $FFFFFFFF);// DefragMem;
aufgerufen. Evtl. sorgte das dafür, dass zwar der
RAM von dem vergessenen TBitmap wieder freigegeben wurde und ich es deshalb im Taskmanager nicht als steigender
Ram-Verbrauch gesehen habe...
-Wahrscheinlich bekomme ich gleich von euch einen freundlichen Hinweis, das "
Handle"s nichts (direkt) mit
RAM zu tun haben, und man die genutzten/"verbrauchten" Handles entsprechend nirgendwo sehen kann. Wäre irgendwo logisch. Die "Handles" im Taskmanager bei "Leistung" - "CPU" lassen übrigens keinen großen Rückschluss darauf zu, ob/dass mein Programm viele Handles verbraucht. Die Werte schwanken so stark durch Windows-Hintergrundprozesse, dass eine klare Aussage nicht möglich ist, was ich damit zu tun habe. Eine klar Zunahme von 10'000 Handles die Stunde ist auf jeden Fall nicht feststellbar... Bei "Resourcenmonitor" - "CPU" - [meine .exe] - "Zugeordnete Handles" ist die Liste auch mehrere Minuten nach dem Start noch unverändert...
-EDIT: Ich hatte EurekaLog 7.0.7 in meinem Programm mitlaufen - Das Springt leider bei dem Fehler nicht an. Wahrscheinlich, weil es zum Erzeugen der Fehlermeldung ein freies
Handle braucht.
Meine Fragen zu dem Thema:
1) (Wie) kann ich
-mit meinem Programm, oder:
-mit Windows
auslesen, wie viele Handles (von meinem Programm!) benutzt wurden, oder alternativ: noch zur Verfügung stehen. Ich könnte mir vorstellen, dass ich in dieser einen Prozedur zwar irgendwann bei "GetDC()" "0" erhalte (--> fehlgeschlagen) und dann wissen würde, dass es ab /jetzt/ einen Fehler gibt, weil keine freien Handles mehr verfügbar sind (bzw. dann würde wohl eher die o.g.
Exception ausgelöst), aber ich würde gerne "Überwachungsmäßig" über die ganze Programmlaufzeit beobachten, ob/wie sich mein Handleverbrauch entwickelt, und ob der aktuelle Trend vermuten lässt, dass das Programm bald irgendwann mangels freier Handles abstürzen wird. Oder auch für meine Logging-Funktion: freie Handles vor/nach Durchlauf der Prozedur
2) Eine Bitte: Schaut euch die nachfolgende Prozedur an, und sagt mir, ob in der jetzigen, korrigierten Form Bedenken für Speicherverbrauch oder nicht freigegebene Handles bestehen:
Delphi-Quellcode:
// Prüft, ob der Inhalt von _Parent komplett Schwarz ist
// Die Prozedur zeichnet, FALLS übergeben, auf _TargetBitmap, was sie "gesehen" hat
// Zum Vermeidung div. Probleme wird der untersuchte Bereich auf 640x480 px skaliert,
// das ist für diese Zwecke mehr als ausreichend genau
function EndOfPresentation_Neu(_Parent: TWinControl; _TargetBitmap: Graphics.TBitmap): Boolean;
var
myBitmap: graphics.TBitmap;
DetectHeight, DetectWidth: Integer;
SourceDC: HDC;
hwParentHandle: HWND;
const
CAPTUREBLT = $40000000;
SCAN_WIDTH = 640;
SCAN_HEIGHT = 480;
begin
Result := False;
// Variablen auf gültige Werte initialisieren, damit sie
// auf jeden Fall korrekt freigegeben werden können
hwParentHandle := 0;
myBitmap :=
NIL;
try
// Kein Parent, keine Analyse
if not assigned(_Parent)
then Exit;
if not _Parent.InheritsFrom(TWinControl)
then Exit;
DetectWidth := _Parent.Width;
DetectHeight := _Parent.Height;
if (DetectHeight < 1)
or (DetectWidth < 1)
then Exit;
// TBitmap initialisieren, und auf die erwartete Größe bringen
try
hwParentHandle := _Parent.Handle;
myBitmap := Graphics.TBitmap.Create;
myBitmap.PixelFormat := pf32bit;
// Zu analysierender Bereich ist lediglich 640 x 480 px
myBitmap.Width := SCAN_WIDTH;
myBitmap.Height := SCAN_HEIGHT;
except
on E:
Exception do begin
log('
EndOfPresentationDetector.EndOfPresentation_Neu.InitializeValues.Exception: ' + E.
Message);
end;
end;
// Screenshot vom betroffenen Bereich erstellen
if assigned(myBitmap)
then begin
try
SourceDC := GetDC(hwParentHandle);
if SourceDC <> 0
then begin
// das "_Parent"-Objekt abfotografieren. Wir skalieren gleichzeitig auf 640 x 480. Hinreichend genau für unseren Zweck.
StretchBlt(myBitmap.Canvas.Handle, 0, 0, SCAN_WIDTH, SCAN_HEIGHT, SourceDC, 0, 0, DetectWidth, DetectHeight, SRCCOPY
or CAPTUREBLT);
ReleaseDC(hwParentHandle, SourceDC);
end;
except
on E:
Exception do begin
log('
EndOfPresentationDetector.EndOfPresentation_Neu.DoBitBlt.Exception: ' + E.
Message);
end;
end;
try
// Prozedur, die nur auf die Scanlines von myBitmap zugreift, und zurückgibt, Keine Leak/Handle Bedenken!
// Gibt an wie viele Prozent des Bildes explizit schwarz sind,
// bzw. bei wieviel Prozent der Höhes des Bildes (von Oben gesehen)
// die letzte Zeile mit mind. einem nicht-schwarzen Pixel ist
Result := (ObereKanteNurSchwarzInProzent(myBitmap) < 25);
// Falls der Parameter übergeben wurde, wird auf _TargetBitmap das erfasste, nun skalierte und mit
// Hilfslininen versehene Bild ausgegeben, so dass es dem Benutzer angezeigt werden könnte
if assigned(_TargetBitmap)
then begin
_TargetBitmap.Assign(myBitmap);
end;
except end;
FreeAndNil(myBitmap);
end;
except
on E:
Exception do begin
log('
EndOfPresentationDetector.EndOfPresentation_Neu.Global.Exception: ' + E.
Message);
end;
end;
end;
Vielen Dank
Delphi 10.4 32-Bit auf Windows 10 Pro 64-Bit, ehem. Delphi 2010 32-Bit auf Windows 10 Pro 64-Bit