正しくエアロ/ DWM上の特定のウィンドウをscreencaptureする方法
-
18-09-2019 - |
質問
背景情報: 私はコード化され、ユーザーがプリントスクリーン/ Altキー+ Print Screenキーを打つとき、かなり自動的にハードディスクにスクリーンショットを保存し、長い時間のために使って、このMFCアプリケーションを持っています。私は今、私は数週間のカップルのためのWindows 7 RCを使用してきたことまで、エアロに関連したものを使用して先送りされています。
問題: 私は、ウィンドウの内容をキャプチャするために、標準のGetDC /のBitBltメソッドを使用しています。通常のフルスクリーングラブをしながら、私は(などを開いているどのように多くのウィンドウに関係なく)この方法で問題ありません。私は、フォアグラウンドウィンドウ(Altキー+ PrintScreenを)をキャプチャしようとすると問題が発生します。ここでは、2つの例があります:
例1 http://indiecodelabs.com/extern/example1.jpgする
実施例2 http://indiecodelabs.com/extern/example2.jpgする
あなたが見ることができるように、私は国境があるべきゴミを取得しています。これは、我々は両方のスクリーンショットでは、ツールバーのいくつかの重複を見ることができるのトップに向かってより顕著です。
私は今、時間これについてグーグルでてきたと私は見つけることができるすべては、DWM下BitBtl / GetDCメソッドは動作しませんという記事がありますが、説明する単一のものを見つけることができない私たち(開発者)がすべきDWM上で動作しているとき、我々のアプリで同じ機能を維持することができるように行います。
すべてのヘルプ、ポインタ、提案は非常に高く評価されます。
解決
これは、私がunfortuneatlyに正確な答えを知らない優れた質問です。私の最初のアイデアは、デスクトップ全体をつかむと、それのうち興味深い部分をカットすることでした。
私は、彼らはそれを行う方法を確認するためにQT 4.5のソースに掘って、このようなものを見つけました。あなたはGetWindowRectにGetClientRectを切り替え、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;
}
他のヒント
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 ::ビットマップのSaveメソッドを使用しています。それは一例に関連していないいくつかのアプリケーション固有のコードを持っているので、それは省略されています。また、追加ボーナスは、あなたのscreengrabにカーソルを含める方法を不思議に思った場合は、コードもあるということです。それが役に立てば幸い。また、伝統的なアプローチに反して、言及する価値が、これはまた、任意のものを含むでしょう、あなたが撮影し、ウィンドウの一番上に持っているかもしれないウィンドウを重ね。あなたは、フル画面キャプチャのために、このコードを使用する場合も、それはビデオゲームのウィンドウをキャプチャしないことを警告されます。私は実際にはDirectXに頼ることなく、適切にそれを行う方法を思ったんだけど。エアロモードで実行しているとき、これは、Windows Vistaの/ 7に影響します。