Pregunta

Información de Antecedentes: Tengo esta aplicación MFC codifiqué y he estado utilizando durante mucho tiempo que ahorra más o menos automáticamente capturas de pantalla para el disco duro cuando el usuario pulsa la tecla de pantalla pantalla de impresión / Alt + Imprimir. He estado posponiendo el uso de cualquier cosa relacionada con Aero hasta ahora que he estado usando Windows 7 RC durante un par de semanas.

El problema: Estoy usando el método estándar GetDC / BitBlt para capturar los contenidos de la ventana. No tengo problemas con este método mientras se hace regularmente en juego a pantalla completa (no importa cómo se abren muchas ventanas, etc). El problema surge cuando intento capturar la ventana de primer plano (Alt + PrintScreen). He aquí dos ejemplos:

Ejemplo 1 http://indiecodelabs.com/extern/example1.jpg

Ejemplo 2 http://indiecodelabs.com/extern/example2.jpg

Como se puede ver, me estoy poniendo la basura en las fronteras deben estar. Esto es más evidente hacia la parte superior, donde podemos ver algunos duplicación de la barra de herramientas en ambas imágenes.

He estado buscando en Google en esto por horas y ahora todo lo que puedo encontrar son artículos diciendo que bajo el método DWM BitBtl / GetDC no va a funcionar, pero no puedo encontrar una sola explicación de lo que nosotros (los desarrolladores) debe no ser capaz de mantener la misma funcionalidad en nuestras aplicaciones cuando se ejecuta en DWM.

Cualquier ayuda, punteros, sugerencias será muy apreciada.

¿Fue útil?

Solución

Es una excelente pregunta que yo no sé unfortuneatly respuesta exacta a. Mi primera idea era tomar todo el escritorio y cortar parte interesante de ella.

He excavado en Qt 4.5 fuentes para ver cómo lo hacen, y encontré algo como esto. Si cambia GetClientRect a GetWindowRect y la tira de código repetitivo QT usted debe conseguir lo que quiere. Parece un truco sin embargo :)


QPixmap QPixmap::grabWindow(WId winId, int x, int y, int w, int h )
{
    RECT r;
    GetClientRect(winId, &r);  
    if (w < 0) w = r.right - r.left;
    if (h < 0) h = r.bottom - r.top;  
    // Create and setup bitmap
    HDC display_dc = GetDC(0);
    HDC bitmap_dc = CreateCompatibleDC(display_dc);
    HBITMAP bitmap = CreateCompatibleBitmap(display_dc, w, h);
    HGDIOBJ null_bitmap = SelectObject(bitmap_dc, bitmap);

    // copy data
    HDC window_dc = GetDC(winId);
    BitBlt(bitmap_dc, 0, 0, w, h, window_dc, x, y, SRCCOPY);

    // clean up all but bitmap
    ReleaseDC(winId, window_dc);
    SelectObject(bitmap_dc, null_bitmap);
    DeleteDC(bitmap_dc);

    QPixmap pixmap = QPixmap::fromWinHBITMAP(bitmap);

    DeleteObject(bitmap);
    ReleaseDC(0, display_dc);

    return pixmap;
}

Otros consejos

BOOL CaptureWindow(const CString& filename)
{
    HWND hWnd = NULL;
    hWnd = ::GetForegroundWindow();   
    if(!hWnd)
    {
        return FALSE;
    }
    CRect rect;
    GetWindowRect(hWnd, &rect);
    rect.NormalizeRect();
    return DoCapture(CPoint(rect.left, rect.top), CSize(rect.Width(), rect.Height()), filename);
}

BOOL DoCapture(const POINT& coords, const SIZE& areaSize, const CString& filename)
{
    CDC dc;
    HDC hdc = GetDC(NULL);  // <-- We use this instead of GetWindowDC. 
                            // This is the only thing I had to change other than 
                            // getting the window coordinates in CaptureWindow()
    dc.Attach(hdc);

    // Create a memory DC into which the bitmap will be captured
    CDC memDC;
    memDC.CreateCompatibleDC(&dc);

    // If there is already a bitmap, delete it as we are going to replace it
    CBitmap bmp;
    bmp.DeleteObject();

    ICONINFO info;
    GetIconInfo((HICON)::GetCursor(), &info);   

    CURSORINFO cursor;
    cursor.cbSize = sizeof(CURSORINFO);
    GetCursorInfo(&cursor);

    bmp.CreateCompatibleBitmap(&dc, areaSize.cx, areaSize.cy);
    CBitmap * oldbm = memDC.SelectObject(&bmp);

    // Before we copy the image in, we blank the bitmap to
    // the background fill color
    memDC.FillSolidRect(&CRect(0,0,areaSize.cx, areaSize.cy), RGB(255,255,255));

    // Copy the window image from the window DC into the memory DC
    memDC.BitBlt(0, 0, areaSize.cx, areaSize.cy, &dc, coords.x, coords.y, SRCCOPY|CAPTUREBLT);

    // This part captures the mouse cursor and paints it on the image.
    if(programSettings.bWantCursor) 
    {    
        int osVersion = OSCheck::GetMajorOSVersion(); // For some reason cursor icons in 
                                                      // versions older than Vista are not
                                                      // top-aligned. So we compensate. 
        int offsetX = (osVersion >= 6) ? 0 : 10;
        int offsetY = (osVersion >= 6) ? 0 : 10;        

        CPoint cursorOffset(cursor.ptScreenPos.x - coords.x - offsetX, cursor.ptScreenPos.y - coords.y - offsetY);

        // Now draw the image of the cursor that we captured during
        // the mouse move. DrawIcon will draw a cursor as well.
        memDC.DrawIcon(cursorOffset, (HICON)cursor.hCursor);
    }
    memDC.SelectObject(oldbm);  

    Bitmap outputBitMap(bmp, NULL);

    // Optionally copy the image to the clipboard.
    if(programSettings.bWantClipboard)
    {
        if(OpenClipboard(NULL))
        {
            EmptyClipboard();
            SetClipboardData(CF_BITMAP, bmp);
            CloseClipboard();
        }
    }

    BOOL success = DumpImage(&outputBitMap, filename);

    DeleteObject(bmp.Detach());
    DeleteDC(dc.Detach());
    DeleteDC(memDC.Detach());
    return success;
}

A modo de referencia: DumpImage utiliza más o menos el IDG :: Guardar método de mapa de bits. Se ha omitido porque tiene algo de código aplicación específica que no es relevante para el ejemplo. También una ventaja añadida es que si usted se preguntaba cómo incluir el cursor en su Screengrab continuación, el código también está ahí. Espero eso ayude. También vale la pena mencionar, a diferencia del enfoque tradicional, esto también va a incluir cualquier ventanas que pueda tener en la parte superior de la ventana superpuesta capturado. Además, si se utiliza este código para las capturas de pantalla completa, se advirtió que no va a capturar ventanas de videojuegos. De hecho, me estoy preguntando cómo hacerlo correctamente sin tener que recurrir a DirectX. Esto sólo afecta a Windows Vista / 7 cuando se ejecuta en modo Aero.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top