2009-07-19 15 views
4

Hintergrundinfo: Ich habe diese MFC-Anwendung, die ich codiert und für eine lange Zeit verwendet, dass ziemlich automatisch Screenshots auf der Festplatte speichert, wenn der Benutzer den Druckbildschirm/Alt + Druck Bildschirmtaste. Ich habe bis jetzt alles, was mit Aero zusammenhängt, aufgeschoben, dass ich Windows 7 RC seit ein paar Wochen benutze. Das Problem: Ich verwende die Standardmethode GetDC/BitBlt, um den Fensterinhalt zu erfassen. Ich habe keine Probleme mit dieser Methode während der normalen Vollbild-Grabs (egal wie viele Fenster geöffnet sind usw.). Das Problem tritt auf, wenn ich versuche, das Vordergrundfenster (Alt + PrintScreen) zu erfassen. Hier sind zwei Beispiele:Wie screencapture ich ein bestimmtes Fenster auf Aero/DWM

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

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

Wie Sie sehen, ich bin immer Müll, wo die Grenzen liegen. Dies ist eher nach oben hin sichtbar, wo wir in beiden Screenshots eine Duplizierung der Werkzeugleiste sehen können.

Ich habe jetzt stundenlang gegooglet und alles, was ich finden kann, sind Artikel, die sagen, dass die BitBtl/GetDC Methode unter DWM nicht funktioniert, aber keinen einzigen finden kann, der erklärt, was wir (die Entwickler) sollte tun, um die gleiche Funktionalität in unseren Apps bei DWM zu erhalten.

Jede Hilfe, Hinweise, Anregungen werden sehr geschätzt.

+0

Viele Entschuldigungen auf den Bildlinks funktionieren nicht. Ich habe die Domain nie erneuert und vergessen, meine Bilder zu sichern. – enriquein

Antwort

2

Es ist eine ausgezeichnete Frage, die ich leider genaue Antwort nicht kenne. Meine erste Idee war, den ganzen Desktop zu schnappen und interessante Teile daraus zu schneiden.

Ich habe in QT 4.5 Quellen gegraben, um zu sehen, wie sie es tun, und so etwas gefunden. Wenn Sie GetClientRect zu GetWindowRect wechseln und den QT-Standardcode entfernen, sollten Sie erhalten, was Sie wollen. Es sieht aus wie ein Hack aber :)


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

Das könnte funktionieren, es für eine Drehung herausnehmen. Vielen Dank, wird mit Ergebnissen folgen. – enriquein

+0

Leider funktioniert dieser Ansatz je nach Situation nicht genau. Aus irgendeinem Grund verpasse ich einen Teil des oberen Fensters. Manchmal kann ich auch die Unschärfe von den Fenstern hinten sehen, und manchmal bekomme ich keinen Rahmen und manchmal einen schwarzen Rand (die Desktop-Farbe ist schwarz). Ich schätze, dieser Ansatz wird schwierig sein, richtig zu arbeiten. Trotzdem danke. – enriquein

+0

"Meine erste Idee war, den ganzen Desktop zu greifen und interessante Teile daraus zu schneiden." Ich bin schließlich dazu gekommen, diese Methode tatsächlich auszuprobieren und es funktioniert großartig. Bonus tolle Punkte, da es nur 3 Zeilen Code aus der bestehenden Codebasis geändert hat. – enriquein

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

Zum Vergleich: DumpImage ziemlich verwendet die Gdi :: Bitmap des Save-Methode. Es wurde weggelassen, da es einen für den Anwendungsfall nicht relevanten appspezifischen Code enthält. Ein zusätzlicher Bonus ist, dass wenn Sie sich fragen, wie Sie den Cursor in Ihren Screengrab einbinden können, der Code auch da ist. Ich hoffe es hilft. Erwähnenswert ist auch, dass dies im Gegensatz zum herkömmlichen Ansatz auch alle überlagerten Fenster über dem erfassten Fenster enthält. Wenn Sie diesen Code für Vollbild-Captures verwenden, sollten Sie außerdem darauf achten, dass keine Videospielfenster erfasst werden. Ich frage mich wirklich, wie man das richtig macht, ohne auf DirectX zurückgreifen zu müssen. Dies betrifft nur Windows Vista/7 im Aero-Modus.