Einzelnen Beitrag anzeigen

berens

Registriert seit: 3. Sep 2004
434 Beiträge
 
Delphi 10.4 Sydney
 
#1

EOutOfResources - Wie viele Handles sind noch verfügbar?

  Alt 19. Dez 2019, 14:41
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

Geändert von berens (19. Dez 2019 um 14:48 Uhr)
  Mit Zitat antworten Zitat