Domanda

Informazioni sul fondo: Ho questa applicazione MFC ho codificato e utilizza da molto tempo che salva praticamente automaticamente le immagini sul disco rigido quando l'utente preme il tasto dello schermo Print Screen / Alt + Stamp. Ho continuato a rimandare utilizzando tutto ciò che riguarda Aero fino ad ora che sono stato utilizzando Windows 7 RC per un paio di settimane.

Il problema: Sto usando il metodo GetDC / BitBlt standard per catturare il contenuto della finestra. Non ho problemi con questo metodo, mentre facendo regolari palio a schermo intero (non importa quanti finestre sono aperte, ecc). Il problema sorge quando provo catturare la finestra in primo piano (Alt + PrintScreen). Ecco due esempi:

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

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

Come si può vedere, sto diventando spazzatura dove i confini dovrebbero essere. Questo è più evidente verso la parte superiore, in cui possiamo vedere alcuni doppioni della barra degli strumenti in entrambe le immagini.

Sono stato googling su questo per ore e che tutto quello che posso trovare sono articoli che dicono che sotto DWM il metodo BitBtl / GetDC non funziona, ma non riesco a trovare una sola spiegare ciò che noi (gli sviluppatori) dovrebbe fare per essere in grado di mantenere la stessa funzionalità nelle nostre applicazioni durante l'esecuzione su DWM.

Qualsiasi aiuto, puntatori, suggerimenti sarà molto apprezzato.

È stato utile?

Soluzione

E 'una domanda eccellente che mi unfortuneatly non so risposta esatta a. La mia prima idea era quella di afferrare l'intero desktop e tagliare parte interessante fuori di esso.

Ho scavato in Qt 4.5 fonti di vedere come lo fanno, e ho trovato qualcosa di simile. Se si passa a GetClientRect GetWindowRect e striscia codice standard QT si dovrebbe ottenere quello che vuoi. Sembra un hack però :)


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

Altri suggerimenti

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

Per riferimento: DumpImage utilizza praticamente la Gdi :: metodo Save di bitmap. E 'stato omesso perché ha un po' di codice specifico app che non è rilevante per l'esempio. Inoltre un ulteriore bonus è che se vi stavate chiedendo come includere il cursore nella vostra screengrab allora il codice è anche lì. Spero che sia d'aiuto. Vale anche la pena menzionare, in contrasto con l'approccio tradizionale, questo includerà anche qualsiasi sovrapposti finestre si potrebbe avere sulla parte superiore della finestra catturata. Inoltre, se si utilizza questo codice per la cattura a schermo intero, essere avvertito che non sarà catturare finestre di videogiochi. In realtà sto chiedendo come fare correttamente senza dover ricorrere a DirectX. Questo riguarda solo Windows Vista / 7 durante l'esecuzione in modalità Aero.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top