Question

Infos générales: J'ai cette application MFC je codifiées et utilise depuis longtemps qui permet d'économiser à peu près automatiquement des captures d'écran sur le disque dur lorsque l'utilisateur touche l'écran d'impression / Alt + Touche d'impression écran. Je suis à l'aide à plus tard tout ce qui concerne Aero jusqu'à présent que je l'ai utilisé Windows 7 RC pour quelques semaines.

Le problème: J'utilise la méthode standard GetDC / BitBlt pour capturer le contenu de la fenêtre. Je n'ai aucun problème avec cette méthode tout en faisant régulièrement gagner plein écran (peu importe combien de fenêtres sont ouvertes, etc.). Le problème se pose lorsque je tente de capturer la fenêtre de premier plan (Alt + Impr). Voici deux exemples:

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

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

Comme vous pouvez le voir, je suis en train de déchets où les frontières devraient être. Ceci est plus visible vers le haut, où l'on peut voir la duplication de la barre d'outils dans les deux captures d'écran.

J'ai été googler sur ce depuis des heures et tout ce que je peux trouver sont des articles disant que sous DWM la méthode BitBtl / GetDC ne fonctionnera pas, mais ne peut pas trouver un seul expliquer ce que nous (les développeurs) devraient faire pour être en mesure de maintenir les mêmes fonctionnalités dans nos applications lors de l'exécution sur DWM.

Toute aide, pointeurs, suggestions seront grandement appréciés.

Était-ce utile?

La solution

Il est une excellente question que je ne sais pas unfortuneatly réponse exacte à. Ma première idée était de saisir l'ensemble bureau et couper une partie intéressante de lui.

J'ai creusé dans l'intervalle QT 4.5 sources pour voir comment ils le font, et a trouvé quelque chose comme ça. Si vous passez GetClientRect à GetWindowRect et de la bande de code QT boilerplate vous devriez obtenir ce que vous voulez. Il ressemble à un bidouillage :)


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

Autres conseils

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

Pour référence: DumpImage assez utilise beaucoup la Gdi :: La méthode Save Bitmap. Il a été omis, car il a un code spécifique à l'application qui ne concerne pas l'exemple. En outre un bonus supplémentaire est que si vous vous demandez comment inclure le curseur dans votre screengrab alors le code est également là. J'espère que ça aide. Il convient également de mentionner, contrairement à l'approche traditionnelle, cela comprend aussi toute recouvrit les fenêtres que vous pourriez avoir au-dessus de la fenêtre capturée. De plus, si vous utilisez ce code pour les captures en plein écran, sachez que ce ne sera pas capturer des fenêtres de jeux vidéo. Je suis en fait se demander comment faire correctement sans avoir à recourir à DirectX. Cela affecte uniquement Windows Vista / 7 lors de l'exécution en mode Aero.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top