Win32: Capture an image

AID: 1739
  • Status: Published

2950 points

  • Bypgnatyuk
  • TypeTips/Tricks
  • Posted on2009-10-11 at 06:17:21
Currently I'm working on a Win32 GDI Application - thousands of polygons and polylines are drawn on an internal device context and then copied on the application screen. Tons of geometrical data and few coordinate transformations made the debugging totally useless operation. So, for the debug purpose, I added a function saving the image from the internal DC. I use this function as the well-know TRACE, but in my case, I have a bitmap file as a report instead of the text.

Here is the function:

BOOL Capture(HDC hDC, LPRECT lpRect, LPCWSTR lpszFile)
{
	BITMAPINFO bmi = { 0 };

	LPBYTE pBits = NULL;
	bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	bmi.bmiHeader.biWidth = lpRect->right - lpRect->left;
	bmi.bmiHeader.biHeight = lpRect->bottom - lpRect->top;
	bmi.bmiHeader.biPlanes = 1;
	bmi.bmiHeader.biBitCount = 32;
	bmi.bmiHeader.biCompression = BI_RGB;
	HBITMAP hBitmap = CreateDIBSection(hDC, &bmi, 
		DIB_RGB_COLORS, (LPVOID*)&pBits, NULL, 0);

	HDC hMemDC = CreateCompatibleDC(hDC);
	HGDIOBJ hOld = SelectObject(hMemDC, hBitmap);
	BitBlt(hMemDC, 0, 0, 
		lpRect->right - lpRect->left,
		lpRect->bottom - lpRect->top,
		hDC, lpRect->left, lpRect->top, SRCCOPY);
	SelectObject(hMemDC, hOld);
	DeleteDC(hMemDC);

	DWORD nImageSize = ((((bmi.bmiHeader.biWidth * 
		bmi.bmiHeader.biBitCount) 
		+ 31) & ~31) >> 3) 
		* bmi.bmiHeader.biHeight;

	BITMAPFILEHEADER header = { 0 };
	header.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + 
		(DWORD)sizeof(BITMAPINFOHEADER); 
	header.bfSize = nImageSize + sizeof(BITMAPFILEHEADER) + 
		sizeof(BITMAPINFOHEADER);
	header.bfType = 0x4D42;

	HANDLE hFile = CreateFile(lpszFile, GENERIC_WRITE,
		0, NULL, CREATE_ALWAYS, 
		FILE_ATTRIBUTE_NORMAL, NULL);   

	if (hFile != INVALID_HANDLE_VALUE) 
	{
		DWORD nWritten = 0;;
		WriteFile(hFile, (LPVOID)&header, 
			sizeof(BITMAPFILEHEADER), 
			&nWritten, NULL);
		WriteFile(hFile, (LPVOID)&bmi, 
			sizeof(BITMAPINFOHEADER), 
			&nWritten, NULL);
		WriteFile(hFile, (LPVOID)pBits, nImageSize, 
			&nWritten, NULL);
		CloseHandle(hFile);
	}

	return TRUE;
}
                                    
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:

Select allOpen in new window



If you'd prefer the C style:

BOOL Capture(HDC hDC, LPRECT lpRect, LPCWSTR lpszFile)
{
	HDC hMemDC;
	HBITMAP hBitmap;
	HGDIOBJ hOld;
	BITMAPINFO bmi;
	LPBYTE pBits;
	DWORD nImageSize;
	DWORD nWritten;
	BITMAPFILEHEADER header;
	HANDLE hFile;

	ZeroMemory((LPVOID)&bmi, sizeof(BITMAPINFO));
	bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	bmi.bmiHeader.biWidth = lpRect->right - lpRect->left;
	bmi.bmiHeader.biHeight = lpRect->bottom - lpRect->top;
	bmi.bmiHeader.biPlanes = 1;
	bmi.bmiHeader.biBitCount = 24;
	bmi.bmiHeader.biCompression = BI_RGB;

	pBits = NULL;

	hBitmap = CreateDIBSection(hDC, &bmi, 
		DIB_RGB_COLORS, (LPVOID*)&pBits, NULL, 0);

	hMemDC = CreateCompatibleDC(hDC);
	hOld = SelectObject(hMemDC, hBitmap);
	BitBlt(hMemDC, 0, 0, 
		lpRect->right - lpRect->left,
		lpRect->bottom - lpRect->top,
		hDC, lpRect->left, lpRect->top, SRCCOPY);
	SelectObject(hMemDC, hOld);
	DeleteDC(hMemDC);

	nImageSize = ((((bmi.bmiHeader.biWidth * 
		bmi.bmiHeader.biBitCount) 
		+ 31) & ~31) >> 3) 
		* bmi.bmiHeader.biHeight;

	ZeroMemory((LPVOID)&header, sizeof(BITMAPFILEHEADER));
	header.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + 
		(DWORD)sizeof(BITMAPINFOHEADER); 
	header.bfSize = nImageSize + sizeof(BITMAPFILEHEADER) + 
		sizeof(BITMAPINFOHEADER);
	header.bfType = 0x4D42;

	hFile = CreateFile(lpszFile,	GENERIC_WRITE,
		0, NULL, CREATE_ALWAYS, 
		FILE_ATTRIBUTE_NORMAL, NULL);   

	if (hFile != INVALID_HANDLE_VALUE) 
	{
		WriteFile(hFile, (LPVOID)&header, 
			sizeof(BITMAPFILEHEADER), &nWritten, NULL);
		WriteFile(hFile, (LPVOID)&bmi, 
			sizeof(BITMAPINFOHEADER), &nWritten, NULL);
		WriteFile(hFile, (LPVOID)pBits, 
			nImageSize, &nWritten, NULL);
		CloseHandle(hFile);
	}

	return TRUE;
}
                                    
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:

Select allOpen in new window



Brief explanation. The function receives 3 parameters:

1. The device context handle from which the image should be grabbed.

2. The image rectangle.

3. The file name to save the grabbed image.

Capture function creates new DIB section (32-bits but it works with 24 also) and copy the image from the input device context onto this DIB section. The next task is to save this DIB section. In the Win32 application for me was easier to save it as BMP-file. The file structure is simple:

1. File header that contains general information about the file such as the type and size of the image, and the offset to the bit-array of the graphical data. A

2. Bitmap information header contains the information about the image such as width and height, bits per pixel and a compression.

3. The bit-array or the color table - the image pixels.

Note. If your application supports ATL/MFC, you can use CImage class whenever you need to save an image. This MSDN example saves an image in BMP, JPG and PNG formats.

The following code shows how to use this function:
#include <Windows.h>

BOOL Capture(HDC, LPRECT, LPCWSTR);

int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
	LPWSTR lpCmdLine, int nCmdShow)
{
	HWND hWnd;
	HDC hDC;
	RECT rect;
	rect.left = 0;
	rect.top = 0;
	rect.right = 260;
	rect.bottom = 512;

	hWnd = GetDesktopWindow();
	hDC = GetDC(hWnd);

	Capture(hDC, &rect, L"capture.bmp");
	return 0;
}
                                    
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:

Select allOpen in new window


Compiled this application will grab the top-left corner of the desktop.

Of course, when I almost finished my capturing function I found an article in MSDN with an unexpected name "Capturing an Image": http://msdn.microsoft.com/en-us/library/dd183402(VS.85).aspx. It helped me to understand the bitmap file header, but the source code in this article looks for me more complicated that the one I propose it this article.
Asked On
2009-10-11 at 06:17:21ID1739
Tags

Win32

,

GDI

,

CreateDIBSection

,

Bitmap

Topic

Windows Programming

Views
1612

Comments

Add your Comment

Please Sign up or Log in to comment on this article.

Join Experts Exchange Today

Gain Access to all our Tech Resources

Get personalized answers

Ask unlimited questions

Access Proven Solutions

Search 3.2 million solutions

Read In-Depth How-To Guides

1000+ articles, demos, & tips

Watch Step by Step Tutorials

Learn direct from top tech pros

And Much More!

Your complete tech resource

See Plans and Pricing

30-day free trial. Register in 60 seconds.

Loading Advertisement...

Top Win OS Dev Experts

  1. DanRollins

    17,145

    90 points yesterday

    Profile
    Rank: Genius
  2. jkr

    7,800

    0 points yesterday

    Profile
    Rank: Savant
  3. sarabande

    5,000

    0 points yesterday

    Profile
    Rank: Sage
  4. sinisav

    4,000

    0 points yesterday

    Profile
    Rank: Master
  5. Zoppo

    3,500

    0 points yesterday

    Profile
    Rank: Genius
  6. burrcm

    2,800

    0 points yesterday

    Profile
    Rank: Genius
  7. DTHConsulting

    2,000

    0 points yesterday

    Profile
    Rank: Guru
  8. Geert_Gruwez

    2,000

    0 points yesterday

    Profile
    Rank: Genius
  9. ImaCircularSaw

    2,000

    0 points yesterday

    Profile
    Rank: Guru
  10. Geisrud

    2,000

    0 points yesterday

    Profile
    Rank: Guru
  11. Run5k

    2,000

    0 points yesterday

    Profile
    Rank: Genius
  12. ShareefHuddle

    2,000

    0 points yesterday

    Profile
    Rank: Guru
  13. tampnic

    2,000

    0 points yesterday

    Profile
    Rank: Master
  14. pgnatyuk

    1,960

    20 points yesterday

    Profile
    Rank: Genius
  15. Thommy

    1,800

    0 points yesterday

    Profile
    Rank: Wizard
  16. mrwad99

    1,800

    0 points yesterday

    Profile
    Rank: Wizard
  17. momi_sabag

    1,800

    1,800 points yesterday

    Profile
    Rank: Genius
  18. TommySzalapski

    1,600

    0 points yesterday

    Profile
    Rank: Genius
  19. ve3ofa

    1,300

    1,300 points yesterday

    Profile
    Rank: Genius
  20. cpkilekofp

    1,300

    1,300 points yesterday

    Profile
    Rank: Sage
  21. Darr247

    1,000

    0 points yesterday

    Profile
    Rank: Genius
  22. thinkpads_user

    1,000

    0 points yesterday

    Profile
    Rank: Genius
  23. matrixnz

    999

    0 points yesterday

    Profile
    Rank: Genius
  24. joshbula

    900

    0 points yesterday

    Profile
    Rank: Master
  25. adminnrg

    750

    0 points yesterday

    Profile

Hall Of Fame