Showing posts with label win32 screen capture. Show all posts
Showing posts with label win32 screen capture. Show all posts

Friday, May 6, 2011

Simple screen capture example - GDI version.

A Simple screen capture example using windows handle and BitBlt.
This mechanism works for both GDI and DirectX window mode.
However, this screen capture has its own weakness, low performance and
abnormal mouse behaviour. Use it just as reference.



// Functions for captureing WoW screen.
//
HWND        getWoWWindowHandle(void)
{
    HWND        wowWnd;


    //return GetDesktopWindow();

    // Try to find WoW window handle.
    //
    wowWnd = FindWindow(TEXT("GxWindowClass"), TEXT("월드 오브 워크래프트"));
    if( wowWnd )
    {
        // The window is found.
        //
        return wowWnd;
    }
    else
        return NULL;
}


void        captureWoWWindow(HDC hTgtDC)
{
    HWND        hSrcWnd;
    HDC            hSrcDC;

    // Get WoW Window Handle.
    //
    hSrcWnd = getWoWWindowHandle();
    if( hSrcWnd )
    {
        hSrcDC = GetDC(hSrcWnd);

        BitBlt(hTgtDC, 0, 0, 960, 640, hSrcDC, 0, 0, SRCCOPY | CAPTUREBLT);

        ReleaseDC(hSrcWnd, hSrcDC);
    }

    return;
}

Sunday, May 1, 2011

Windows Screen Capture sample code

Following code is a vc++ example for Screen Capture function.
Collected from http://www.ham-radio-deluxe.com/Support/SourceCode/WindowsScreenCapture.aspx
Introduction
The guts of this code is basic Windows programming, the clever part is saving to a BMP on disk then converting the BMP to JPG using custom code taken from www.codeproject.com .
The code uses a few custom classes for saving and converting from BMP to JPG, to do this see http://www.codeproject.com/KB/graphics/cximage.aspx . The code to save a BMP to a file is below. Take it, make it your own.
The main function is ScreenCapture() which I put in CMainFrame.

class CSaveBitmapToFile 
{
public:
 CSaveBitmapToFile();
 virtual ~CSaveBitmapToFile();

 PBITMAPINFO CreateBitmapInfoStruct(CWnd*, HBITMAP hBmp);
 void        CreateBMPFile(CWnd*, LPCTSTR pszFile, PBITMAPINFO pbi, HBITMAP hBMP, HDC hDC);
};


//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CSaveBitmapToFile::CSaveBitmapToFile()
{
}

CSaveBitmapToFile::~CSaveBitmapToFile()
{
}

//
// Create structures.
//
PBITMAPINFO CSaveBitmapToFile::CreateBitmapInfoStruct(CWnd* pParent, HBITMAP hBmp)
{
    BITMAP  bmp      = {0};
    PBITMAPINFO pbmi     = NULL;
    WORD  cClrBits = 0;

 //
    // Retrieve the bitmap's color format, width, and height.
 //
    if (!::GetObject(hBmp, sizeof(BITMAP), (LPSTR)&bmp))
 {
  CErrorText err;
  CString    strText;

  strText.Format(_T("Error getting bitmap colour format, width and height: %s"), err.Text());
  pParent->MessageBox(strText, _T("Error"), MB_OK | MB_ICONERROR);
        return NULL;
 }

 //
    // Convert the color format to a count of bits.
 //
    cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel);

    if (cClrBits == 1)
        cClrBits = 1;
    else if (cClrBits <= 4)
        cClrBits = 4;
    else if (cClrBits <= 8)
        cClrBits = 8;
    else if (cClrBits <= 16)
        cClrBits = 16;
    else if (cClrBits <= 24)
        cClrBits = 24;
    else cClrBits = 32;

 //
    // Allocate memory for the BITMAPINFO structure. (This structure
    // contains a BITMAPINFOHEADER structure and an array of RGBQUAD
    // data structures.)
 //
 if (cClrBits != 24)
 {
  pbmi = (PBITMAPINFO)LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << cClrBits));
 }

 //
 // There is no RGBQUAD array for the 24-bit-per-pixel format.
 //
 else
 {
         pbmi = (PBITMAPINFO)LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER));
 }

 //
    // Initialize the fields in the BITMAPINFO structure.
 ///
    pbmi->bmiHeader.biSize     = sizeof(BITMAPINFOHEADER);
    pbmi->bmiHeader.biWidth    = bmp.bmWidth;
    pbmi->bmiHeader.biHeight   = bmp.bmHeight;
    pbmi->bmiHeader.biPlanes   = bmp.bmPlanes;
    pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel;

    if (cClrBits < 24)
        pbmi->bmiHeader.biClrUsed = (1 << cClrBits);

 //
    // If the bitmap is not compressed, set the BI_RGB flag.
 //
    pbmi->bmiHeader.biCompression = BI_RGB;

 //
    // Compute the number of bytes in the array of color
    // indices and store the result in biSizeImage.
    // For Windows NT/2000, the width must be DWORD aligned unless
    // the bitmap is RLE compressed. This example shows this.
    // For Windows 95/98, the width must be WORD aligned unless the
    // bitmap is RLE compressed.
 //
    pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * cClrBits + 31) & ~31) / 8 * pbmi->bmiHeader.biHeight;

 //
    // Set biClrImportant to 0, indicating that all of the
    // device colors are important.
 //
 pbmi->bmiHeader.biClrImportant = 0;

 return pbmi;
}

//
// The following example code defines a function that initializes the remaining structures,
// retrieves the array of palette indices, opens the file, copies the data, and closes the file.
//
void CSaveBitmapToFile::CreateBMPFile(CWnd*       pParent,
           LPCTSTR     pszFile,
           PBITMAPINFO pbi,
           HBITMAP     hBMP,
           HDC         hDC)
{
 HANDLE    hf      = NULL;  // File handle
 BITMAPFILEHEADER hdr     = {0};  // Bitmap file-header
 PBITMAPINFOHEADER pbih    = NULL;  // Bitmap info-header
 LPBYTE    lpBits  = NULL;  // Memory pointer
 DWORD    dwTotal = 0;  // Total count of bytes
 DWORD    cb      = 0;  // Incremental count of bytes
 BYTE*    hp      = NULL;  // Byte pointer
 DWORD    dwTmp   = 0;

 pbih   = (PBITMAPINFOHEADER) pbi;
 lpBits = (LPBYTE)GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);

 if (!lpBits)
 {
  pParent->MessageBox(_T("Error allocating temporary storage."),
       _T("Error"),
       MB_OK | MB_ICONERROR);
  return;
 }

 //
 // Retrieve the color table (RGBQUAD array) and the bits
 // (array of palette indices) from the DIB.
 //
 if (!GetDIBits(hDC, hBMP, 0, (WORD) pbih->biHeight, lpBits, pbi, DIB_RGB_COLORS))
 {
  pParent->MessageBox(_T("Error retrieving colour table and bits."),
       _T("Error"),
       MB_OK | MB_ICONERROR);

  GlobalFree((HGLOBAL)lpBits);
  return;
 }

 //
 // Create the .BMP file.
 //
 hf = CreateFile(pszFile,
     GENERIC_READ | GENERIC_WRITE,
     (DWORD)0,
     NULL,
     CREATE_ALWAYS,
     FILE_ATTRIBUTE_NORMAL,
     (HANDLE) NULL);

 if (hf == INVALID_HANDLE_VALUE)
 {
  CString  strText;
  CErrorText error;

  strText.Format(_T("Error creating output file: %s -\n%s"),
        pszFile,
        error.Text());

  pParent->MessageBox(strText,
       _T("Error"),
       MB_OK | MB_ICONERROR);

  GlobalFree((HGLOBAL)lpBits);
  return;
 }

 //
 // BM 0x42 = "B" 0x4d = "M".
 //
 hdr.bfType = 0x4d42;

 //
 // Compute the size of the entire file.
 //
 hdr.bfSize      = (DWORD)(sizeof(BITMAPFILEHEADER) +  pbih->biSize + pbih->biClrUsed * sizeof(RGBQUAD) + pbih->biSizeImage);
 hdr.bfReserved1 = 0;
 hdr.bfReserved2 = 0;

 //
 // Compute the offset to the array of color indices.
 //
 hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) + pbih->biSize + pbih->biClrUsed * sizeof (RGBQUAD);

 //
 // Copy the BITMAPFILEHEADER into the .BMP file.
 //
 if (!WriteFile(hf,
       (LPVOID)&hdr,
       sizeof(BITMAPFILEHEADER),
       (LPDWORD) &dwTmp,  NULL))
 {
  pParent->MessageBox(_T("Error writing to output file."),
       _T("Error"),
       MB_OK | MB_ICONERROR);

  GlobalFree((HGLOBAL)lpBits);
  CloseHandle(hf);
  return;
 }

 //
 // Copy the BITMAPINFOHEADER and RGBQUAD array into the file.
 //
 if (!WriteFile(hf,
       (LPVOID)pbih,
       sizeof(BITMAPINFOHEADER) + pbih->biClrUsed * sizeof (RGBQUAD),
       (LPDWORD)&dwTmp,
       (NULL)))
 {
  pParent->MessageBox(_T("Error writing to output file."),
       _T("Error"),
       MB_OK | MB_ICONERROR);

  GlobalFree((HGLOBAL)lpBits);
  CloseHandle(hf);
  return;
 }

 //
 // Copy the array of color indices into the .BMP file.
 //
 dwTotal = cb = pbih->biSizeImage;
 hp      = lpBits;

 if (!WriteFile(hf,
       (LPSTR)hp,
       (int)cb,
       (LPDWORD)&dwTmp,
       NULL))
 {
  pParent->MessageBox(_T("Error writing to output file."),
       _T("Error"),
       MB_OK | MB_ICONERROR);

  GlobalFree((HGLOBAL)lpBits);
  CloseHandle(hf);
  return;
 }

 //
 // Close the .BMP file.
 //
 CloseHandle(hf);

 //
 // Free memory.
 //
 GlobalFree((HGLOBAL)lpBits);
}








The Code - from MainFrm.cpp
// MainFrm.cpp : implementation of the CMainFrame class
//

#include "stdafx.h"
#include "HRDLogbook.h"

#include "MainFrm.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

//
// Clipboard.
//
void CMainFrame::OnToolsScreenCapture()
{
 ScreenCapture(this, TRUE, FALSE);
}

//
// File.
//
void CMainFrame::OnToolsScreenCaptureFile()
{
 ScreenCapture(this, FALSE, TRUE);
}

void CMainFrame::ScreenCapture(CWnd*      pWnd,
          const BOOL bClipboard,
          const BOOL bFile)
{
 CRect rectClient(0,0,0,0);
 CRect rectWindow(0,0,0,0);

 pWnd->GetClientRect(&rectClient);
 pWnd->GetWindowRect(&rectWindow);

 BOOL bZoom          = pWnd->IsZoomed();
 int  x              = (rectWindow.Width()  - rectClient.Width()) / 2;
 int  y              = (rectWindow.Height() - rectClient.Height()) - x;
 HWND hDesktopWnd    = pWnd->m_hWnd;
 HDC  hDesktopDC     = ::GetDC(hDesktopWnd);
 HDC  hCaptureDC     = ::CreateCompatibleDC(hDesktopDC);

 int xSrc          = bZoom ? 0 : -x;
 int ySrc          = bZoom ? 0 : -y;
 int nScreenWidth  = bZoom ? (rectWindow.Width()  - x - x) : rectWindow.Width();
 int nScreenHeight = bZoom ? (rectWindow.Height() - y - x) : rectWindow.Height();

// nScreenWidth  = rectWindow.Width();
// nScreenHeight = rectWindow.Height();

 HBITMAP hCaptureBitmap = ::CreateCompatibleBitmap(hDesktopDC,
               nScreenWidth,
               nScreenHeight);
 SelectObject(hCaptureDC, hCaptureBitmap);

 BitBlt(hCaptureDC,
     0,
     0,
     nScreenWidth,
     nScreenHeight,
     hDesktopDC,
     xSrc,
     ySrc,
     SRCCOPY | CAPTUREBLT);

 //
 // Copy to clipboard.
 //
 if (bClipboard)
 {
  //
  // Open clipboard and empty its contents.
  //
  OpenClipboard();
  EmptyClipboard();

  //
  // Copy bitmap to clipboard and close it.
  //
  SetClipboardData(CF_BITMAP, hCaptureBitmap);
  CloseClipboard();
 }

 //
 // Save to file.
 //
 if (bFile)
 {
  CSaveBitmapToFile save;
  CString    strFilenameBmp;
  CString    strFilenameJpg;
  CString    strFolderBmp;
  CString    strFolderJpg;
  CTime    now = CTime::GetCurrentTime();
  PBITMAPINFO   pbi = save.CreateBitmapInfoStruct(this, hCaptureBitmap);

  if (pbi)
  {
   Dirs().GetFolderPath(CSIDL_MYPICTURES,    strFolderJpg);
   Dirs().GetFolderPath(CSIDL_LOCAL_APPDATA, strFolderBmp);

   strFilenameBmp.Format(_T("%s\\HRD-Logfile-Screenshot-%s.bmp"),
          strFolderBmp,
          now.Format(_T("%Y-%m-%d-%H%M%S")));

   strFilenameJpg.Format(_T("%s\\HRD-Logfile-Screenshot-%s.jpg"),
          strFolderJpg,
          now.Format(_T("%Y-%m-%d-%H%M%S")));

   CFileDialogSave dlg(strFilenameJpg,
        FD_JPG_FILTER,
        _T("Save Screen Capture file..."),
        this);
   if (dlg.DoModal() == IDOK)
   {
    CMyImage image;

    //
    // Create file.
    //
    save.CreateBMPFile(this,
           (LPCTSTR)strFilenameBmp,
           pbi,
           hCaptureBitmap,
           hCaptureDC);

    //
    // Now convert to JPG, 90% is good enough...
    //
    image.ConvertToJPG(strFilenameBmp,
           dlg.Filename(),
           90);

    try
    {
     CFile::Remove(strFilenameBmp);
    }
    catch(...)
    {
    }

    //
    // Display.
    //
    OwnShellExecuteEdit(this, dlg.Filename());
   }

   //
   // Tidy up.
   //
   LocalFree(pbi);
  }
 }

 ::ReleaseDC(hDesktopWnd,hDesktopDC);
 ::DeleteDC(hCaptureDC);
 ::DeleteObject(hCaptureBitmap);
}