2017-08-27 5 views
0

fragte ich bereits eine sehr ähnliche Frage einen oder zwei Tage vor, aber mein Problem war nicht ganz klar, so dass ich versuchen, es zu formulieren hier:Screenshot WinAPI - Free Memory

ich einen Screenshot von meinem Bildschirm tun will und zu tun, dass ich gesucht und gefunden diesen Code:

#include <windows.h> 
#include <stdio.h> 

inline int GetFilePointer(HANDLE FileHandle) 
{ 
    return SetFilePointer(FileHandle, 0, 0, FILE_CURRENT); 
} 

extern _Bool SaveBMPFile(char* filePath, HBITMAP bitmap, HDC bitmapDC, int width, int height) 
{ 
    _Bool Success = 0; 
    HDC SurfDC = NULL;  // GDI-compatible device context for the surface 
    HBITMAP OffscrBmp = NULL; // bitmap that is converted to a DIB 
    HDC OffscrDC = NULL;  // offscreen DC that we can select OffscrBmp into 
    LPBITMAPINFO lpbi = NULL; // bitmap format info; used by GetDIBits 
    LPVOID lpvBits = NULL; // pointer to bitmap bits array 
    HANDLE BmpFile = INVALID_HANDLE_VALUE; // destination .bmp file 
    BITMAPFILEHEADER bmfh; // .bmp file header 

          // We need an HBITMAP to convert it to a DIB: 
    if ((OffscrBmp = CreateCompatibleBitmap(bitmapDC, width, height)) == NULL) 
     return 0; 

    // The bitmap is empty, so let's copy the contents of the surface to it. 
    // For that we need to select it into a device context. We create one. 
    if ((OffscrDC = CreateCompatibleDC(bitmapDC)) == NULL) 
     return 0; 

    // Select OffscrBmp into OffscrDC: 
    HBITMAP OldBmp = (HBITMAP)SelectObject(OffscrDC, OffscrBmp); 

    // Now we can copy the contents of the surface to the offscreen bitmap: 
    BitBlt(OffscrDC, 0, 0, width, height, bitmapDC, 0, 0, SRCCOPY); 

    // GetDIBits requires format info about the bitmap. We can have GetDIBits 
    // fill a structure with that info if we pass a NULL pointer for lpvBits: 
    // Reserve memory for bitmap info (BITMAPINFOHEADER + largest possible 
    // palette): 
    if ((lpbi = (LPBITMAPINFO)malloc(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD))) == NULL) 
     return 0; 


    ZeroMemory(&lpbi->bmiHeader, sizeof(BITMAPINFOHEADER)); 
    lpbi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 
    // Get info but first de-select OffscrBmp because GetDIBits requires it: 
    SelectObject(OffscrDC, OldBmp); 
    if (!GetDIBits(OffscrDC, OffscrBmp, 0, height, NULL, lpbi, DIB_RGB_COLORS)) 
     return 0; 

    // Reserve memory for bitmap bits: 
    if ((lpvBits = malloc(lpbi->bmiHeader.biSizeImage)) == NULL) 
     return 0; 

    // Have GetDIBits convert OffscrBmp to a DIB (device-independent bitmap): 
    if (!GetDIBits(OffscrDC, OffscrBmp, 0, height, lpvBits, lpbi, DIB_RGB_COLORS)) 
     return 0; 


    //ANSI->Unicode 
    LPCSTR szAnsi = filePath; 
    int Size = MultiByteToWideChar(CP_ACP, 0, szAnsi, -1, NULL, 0); 
    LPWSTR filename = malloc(sizeof(LPWSTR) * Size); 
    MultiByteToWideChar(CP_ACP, 0, szAnsi, -1, filename, Size); 
    // Create a file to save the DIB to: 
    if ((BmpFile = CreateFile(filename, 
     GENERIC_WRITE, 
     0, NULL, 
     CREATE_ALWAYS, 
     FILE_ATTRIBUTE_NORMAL, 
     NULL)) == INVALID_HANDLE_VALUE) 

     return 0; 

    DWORD Written; // number of bytes written by WriteFile 

         // Write a file header to the file: 
    bmfh.bfType = 19778;  // 'BM' 
           // bmfh.bfSize = ???  // we'll write that later 
    bmfh.bfReserved1 = bmfh.bfReserved2 = 0; 
    // bmfh.bfOffBits = ???  // we'll write that later 
    if (!WriteFile(BmpFile, &bmfh, sizeof(bmfh), &Written, NULL)) 
     return 0; 

    if (Written < sizeof(bmfh)) 
     return 0; 

    // Write BITMAPINFOHEADER to the file: 
    if (!WriteFile(BmpFile, &lpbi->bmiHeader, sizeof(BITMAPINFOHEADER), &Written, NULL)) 
     return 0; 

    if (Written < sizeof(BITMAPINFOHEADER)) 
     return 0; 

    // Calculate size of palette: 
    int PalEntries; 
    // 16-bit or 32-bit bitmaps require bit masks: 
    if (lpbi->bmiHeader.biCompression == BI_BITFIELDS) 
     PalEntries = 3; 
    else 
     // bitmap is palettized? 
     PalEntries = (lpbi->bmiHeader.biBitCount <= 8) ? 
     // 2^biBitCount palette entries max.: 
     (int)(1 << lpbi->bmiHeader.biBitCount) 
     // bitmap is TrueColor -> no palette: 
     : 0; 
    // If biClrUsed use only biClrUsed palette entries: 
    if (lpbi->bmiHeader.biClrUsed) 
     PalEntries = lpbi->bmiHeader.biClrUsed; 

    // Write palette to the file: 
    if (PalEntries) { 
     if (!WriteFile(BmpFile, &lpbi->bmiColors, PalEntries * sizeof(RGBQUAD), &Written, NULL)) 
      return 0; 

     if (Written < PalEntries * sizeof(RGBQUAD)) 
      return 0; 
    } 

    // The current position in the file (at the beginning of the bitmap bits) 
    // will be saved to the BITMAPFILEHEADER: 
    bmfh.bfOffBits = GetFilePointer(BmpFile); 

    // Write bitmap bits to the file: 
    if (!WriteFile(BmpFile, lpvBits, lpbi->bmiHeader.biSizeImage, &Written, NULL)) 
     return 0; 

    if (Written < lpbi->bmiHeader.biSizeImage) 
     return 0; 

    // The current pos. in the file is the final file size and will be saved: 
    bmfh.bfSize = GetFilePointer(BmpFile); 

    // We have all the info for the file header. Save the updated version: 
    SetFilePointer(BmpFile, 0, 0, FILE_BEGIN); 
    if (!WriteFile(BmpFile, &bmfh, sizeof(bmfh), &Written, NULL)) 
     return 0; 

    if (Written < sizeof(bmfh)) 
     return 0; 

    return 1; 
} 



_Bool ScreenCapture(char* filePath, int xStart, int yStart, int width, int height) 
{ 
    // get a DC compat. w/ the screen 
    HDC hDc = CreateCompatibleDC(0); 

    // make a bmp in memory to store the capture in 
    HBITMAP hBmp = CreateCompatibleBitmap(GetDC(0), width, height); 

    // join em up 
    SelectObject(hDc, hBmp); 

    // copy from the screen to my bitmap 
    BitBlt(hDc, 0, 0, width, height, GetDC(0), xStart, yStart, SRCCOPY); 

    // save my bitmap 
    _Bool ret = SaveBMPFile(filePath, hBmp, hDc, width, height); 

    // free the bitmap memory 
    DeleteObject(hBmp); 

    return ret; 
} 

main() 
{ 
    ScreenCapture("screenshot.bmp", 0, 0, 1920, 1080); 

    FILE* Screen = NULL; 
    Screen = fopen("screenshot.bmp", "r"); //Error, the image is "used" somewhere...  

    return 0; 
} 

ich weiß kein Wort des WinAPI aber ich sah in Viual Studio, das zu Screen bei jedem Anruf(), gibt es nicht befreit RAM und das ist ein Problem.

Wenn einige von euch weiß, wo der Fehler ist ...

helfen Schätzt, Dank :)

+0

Sie schließen 'Bmpfile' nicht. Bevor Sie von 'SaveBMPFile' zurückkommen, rufen Sie' CloseHandle (BmpFile); ' –

+0

Ja, * alle * die von Ihnen erstellten Objekte müssen freigegeben/zerstört werden. Für jede Erstellungsfunktion gibt es eine entsprechende Funktion. Sie finden es in der MSDN-Dokumentation für die Erstellungsfunktion aufgelistet. –

+0

Danke!Das erledigt ist, keine Probleme mehr :) –

Antwort

-2

Hier ist der endgültige Code, ohne Probleme arbeiten, ohne Speicherlecks arbeiten:

#include <windows.h> 
#include <stdio.h> 

// Helper function to retrieve current position of file pointer: 
inline int GetFilePointer(HANDLE FileHandle) 
{ 
    return SetFilePointer(FileHandle, 0, 0, FILE_CURRENT); 
} 
//--------------------------------------------------------------------------- 

// Screenshot 
// -> FileName: Name of file to save screenshot to 
// -> lpDDS: DirectDraw surface to capture 
// <- Result: Success 
// 
extern _Bool SaveBMPFile(char* filePath, HBITMAP bitmap, HDC bitmapDC, int width, int height) 
{ 
    _Bool Success = 0; 
    HBITMAP OffscrBmp = NULL; // bitmap that is converted to a DIB 
    HDC OffscrDC = NULL;  // offscreen DC that we can select OffscrBmp into 
    LPBITMAPINFO lpbi = NULL; // bitmap format info; used by GetDIBits 
    LPVOID lpvBits = NULL; // pointer to bitmap bits array 
    HANDLE BmpFile = INVALID_HANDLE_VALUE; // destination .bmp file 
    BITMAPFILEHEADER bmfh; // .bmp file header 

          // We need an HBITMAP to convert it to a DIB: 
    if ((OffscrBmp = CreateCompatibleBitmap(bitmapDC, width, height)) == NULL) 
     return 0; 

    // The bitmap is empty, so let's copy the contents of the surface to it. 
    // For that we need to select it into a device context. We create one. 
    if ((OffscrDC = CreateCompatibleDC(bitmapDC)) == NULL) 
     return 0; 

    // Select OffscrBmp into OffscrDC: 
    HBITMAP OldBmp = (HBITMAP)SelectObject(OffscrDC, OffscrBmp); 

    // Now we can copy the contents of the surface to the offscreen bitmap: 
    BitBlt(OffscrDC, 0, 0, width, height, bitmapDC, 0, 0, SRCCOPY); 

    // GetDIBits requires format info about the bitmap. We can have GetDIBits 
    // fill a structure with that info if we pass a NULL pointer for lpvBits: 
    // Reserve memory for bitmap info (BITMAPINFOHEADER + largest possible 
    // palette): 
    if ((lpbi = (LPBITMAPINFO)malloc(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD))) == NULL) 
     return 0; 


    ZeroMemory(&lpbi->bmiHeader, sizeof(BITMAPINFOHEADER)); 
    lpbi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 
    // Get info but first de-select OffscrBmp because GetDIBits requires it: 
    SelectObject(OffscrDC, OldBmp); 
    if (!GetDIBits(OffscrDC, OffscrBmp, 0, height, NULL, lpbi, DIB_RGB_COLORS)) 
     return 0; 

    // Reserve memory for bitmap bits: 
    if ((lpvBits = malloc(lpbi->bmiHeader.biSizeImage)) == NULL) 
     return 0; 

    // Have GetDIBits convert OffscrBmp to a DIB (device-independent bitmap): 
    if (!GetDIBits(OffscrDC, OffscrBmp, 0, height, lpvBits, lpbi, DIB_RGB_COLORS)) 
     return 0; 


    //ANSI->Unicode 
    LPCSTR szAnsi = filePath; 
    int Size = MultiByteToWideChar(CP_ACP, 0, szAnsi, -1, NULL, 0); 
    LPWSTR filename = malloc(sizeof(LPWSTR) * Size); 
    MultiByteToWideChar(CP_ACP, 0, szAnsi, -1, filename, Size); 
    // Create a file to save the DIB to: 
    if ((BmpFile = CreateFile(filename, 
     GENERIC_WRITE, 
     0, NULL, 
     CREATE_ALWAYS, 
     FILE_ATTRIBUTE_NORMAL, 
     NULL)) == INVALID_HANDLE_VALUE) 

     return 0; 

    DWORD Written; // number of bytes written by WriteFile 

         // Write a file header to the file: 
    bmfh.bfType = 19778;  // 'BM' 
           // bmfh.bfSize = ???  // we'll write that later 
    bmfh.bfReserved1 = bmfh.bfReserved2 = 0; 
    // bmfh.bfOffBits = ???  // we'll write that later 
    if (!WriteFile(BmpFile, &bmfh, sizeof(bmfh), &Written, NULL)) 
     return 0; 

    if (Written < sizeof(bmfh)) 
     return 0; 

    // Write BITMAPINFOHEADER to the file: 
    if (!WriteFile(BmpFile, &lpbi->bmiHeader, sizeof(BITMAPINFOHEADER), &Written, NULL)) 
     return 0; 

    if (Written < sizeof(BITMAPINFOHEADER)) 
     return 0; 

    // Calculate size of palette: 
    int PalEntries; 
    // 16-bit or 32-bit bitmaps require bit masks: 
    if (lpbi->bmiHeader.biCompression == BI_BITFIELDS) 
     PalEntries = 3; 
    else 
     // bitmap is palettized? 
     PalEntries = (lpbi->bmiHeader.biBitCount <= 8) ? 
     // 2^biBitCount palette entries max.: 
     (int)(1 << lpbi->bmiHeader.biBitCount) 
     // bitmap is TrueColor -> no palette: 
     : 0; 
    // If biClrUsed use only biClrUsed palette entries: 
    if (lpbi->bmiHeader.biClrUsed) 
     PalEntries = lpbi->bmiHeader.biClrUsed; 

    // Write palette to the file: 
    if (PalEntries) { 
     if (!WriteFile(BmpFile, &lpbi->bmiColors, PalEntries * sizeof(RGBQUAD), &Written, NULL)) 
      return 0; 

     if (Written < PalEntries * sizeof(RGBQUAD)) 
      return 0; 
    } 

    // The current position in the file (at the beginning of the bitmap bits) 
    // will be saved to the BITMAPFILEHEADER: 
    bmfh.bfOffBits = GetFilePointer(BmpFile); 

    // Write bitmap bits to the file: 
    if (!WriteFile(BmpFile, lpvBits, lpbi->bmiHeader.biSizeImage, &Written, NULL)) 
     return 0; 

    if (Written < lpbi->bmiHeader.biSizeImage) 
     return 0; 

    // The current pos. in the file is the final file size and will be saved: 
    bmfh.bfSize = GetFilePointer(BmpFile); 

    // We have all the info for the file header. Save the updated version: 
    SetFilePointer(BmpFile, 0, 0, FILE_BEGIN); 
    if (!WriteFile(BmpFile, &bmfh, sizeof(bmfh), &Written, NULL)) 
     return 0; 

    if (Written < sizeof(bmfh)) 
     return 0; 

    DeleteObject(OffscrBmp); 
    DeleteObject(OldBmp); 
    DeleteDC(OffscrDC); 
    CloseHandle(BmpFile); 

    free(lpbi); 
    free(lpvBits); 
    free(filename); 

    return 1; 
} 

_Bool ScreenCapture(char* filePath, int xStart, int yStart, int width, int height) 
{ 
    // get a DC compat. w/ the screen 
    HDC hDc = CreateCompatibleDC(0); 

    // make a bmp in memory to store the capture in 
    HBITMAP hBmp = CreateCompatibleBitmap(GetDC(0), width, height); 

    // join em up 
    SelectObject(hDc, hBmp); 

    // copy from the screen to my bitmap 
    BitBlt(hDc, 0, 0, width, height, GetDC(0), xStart, yStart, SRCCOPY); 

    // save my bitmap 
    _Bool ret = SaveBMPFile(filePath, hBmp, hDc, width, height); 

    // free the bitmap memory 
    DeleteObject(hBmp); 
    DeleteDC(hDc); 

    return ret; 
} 

main() 
{ 
    ScreenCapture("screenshot.png", 0, 0, 1920, 1080); 

    return 0; 
} 
+0

Code sowieso falsch, und Sie werden kostenlose Ressource bei jedem Fehler – RbMm

+0

Bitte beschreiben Sie in Text nicht, was der Fehler war. –

+0

Bei jedem Aufruf von ScreenCapture traten Speicherlecks auf, es gibt kein Speicherleck mehr. Vielleicht gibt es noch ein paar Fehler, aber ich kann den Code nicht reparieren ... Weiß nicht die WinAPI –

0

HBITMAP hBmp = CreateCompatibleBitmap(GetDC(0), width, height);

GetDC(0) muss mit ReleasedDC gereinigt werden. Wenn Sie auf diese Weise GetDC(0) schreiben, ist es unmöglich zu bereinigen. Sie haben nur 10.000 GDI-Handles für Ihr Programm verfügbar, so dass dieses Ressourcenleck ein ernstes Problem sein kann.

Sie sollten stattdessen tun:

HDC hdc_desktop = GetDC(HWND_DESKTOP); 
HBITMAP hBmp = CreateCompatibleBitmap(hdc_desktop, width, height); 
... 
ReleaseDC(HWND_DESKTOP, hdc_desktop); 

HWND_DESKTOP nur 0 ist, habe ich es für Klarheit. GetDC gibt einen Griff hdc_desktop zurück, der am Ende aufgeräumt werden kann.

DeleteObject(hBmp) ist ein kleiner Fehler, weil hBmp derzeit in hDc ausgewählt ist und nicht gelöscht werden kann. Viele Programmierer machen diesen Fehler, so dass neuere Windows-Versionen (ich denke seit XP) dies erwarten, also behebt Windows diesen Fehler (zumindest in dieser Situation), aber Sie sollten wirklich ein Handle auf der alten Bitmap speichern, das wiederherstellen alte Bitmap und löschen Sie dann die neue Bitmap.

Es gibt einen weiteren kleinen Fehler mit GetDIBits. Die Dokumentation besagt, dass hbitmap beim Aufruf der Funktion nicht in dc ausgewählt werden darf. Aber wieder Windows ist es gewohnt, diesen Fehler zu sehen und zu beheben.

Ihre SaveBMPFile kann vereinfacht werden. Sie speichern keine Paletten-Bitmap, Sie können die Palette vollständig ignorieren.

Unreleated Probleme: Sie Unicode-Funktionen verwenden, so dass Sie UTF-16 Stringliterale: L"screenshot.bmp" statt "screenshot.bmp"

extern _Bool SaveBMPFile(const wchar_t* filePath, 
    HDC memdc, HBITMAP hbitmap, int width, int height) 
{ 
    _Bool success = 0; 
    WORD bpp = 24; //or 32 for 32-bit bitmap 
    DWORD size = ((width * bpp + 31)/32) * 4 * height; 

    BITMAPFILEHEADER filehdr = { 0 }; 
    filehdr.bfType = 19778; 
    filehdr.bfSize = 54 + size; 
    filehdr.bfOffBits = 54; 
    //54 = 14 + 40, sizeof BITMAPFILEHEADER & BITMAPINFOHEADER 

    BITMAPINFOHEADER infohdr = { sizeof(infohdr) }; 
    infohdr.biWidth = width; 
    infohdr.biHeight = height; 
    infohdr.biPlanes = 1; 
    infohdr.biBitCount = bpp; 

    BYTE *bits = malloc(size); 
    GetDIBits(memdc, hbitmap, 0, height, bits, (BITMAPINFO*)&infohdr, DIB_RGB_COLORS); 

    HANDLE hfile = CreateFileW(filePath, 
     GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 
    if(hfile != INVALID_HANDLE_VALUE) 
    { 
     DWORD temp; 
     WriteFile(hfile, &filehdr, 14, &temp, NULL); 
     WriteFile(hfile, &infohdr, 40, &temp, NULL); 
     WriteFile(hfile, bits, size, &temp, NULL); 
     CloseHandle(hfile); 
     success = 1; 
    } 

    free(bits);  
    return success; 
} 

_Bool ScreenCapture(const wchar_t* filePath, int x, int y, int width, int height) 
{ 
    HDC hdc = GetDC(HWND_DESKTOP); 
    HDC memdc = CreateCompatibleDC(hdc); 
    HBITMAP hbitmap = CreateCompatibleBitmap(hdc, width, height); 
    HBITMAP oldbitmap = SelectObject(memdc, hbitmap); 
    BitBlt(memdc, 0, 0, width, height, hdc, x, y, SRCCOPY); 
    SelectObject(memdc, oldbitmap); 

    _Bool ret = SaveBMPFile(filePath, memdc, hbitmap, width, height); 

    DeleteObject(hbitmap); 
    DeleteDC(memdc); 
    ReleaseDC(HWND_DESKTOP, hdc); 

    return ret; 
} 

int main(void) 
{ 
    ScreenCapture(L"screenshot.bmp", 0, 0, 
     GetSystemMetrics(SM_CXFULLSCREEN), GetSystemMetrics(SM_CYFULLSCREEN)); 
    return 0; 
} 

Sie können dies in einer Schleife laufen und beobachten GDI in Taskman Griffe gibt, um sicherzustellen, ist Kein Leck.