bmp-Datei speichern funktioniert nicht

bluejoky2

Grünschnabel
Hallo,

versuche gerade ein kleines Programm zum Laufen zu bekommen, mit dem man per Button in der Toolbar den Inhalt des aktuellen Fensters als bmp-Dateien auf der Festplatte ablegen kann, wobei der File-Name bei jeder Betätigung des Buttons um eins erhöht wird. Der Code ist aus dem Netz und ich habe versucht ihn in meine Anwendung einzubauen. Leider habe ich nicht viel Erfahrung mit Visual C++ und komme nicht weiter. Die Funktion DDBToDIB liefert offenbar an folgender Stelle „NULL“ zurück:

Code:
// Realloc the buffer so that it can hold all the bits
dwLen += bi.biSizeImage;
if (lpbi2 = (LPBITMAPINFOHEADER) GlobalReAlloc (lpbi, dwLen, GMEM_MOVEABLE))
  lpbi = lpbi2;
else{
  GlobalFree(lpbi);
 
   // Reselect the original palette
  SelectPalette (hDC,hPal,FALSE);
  ReleaseDC(NULL,hDC);
TRACE ("DDBToDIB 2 \n");
          return NULL;
TRACE ("DDBToDIB 3 \n");
}

Habe das mit dem Einbau mehrerer TRACE-Meldungen überprüft, wobei „TRACE ("DDBToDIB 2 \n");“ im Debug-Mode noch zu sehen war. „Kann mir jemand sagen, woran das liegt?

MfG bluejoky2
 
Ja, GlobalReAlloc schlägt fehl.

Warum das so ist, kannst du nach dem Aufruf mit GetLastError (den Wert im Error Lookup Tool nachsehen) prüfen.

Ich vermute mal eben, dass das Handle in GlobalReAlloc nicht vorher durch GlobalAlloc oder GlobalReAlloc entstanden ist.
 
Hallo Endurion,

erst mal danke für die schnelle Antwort, aber mit GetLastError() komme ich nicht richtig klar (Anfänger). Zumindest habe ich den Fehlercode nicht zur Anzeige bringen können. Habe mir mit folgendem Code aus der MSDN geholfen:

Code:
LPVOID lpMsgBuf;
FormatMessage( 
    FORMAT_MESSAGE_ALLOCATE_BUFFER | 
    FORMAT_MESSAGE_FROM_SYSTEM | 
    FORMAT_MESSAGE_IGNORE_INSERTS,
    NULL,
    GetLastError(),
    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
    (LPTSTR) &lpMsgBuf,
    0,
    NULL 
);
// Process any inserts in lpMsgBuf.
// ...
// Display the string.
MessageBox( NULL, (LPCTSTR)lpMsgBuf, "Error", MB_OK | MB_ICONINFORMATION );
// Free the buffer.
LocalFree( lpMsgBuf );

Damit erhalte ich folgende Meldung: "Es ist nicht genügend Speicher verfügbar, um diesen Befehl auszuführen". Damit kann ich jetzt aber auch nicht so richtig was anfangen. Kannst Du mir hier weiterhelfen?

MfG bluejoky2
 
Warum denn so kompliziert?
Bau in deine Funktion vor dem return NULL ein
Code:
int fehler=GetLastError();

Dann gehst du mit dem Debugger im Einzelschrittmode durch und kannst dann im Anzeigefenster den Wert von fehler sehen. Was der dann bedeutet, steht in der MSDN.

Mfg

langer
 
Hallo, und danke für den Tipp. Habe aber keinen Zugriff auf den Einzelschrittmode. Das gesamte Menü "Test" fehlt, in dem sich die Elemente "Einzelschritt", "Prozedurschritt" und "Prozedur abschließen" befinden. Auch F8, wie in der MSDN, beschrieben funktioniert bei mir nicht. Muss das irgendwo aktiviert werden
(Visual C++ 6)?

MfG bluejoky2
 
Den Fehler hat er ja schon. Tip: Wenn du Visual Studio hast, gibt es einen Menüpunkt Extras (oder Tools), da drin gibt es ein Error Lookup Tool. Starten, Code reintippen, fettich.

Prüf mal den Wert von dwLen, wenn es fehlschlägt. Evtl. ist auch biSizeImage auf 0 (was übrigens ein gültiger Wert ist!). Sicherer ist es, die tatsächliche Speichergrösse selbst zu berechnen (auf 32bit gepaddete Bytes pro Pixel pro Zeile multipliziert mit Höhe in Pixel).
 
Komisch, der Menu-Punkt Debuggen sollte schon dasein. Einfach mit F5 Compilieren starten. Vorher Haltepunkt setzen vor der vermuteten Fehlerstelle. Und F8 stimmt nicht, bei C++ ist die Einzelschritt-Taste F10. (F8 ist es bei VB). Und ich glaube, bei VC 6 mußte man die Symbol-Leiste "Debug-/Release-Modus" selbst aktivieren.

Mfg

langer
 
Danke erst mal für Eure Tipps. Das Tool für die Fehler-Code habe ich gefunden. Mit der Einzelschritt-Taste hattet auch Ihr Recht – ich war beim Stöbern in der MSDN unbemerkt in VB gerutscht. Den Code bin ich mal im Einzelschritt durchgegangen und habe dabei festgestellt, dass es schon eher zu einem Fehler kommt. Hier erst mal der gesamte Code:

Code:
LPBITMAPINFOHEADER DDBToDIB(HBITMAP hbitmap, HPALETTE hPal)
{
 
TRACE ("DDBToDIB 1 \n");
 
BITMAP              bm;
BITMAPINFOHEADER    bi;
LPBITMAPINFOHEADER  lpbi, lpbi2;
DWORD               dwLen;
HDC                 hDC;
HPALETTE            hPal2;
BOOL bGotBits;
int nColors;
 
// If a palette has not been supplied, use default palette
if (hPal==NULL)
 hPal = (HPALETTE) GetStockObject(DEFAULT_PALETTE);
 
// Get bitmap information
GetObject (hbitmap, sizeof(bm), (LPSTR)&bm);
 
// Initialize the bitmap infoheader
bi.biSize          = sizeof(BITMAPINFOHEADER);
bi.biWidth         = bm.bmWidth;
bi.biHeight        = bm.bmHeight;
bi.biPlanes        = 1;
bi.biBitCount      = bm.bmPlanes * bm.bmBitsPixel;
 //bm.bmPlanes    * bm.bmBitsPixel;
bi.biCompression   = BI_RGB;
bi.biSizeImage     = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed       = 0;
bi.biClrImportant  = 0;
 
// Compute the size of the infoheader and the color table
nColors = (1 << bi.biBitCount);
if( nColors > 256 )
 nColors = 0;
dwLen  = bi.biSize + nColors * sizeof(RGBQUAD);
 
// We need a device context to get the DIB from
hDC   = GetDC(NULL);
hPal2 = SelectPalette(hDC,hPal,FALSE);
RealizePalette (hDC);
 
// Allocate enough memory to hold bitmap infoheader and
// color table
lpbi = (LPBITMAPINFOHEADER) GlobalAlloc (GMEM_FIXED,dwLen);
 
if (!lpbi){
 SelectPalette (hDC,hPal,FALSE);
 ReleaseDC (NULL,hDC);
 return NULL;
}
 
*lpbi = bi;
 
// Call GetDIBits with a NULL lpBits param, so the device
// driver will calculate the biSizeImage field
GetDIBits(hDC, hbitmap, 0L,
              (DWORD)bi.biHeight,
              (LPBYTE)NULL, (LPBITMAPINFO)lpbi,
              (DWORD)DIB_RGB_COLORS);
 
bi = *lpbi;
 
// If the driver did not fill in the biSizeImage field, then
// compute it
// Each scan line of the image is aligned on a DWORD (32bit)
// boundary
if (bi.biSizeImage == 0){
 bi.biSizeImage = ((((bi.biWidth * bi.biBitCount) + 31)
                     & ~31) / 8) * bi.biHeight;
 
 
}
 
// Realloc the buffer so that it can hold all the bits
dwLen += bi.biSizeImage;
if (lpbi2 = (LPBITMAPINFOHEADER) GlobalReAlloc (lpbi, dwLen, GMEM_MOVEABLE))
 lpbi2 = lpbi;
else{
 GlobalFree(lpbi);
 
 // Reselect the original palette
 SelectPalette (hDC,hPal,FALSE);
 ReleaseDC(NULL,hDC);
TRACE ("DDBToDIB 2 \n");
 
 
int fehler=GetLastError();
 
         return NULL;
 
TRACE ("DDBToDIB 3 \n");    
}
 
 
 
// FINALLY get the DIB
bGotBits = GetDIBits( hDC, hbitmap,
     0L,                        // Start scan line
     (DWORD)bi.biHeight,        // # of scan lines
     (LPBYTE)lpbi               // address for bitmap bits
     + (bi.biSize + nColors * sizeof(RGBQUAD)),
     (LPBITMAPINFO)lpbi,        // address of bitmapinfo
     (DWORD)DIB_RGB_COLORS);    // Use RGB for color table
 
SelectPalette(hDC,hPal,FALSE);
ReleaseDC(NULL,hDC);
 
if( !bGotBits )
{
 GlobalFree(lpbi);
         return NULL;
}
 
DeleteObject (hPal);
 
 
 
return lpbi;
 
}
 
 
 
BOOL BMPtoFILE(LPBITMAPINFOHEADER lpbi)
{
 
FILE *Datei;
BITMAPFILEHEADER hdr;
int nColors;
 
char           Dateiname [100];
static short   Dateinummer;
BOOL           gibts;
 
do   // Dateinamen bestimmen
{
           gibts = FALSE;
           sprintf (Dateiname, "CAP%04d.bmp", Dateinummer);
           // existiert schon 
           Datei = fopen (Dateiname, "r");
           if (Datei)
           {
                       gibts = TRUE;
                       fclose (Datei);
           }
           Dateinummer ++;
}
while (gibts);
 
Datei = fopen (Dateiname, "wb");   // write, binaer...
 
nColors= 1 << lpbi->biBitCount;
hdr.bfType= ((WORD) ('M' << 8) | 'B');
hdr.bfSize=sizeof(hdr) + GlobalSize(lpbi);
hdr.bfReserved1=0;
hdr.bfReserved2=0;
hdr.bfOffBits=(DWORD) (sizeof(hdr) + nColors * sizeof(RGBQUAD) + lpbi->biSize);
 
fwrite (&hdr,sizeof(hdr), 1, Datei);
fwrite (lpbi,GlobalSize(lpbi), 1, Datei);
fclose (Datei);
 
return (TRUE);
 
}
 
 
 
 
BOOL SaveWin(HWND hwnd)
{
 
//it will capture a wnd image and save it into a bmp file
 
         TRACE ("SaveWin 1 \n");
 
HBITMAP hbmp,hOldBmp;
HDC  hdc;
RECT rect; 
BOOL flg=0;
HPALETTE hpal;
LOGPALETTE *pLp;
HDC hmemdc;
LPBITMAPINFOHEADER lpbi;
 
hdc = GetDC (hwnd);        // Device Context aus Fenster
 
GetClientRect (hwnd, &rect); //Get dimension of Window
 
 
hmemdc = CreateCompatibleDC (hdc);  //Make Compatible DC for memdc
hbmp   = CreateCompatibleBitmap(hdc, rect.right, rect.bottom);
 //Create Compatible DDB
hOldBmp = (HBITMAP) SelectObject(hmemdc, hbmp);
BitBlt (hmemdc, 0,0,rect.right, rect.bottom, hdc, 0, 0, SRCCOPY);
 
//The following code will detect whether the BMP uses a Raster
//palette or not.
 
if(GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE)
{
 int nSize;
 nSize=sizeof(LOGPALETTE) + sizeof(PALETTEENTRY) * 256;
 pLp = (LOGPALETTE*) malloc (nSize);
 pLp->palVersion=0x300;
 pLp->palNumEntries=GetSystemPaletteEntries(
      hdc,0,255,pLp->palPalEntry);
 hpal = CreatePalette(pLp);
}
SelectObject(hmemdc, hOldBmp);
 
// convert bitmap from DDB to DIB see DDBToDIB()
// See DDBToDIB function for more..
 
 
 
lpbi = DDBToDIB (hbmp, hpal);
 
if (lpbi==NULL)
TRACE ("SaveWin 2 \n");         
           return FALSE;
TRACE ("SaveWin 3 \n");  
 
 
BMPtoFILE (lpbi);   // und ab damit...!
 
ReleaseDC (hwnd, hdc);
DeleteDC  (hmemdc);
DeleteObject (hpal);
DeleteObject (hbmp);
//free (pLp);
 
 
 
return (TRUE);
 
 
}
 
HWND hwnd;
 
void CBitmapDoc::OnButtonStore() 
{
         // TODO: Code für Befehlsbehandlungsroutine hier einfügen
 
         TRACE ("OnButtonStore 1 \n");
 
         SaveWin(hwnd);
 
         TRACE ("OnButtonStore 2 \n");
 
}

Die Abfolge, mit Fehlern und den Werten von „dwLen“ und „biSizeImage“, nach dem Klick auf den Store-Button habe ich mal in Tabellenform in ein Word-Dokument gepackt. Habe immer die Zeilen aufgeführt, bei denen sich an den genannten Werten etwas geändert hat, wobei der neue Wert rot gekennzeichnet ist. Hoffe das ist auch so erkennbar. Könnt Ihr damit was anfangen? Im Forum habe ich etwas gelesen, dass für die Speicherreservierung keine verschiedenen Varianten verwendet werden sollen. So wie ich das verstehe, wäre das aber hier der Fall, denn in „OnDraw“ habe ich z.B. „new CDC“ und hier „GlobalAlloc“, oder bringe ich da was durcheinander.
Warum bekommt man nicht bereits beim Erstellen eine Fehlermeldung, wenn ein Parameter nicht stimmt? Der Compiler meldet keine Fehler.

MfG bluejoky2
 

Anhänge

  • 26693attachment.doc
    20,5 KB · Aufrufe: 52
Das fängt ja schon beim ersten Befehl an, also da ansetzen.

Lass dir mal rect.right und rect.bottom ausgeben. Ebenso den HDC (sollte ungleich NULL sein).
 
Hallo bluejoky2,

Ich hatte mal ein einfaches Screenshot-Programm geschrieben und poste mal die relevante Funktion. Der Code ist nicht so komplex wie bei dir, speziell das Paletten- bzw. Farbenhandling hatte ich sehr vereinfacht. Außerdem benutze ich nur "new" für die Speicherreservierung.
Vielleicht hilft's dir weiter.

Gruß
MCoder

C++:
void SaveWindowBitmap(HWND hWnd, LPCTSTR lpszFile)
{
    /////////////////////////////////////////////////////////////////
    // Bild aufnehmen

    RECT rc;
    ::GetWindowRect(hWnd, &rc);
    int nWidth  = rc.right  - rc.left;
    int nHeight = rc.bottom - rc.top;

    HWND    hWndSc = ::GetDesktopWindow();
    HDC     hdc    = ::GetDC(hWndSc);
    HDC     memDC  = ::CreateCompatibleDC(hdc);
    HBITMAP hbm    = ::CreateCompatibleBitmap(hdc, nWidth, nHeight);
    HBITMAP hbmOld = (HBITMAP)::SelectObject(memDC, hbm);

    ::BringWindowToTop(hWnd);
    ::BitBlt(memDC, 0, 0, nWidth,  nHeight, hdc, rc.left, rc.top, SRCCOPY);

    /////////////////////////////////////////////////////////////////
    // Bilddaten ermitteln

    BITMAPINFO bmi;
    ZeroMemory(&bmi, sizeof(bmi));

    bmi.bmiHeader.biSize         = sizeof(BITMAPINFOHEADER);
    bmi.bmiHeader.biWidth        = nWidth;
    bmi.bmiHeader.biHeight       = nHeight;
    bmi.bmiHeader.biBitCount     = 24;
    bmi.bmiHeader.biPlanes       = 1;
    bmi.bmiHeader.biCompression  = BI_RGB;
    bmi.bmiHeader.biSizeImage    = 32 * nWidth * nHeight / 8;

    BYTE *pbBits = new BYTE[bmi.bmiHeader.biSizeImage];

    ::GetDIBits( memDC, 
                 hbm,
                 0,
                 bmi.bmiHeader.biHeight,
                 pbBits,
                 &bmi,
                 DIB_RGB_COLORS );

    BITMAPFILEHEADER bfh;

    bfh.bfType      = ('M' << 8) + 'B';
    bfh.bfSize      = sizeof(BITMAPFILEHEADER)  +
                          bmi.bmiHeader.biSizeImage +
                          sizeof(BITMAPINFOHEADER); 
    bfh.bfReserved1 = 0;
    bfh.bfReserved2 = 0;
    bfh.bfOffBits   = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

    /////////////////////////////////////////////////////////////////
    // Bilddaten speichern

    HANDLE hfile = CreateFile( lpszFile,
                               GENERIC_WRITE,
                               0,
                               0,
                               OPEN_ALWAYS,
                               0,
                               0 ); 

    DWORD dwWritten;
 
    WriteFile(hfile, &bfh,           sizeof(bfh),               &dwWritten, NULL); 
    WriteFile(hfile, &bmi.bmiHeader, sizeof(BITMAPINFOHEADER),  &dwWritten, NULL); 
    WriteFile(hfile, pbBits,         bmi.bmiHeader.biSizeImage, &dwWritten, NULL); 

    CloseHandle(hfile); 

    ::SelectObject(memDC, hbmOld);
    ::DeleteDC(memDC);
    ::ReleaseDC(hWndSc,hdc); 
    ::DeleteObject(hbm);

    delete[] pbBits; 
}
 

Neue Beiträge

Zurück