문제

배경 정보 : 사용자가 인쇄 화면/Alt+인쇄 화면 키를 누르면 스크린 샷을 하드 디스크에 자동으로 저장하는이 MFC 응용 프로그램이 오랫동안 사용되어 왔습니다. 나는 몇 주 동안 Windows 7 RC를 사용하고 있었기 때문에 지금까지 Aero와 관련된 것을 사용해 왔습니다.

문제 : 표준 GETDC/BITBLT 메소드를 사용하여 창 컨텐츠를 캡처하고 있습니다. 정기적 인 풀 스크린 횡령을하는 동안이 방법에 아무런 문제가 없습니다 (얼마나 많은 창문이 열려 있더라도). 전경 창 (Alt+Printscreen)을 캡처 할 때 문제가 발생합니다. 두 가지 예는 다음과 같습니다.

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

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

보시다시피, 나는 국경이 있어야하는 곳에 쓰레기를 얻고 있습니다. 이것은 두 스크린 샷에서 툴바의 중복을 볼 수있는 상단에 더 눈에 띄게 나타납니다.

나는 지금 몇 시간 동안 이것에 대해 인터넷 검색을 해왔고 내가 찾을 수있는 것은 DWM에서 bitbtl/getdc 메소드가 작동하지 않지만 우리 (개발자)가해야 할 일을 설명하는 하나를 찾을 수 없다는 기사 만 있습니다. DWM에서 실행할 때 앱에서 동일한 기능을 유지할 수 있습니다.

모든 도움, 포인터, 제안은 대단히 감사하겠습니다.

도움이 되었습니까?

해결책

불행히도 정확한 답변을 모르는 훌륭한 질문입니다. 나의 첫 번째 아이디어는 전체 데스크탑을 잡고 흥미로운 부분을 잘라내는 것이 었습니다.

나는 QT 4.5 소스를 파고 그들이 어떻게하는지 확인하고 이와 같은 것을 발견했습니다. GetWindOwRect로 GetClientRect를 전환하고 QT BoilerPlate 코드를 스트립하는 경우 원하는 것을 얻어야합니다. 그래도 해킹처럼 보입니다 :)


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

다른 팁

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는 거의 gdi :: bitmap의 저장 메소드를 사용합니다. 예제와 관련이없는 앱 특정 코드가 있기 때문에 생략되었습니다. 또한 추가 보너스는 스크린 그라브에 커서를 포함시키는 방법이 궁금하다면 코드도 있다는 것입니다. 도움이되기를 바랍니다. 또한 기존의 접근 방식과는 달리 언급 할 가치가있는 곳에는 캡처 된 창 위에있는 오버레이 된 창문도 포함됩니다. 또한이 코드를 전체 화면 캡처에 사용하는 경우 비디오 게임 창을 캡처하지 않을 것입니다. 실제로 DirectX에 의지하지 않고 제대로 수행하는 방법이 궁금합니다. 이는 에어로 모드에서 실행할 때만 Windows Vista/7에만 영향을 미칩니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top