Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   GetMem Problem (https://www.delphipraxis.net/171480-getmem-problem.html)

blablab 8. Nov 2012 05:15

GetMem Problem
 
Hallo!

ich habe folgenden Code:
Delphi-Quellcode:
var
   buf1, buf2, buf3: Pointer;
   [...]
begin
   [...]
   getmem(buf1, biSizeImage);
   getmem(buf2, biSizeImage);
   getmem(buf3, biSizeImage);

   if GetDIBits(ssDC, ssHBmp, 0, biHeight, buf1, ssBmpInfo, DIB_RGB_COLORS) = 0 then begin
      showmessage('Fehler buf1');
   end;

   if GetDIBits(ssDC, ssHBmp, 0, biHeight, buf2, ssBmpInfo, DIB_RGB_COLORS) = 0 then begin
      showmessage('Fehler buf2');
   end;

   if GetDIBits(ssDC, ssHBmp, 0, biHeight, buf3, ssBmpInfo, DIB_RGB_COLORS) = 0 then begin
      showmessage('Fehler buf3');
   end;
   [...]
end;
GetDIBits funktioniert immer 1 mal und schlägt 2 mal fehl. Dabei ist die Reihenfolge in der ich GetMem aufrufe entscheidend. GetDIBits funktioniert immer nur mit dem Buffer, der als erstes reserviert wird, bei den anderen schlägt er fehl. Hier zum Beispiel klappt der erste Aufruf von GetDIBits, die anderen beiden schlagen fehl.

Um überhaupt den Fehler so weit einzugrenzen habe ich ewig gebraucht. Und jetzt habe ich keine Ahnung was der Fehler zu bedeuten hat. Leider hilft RaiseLastOSError nicht weiter. (Deshalb hat die Fehlereingrenzung so lange gedauert.) Und ähnliche Funktionen bringen mich auch nicht weiter. Ich habe alles in der Hilfe-Kategorie Speicherverwaltung ausprobiert:
GetMem, AllocMem, SysGetMem - hat aber auch nichts geändert.

Hat jemand eine Idee?

Grüße
blablab

Bernhard Geyer 8. Nov 2012 07:17

AW: GetMem Problem
 
Welchen Wert hat biSizeImage?

blablab 8. Nov 2012 07:39

AW: GetMem Problem
 
von 2-8 MB
biSizeImage := Height * (((PixelsPerScanline * BitsPerPixel) + 31) and not 31) div 8;

Ich hab auch schon mit der maximalen Stackgröße rumprobiert. Egal ob kein Bild oder alle Bilder in den Stack passen würden, der Fehler bleibt der gleiche.
(Sollte ja auch so sein, da GetMem Speicher im Heap und nicht im Stack reserviert, oder?)

blablab 8. Nov 2012 08:44

AW: GetMem Problem
 
Ich hab endlich ein Workaround! :-D
Delphi-Quellcode:
GetMem(buf1, 2*biSizeImage);
buf2 := Pointer(Integer(buf1) + biSizeImage);
Das ich da nicht schon früher draufgekommen bin...

Ich kapier den Fehler aber immer noch nicht. Es hängt jedenfalls mit der Größe des reservierten Speichers zusammen.
Mit einem 100x100 Pixel Bild funktionierts immer bei 200x300 klappts nurnoch zu ca. 50%, und bei 300x300 gar nicht mehr.

Hier ist der gesamte Test-Quellcode:
(meine Test-Anwendung besteht nur aus einem Knopf mit diesem Code)
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
   compHBmp: HBITMAP;
   compDC, deskDC: HDC;
   ssBmpInfo: TBitmapInfo;

   buf1, buf2: array of Byte;
begin
   deskDC := GetWindowDC(GetDesktopWindow);

   ZeroMemory(@ssBmpInfo, SizeOf(ssBmpInfo));
   with ssBmpInfo.bmiHeader do begin
      biSize := SizeOf(TBitmapInfoHeader);
      biWidth := 300;
      biHeight := 300;
      biPlanes := 1;
      biBitCount := 24;
      biSizeImage := biHeight * (((biWidth * biBitCount) + 31) and not 31);

      compDC := CreateCompatibleDC(deskDC);
      compHBmp := CreateCompatibleBitmap(deskDC, biWidth, biHeight);
      SelectObject(compDC, compHBmp);

      BitBlt(compDC, 0, 0, biWidth, biHeight, deskDC, 0, 0, SRCCOPY);

      SetLength(buf1, biSizeImage);
      SetLength(buf2, biSizeImage);

      if GetDIBits(compDC, compHBmp, 0, biHeight, buf1, ssBmpInfo, DIB_RGB_COLORS) = 0 then begin
         ShowMessage('buf1');
      end;

      if GetDIBits(compDC, compHBmp, 0, biHeight, buf2, ssBmpInfo, DIB_RGB_COLORS) = 0 then begin
         ShowMessage('buf2');
      end;
   end;
end;
Hier bekomme ich die Meldung 'buf2'.
Setze ich biWidth und biHeight auf 100 funktionierts...

Ob es funktioniert oder nicht entscheidet sich beim Programmstart. Das heißt, wird das Programm gestartet funktioniert es entweder immer oder nie, egal wie oft man den Knopf drückt.

Ist doch komisch, oder ???

Zacherl 8. Nov 2012 09:19

AW: GetMem Problem
 
Was sagt GetLastError? Und warum die and not 31?

himitsu 8. Nov 2012 09:48

AW: GetMem Problem
 
Delphi-Quellcode:
(x + 31) and not 31
= auf 32-er-Schritte aufrunden (Info: geht natürlich nur mit 2er-Potenzen)

blablab 8. Nov 2012 09:59

AW: GetMem Problem
 
GetLastError ist immer 0
(Wie vorher geschrieben hat deshalb die Fehlereingrenzung so lange gedauert)

"and not 31"
Das ist aus der function BytesPerScanline aus der unit Graphics geklaut.
Das "... + 31) and not 31" bewirkt, dass das Ergebnis immer ein Vielfaches von 32 ist. Zum Schluss wird es dann mit div 8 von Bit in Byte umgerechnet. Das bedeutet die benötigten Bytes einer Scanline werden immer zur nächsten 4Byte-Grenze aufgerundet. Das hab aber nicht ich so bestimmt, sondern Windows. Ich halt mich nur dran :)

Aber der Witz an der Sache ist ja, dass es immer einmal funktioniert und einmal nicht.
Es funktioniert immer bei dem Buffer, dessen Speicher zuerst reserviert wird. Ich kann GetDIBits tausend mal aufrufen mit hundert verschiedenen Puffern und es funktioniert immer genau bei dem Buffer, dessen Speicher zuerst reserviert wurde und bei allen anderen nicht. Die restlichen Parameter sind identisch. Und wenn die Parameter einmal funktionieren, dann müssten sie doch beim zweiten mal immer noch funktionieren...
(Windows könnte zwar meine übergebene Variable ssBmpInfo verändern, wenn ich aber immer nur eine Kopie von ssBmpInfo übergebe ändert sich auch nichts.)

blablab 9. Nov 2012 13:27

AW: GetMem Problem
 
OK, ich habs jetzt verstanden.

Man sollte tatsächlich nie GetMem zusammen mit GetDIBits verwenden. Es geht nur mit VirtualAlloc, da sich die Daten in einem physikalisch zusammenhängenden Speicherblock befinden müssen, was bei GetMem nicht unbedingt gegeben ist. Das ist scheinbar eine undokumentierte Bedingung von GetDIBits.

Danke Microsoft für die vielen Stunden Rätselspaß...

Medium 9. Nov 2012 13:49

AW: GetMem Problem
 
Ich finde das es recht nachvollziehbar ist, dass ein Bitmap ein Stück Speicher ohne Löcher braucht. Wertvoller fand ich jetzt die Info, dass GetMem fragmentierte Bereiche liefern kann! D.h. GetMem gibt aus Prozesssicht schon zusammenhängende Adressen, aber physikalisch können die wild verteilt sein, auch wenn es als ein Block reserviert wurde - verstehe ich das richtig?

blablab 10. Nov 2012 10:07

AW: GetMem Problem
 
Scheinbar ist das so und zwar nicht nur bei Delphis GetMem sondern bei so ziemlich allen Standard-Speicher-Allozier-Funktionen :-D jeglicher Programmiersprachen.
Das Problem ist auch, dass der Speicher für GetDIBits mit einem Aufruf von VirtualAlloc reserviert werden muss und GetMem benutzt scheinbar mehrere Aufrufe. (Ob es VirtualAlloc ist weiß ich nicht.)

Das ein Bitmap ein unfragmentiertes Stück Speicher braucht mag sinnvoll sein. Aber wenn man solch ein unfragmentiertes Stück Speicher mit keiner Standard-Speicher-Allozier-Funktion jeglicher Programmiersprachen bekommt, dann wäre das doch wenigstens ein Mini-Kommentar in der Dokumentation wert, oder?


Alle Zeitangaben in WEZ +1. Es ist jetzt 18:49 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