静默截屏工具


#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;
}

 

在 Android 后台静默截屏需要使用系统权限,因此需要在 AndroidManifest.xml 文件中添加权限声明: ```xml <uses-permission android:name="android.permission.READ_FRAME_BUFFER"/> ``` 然后可以使用以下代码进行截屏: ```java // 获取屏幕宽高 DisplayMetrics metrics = new DisplayMetrics(); WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE); windowManager.getDefaultDisplay().getMetrics(metrics); int width = metrics.widthPixels; int height = metrics.heightPixels; // 获取屏幕截图 Bitmap screenshot = null; try { screenshot = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(screenshot); Method method = Class.forName("android.view.SurfaceControl").getDeclaredMethod("screenshot", int.class, int.class); method.setAccessible(true); Object[] args = new Object[]{width, height}; method.invoke(null, args); Bitmap bitmap = (Bitmap) args[0]; canvas.drawBitmap(bitmap, 0, 0, null); } catch (Exception e) { e.printStackTrace(); } // 保存截图到文件 FileOutputStream fos = null; try { fos = new FileOutputStream(new File("/sdcard/screenshot.png")); screenshot.compress(Bitmap.CompressFormat.PNG, 100, fos); fos.flush(); } catch (Exception e) { e.printStackTrace(); } finally { if (fos != null) { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } ``` 需要注意的是,这种方式只能在拥有系统权限的情况下使用,普通应用程序无法使用该方法进行截屏
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值