Click here to Skip to main content
15,885,767 members
Articles / Desktop Programming / MFC

Various methods for capturing the screen

Rate me:
Please Sign up or sign in to vote.
4.90/5 (113 votes)
19 Sep 2006LGPL310 min read 1.8M   58.8K   364  
Explains techniques for capturing the screen programmatically.
// ScreenCap.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include "ScreenCap.h"
#include <stdio.h>
#include "avifile.h"
#include <shellapi.h>
#include <commdlg.h>

#define MAX_LOADSTRING 100
#define	WM_NOTIFYICON_MESSAGE	WM_USER+1000

#define	ErrorMessage(x)		MessageBox(NULL,x,"Error",MB_OK|MB_ICONERROR)

#define BITSPERPIXEL		32

HINSTANCE hInst;								// current instance
TCHAR szTitle[MAX_LOADSTRING];					// The title bar text
TCHAR szWindowClass[MAX_LOADSTRING];			// the main window class name

void	SaveBitmap(char *szFilename,HBITMAP hBitmap);

UINT_PTR	nTimerId=0;
LPVOID		pBits=NULL;
CAviFile	*pAviFile=NULL;
HMENU		ghMenu=NULL;
bool		gbCapturing=false;

// Forward declarations of functions included in this code module:
ATOM				MyRegisterClass(HINSTANCE hInstance);
BOOL				InitInstance(HINSTANCE, int);
LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK	About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
 	// TODO: Place code here.
	MSG msg;
	HACCEL hAccelTable;

	// Initialize global strings
	LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
	LoadString(hInstance, IDC_SCREENCAPTURE, szWindowClass, MAX_LOADSTRING);
	MyRegisterClass(hInstance);

	// Perform application initialization:
	if (!InitInstance (hInstance, nCmdShow)) 
	{
		return FALSE;
	}

	hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_SCREENCAPTURE);

	// Main message loop:
	while (GetMessage(&msg, NULL, 0, 0)) 
	{
		if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) 
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

	return (int) msg.wParam;
}



//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
//  COMMENTS:
//
//    This function and its usage are only necessary if you want this code
//    to be compatible with Win32 systems prior to the 'RegisterClassEx'
//    function that was added to Windows 95. It is important to call this function
//    so that the application will get 'well formed' small icons associated
//    with it.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX); 

	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= (WNDPROC)WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= LoadIcon(hInstance, (LPCTSTR)IDI_SCREENCAPTURE);
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= NULL;//(HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= (LPCTSTR)IDC_SCREENCAPTURE;
	wcex.lpszClassName	= szWindowClass;
	wcex.hIconSm		= LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);

	return RegisterClassEx(&wcex);
}

//
//   FUNCTION: InitInstance(HANDLE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hInst = hInstance; // Store instance handle in our global variable

   hWnd = CreateWindow(szWindowClass, szTitle, WS_POPUPWINDOW|WS_CAPTION|WS_VISIBLE,
      480, 400, 320, 240, NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
      return FALSE;
   }

  // ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

//
//  FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
//
//  PURPOSE:  Processes messages for the main window.
//
//  WM_COMMAND	- process the application menu
//  WM_PAINT	- Paint the main window
//  WM_DESTROY	- post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static HBITMAP	hDesktopCompatibleBitmap=NULL;
	static HDC		hDesktopCompatibleDC=NULL;
	static HDC		hDesktopDC=NULL;
	static HWND		hDesktopWnd=NULL;
	static RECT		clientRect = {0,0,0,0};
	static NOTIFYICONDATA	nid;

	static bool		bMinimized=false;

	int wmId, wmEvent;
	PAINTSTRUCT ps;
	HDC hdc;

	switch (message) 
	{
	case WM_SYSCOMMAND:
		{
			if(wParam==SC_CLOSE)
			{
				SendMessage(hWnd,WM_SYSCOMMAND,SC_MINIMIZE,0);
				return 0;
			}
			if(wParam==SC_MINIMIZE)
			{
				bMinimized=true;
				LONG_PTR ret=DefWindowProc(hWnd,message,wParam,lParam);
				SendMessage(hWnd,WM_COMMAND,MAKEWPARAM(ID_FILE_HIDEWINDOW,0),0);
				return ret;
			}
			return DefWindowProc(hWnd, message, wParam, lParam);
		}
	case WM_COMMAND:
		wmId    = LOWORD(wParam); 
		wmEvent = HIWORD(wParam); 
		// Parse the menu selections:
		switch (wmId)
		{
		case IDM_ABOUT:
			DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
			break;
		case ID_FILE_STARTCAPTURINGTOAVI:
		case ID_CAPTURE_START:
			{
				if(pAviFile==NULL)
				{
					OPENFILENAME	ofn;
					char	szFileName[512];
					strcpy(szFileName,"Output.avi");
					ZeroMemory(&ofn,sizeof(ofn));
					ofn.lStructSize=sizeof(OPENFILENAME);
					ofn.Flags=OFN_HIDEREADONLY|OFN_PATHMUSTEXIST;
					ofn.lpstrFilter="Avi Files (*.avi)\0*.avi\0";
					ofn.lpstrDefExt="avi";				
					ofn.lpstrFile=szFileName;
					ofn.nMaxFile=512;
					ofn.hwndOwner = hWnd;
					if(!GetSaveFileName(&ofn))	break;

					pAviFile = new CAviFile(szFileName);
				}
			
				nid.hIcon=LoadIcon(hInst,MAKEINTRESOURCE(IDI_ICON_PAUSE));
				strcpy(nid.szTip,"Capturing the Screen - Double Click to Pause");

				if(!Shell_NotifyIcon(NIM_MODIFY,&nid))				//Modify the Icon State
					MessageBox(NULL,"Unable to Set Notification Icon","Error",MB_ICONINFORMATION|MB_OK);

				SendMessage(hWnd,WM_SYSCOMMAND,SC_MINIMIZE,0);

                gbCapturing = true;

				break;
			}
		case ID_FILE_PAUSECAPTURINGTOAVI:
		case ID_CAPTURE_STOP:
			{				
				nid.hIcon=LoadIcon(hInst,MAKEINTRESOURCE(IDI_ICON_START));
				strcpy(nid.szTip,"Screen Capture Paused - Double Click to Resume");

				if(!Shell_NotifyIcon(NIM_MODIFY,&nid))				//Modify the Icon State
					MessageBox(NULL,"Unable to Set Notification Icon","Error",MB_ICONINFORMATION|MB_OK);

				gbCapturing = false;

				break;
			}
		case ID_FILE_SHOWWINDOW:
			{
				if(bMinimized)	ShowWindow(hWnd,SW_RESTORE);
				else			ShowWindow(hWnd,SW_SHOW);
				SetForegroundWindow(hWnd);
				bMinimized=false;
				break;
			}
		case ID_FILE_HIDEWINDOW:
			{
				ShowWindow(hWnd,SW_HIDE);
				break;
			}
		case ID_FILE_CAPTURESCREENSHOT:
		case ID_EDIT_SAVEASBMP:
			{
				OPENFILENAME	ofn;
				char	szFileName[512]; 

				strcpy(szFileName,"ScreenShot.bmp");

                ZeroMemory(&ofn,sizeof(ofn));
				ofn.lStructSize=sizeof(OPENFILENAME);
				ofn.Flags=OFN_HIDEREADONLY|OFN_PATHMUSTEXIST;
				ofn.lpstrFilter="Bitmap Files (*.bmp)\0*.bmp\0";
				ofn.lpstrDefExt="bmp";
				ofn.lpstrFile=szFileName;
				ofn.nMaxFile=512;
				ofn.hwndOwner = hWnd;
				if(!GetSaveFileName(&ofn))	break;

				SetCursor(LoadCursor(NULL,IDC_WAIT));	
				int		nWidth=GetSystemMetrics(SM_CXSCREEN);
				int		nHeight=GetSystemMetrics(SM_CYSCREEN);
				HDC		hBmpFileDC=CreateCompatibleDC(hDesktopDC);
				HBITMAP	hBmpFileBitmap=CreateCompatibleBitmap(hDesktopDC,nWidth,nHeight);
				HBITMAP hOldBitmap = (HBITMAP) SelectObject(hBmpFileDC,hBmpFileBitmap);
				BitBlt(hBmpFileDC,0,0,nWidth,nHeight,hDesktopDC,0,0,SRCCOPY|CAPTUREBLT);
				SelectObject(hBmpFileDC,hOldBitmap);

				SaveBitmap(ofn.lpstrFile,hBmpFileBitmap);

				DeleteDC(hBmpFileDC);
				DeleteObject(hBmpFileBitmap);
				SetCursor(LoadCursor(NULL,IDC_ARROW));
				break;
			}
		case IDM_EXIT:
			DestroyWindow(hWnd);
			break;
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
		}
		break;
	case WM_INITMENU:
		{
			HMENU	hMenu = (HMENU)wParam;
			EnableMenuItem(hMenu,ID_FILE_SHOWWINDOW,IsWindowVisible(hWnd)?MF_GRAYED:MF_ENABLED|MF_BYCOMMAND);
			EnableMenuItem(hMenu,ID_FILE_HIDEWINDOW,IsWindowVisible(hWnd)?MF_ENABLED:MF_GRAYED|MF_BYCOMMAND);
			EnableMenuItem(hMenu,ID_FILE_CAPTURESCREENSHOT,gbCapturing?MF_GRAYED|MF_BYCOMMAND:MF_ENABLED);
			EnableMenuItem(hMenu,ID_FILE_STARTCAPTURINGTOAVI,gbCapturing?MF_GRAYED|MF_BYCOMMAND:MF_ENABLED);
			EnableMenuItem(hMenu,ID_FILE_PAUSECAPTURINGTOAVI,gbCapturing?MF_ENABLED:MF_GRAYED|MF_BYCOMMAND);
			EnableMenuItem(hMenu,ID_CAPTURE_START,gbCapturing?MF_GRAYED|MF_BYCOMMAND:MF_ENABLED);
			EnableMenuItem(hMenu,ID_CAPTURE_STOP,gbCapturing?MF_ENABLED:MF_GRAYED|MF_BYCOMMAND);			
			break;
		}
	case WM_INITMENUPOPUP:
		{
			EnableMenuItem(ghMenu,ID_FILE_SHOWWINDOW,IsWindowVisible(hWnd)?MF_GRAYED:MF_ENABLED|MF_BYCOMMAND);
			EnableMenuItem(ghMenu,ID_FILE_HIDEWINDOW,IsWindowVisible(hWnd)?MF_ENABLED:MF_GRAYED|MF_BYCOMMAND);
			EnableMenuItem(ghMenu,ID_FILE_CAPTURESCREENSHOT,gbCapturing?MF_GRAYED|MF_BYCOMMAND:MF_ENABLED);
			EnableMenuItem(ghMenu,ID_FILE_STARTCAPTURINGTOAVI,gbCapturing?MF_GRAYED|MF_BYCOMMAND:MF_ENABLED);
			EnableMenuItem(ghMenu,ID_FILE_PAUSECAPTURINGTOAVI,gbCapturing?MF_ENABLED:MF_GRAYED|MF_BYCOMMAND);
			EnableMenuItem(ghMenu,ID_CAPTURE_START,gbCapturing?MF_GRAYED|MF_BYCOMMAND:MF_ENABLED);
			EnableMenuItem(ghMenu,ID_CAPTURE_STOP,gbCapturing?MF_ENABLED:MF_GRAYED|MF_BYCOMMAND);			
			break;
		}
	case WM_CREATE:
		{
			BITMAPINFO	bmpInfo;
			ZeroMemory(&bmpInfo,sizeof(BITMAPINFO));
			bmpInfo.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
			bmpInfo.bmiHeader.biBitCount=BITSPERPIXEL;
			bmpInfo.bmiHeader.biCompression = BI_RGB;
			bmpInfo.bmiHeader.biWidth=GetSystemMetrics(SM_CXSCREEN);
			bmpInfo.bmiHeader.biHeight=GetSystemMetrics(SM_CYSCREEN);
			bmpInfo.bmiHeader.biPlanes=1;
			bmpInfo.bmiHeader.biSizeImage=abs(bmpInfo.bmiHeader.biHeight)*bmpInfo.bmiHeader.biWidth*bmpInfo.bmiHeader.biBitCount/8;

			hDesktopWnd=GetDesktopWindow();
			hDesktopDC=GetDC(hDesktopWnd);
			hDesktopCompatibleDC=CreateCompatibleDC(hDesktopDC);
			hDesktopCompatibleBitmap=CreateDIBSection(hDesktopDC,&bmpInfo,DIB_RGB_COLORS,&pBits,NULL,0);
			if(hDesktopCompatibleDC==NULL || hDesktopCompatibleBitmap == NULL)
			{
				ErrorMessage("Unable to Create Desktop Compatible DC/Bitmap");	return -1;
			}
			SelectObject(hDesktopCompatibleDC,hDesktopCompatibleBitmap);

			ZeroMemory(&nid,sizeof(nid));
			nid.cbSize=sizeof(nid);
			nid.uID=1000;
			nid.uFlags=NIF_ICON|NIF_MESSAGE|NIF_TIP;
			nid.hIcon=LoadIcon(hInst,MAKEINTRESOURCE(IDI_SCREENCAPTURE));
			nid.hWnd=hWnd;
			strcpy(nid.szTip,"Screen Capture Application - Double Click to Start Capturing");
			nid.uCallbackMessage=WM_NOTIFYICON_MESSAGE;
			if(!Shell_NotifyIcon(NIM_ADD,&nid))	MessageBox(NULL,"Unable to Set Notification Icon","Error",MB_ICONINFORMATION|MB_OK);

			if((ghMenu=LoadMenu(hInst,MAKEINTRESOURCE(IDC_SCREENCAPTURE)))==NULL)
			{
				ErrorMessage("Unable to Load Menu");	return -1;
			}

			nTimerId=SetTimer(hWnd,12345,500,NULL);	//Timer set to 500 ms.
			break;
		}
	case WM_CHAR:
		{
			if(wParam==VK_ESCAPE)
				DestroyWindow(hWnd);
			break;
		}
	case WM_SIZE:
		{
			clientRect.right = LOWORD(lParam);
			clientRect.bottom = HIWORD(lParam);
			break;
		}
	case WM_PAINT:
		{
			hdc = BeginPaint(hWnd, &ps);
			StretchBlt(hdc,0,0,clientRect.right,clientRect.bottom,hDesktopCompatibleDC,0,0,GetSystemMetrics(SM_CXSCREEN),GetSystemMetrics(SM_CYSCREEN),SRCCOPY);
			EndPaint(hWnd, &ps);
			break;
		}
	case WM_TIMER:
		{
			if(gbCapturing == false)	break;

			int	nWidth=GetSystemMetrics(SM_CXSCREEN);
			int	nHeight=GetSystemMetrics(SM_CYSCREEN);

			BitBlt(hDesktopCompatibleDC,0,0,nWidth,nHeight,hDesktopDC,0,0,SRCCOPY|CAPTUREBLT);

			InvalidateRect(hWnd,NULL,false);

			pAviFile->AppendNewFrame(nWidth,nHeight,pBits);			//Append this Captured ScreenShot to the Movie

			break;
		}
	case WM_NOTIFYICON_MESSAGE:
		{
			switch(lParam)
			{
				case WM_MOUSEMOVE:break;
				case WM_LBUTTONDBLCLK:
					{						
						SendMessage(hWnd,WM_COMMAND,MAKEWPARAM(gbCapturing?ID_FILE_PAUSECAPTURINGTOAVI:ID_FILE_STARTCAPTURINGTOAVI,0),0);
						break;
					}
				case WM_RBUTTONDOWN:
					{
						POINT pt;GetCursorPos(&pt);
						SetForegroundWindow(hWnd);
						TrackPopupMenu(GetSubMenu(ghMenu,0),TPM_LEFTALIGN|TPM_BOTTOMALIGN,pt.x,pt.y,0,hWnd,NULL);
						break;
					}
			}
			break;
		}
	case WM_DESTROY:
		{
			if(nTimerId)
			{
				KillTimer(hWnd,nTimerId);
				nTimerId=0;
			}
			if(pAviFile)
			{
				delete pAviFile;
				pAviFile = NULL;
			}
			if(ghMenu)	
				DestroyMenu(ghMenu);
			if(hDesktopCompatibleDC)
				DeleteDC(hDesktopCompatibleDC);
			if(hDesktopCompatibleBitmap)
				DeleteObject(hDesktopCompatibleBitmap);
			ReleaseDC(hDesktopWnd,hDesktopDC);
			Shell_NotifyIcon(NIM_DELETE,&nid);
			PostQuitMessage(0);
			break;
		}
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

// Message handler for about box.
LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case WM_INITDIALOG:
		return TRUE;

	case WM_COMMAND:
		if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) 
		{
			EndDialog(hDlg, LOWORD(wParam));
			return TRUE;
		}
		break;
	}
	return FALSE;
}

void	SaveBitmap(char *szFilename,HBITMAP hBitmap)
{
	HDC					hdc=NULL;
	FILE*				fp=NULL;
	LPVOID				pBuf=NULL;
	BITMAPINFO			bmpInfo;
	BITMAPFILEHEADER	bmpFileHeader;

	do{

		hdc=GetDC(NULL);
		ZeroMemory(&bmpInfo,sizeof(BITMAPINFO));
		bmpInfo.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
		GetDIBits(hdc,hBitmap,0,0,NULL,&bmpInfo,DIB_RGB_COLORS);

		if(bmpInfo.bmiHeader.biSizeImage<=0)
			bmpInfo.bmiHeader.biSizeImage=bmpInfo.bmiHeader.biWidth*abs(bmpInfo.bmiHeader.biHeight)*(bmpInfo.bmiHeader.biBitCount+7)/8;

		if((pBuf=malloc(bmpInfo.bmiHeader.biSizeImage))==NULL)
		{
			MessageBox(NULL,_T("Unable to Allocate Bitmap Memory"),_T("Error"),MB_OK|MB_ICONERROR);
			break;
		}
		
		bmpInfo.bmiHeader.biCompression=BI_RGB;
		GetDIBits(hdc,hBitmap,0,bmpInfo.bmiHeader.biHeight,pBuf,&bmpInfo,DIB_RGB_COLORS);	

		if((fp=fopen(szFilename,"wb"))==NULL)
		{
			MessageBox(NULL,_T("Unable to Create Bitmap File"),_T("Error"),MB_OK|MB_ICONERROR);
			break;
		}

		bmpFileHeader.bfReserved1=0;
		bmpFileHeader.bfReserved2=0;
		bmpFileHeader.bfSize=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+bmpInfo.bmiHeader.biSizeImage;
		bmpFileHeader.bfType='MB';
		bmpFileHeader.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);

		fwrite(&bmpFileHeader,sizeof(BITMAPFILEHEADER),1,fp);
		fwrite(&bmpInfo.bmiHeader,sizeof(BITMAPINFOHEADER),1,fp);
		fwrite(pBuf,bmpInfo.bmiHeader.biSizeImage,1,fp);

	}while(false);

		if(hdc)
			ReleaseDC(NULL,hdc);

		if(pBuf)
			free(pBuf);

		if(fp)
			fclose(fp);
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)


Written By
India India
Creator of CarMusTy - Carnatic Music Typesetting Application, and the CFugue C++ Runtime Environment for MusicNote Programming.

Comments and Discussions