J'ai trouvé ce code (visible en dessous) sur Internet, il me sert à faire des captures de l'écran et ça m'est bien pratique. le problème c'est que y'a une fuite de mémoire et à chaque appel de la fonction ScreenCapture, un peu de mémoire en plus est utilisée par le programme. Plus la screenshot effectuée est grosse, plus la fuite est importante. Une screenshot de 1920*1080 pixels engendrera plus de mémoire "perdue" qu'une 100*100 pixels.
Le petit code dans le main du programme ci dessous est juste là pour montrer à quel point l'exécution ralenti (à cause de toutes les fuites de RAM cumulées) au fur et à mesure que les screenshots sont faites.
#include <stdio.h>
#include <Windows.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)
{
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;
}
int main()
{
int i = 0;
while (1)
{
ScreenCapture("Screenshot.png", 0, 0, 100, 100);//Screenshot de 100*100 pixels
printf("%d ", i++);
}
}
Après 2min d'exécution, la RAM est passée de 500ko à 5Mo et on voit clairement que les chiffres qui s'affichent dans la console, ceux qui comptent le nombre de screen qui se sont effectuées, défilent clairement moins vite et ça met aussi le PC en PLS, j'ai le droit à un bel effet Superman en écrivant ce que vous lisez maintenant...
Si vous avez une solution...
- Edité par Tom Clabault 20 septembre 2019 à 19:13:20
Il y a un paquet de return à partir de la ligne 45 qui ne libèrent pas la mémoire.. Il faudrait (avec un debugger ou avec printf() ) vérifer si ta fonction arrive vraiment à sa fin.
Edit 1: en plus, le code de retour de SaveBMPFile() et ScreenCapture() n'est pas testé
Edit 2: et un code publié sur internet n'est pas exempt d'erreur, loin s'en faut
- Edité par edgarjacobs 20 septembre 2019 à 19:47:01
On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent
Je suis allé voir sur un vieux forum où j'avais posé la question y'a un bout de temps et ils ont répondu avec un code qui fonctionne, sans fuites de mémoire que voici:
GetDC(0) doit être libéré sauf qu'utilisé comme ça, directement dans l'appel de la fonction, impossible de récupérer l'HDC qu'il crée pour ensuite le libérer. Il faut plutôt faire:
On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent
On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent