views:

144

answers:

1

Hi,

I've been writing some code to do a screen grab of a window (in Windows). The code works fine, however prior to the screen grab I have to bring the window to the front that I want to capture and force a redraw.

I force the redraw with InvalidateRect, I then have to pump some messages from the message loop in order for the WM_PAINT to get processed. This is obviously a bit lame, as i don't know how many messages to pump.

I tried using RedrawWindow with RDW_ALLCHILDREN, however the app I am grabbing a screen from is an MDI app and doesn't seem to redraw all of it's children.

So my question is, is there a better way to redraw the window prior to the screen grab?

Cheers Rich

+4  A: 

Since you have not mentioned the language you are using, I hope the following code in C++ helps you!

void getScreenShot( int texWidth, int texHeight, unsigned char* pBuffer, HWND handle )
{
    /* Local variables */
    HDC screenDC;
    RECT screenRect;
    int extraBytesPerRow;
    BITMAPINFO bitmapInfo;
    HDC bitmapDC;
    void* bitmapDataPtr;
    HBITMAP hBitmap;
    HBITMAP hPrevBitmap;
    unsigned char* pIn;
    unsigned char* pOut;
    int rowIndex;
    int colIndex;
    /* Get a DC from the desktop window */
    screenDC = GetDC(handle);
    GetClientRect(handle, &screenRect );
    /* Determine the extra bytes we need per row (each row of bitmap data must end on a 32bit boundary) */
    extraBytesPerRow = ( texWidth * 3 ) % 4;
    extraBytesPerRow = extraBytesPerRow ? 4 - extraBytesPerRow : 0;
    /* Setup the bitmap info structure */
    memset( &bitmapInfo, 0, sizeof( bitmapInfo ) );
    bitmapInfo.bmiHeader.biSize = sizeof( BITMAPINFOHEADER );
    bitmapInfo.bmiHeader.biWidth = texWidth;
    bitmapInfo.bmiHeader.biHeight = texHeight;
    bitmapInfo.bmiHeader.biPlanes = 1;
    bitmapInfo.bmiHeader.biBitCount = 24;
    bitmapInfo.bmiHeader.biCompression = BI_RGB;
    /* Create a bitmap device context (bitmapDataPtr will be a pointer to the bits in the bitmap) */
    bitmapDC = CreateCompatibleDC( NULL );
    hBitmap = CreateDIBSection( bitmapDC, ( BITMAPINFO* )&bitmapInfo.bmiHeader, DIB_RGB_COLORS, &bitmapDataPtr, NULL, 0 );
    hPrevBitmap = ( HBITMAP )SelectObject( bitmapDC, hBitmap );
    /* BitBlt or StretchBlt the image from the input DC into our bitmap DC */
    if ( ( texWidth != screenRect.right ) || ( texHeight != screenRect.bottom ) )
    {
        SetStretchBltMode( bitmapDC, HALFTONE );
        StretchBlt( bitmapDC, 0, 0, texWidth, texHeight, screenDC, 0, 0, screenRect.right, screenRect.bottom, SRCCOPY );
    }
    else
    {
        BitBlt( bitmapDC, 0, 0, texWidth, texHeight, screenDC, 0, 0, SRCCOPY);
    }
    /* Copy the data from the bitmap to the user's buffer (bitmap data is BGR and 4 byte aligned on each row, we want tightly-packed RGB) */
    pIn = ( unsigned char* )bitmapDataPtr;
    pOut = pBuffer;
    for ( rowIndex = 0; rowIndex < texHeight; rowIndex++ )
    {
        for ( colIndex = 0; colIndex < texWidth; colIndex++ )
        {
            pOut[ 0 ] = pIn[2];
            pOut[ 1 ] = pIn[1];
            pOut[ 2 ] = pIn[0];
            pOut += 3;
            pIn += 3;
        }
        pIn += extraBytesPerRow;
    }
    /* Free memory used by the bitmap */
    SelectObject( bitmapDC, hPrevBitmap );
    DeleteObject( hBitmap );
    DeleteDC( bitmapDC );
    /* Release the screen DC */
    ReleaseDC(handle, screenDC );
}

You dont actually need to force a redraw.. But in case the window is minimised, you might need to bring it up, before you call the function with the window handle..! texWidth and texHeight are dimension of the window you are about to capture; to get this you can use, GetWindowRect(..) or check out the link here: link

Srivatsan Iyer