2024-06-23 17:36:53 +08:00

461 lines
13 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ScreenSpy.cpp: implementation of the CScreenSpys class.
//
//////////////////////////////////////////////////////////////////////
#include "ScreenSpys.h"
#include "../until.h"
#define RGB2GRAY(r,g,b) (((b)*117 + (g)*601 + (r)*306) >> 10)
#define DEF_STEP 19
#define OFF_SET 24
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
#ifdef _CONSOLE
#include <stdio.h>
#endif
CScreenSpys::CScreenSpys(int biBitCount, bool bIsGray, UINT nMaxFrameRate)
{
switch (biBitCount)
{
case 1:
case 4:
case 8:
case 16:
case 32:
m_biBitCount = biBitCount;
break;
default:
m_biBitCount = 8;
}
if (!SelectInputWinStation())
{
m_hDeskTopWnd = GetDesktopWindow();
m_hFullDC = GetDC(m_hDeskTopWnd);
}
m_dwBitBltRop = SRCCOPY;
m_bAlgorithm = ALGORITHM_SCAN; // ĬÈÏʹÓøôÐÐɨÃèËã·¨
m_dwLastCapture = GetTickCount();
m_nMaxFrameRate = nMaxFrameRate;
m_dwSleep = 1000 / nMaxFrameRate;
m_bIsGray = bIsGray;
m_nFullWidth = ::GetSystemMetrics(SM_CXSCREEN);
m_nFullHeight = ::GetSystemMetrics(SM_CYSCREEN);
m_nIncSize = 32 / m_biBitCount;
m_nStartLine = 0;
m_hFullMemDC = ::CreateCompatibleDC(m_hFullDC);
m_hDiffMemDC = ::CreateCompatibleDC(m_hFullDC);
m_hLineMemDC = ::CreateCompatibleDC(NULL);
m_hRectMemDC = ::CreateCompatibleDC(NULL);
m_lpvLineBits = NULL;
m_lpvFullBits = NULL;
m_lpbmi_line = ConstructBI(m_biBitCount, m_nFullWidth, 1);
m_lpbmi_full = ConstructBI(m_biBitCount, m_nFullWidth, m_nFullHeight);
m_lpbmi_rect = ConstructBI(m_biBitCount, m_nFullWidth, 1);
m_hLineBitmap = ::CreateDIBSection(m_hFullDC, m_lpbmi_line, DIB_RGB_COLORS, &m_lpvLineBits, NULL, NULL);
m_hFullBitmap = ::CreateDIBSection(m_hFullDC, m_lpbmi_full, DIB_RGB_COLORS, &m_lpvFullBits, NULL, NULL);
m_hDiffBitmap = ::CreateDIBSection(m_hFullDC, m_lpbmi_full, DIB_RGB_COLORS, &m_lpvDiffBits, NULL, NULL);
::SelectObject(m_hFullMemDC, m_hFullBitmap);
::SelectObject(m_hLineMemDC, m_hLineBitmap);
::SelectObject(m_hDiffMemDC, m_hDiffBitmap);
::SetRect(&m_changeRect, 0, 0, m_nFullWidth, m_nFullHeight);
// ×ã¹»ÁË
m_rectBuffer = new BYTE[m_lpbmi_full->bmiHeader.biSizeImage * 2];
m_nDataSizePerLine = m_lpbmi_full->bmiHeader.biSizeImage / m_nFullHeight;
m_rectBufferOffset = 0;
}
CScreenSpys::~CScreenSpys()
{
::ReleaseDC(m_hDeskTopWnd, m_hFullDC);
::DeleteDC(m_hLineMemDC);
::DeleteDC(m_hFullMemDC);
::DeleteDC(m_hRectMemDC);
::DeleteDC(m_hDiffMemDC);
::DeleteObject(m_hLineBitmap);
::DeleteObject(m_hFullBitmap);
::DeleteObject(m_hDiffBitmap);
if (m_rectBuffer)
delete[] m_rectBuffer;
delete[] m_lpbmi_full;
delete[] m_lpbmi_line;
delete[] m_lpbmi_rect;
}
LPVOID CScreenSpys::getNextScreen(LPDWORD lpdwBytes)
{
static LONG nOldCursorPosY = 0;
if (lpdwBytes == NULL || m_rectBuffer == NULL)
return NULL;
SelectInputWinStation();
// ÖØÖÃrect»º³åÇøÖ¸Õë
m_rectBufferOffset = 0;
// дÈëʹÓÃÁËÄÄÖÖËã·¨
WriteRectBuffer((LPBYTE)&m_bAlgorithm, sizeof(m_bAlgorithm));
// дÈë¹â±êλÖÃ
POINT CursorPos;
GetCursorPos(&CursorPos);
WriteRectBuffer((LPBYTE)&CursorPos, sizeof(POINT));
// дÈ뵱ǰ¹â±êÀàÐÍ
BYTE bCursorIndex = m_CursorInfo.getCurrentCursorIndex();
WriteRectBuffer(&bCursorIndex, sizeof(BYTE));
// ²îÒì±È½ÏËã·¨
if (m_bAlgorithm == ALGORITHM_DIFF)
{
// ·Ö¶ÎɨÃèÈ«ÆÁÄ»
ScanScreen(m_hDiffMemDC, m_hFullDC, m_lpbmi_full->bmiHeader.biWidth, m_lpbmi_full->bmiHeader.biHeight);
*lpdwBytes = m_rectBufferOffset +
Compare((LPBYTE)m_lpvDiffBits, (LPBYTE)m_lpvFullBits, m_rectBuffer + m_rectBufferOffset, m_lpbmi_full->bmiHeader.biSizeImage);
return m_rectBuffer;
}
// Êó±êλÖ÷¢±ä»¯²¢ÇÒÈȵãÇøÓòÈç¹û·¢Éú±ä»¯£¬ÒÔ(·¢Éú±ä»¯µÄÐÐ + DEF_STEP)ÏòÏÂɨÃè
// ÏòÉÏÌá
int nHotspot = max(0, CursorPos.y - DEF_STEP);
for (
int i = ((CursorPos.y != nOldCursorPosY) && ScanChangedRect(nHotspot)) ? (nHotspot + DEF_STEP) : m_nStartLine;
i < m_nFullHeight;
i += DEF_STEP
)
{
if (ScanChangedRect(i))
{
i += DEF_STEP;
}
}
nOldCursorPosY = CursorPos.y;
m_nStartLine = (m_nStartLine + 3) % DEF_STEP;
*lpdwBytes = m_rectBufferOffset;
// ÏÞÖÆ·¢ËÍÖ¡µÄËÙ¶È
while (GetTickCount() - m_dwLastCapture < m_dwSleep)
Sleep(1);
InterlockedExchange((LPLONG)&m_dwLastCapture, GetTickCount());
return m_rectBuffer;
}
bool CScreenSpys::ScanChangedRect(int nStartLine)
{
bool bRet = false;
LPDWORD p1, p2;
::BitBlt(m_hLineMemDC, 0, 0, m_nFullWidth, 1, m_hFullDC, 0, nStartLine, m_dwBitBltRop);
// 0 ÊÇ×îºóÒ»ÐÐ
p1 = (PDWORD)((DWORD)m_lpvFullBits + ((m_nFullHeight - 1 - nStartLine) * m_nDataSizePerLine));
p2 = (PDWORD)m_lpvLineBits;
::SetRect(&m_changeRect, -1, nStartLine - DEF_STEP, -1, nStartLine + DEF_STEP * 2);
for (int j = 0; j < m_nFullWidth; j += m_nIncSize)
{
if (*p1 != *p2)
{
if (m_changeRect.right < 0)
m_changeRect.left = j - OFF_SET;
m_changeRect.right = j + OFF_SET;
}
p1++;
p2++;
}
if (m_changeRect.right > -1)
{
m_changeRect.left = max(m_changeRect.left, 0);
m_changeRect.top = max(m_changeRect.top, 0);
m_changeRect.right = min(m_changeRect.right, m_nFullWidth);
m_changeRect.bottom = min(m_changeRect.bottom, m_nFullHeight);
// ¸´ÖƸıäµÄÇøÓò
CopyRect(&m_changeRect);
bRet = true;
}
return bRet;
}
void CScreenSpys::setAlgorithm(UINT nAlgorithm)
{
InterlockedExchange((LPLONG)&m_bAlgorithm, nAlgorithm);
}
LPBITMAPINFO CScreenSpys::ConstructBI(int biBitCount, int biWidth, int biHeight)
{
/*
biBitCount Ϊ1 (ºÚ°×¶þɫͼ) ¡¢4 (16 ɫͼ) ¡¢8 (256 ɫͼ) ʱÓÉÑÕÉ«±íÏîÊýÖ¸³öÑÕÉ«±í´óС
biBitCount Ϊ16 (16 λɫͼ) ¡¢24 (Õæ²Êɫͼ, ²»Ö§³Ö) ¡¢32 (32 λɫͼ) ʱûÓÐÑÕÉ«±í
*/
int color_num = biBitCount <= 8 ? 1 << biBitCount : 0;
int nBISize = sizeof(BITMAPINFOHEADER) + (color_num * sizeof(RGBQUAD));
BITMAPINFO *lpbmi = (BITMAPINFO *) new BYTE[nBISize];
BITMAPINFOHEADER *lpbmih = &(lpbmi->bmiHeader);
lpbmih->biSize = sizeof(BITMAPINFOHEADER);
lpbmih->biWidth = biWidth;
lpbmih->biHeight = biHeight;
lpbmih->biPlanes = 1;
lpbmih->biBitCount = biBitCount;
lpbmih->biCompression = BI_RGB;
lpbmih->biXPelsPerMeter = 0;
lpbmih->biYPelsPerMeter = 0;
lpbmih->biClrUsed = 0;
lpbmih->biClrImportant = 0;
lpbmih->biSizeImage = (((lpbmih->biWidth * lpbmih->biBitCount + 31) & ~31) >> 3) * lpbmih->biHeight;
// 16λºÍÒÔºóµÄûÓÐÑÕÉ«±í£¬Ö±½Ó·µ»Ø
if (biBitCount >= 16)
return lpbmi;
/*
Windows 95ºÍWindows 98£ºÈç¹ûlpvBits²ÎÊýΪNULL²¢ÇÒGetDIBits³É¹¦µØÌî³äÁËBITMAPINFO½á¹¹£¬ÄÇô·µ»ØÖµÎªÎ»Í¼ÖÐ×ܹ²µÄɨÃèÏßÊý¡£
Windows NT£ºÈç¹ûlpvBits²ÎÊýΪNULL²¢ÇÒGetDIBits³É¹¦µØÌî³äÁËBITMAPINFO½á¹¹£¬ÄÇô·µ»ØÖµÎª·Ç0¡£Èç¹ûº¯ÊýÖ´ÐÐʧ°Ü£¬ÄÇô½«·µ»Ø0Öµ¡£Windows NT£ºÈôÏë»ñµÃ¸ü¶à´íÎóÐÅÏ¢£¬Çëµ÷ÓÃcallGetLastErrorº¯Êý¡£
*/
HDC hDC = GetDC(NULL);
HBITMAP hBmp = CreateCompatibleBitmap(hDC, 1, 1); // ¸ß¿í²»ÄÜΪ0
GetDIBits(hDC, hBmp, 0, 0, NULL, lpbmi, DIB_RGB_COLORS);
ReleaseDC(NULL, hDC);
DeleteObject(hBmp);
if (m_bIsGray)
{
for (int i = 0; i < color_num; i++)
{
int color = RGB2GRAY(lpbmi->bmiColors[i].rgbRed, lpbmi->bmiColors[i].rgbGreen, lpbmi->bmiColors[i].rgbBlue);
lpbmi->bmiColors[i].rgbRed = lpbmi->bmiColors[i].rgbGreen = lpbmi->bmiColors[i].rgbBlue = color;
}
}
return lpbmi;
}
void CScreenSpys::WriteRectBuffer(LPBYTE lpData, int nCount)
{
memcpy(m_rectBuffer + m_rectBufferOffset, lpData, nCount);
m_rectBufferOffset += nCount;
}
LPVOID CScreenSpys::getFirstScreen()
{
::BitBlt(m_hFullMemDC, 0, 0, m_nFullWidth, m_nFullHeight, m_hFullDC, 0, 0, m_dwBitBltRop);
return m_lpvFullBits;
}
void CScreenSpys::CopyRect( LPRECT lpRect )
{
int nRectWidth = lpRect->right - lpRect->left;
int nRectHeight = lpRect->bottom - lpRect->top;
LPVOID lpvRectBits = NULL;
// µ÷Õûm_lpbmi_rect
m_lpbmi_rect->bmiHeader.biWidth = nRectWidth;
m_lpbmi_rect->bmiHeader.biHeight = nRectHeight;
m_lpbmi_rect->bmiHeader.biSizeImage = (((m_lpbmi_rect->bmiHeader.biWidth * m_lpbmi_rect->bmiHeader.biBitCount + 31) & ~31) >> 3)
* m_lpbmi_rect->bmiHeader.biHeight;
HBITMAP hRectBitmap = ::CreateDIBSection(m_hFullDC, m_lpbmi_rect, DIB_RGB_COLORS, &lpvRectBits, NULL, NULL);
::SelectObject(m_hRectMemDC, hRectBitmap);
::BitBlt(m_hFullMemDC, lpRect->left, lpRect->top, nRectWidth, nRectHeight, m_hFullDC, lpRect->left, lpRect->top, m_dwBitBltRop);
::BitBlt(m_hRectMemDC, 0, 0, nRectWidth, nRectHeight, m_hFullMemDC, lpRect->left, lpRect->top, SRCCOPY);
WriteRectBuffer((LPBYTE)lpRect, sizeof(RECT));
WriteRectBuffer((LPBYTE)lpvRectBits, m_lpbmi_rect->bmiHeader.biSizeImage);
DeleteObject(hRectBitmap);
}
UINT CScreenSpys::getFirstImageSize()
{
return m_lpbmi_full->bmiHeader.biSizeImage;
}
void CScreenSpys::setCaptureLayer(bool bIsCaptureLayer)
{
DWORD dwRop = SRCCOPY;
if (bIsCaptureLayer)
dwRop |= CAPTUREBLT;
InterlockedExchange((LPLONG)&m_dwBitBltRop, dwRop);
}
LPBITMAPINFO CScreenSpys::getBI()
{
return m_lpbmi_full;
}
UINT CScreenSpys::getBISize()
{
int color_num = m_biBitCount <= 8 ? 1 << m_biBitCount : 0;
return sizeof(BITMAPINFOHEADER) + (color_num * sizeof(RGBQUAD));
}
extern bool SwitchInputDesktop();
bool CScreenSpys::SelectInputWinStation()
{
bool bRet = ::SwitchInputDesktop();
if (bRet)
{
ReleaseDC(m_hDeskTopWnd, m_hFullDC);
m_hDeskTopWnd = GetDesktopWindow();
m_hFullDC = GetDC(m_hDeskTopWnd);
}
return bRet;
}
void CScreenSpys::ScanScreen( HDC hdcDest, HDC hdcSrc, int nWidth, int nHeight)
{
UINT nJumpLine = 50;
UINT nJumpSleep = nJumpLine / 10; // ɨÃè¼ä¸ô
// ɨÃèÆÁÄ»
for (int i = 0, int nToJump = 0; i < nHeight; i += nToJump)
{
int nOther = nHeight - i;
if (nOther > nJumpLine)
nToJump = nJumpLine;
else
nToJump = nOther;
BitBlt(hdcDest, 0, i, nWidth, nToJump, hdcSrc, 0, i, m_dwBitBltRop);
Sleep(nJumpSleep);
}
}
// ²îÒì±È½ÏËã·¨¿éµÄº¯Êý
int CScreenSpys::Compare( LPBYTE lpSource, LPBYTE lpDest, LPBYTE lpBuffer, DWORD dwSize )
{
// Windows¹æ¶¨Ò»¸öɨÃèÐÐËùÕ¼µÄ×Ö½ÚÊý±ØÐëÊÇ4µÄ±¶Êý, ËùÒÔÓÃDWORD±È½Ï
LPDWORD p1, p2;
p1 = (LPDWORD)lpDest;
p2 = (LPDWORD)lpSource;
// Æ«ÒÆµÄÆ«ÒÆ£¬²»Í¬³¤¶ÈµÄÆ«ÒÆ
int nOffsetOffset = 0, nBytesOffset = 0, nDataOffset = 0;
int nCount = 0; // Êý¾Ý¼ÆÊýÆ÷
// p1++ʵ¼ÊÉÏÊǵÝÔöÁËÒ»¸öDWORD
for (int i = 0; i < dwSize; i += 4, p1++, p2++)
{
if (*p1 == *p2)
continue;
// Ò»¸öÐÂÊý¾Ý¿é¿ªÊ¼
// дÈëÆ«ÒÆµØÖ·
*(LPDWORD)(lpBuffer + nOffsetOffset) = i;
// ¼Ç¼Êý¾Ý´óСµÄ´æ·ÅλÖÃ
nBytesOffset = nOffsetOffset + sizeof(int);
nDataOffset = nBytesOffset + sizeof(int);
nCount = 0; // Êý¾Ý¼ÆÊýÆ÷¹éÁã
// ¸üÐÂDestÖеÄÊý¾Ý
*p1 = *p2;
*(LPDWORD)(lpBuffer + nDataOffset + nCount) = *p2;
nCount += 4;
i += 4, p1++, p2++;
for (int j = i; j < dwSize; j += 4, i += 4, p1++, p2++)
{
if (*p1 == *p2)
break;
// ¸üÐÂDestÖеÄÊý¾Ý
*p1 = *p2;
*(LPDWORD)(lpBuffer + nDataOffset + nCount) = *p2;
nCount += 4;
}
// дÈëÊý¾Ý³¤¶È
*(LPDWORD)(lpBuffer + nBytesOffset) = nCount;
nOffsetOffset = nDataOffset + nCount;
}
// nOffsetOffset ¾ÍÊÇдÈëµÄ×Ü´óС
return nOffsetOffset;
// ¸Ð¾õ»ã±àËÙ¶È»¹Ã»ÓÐCËã·¨¿ì£¬ÓôÃÆÀ´×Å
// int Bytes = 0;
// __asm
// {
// mov esi, [lpSource]
// mov edi, [lpDest]
// mov ebx, [lpBuffer]
// xor ecx, ecx
// jmp short Loop_Compare // ¿ªÊ¼½øÐбȽÏ
// Loop_Compare_Continue:
// mov eax, dword ptr [esi]
// cmp eax, dword ptr [edi]
// je short Loop_Compare_Equal // Èç¹ûÏàµÈ, ¼ÌÐø±È½Ï
// mov [edi], eax // дÈ벻ͬµÄÊý¾Ýµ½Ä¿±êÖÐ
// mov edx, ebx // edx ¼Ç¼µÚÒ»¸ö²»Í¬ÇøÓò¿éµÄÆðʼλÖÃ
// mov dword ptr [ebx], ecx
// add ebx, 8
// mov dword ptr [ebx], eax
// add ebx, 4
// add esi, 4
// add edi, 4
// add ecx, 4
// jmp short Loop_Block_Compare // Êý¾Ý²»Í¬£¬²úÉúÒ»¸öÇø¿é£¬¶ÔÕâ¸öÇø¿é½øÐбȽÏ
// Loop_Block_Compare_Continue:
// mov eax, dword ptr [esi]
// cmp eax, dword ptr [edi]
// jnz short Loop_Block_Compare_Not_Equal // Êý¾Ý²»ÏàµÈ£¬Ð´Èë
// mov eax, ecx
// sub eax, dword ptr [edx]
// add edx, 4
// mov dword ptr [edx], eax
// add eax, 8
// add [Bytes], eax
// add esi, 4
// add edi, 4
// add ecx, 4
// jmp short Loop_Block_Compare_Finish
// Loop_Block_Compare_Not_Equal:
// mov dword ptr [ebx], eax
// mov [edi], eax // дÈ벻ͬµÄÊý¾Ýµ½Ä¿±êÖÐ
// add ebx, 4
// add esi, 4
// add edi, 4
// add ecx, 4
// Loop_Block_Compare:
// cmp ecx, [dwSize]
// jb short Loop_Block_Compare_Continue
// Loop_Block_Compare_Finish:
// cmp ecx, [dwSize]
// jnz short Loop_Compare
// mov eax, ecx
// sub eax, dword ptr [edx]
// add edx, 4
// mov dword ptr [edx], eax
// add eax, 8
// add [Bytes], eax
// jmp short Loop_Compare
// Loop_Compare_Equal:
// add esi, 4
// add edi, 4
// add ecx, 4
// Loop_Compare:
// cmp ecx, [dwSize]
// jb short Loop_Compare_Continue
// }
// return Bytes;
}