2017-08-27 17 views
0

我已經問了一個非常類似的問題一兩天以前,但我的問題是不是很清楚,所以我在這裏試着重新制定它:截圖WinAPI的 - 可用內存

我想要做我的屏幕截圖要做到這一點我搜索,發現這個代碼:

#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; 
} 

我不知道WinAPI的一個字,但我在Viual工作室看到,在每次調用抓屏(),有非釋放的RAM,這就是一個問題。

如果你們當中有些人知道哪來的錯誤...

讚賞幫助,謝謝:)

+0

您不關閉'BmpFile'。在從'SaveBMPFile'返回之前調用'CloseHandle(BmpFile);' –

+0

是的,所有*你創建的對象需要被釋放/銷燬。每個創建功能都有相應的功能。你會發現它在創建函數的MSDN文檔中列出。 –

+0

謝謝!這樣做,沒有更多的問題:) –

回答

-2

下面是最後的代碼,沒有問題的工作,沒有內存泄漏的工作:

#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

無論如何你的代碼會被誤認爲是錯誤的,並且你將在任何錯誤上失敗免費資源 – RbMm

+0

請在文本中描述錯誤是什麼。 –

+0

每次調用ScreenCapture時都有內存泄漏,不再有內存泄漏。也許還有一些錯誤,但我無法修復代碼...不知道WinAPI –

0

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

必須使用ReleasedDC清理。當你用這種方式寫GetDC(0)時,就不可能清理。您的程序只有10,000個GDI句柄可用,所以此資源泄漏可能是一個嚴重問題。

你應該這樣做,而不是:

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

HWND_DESKTOP只是0,我用它的清晰度。 GetDC返回一個句柄hdc_desktop,最後可以清理它。

DeleteObject(hBmp)是一個小錯誤,因爲hBmp目前在hDc中被選中,無法刪除。很多程序員犯這個錯誤,所以更新的Windows版本(我認爲自XP以來)已經期待這個,所以Windows修復了這個錯誤(至少在這種情況下),但是您應該真正保存舊位圖的句柄,恢復舊的位圖,然後刪除新的位圖。

還有一個小錯誤GetDIBits。文檔說,調用該函數時,hbitmap不能選擇爲直流。但是Windows又習慣看到這個錯誤並修復它。可以簡化你的SaveBMPFile。您不保存調色板位圖,您可以完全忽略調色板。

Unreleated問題:您正在使用Unicode的功能,讓您UTF-16字符串文字:中L"screenshot.bmp"代替"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; 
} 

您可以在一個循環中運行這個,看GDI處理在任務管理器,以確保有沒有泄漏。