Pergunta

info Background: Eu tenho esse aplicativo I MFC codificado e usado por um longo tempo que salva praticamente automaticamente screenshots para o disco rígido quando o usuário pressiona o Alt + tecla tela Print Screen / Imprimir. Eu fui adiando usando qualquer coisa relacionada ao Aero até agora que eu estive usando o Windows 7 RC para um par de semanas.

O problema: Eu estou usando o método GetDC / BitBlt padrão para capturar o conteúdo da janela. Não tenho problemas com este método ao fazer grabs em tela cheia regulares (não importa quantas janelas são abertas etc). O problema surge quando eu tento capturar a janela em primeiro plano (Alt + PrintScreen). Aqui estão dois exemplos:

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

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

Como você pode ver, eu estou recebendo lixo onde as fronteiras deveria ser. Isso é mais perceptível em direção ao topo, onde podemos ver alguma duplicação da barra de ferramentas em ambos os screenshots.

Estive pesquisando sobre isso por horas e agora tudo que eu posso encontrar são artigos dizendo que sob DWM o método BitBtl / GetDC não vai funcionar, mas não consegue encontrar um único explicando o que nós (os desenvolvedores) deve não ser capaz de manter a mesma funcionalidade em nossos aplicativos quando executado no DWM.

Qualquer ajuda, ponteiros, sugestões serão muito apreciados.

Foi útil?

Solução

É uma excelente pergunta que eu unfortuneatly não sei resposta exata a. Minha primeira idéia era pegar toda a área de trabalho e cortar parte interessante de fora.

Eu cavei já em QT 4,5 fontes para ver como eles fazem isso, e encontrou algo como isto. Se você alternar GetClientRect para GetWindowRect e tira código clichê QT você deve obter o que deseja. Parece que um hack embora :)


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;
}

Outras dicas

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;
}

Para referência: DumpImage praticamente usa método Save o GDI :: do Bitmap. Ele foi omitido porque tem algum código específico do aplicativo que não é relevante para o exemplo. Também um bônus adicional é que, se você estava pensando como incluir o cursor em sua screengrab em seguida, o código também está lá. Espero que ajude. Também vale a pena mencionar, ao contrário da abordagem tradicional, isto irá também incluir quaisquer janelas sobrepostas que possa ter no topo da janela capturado. Além disso, se você usar este código para capturas de tela cheia, ser avisado que não irá capturar janelas do jogo de vídeo. Na verdade, estou querendo saber como fazer isso corretamente sem ter que recorrer a DirectX. Isso só afeta o Windows Vista / 7 quando executado no modo Aero.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top