2009-07-19 6 views
4

Фоновая информация: У меня есть приложение MFC Я закодировал и долгое время использовал, что в значительной степени автоматически сохраняет скриншоты на жесткий диск, когда пользователь нажимает на экран печати/Клавиша Alt + Print Screen. Я откладывал использование чего-либо, связанного с Aero, до тех пор, пока я не использовал Windows 7 RC пару недель.Как правильно screencapture конкретного окна на Aero/DWM

Проблема: Я использую стандартный метод GetDC/BitBlt для захвата содержимого окна. У меня нет проблем с этим методом при выполнении регулярных полноэкранных захватов (независимо от того, сколько окон открыто и т. Д.). Проблема возникает, когда я пытаюсь захватить окно переднего плана (Alt + PrintScreen). Вот два примера:

Пример 1 http://indiecodelabs.com/extern/example1.jpg

Пример 2 http://indiecodelabs.com/extern/example2.jpg

Как вы можете видеть, я получаю мусор, где границы должны быть. Это более заметно по отношению к вершине, где мы можем увидеть некоторое дублирование панели инструментов на обоих снимках экрана.

Я уже давно работаю над этим часом, и все, что я могу найти, это статьи, в которых говорится, что в DWM метод BitBtl/GetDC не будет работать, но не может найти никого, объясняющего, что мы (разработчики) должен делать, чтобы поддерживать те же функции в наших приложениях при работе на DWM.

Любая помощь, указатели, предложения будут очень признательны.

+0

Многие извинения за ссылки на изображение не работают. Я никогда не обновлял домен и забыл создавать резервные копии моих изображений. – enriquein

ответ

2

Это отличный вопрос, о котором я, к сожалению, не знаю точного ответа. Моя первая идея заключалась в том, чтобы захватить весь рабочий стол и вырезать из него интересную часть.

Я вырыл в источники QT 4.5, чтобы посмотреть, как они это делают, и нашел что-то вроде этого. Если вы переключите GetClientRect на GetWindowRect и разделите код шаблона QT, вы получите то, что хотите. Это выглядит как взломать, хотя :)


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

Это может сработать, вытащив его для вращения. Большое спасибо, будет следить за результатами. – enriquein

+0

К сожалению, в зависимости от ситуации этот подход не совсем работает. По какой-то причине я пропускаю часть верхней части окна. Также иногда я вижу размытие из окон позади, а в других случаях у меня нет границы, а в других случаях черная рамка (цвет рабочего стола - черный). Я предполагаю, что этот подход будет трудным для правильной работы. Спасибо хоть. – enriquein

+0

«Моя первая идея состояла в том, чтобы захватить весь рабочий стол и вырезать из него интересную часть». Я, наконец, добрался, чтобы попробовать этот метод, и он отлично работает. Бонусные огромные очки, поскольку он включал только изменение трех строк кода из существующей кодовой базы. – 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; 
} 

Для справки: DumpImage в значительной степени использует ВВД :: Сохранить метод растрового изображения. Он был опущен, поскольку в нем есть код приложения, который не имеет отношения к этому примеру. Кроме того, дополнительный бонус заключается в том, что если вам интересно, как включить курсор в ваш screengrab, тогда код тоже есть. Надеюсь, поможет. Также стоит упомянуть, вопреки традиционному подходу, это также будет включать в себя любые наложенные окна, которые вы могли бы иметь поверх захваченного окна. Кроме того, если вы используете этот код для полноэкранных захватов, будьте предупреждены, что он не будет захватывать окна видеоигр. Мне действительно интересно, как это сделать, не прибегая к DirectX. Это влияет только на Windows Vista/7 при работе в режиме Aero.