#include <iostream>
#include <memory>
#include <queue>
#include <sstream>
#include <string>
#include <time.h>
#include <vector>
#include <windows.h>
// 无符号
using u8 = unsigned char;
using u16 = unsigned short;
using u32 = unsigned long;
using u64 = unsigned long long;
// 有符号
using s8 = char;
using s16 = short;
using s32 = long;
using s64 = long long;
// 32位有符号数最大值
#define MAX_S32 0x7fffffff
// 32位无符号数最大值
#define MAX_U32 0xffffffff
typedef WINGDIAPI HDC (WINAPI* F_CreateDCA)(LPCSTR, LPCSTR, LPCSTR, const DEVMODEA*);
typedef WINGDIAPI int (WINAPI* F_GetDeviceCaps)(HDC, int);
typedef WINGDIAPI HDC (WINAPI* F_CreateCompatibleDC)(HDC);
typedef WINGDIAPI HBITMAP (WINAPI* F_CreateCompatibleBitmap)(HDC, int, int);
typedef WINGDIAPI HGDIOBJ (WINAPI* F_SelectObject)(HDC, HGDIOBJ);
typedef WINGDIAPI int (WINAPI* F_GetObjectA)(HGDIOBJ, int, PVOID);
typedef WINGDIAPI BOOL (WINAPI* F_BitBlt)(HDC, int, int, int, int, HDC, int, int, DWORD);
typedef WINGDIAPI int (WINAPI* F_GetDIBits)(HDC, HBITMAP, UINT, UINT, PVOID, LPBITMAPINFO, UINT);
typedef WINGDIAPI int (WINAPI* F_DeleteObject)(HGDIOBJ);
typedef WINGDIAPI int (WINAPI* F_DeleteDC)(HDC);
#define DEFINE_FUNC(name) F_ ## name m_ ## name = NULL
#define GET_FUNC_PROC(hDll, name) m_ ## name = (F_ ## name)::GetProcAddress(hDll, #name)
#define EXEC_FUNC(name, args...) (*m_ ## name)(args)
#pragma pack(push,1)
struct DDBitMapInfoHeader {
u32 biSize;
u32 biWidth;
u32 biHeight;
u16 biPlanes;
u16 biBitCount;
u32 biCompression;
u32 biSizeImage;
u32 biXPelsPerMeter;
u32 biYPelsPerMeter;
u32 biClrUsed;
u32 biClrImportant;
};
// 文件头,详细信息参考BITMAPFILEHEADER
struct DDBitMapFileHeader {
u16 bfType = ((u16)('M' << 8) | 'B'); // ??
u32 bfSize = 0; // 文件总大小
u16 bfReserved1; // ??
u16 bfReserved2; // ??
u32 bfOffBits; // buff位置 = sizeof(DDBitMapFileHeader) + sizeof(DDBitMapInfoHeader)
};
struct DDBitMap {
DDBitMapFileHeader m_fileHeader;
DDBitMapInfoHeader m_infoHeader;
std::vector<u8> m_buff;
};
#pragma pack(pop)
class ScreenCapture {
public:
~ScreenCapture()
{
if (m_hDll != NULL) {
::FreeLibrary(m_hDll);
}
}
void InitFunc()
{
m_hDll = ::LoadLibraryA("gdi32.dll");
if (m_hDll != NULL) {
GET_FUNC_PROC(m_hDll, CreateDCA);
GET_FUNC_PROC(m_hDll, GetDeviceCaps);
GET_FUNC_PROC(m_hDll, CreateCompatibleDC);
GET_FUNC_PROC(m_hDll, CreateCompatibleBitmap);
GET_FUNC_PROC(m_hDll, SelectObject);
GET_FUNC_PROC(m_hDll, GetObjectA);
GET_FUNC_PROC(m_hDll, BitBlt);
GET_FUNC_PROC(m_hDll, GetDIBits);
GET_FUNC_PROC(m_hDll, GetDIBits);
GET_FUNC_PROC(m_hDll, DeleteObject);
GET_FUNC_PROC(m_hDll, DeleteDC);
}
}
public:
void CaptureDC(HDC hdc, uint32_t l, uint32_t t, uint32_t r, uint32_t b, DDBitMap& ddBitMap)
{
HDC hmdc = NULL;
HBITMAP hBmpMap = NULL;
do {
uint32_t w = r - l;
uint32_t h = b - t;
// 创建mdc,创建画板
hmdc = EXEC_FUNC(CreateCompatibleDC, hdc);
if (hmdc == NULL) {
break;
}
// 创建bitmap,创建画布
hBmpMap = EXEC_FUNC(CreateCompatibleBitmap, hdc, w, h);
if (hBmpMap == NULL) {
break;
}
// 将画布贴到画板上
EXEC_FUNC(SelectObject, hmdc, hBmpMap);
// 获得这个画布的基本信息,bitmap信息头
BITMAP bm;
EXEC_FUNC(GetObjectA, hBmpMap, sizeof(bm), &bm);
ddBitMap.m_infoHeader.biSize = sizeof(DDBitMapInfoHeader);
ddBitMap.m_infoHeader.biWidth = bm.bmWidth;
ddBitMap.m_infoHeader.biHeight = bm.bmHeight;
ddBitMap.m_infoHeader.biPlanes = bm.bmPlanes;
ddBitMap.m_infoHeader.biBitCount = bm.bmBitsPixel;
ddBitMap.m_infoHeader.biCompression = BI_RGB;
ddBitMap.m_infoHeader.biSizeImage = bm.bmHeight * bm.bmWidthBytes;
ddBitMap.m_buff.resize(ddBitMap.m_infoHeader.biSizeImage);
// 将hdc画到mdc上
EXEC_FUNC(BitBlt, hmdc, 0, 0, w, h, hdc, l, t, SRCCOPY);
// 将画布上的信息保存
EXEC_FUNC(GetDIBits, hmdc, hBmpMap, 0L, (u32)h, &(ddBitMap.m_buff[0]), (LPBITMAPINFO)&(ddBitMap.m_infoHeader), (u32)DIB_RGB_COLORS);
// bitmap文件头
ddBitMap.m_fileHeader.bfType = ((WORD)('M' << 8) | 'B');
ddBitMap.m_fileHeader.bfOffBits = sizeof(DDBitMapFileHeader) + sizeof(DDBitMapInfoHeader);
ddBitMap.m_fileHeader.bfSize = ddBitMap.m_fileHeader.bfOffBits + ddBitMap.m_infoHeader.biSizeImage;
} while(0);
if (hmdc != NULL) {
EXEC_FUNC(DeleteObject, hmdc);
}
if (hBmpMap != NULL) {
EXEC_FUNC(DeleteObject, hBmpMap);
}
}
void CaptureWnd(HWND hWnd, uint32_t l, uint32_t t, uint32_t r, uint32_t b, DDBitMap& ddBitMap)
{
HDC hdc = NULL;
do {
// 句柄dc
if (hWnd != NULL) {
hdc = ::GetDC(hWnd);
} else {
hdc = EXEC_FUNC(CreateDCA, "DISPLAY", NULL, NULL, NULL);
}
if (hdc == NULL) {
break;
}
// 获得长宽
int32_t w = EXEC_FUNC(GetDeviceCaps, hdc, HORZRES);
int32_t h = EXEC_FUNC(GetDeviceCaps, hdc, VERTRES);
if (r > w) {
r = w;
}
if (b > h) {
b = h;
}
if (l >= r) {
l = 0;
}
if (t >= b) {
t = 0;
}
CaptureDC(hdc, l, t, r, b, ddBitMap);
} while(0);
if (hWnd != NULL) {
::ReleaseDC(hWnd, hdc);
} else {
EXEC_FUNC(DeleteDC, hdc);
}
}
void CaptureWnd(HWND hWnd, DDBitMap& ddBitMap)
{
CaptureWnd(hWnd, MAX_U32, MAX_U32, MAX_U32, MAX_U32, ddBitMap);
}
void SaveDDBitMapTo(const DDBitMap& ddBitMap, const std::string& fileName)
{
u32 dwWrite;
HANDLE hFile = ::CreateFileA(fileName.c_str(), GENERIC_WRITE, 0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if (hFile != INVALID_HANDLE_VALUE) {
::WriteFile(hFile, &ddBitMap.m_fileHeader, sizeof(DDBitMapFileHeader), &dwWrite, NULL);
::WriteFile(hFile, &ddBitMap.m_infoHeader, sizeof(DDBitMapInfoHeader), &dwWrite, NULL);
::WriteFile(hFile, &(ddBitMap.m_buff[0]), ddBitMap.m_infoHeader.biSizeImage, &dwWrite, NULL);
::CloseHandle(hFile);
}
}
private:
DEFINE_FUNC(CreateDCA);
DEFINE_FUNC(GetDeviceCaps);
DEFINE_FUNC(CreateCompatibleDC);
DEFINE_FUNC(CreateCompatibleBitmap);
DEFINE_FUNC(SelectObject);
DEFINE_FUNC(GetObjectA);
DEFINE_FUNC(BitBlt);
DEFINE_FUNC(GetDIBits);
DEFINE_FUNC(DeleteObject);
DEFINE_FUNC(DeleteDC);
HMODULE m_hDll = NULL;
};
LRESULT CALLBACK LowLevelKeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK LowLevelMouseHookProc(int nCode, WPARAM wParam, LPARAM lParam);
bool CALLBACK ConsoleProc(DWORD CtrlType);
class Hook {
public:
~Hook()
{
for (int i = 0; i < m_hooks.size(); ++i) {
::UnhookWindowsHookEx(m_hooks[i]);
}
}
void SetHook()
{
ResetPoint();
m_cap.InitFunc();
HHOOK keyBoardHook = ::SetWindowsHookEx(WH_KEYBOARD_LL, (HOOKPROC)&LowLevelKeyboardHookProc, 0, 0);
HHOOK mouseHook = ::SetWindowsHookEx(WH_MOUSE_LL, (HOOKPROC)&LowLevelMouseHookProc, 0, 0);
m_hooks.push_back(keyBoardHook);
m_hooks.push_back(mouseHook);
}
void OnKeyBoard(int nCode, WPARAM wParam, LPARAM lParam)
{
bool isCtrlDown = ::GetKeyState(VK_CONTROL) & 0Xf0;
PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT)lParam;
if (wParam == WM_KEYDOWN) {
// ctrl + ~
if (isCtrlDown && p->vkCode == 192) {
CaptureScreen();
}
} else if (wParam == WM_KEYUP) {
} else if (wParam == WM_SYSKEYDOWN) {
// sys key dn
} else if (wParam == WM_SYSKEYUP) {
// sys key up
}
}
void OnMouse(int nCode, WPARAM wParam, LPARAM lParam)
{
POINT mousePoint;
::GetCursorPos(&mousePoint);
if (wParam == WM_RBUTTONDOWN) {
if (::clock() - m_preMouseRightUpTime < 200) {
m_mouseDoubleClickBeg = true;
m_point[0] = mousePoint.x;
m_point[1] = mousePoint.y;
std::cout << "select left_top point [" << m_point[0] << ", " << m_point[1] << "]" << std::endl;
}
} else if (wParam == WM_RBUTTONUP) {
if (m_mouseDoubleClickBeg) {
m_mouseDoubleClickBeg = false;
m_preMouseRightUpTime = 0;
m_point[2] = mousePoint.x;
m_point[3] = mousePoint.y;
std::cout << "select right_bottom point [" << m_point[2] << ", " << m_point[3] << "]" << std::endl;
CaptureScreen();
} else {
m_preMouseRightUpTime = ::clock();
}
}
}
void Run()
{
::SetConsoleCtrlHandler((PHANDLER_ROUTINE)&ConsoleProc, TRUE);
MSG msg;
while (::GetMessage(&msg, NULL, 0, 0)) {
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
}
private:
void CaptureScreen()
{
std::cout << "Capture from [" << m_point[0] << ", " << m_point[1] << "] ";
std::cout << "to [" << m_point[2] << ", " << m_point[3] << "]" << std::endl;
DDBitMap ddBitMap;
m_cap.CaptureWnd(NULL, m_point[0], m_point[1], m_point[2], m_point[3], ddBitMap);
// 加上日期
time_t t = time(0);
s8 date[64];
::strftime(date, sizeof(date), "%Y-%m-%d %H-%M-%S", localtime(&t));
std::stringstream tmp;
tmp << date;
tmp << "_capture_";
tmp << m_fileNameFix++;
tmp << ".bmp";
m_cap.SaveDDBitMapTo(ddBitMap, tmp.str());
}
void ResetPoint()
{
m_point.resize(4);
m_point[0] = MAX_U32;
m_point[1] = MAX_U32;
m_point[2] = MAX_U32;
m_point[3] = MAX_U32;
}
private:
std::vector<u32> m_point;
std::vector<HHOOK> m_hooks;
ScreenCapture m_cap;
u32 m_fileNameFix = 1;
bool m_mouseDoubleClickBeg = false;
u64 m_preMouseRightUpTime = 0;
};
Hook* g_hook;
LRESULT CALLBACK LowLevelKeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
g_hook->OnKeyBoard(nCode, wParam, lParam);
return ::CallNextHookEx(NULL, nCode, wParam, lParam);
}
LRESULT CALLBACK LowLevelMouseHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
g_hook->OnMouse(nCode, wParam, lParam);
return ::CallNextHookEx(NULL, nCode, wParam, lParam);
}
bool CALLBACK ConsoleProc(DWORD CtrlType)
{
delete g_hook;
g_hook = nullptr;
return FALSE;
};
int main()
{
g_hook = new Hook();
g_hook->SetHook();
g_hook->Run();
delete g_hook;
g_hook = nullptr;
return 0;
}