2017-08-27 5 views
0

私はすでに1日か2日前に非常によく似た質問をしますが、私の問題は非常に明確ではなかったので、私はここでそれを再公式化しよう:スクリーンショットWinAPIの - 空きメモリー

私は私の画面のスクリーンショットをしたいです私はWinAPIのの言葉を知らないが、私はScreenCapture()への各呼び出しで、非解放されたRAMがあることをViual Studioで見て、それはだ

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

:と私はこのコードを検索し、見つかったことを行うには問題。

が助けに感謝

エラーですどこのいくつかは知っている場合

...、ありがとう:)

+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);

GetDC(0)ReleasedDCでクリーンアップする必要があります。このようにしてGetDC(0)と書くと、クリーンアップすることはできません。プログラムに使用できるGDIハンドルは10,000個しかないため、このリソースリークは深刻な問題になります。

あなたは代わりに実行する必要があります。

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に別のマイナーなバグがあります。ドキュメンテーションは、関数が呼び出されたときに、dcにhbitmapを選択してはならないと述べています。しかし、やはりWindowsはこのエラーを見て修正するのに慣れています。

SaveBMPFileを簡略化することができます。パレットビットマップを保存していないので、パレットを完全に無視することができます。

Unreleated問題:あなたはループでこれを実行し、GDIがあることを確認するTaskmanでハンドルを見ることができる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; 
} 

:あなたは、Unicodeの機能なので、あなたのUTF-16文字列リテラルを使用しています漏れはありません。

関連する問題