Productivity Tools : Screen Capture


In case of capturing a lot of deskcop screen shots by pressing PrintScreen Key, the operation will get complicated a bit because it is just capture into clipbord. We have to paste it onto other App in order to save as image.
The decelopment purpose of this tool is to solve the issue. We can save screen image by one key operation with this tool.

In order to control this program by keybord action, this program uses RegisterHotKey Function. Since This API requires window handle, it creates a window before main loop.
RegisterHotKey(hwnd, RegList[i].id, MOD_ALT, RegList[i].key);

In a Windows Process Handle Function, it calls core part : Screen Capture Function.

Here's the core part. After retrieve a window handle for Desktop or Active Window, it gets RECT structure rc indicating area to capture.

ScrCap.cpp

#include <windows.h>
#include <Shlobj.h>
#include <string>
#include <time.h>
#include <Shlwapi.h>

int CaptureAsBitmap(int counter, int mode);
void DrawCursor(HDC hdc);
BOOL WriteBitmap(LPTSTR lpszFileName, int nWidth, int nHeight, LPVOID lpBits);
HBITMAP CreateBackbuffer(int nWidth, int nHeight);

struct RegInfo{
	char key;
	int  id;
};

#define CAPTURE_DESKTOP	1
#define CAPTURE_ACTIVE	2

#define ID_KEY_D	100
#define ID_KEY_A	101
#define ID_KEY_F	102
RegInfo info1 = {'D', ID_KEY_D};
RegInfo info2 = {'A', ID_KEY_A};
RegInfo info3 = {'F', ID_KEY_F};
RegInfo RegList[] = {
		info1,
		info2,
		info3
};

int nCounter = 1;
char cWorkPath[1024];

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg) {
		case WM_CREATE:
			for(int i = 0; i < (int)(sizeof(RegList)/sizeof(RegList[0])); i++){
				RegisterHotKey(hwnd, RegList[i].id, MOD_ALT, RegList[i].key);
			}
			return 0;
		case WM_HOTKEY:
			switch(wParam){
			case ID_KEY_A:
				CaptureAsBitmap(nCounter, CAPTURE_ACTIVE);
				nCounter++;
				break;
			case ID_KEY_D:
				CaptureAsBitmap(nCounter, CAPTURE_DESKTOP);
				nCounter++;
				break;
			case ID_KEY_F:
				MessageBox(NULL, "Terminate this app", "ScrCap", MB_OK);
				PostMessage(hwnd, WM_CLOSE, 0, 0);
				break;
			}
			break;
		case WM_DESTROY:
			for(int i = 0; i < (int)(sizeof(RegList)/sizeof(RegList[0])); i++){
				UnregisterHotKey(hwnd, RegList[i].id);
			}
			PostQuitMessage(0);
			return 0;
		default:
			break;
	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	if(!IsUserAnAdmin()){
		MessageBox(NULL, "This app requires admin right", "ScrCap", MB_OK);
		return 1;
	}

	LPSTR windowClsName = (LPSTR)"WindowForScrCap";
	WNDCLASS wc;

	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc = WindowProc;
	wc.cbClsExtra = wc.cbWndExtra=0;
	wc.hInstance = hinst;
	wc.hCursor = wc.hIcon=NULL;
	wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	wc.lpszClassName = windowClsName;
	wc.lpszMenuName = NULL;

	if(!RegisterClass(&wc)){
		DWORD err = GetLastError();
		std::string errMsg = "Failed to register class. Err=" + std::to_string(err);
		MessageBox(NULL, errMsg.c_str(), "ScrCap", MB_OK);
		return err;
	}

	HWND hWnd = CreateWindow(windowClsName,
			"",
			WS_VISIBLE | WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX,
			0,
			0,
			0,
			0,
			NULL,
			NULL,
			hinst,
			NULL);
	if(hWnd==NULL){
		UnregisterClass(windowClsName, hinst);
		DWORD err = GetLastError();
		std::string errMsg = "Failed to create a window. Err=" + std::to_string(err);
		MessageBox(NULL, errMsg.c_str(), "ScrCap", MB_OK);
		return err;
	}
	ShowWindow(hWnd, SW_HIDE);

	time_t now = time(NULL);
	struct tm *pnow = localtime(&now);

	char cDirectory[10] = {0};
	sprintf(cDirectory,"%4d%2d%2d",pnow->tm_year + 1900, pnow->tm_mon + 1, pnow->tm_mday);

	//create directory
	if(!GetCurrentDirectory(sizeof(cWorkPath), cWorkPath)){
		DWORD err = GetLastError();
		std::string errMsg = "Failed to activate this app. Err=" + std::to_string(err);
		MessageBox(NULL, errMsg.c_str(), "ScrCap", MB_OK);
		return err;
	}
	strcat(cWorkPath, "\\");
	strcat(cWorkPath, cDirectory);

	int idx = 1;
	while(1){
		std::string wp = cWorkPath;
		wp += "-";
		wp += std::to_string(idx);
		if(PathFileExists(wp.c_str())){
			idx++;
			continue;
		}
		strcpy(cWorkPath, wp.c_str());

		std::string warnMsg = "Create capture records on ";
		warnMsg += cWorkPath;
		MessageBox(NULL, warnMsg.c_str(), "ScrCap",MB_OK);

		CreateDirectory(cWorkPath, NULL);
		break;
	}

	MSG msg = {0};
	while (1) {
		if (!GetMessage (&msg,NULL,0,0)){
			return msg.wParam ;
		}
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	if(hWnd != NULL){
		DestroyWindow(hWnd);
		UnregisterClass(windowClsName, hinst);
		hWnd = NULL;
	}

	return 0;
}

int CaptureAsBitmap(int counter, int mode)
{
	HDC     hdc;
	HWND    hwndDesk;
	RECT    rc;
	BITMAP  bm;
	HBITMAP hbmp;
	HBITMAP hbmpPrev;

	switch(mode){
	case CAPTURE_DESKTOP:
		hwndDesk = GetDesktopWindow();
		break;
	case CAPTURE_ACTIVE:
		hwndDesk =  GetForegroundWindow();
		break;
	}
	if(!hwndDesk){
		DWORD err = GetLastError();
		return err;
	}
	GetWindowRect(hwndDesk, &rc);

	hdc = CreateCompatibleDC(NULL);
	hbmp = CreateBackbuffer(rc.right, rc.bottom);
	hbmpPrev = (HBITMAP)SelectObject(hdc, hbmp);

	BitBlt(hdc, 0, 0, rc.right, rc.bottom, GetWindowDC(hwndDesk), 0, 0, SRCCOPY);
	DrawCursor(hdc);

	GetObject(hbmp, sizeof(BITMAP), &bm);
	std::string file = (LPSTR)cWorkPath;
	file += "\\capture" + std::to_string(counter) + ".bmp";
	WriteBitmap((char*)file.c_str(), rc.right, rc.bottom, bm.bmBits);

	SelectObject(hdc, hbmpPrev);
	DeleteObject(hbmp);
	DeleteDC(hdc);

	return 0;
}

void DrawCursor(HDC hdc)
{
	int        x, y;
	CURSORINFO cursorInfo;
	ICONINFO   iconInfo;

	cursorInfo.cbSize = sizeof(CURSORINFO);
	GetCursorInfo(&cursorInfo);

	GetIconInfo(cursorInfo.hCursor, &iconInfo);

	x = cursorInfo.ptScreenPos.x - iconInfo.xHotspot;
	y = cursorInfo.ptScreenPos.y - iconInfo.yHotspot;
	DrawIcon(hdc, x, y, cursorInfo.hCursor);
}

BOOL WriteBitmap(LPTSTR lpszFileName, int nWidth, int nHeight, LPVOID lpBits)
{
	HANDLE           hFile;
	DWORD            dwResult;
	DWORD            dwSizeImage;
	BITMAPFILEHEADER bmfHeader;
	BITMAPINFOHEADER bmiHeader;

	hFile = CreateFile(lpszFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hFile == INVALID_HANDLE_VALUE)
		return FALSE;

	dwSizeImage = nHeight * ((3 * nWidth + 3) / 4) * 4;

	ZeroMemory(&bmfHeader, sizeof(BITMAPFILEHEADER));
	bmfHeader.bfType    = *(LPWORD)"BM";
	bmfHeader.bfSize    = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + dwSizeImage;
	bmfHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

	WriteFile(hFile, &bmfHeader, sizeof(BITMAPFILEHEADER), &dwResult, NULL);

	ZeroMemory(&bmiHeader, sizeof(BITMAPINFOHEADER));
	bmiHeader.biSize        = sizeof(BITMAPINFOHEADER);
	bmiHeader.biWidth       = nWidth;
	bmiHeader.biHeight      = nHeight;
	bmiHeader.biPlanes      = 1;
	bmiHeader.biBitCount    = 24;
	bmiHeader.biSizeImage   = dwSizeImage;
	bmiHeader.biCompression = BI_RGB;

	WriteFile(hFile, &bmiHeader, sizeof(BITMAPINFOHEADER), &dwResult, NULL);

	WriteFile(hFile, lpBits, dwSizeImage, &dwResult, NULL);

	CloseHandle(hFile);

	return TRUE;
}

HBITMAP CreateBackbuffer(int nWidth, int nHeight)
{
	LPVOID           lp;
	BITMAPINFO       bmi;
	BITMAPINFOHEADER bmiHeader;

	ZeroMemory(&bmiHeader, sizeof(BITMAPINFOHEADER));
	bmiHeader.biSize      = sizeof(BITMAPINFOHEADER);
	bmiHeader.biWidth     = nWidth;
	bmiHeader.biHeight    = nHeight;
	bmiHeader.biPlanes    = 1;
	bmiHeader.biBitCount  = 24;

	bmi.bmiHeader = bmiHeader;

	return CreateDIBSection(NULL, (LPBITMAPINFO)&bmi, DIB_RGB_COLORS, &lp, NULL, 0);
}
Profile
I have technical job experience in enbedded software development and server side infrastructure/application engineering. I'm interested in programming and computer security.
Objective
To write down my technical knowledge in the place where I can access from anywhere. To share my program source code. To train my writing skill.
Link
  • LinkedIn (preparing)

  • Twitter

  • Facebook (preparing)

  • GitHub

  • StackOverFlow (preparing)

Archives