返回GDI+的Bitmap的屏幕捕捉方法和问题

Baryon 2004-06-21 04:34:12
我今天写出了返回GDI+的Bitmap的屏幕捕捉方法,供需要者参考。(记得前几天看到有人问这个问题)代码如下:
enum ScreenType
{
FullScreen,
WorkArea,
ActiveWindow
};

Bitmap* CaptureScreen(ScreenType type)
{
HWND wnd;
HDC hDC;
if (type == ActiveWindow) {
wnd = ::GetActiveWindow();
hDC = ::GetWindowDC(wnd);
}
else {
wnd = ::GetDesktopWindow();
hDC = ::GetDC(NULL);
}

ATLASSERT(::IsWindow(wnd));

CRect rect;

if(type==WorkArea )
{
::SystemParametersInfo(SPI_GETWORKAREA,0,&rect,0);
}
else
{
::GetWindowRect(wnd, &rect);
}

HDC hMemDC = ::CreateCompatibleDC(hDC);

//most of the remaining code is normal GDI stuff
HBITMAP hBitmap = ::CreateCompatibleBitmap(hDC, rect.Width(), rect.Height());
if (hBitmap)
{
HBITMAP hOld = (HBITMAP) ::SelectObject(hMemDC, hBitmap);
BitBlt(hMemDC, 0, 0, rect.Width(), rect.Height(), hDC, 0, 0, SRCCOPY);
SelectObject(hMemDC, hOld);
DeleteDC(hMemDC);
ReleaseDC(NULL, hDC);
}

return Bitmap::FromHBITMAP(hBitmap, NULL);
}

这个方法在大多数情况下是有效的。

存在的问题:
如果屏幕上有半透明窗口,在取得Desktop的时候无法取到。半透明窗口就像完全透明了一下。而键盘上的「Print Screen」却可以正确取到。如果你有正确的办法请告知。
...全文
220 点赞 收藏 11
写回复
11 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
cxzh9888 2004-08-26
不知道有谁有directdraw捕捉屏幕的代码呀?拿出来分享一下呀!
回复
ligh123 2004-08-24
z只要是自己画上去的,就能抓回来,不管是半透明的还是透明的!
回复
Baryon 2004-07-01
经工具检测我上面写的代码仍然存在错误,主要是CImage析构的时机问题。
代码重新修改如下,注意多层大括号的使用技巧,它影响了自动变量的生存期间。

Bitmap* CGdiPlusHelper::CaptureScreen(ScreenType type)
{
HWND wnd;
if (type == ActiveWindow) {
wnd = ::GetActiveWindow();
}
else {
wnd = ::GetDesktopWindow();
}

ATLASSERT(::IsWindow(wnd));

CRect rect;
if(type==WorkArea )
{
::SystemParametersInfo(SPI_GETWORKAREA,0,&rect,0);
}
else
{
::GetWindowRect(wnd, &rect);
}

CWindowDC winDC(wnd);
CImage image;

int nBPP;
nBPP = winDC.GetDeviceCaps(BITSPIXEL) * winDC.GetDeviceCaps(PLANES);
if (nBPP < 24)
nBPP = 24;

BOOL bStat = image.Create(rect.Width(), rect.Height(), nBPP);
ATLASSERT(bStat);

if (!bStat)
return NULL;

Bitmap* pBitmap = NULL;
{
CImageDC imageDC(image);
::BitBlt(imageDC, 0, 0, rect.Width(), rect.Height(), winDC, rect.left, rect.top, SRCCOPY);
{
Bitmap bmpTemp((HBITMAP)image, NULL);
pBitmap = bmpTemp.Clone(0, 0, rect.Width(), rect.Height(), PixelFormat32bppARGB);
if (bmpTemp.GetLastStatus() != Gdiplus::Ok)
{
pBitmap = NULL;
}
}
}

return pBitmap;
}

取得带有透明窗口的Desktop另有办法解决,上面的方法仍然无法解决这个问题。
回复
Baryon 2004-06-23
HDC hDC = ::CreateDC("DISPLAY", NULL, NULL, NULL);
无效。无法解决带有透明窗口的Desktop问题。


回复
dongfa 2004-06-23
对于关透明的问题,试试用下面方法得到屏幕DC

HDC hDC = ::CreateDC("DISPLAY", NULL, NULL, NULL);
回复
Baryon 2004-06-23
发现有一行贴错了
错:CWindowDC winDC(NULL);
正:CWindowDC winDC(wnd);
回复
Baryon 2004-06-23
另外如果desktop上有半透明的窗口,怎样才能像PrintScreen一样正确捕捉到,仍然是技术难题。
难道::GetDC(NULL); 取到的DC里面就没有半透明的图像?
还是根本就不能用GDI方法呢?
或者BitBlt无法正确拷贝半透明图像????
回复
Baryon 2004-06-23
谢谢yuanfeng,

1) GetDC(NULL) does not match with ReleaseDC(hwnd, hDC).

如果GetDC(NULL)不应该对应ReleaseDC(hwnd, hDC)的话,那么MFC/WTL的CWindowDC类就存在BUG,因为他们都没有考虑hwnd==NULL的情况。

2) CreateCompatibleBitmap should be avoid. It will create 256-color bitmap under 8-bit screen display mode. You need a palette for it to be displayed properly. Create a 24-bpp DIB section or 32-bpp DIB section for always getting good result.
谢谢你教会我这么重要的知识。我尝试了去生成DIB,发现完美的实现还是需要做好多工作的。我在后面的实现里使用了CImage封装类来帮助我作这个工作。

3) Bitmap::FromHBITMAP is called twice. Typo?
这是贴代码是时的失误。

4) Are you sure you can call DeleteObject(hBitmap)? Double check GDI+ documentation. Add a comment if it's safe to delete the HBITMAP.
Thanks again。确是如你所说,我做了危险的工作。在后面的代码里我使用了Clone来解决这个问题。

5) Design issue: seperate into two functions, first capture window into HBITMAP, and then the second one convert HBITMAP into GDI+ bitmap, for better reusability.
因为必须考虑资源泄漏等问题,如果我分成不同的函数,那么何时进行资源释放必须谨慎小心。我还是考虑相对安全的实现,返回Bitmap指针。

下面是我修改过的代码,如有问题请指正,谢谢
Bitmap* CGdiPlusHelper::CaptureScreen(ScreenType type)
{
HWND wnd;
if (type == ActiveWindow) {
wnd = ::GetActiveWindow();
}
else {
wnd = ::GetDesktopWindow();
}

ATLASSERT(::IsWindow(wnd));

CRect rect;
if(type==WorkArea )
{
::SystemParametersInfo(SPI_GETWORKAREA,0,&rect,0);
}
else
{
::GetWindowRect(wnd, &rect);
}

CWindowDC winDC(NULL);
CImage image;

int nBPP = winDC.GetDeviceCaps(BITSPIXEL) * winDC.GetDeviceCaps(PLANES);
if (nBPP < 24)
nBPP = 24;

BOOL bStat = image.Create(rect.Width(), rect.Height(), nBPP);
ATLASSERT(bStat);

if (!bStat)
return NULL;

CImageDC imageDC(image);
::BitBlt(imageDC, 0, 0, rect.Width(), rect.Height(), winDC, 0, 0, SRCCOPY);

Bitmap* pBitmap;
{
Bitmap bmpTemp((HBITMAP)image, NULL);
pBitmap = bmpTemp.Clone(0, 0, rect.Width(), rect.Height(), PixelFormat32bppARGB);
}

return pBitmap;
}
回复
FengYuanMSFT 2004-06-22
Bugs and issues

1) GetDC(NULL) does not match with ReleaseDC(hwnd, hDC).

2) CreateCompatibleBitmap should be avoid. It will create 256-color bitmap under 8-bit screen display mode. You need a palette for it to be displayed properly. Create a 24-bpp DIB section or 32-bpp DIB section for always getting good result.

3) Bitmap::FromHBITMAP is called twice. Typo?

4) Are you sure you can call DeleteObject(hBitmap)? Double check GDI+ documentation. Add a comment if it's safe to delete the HBITMAP.

5) Design issue: seperate into two functions, first capture window into HBITMAP, and then the second one convert HBITMAP into GDI+ bitmap, for better reusability.
回复
Baryon 2004-06-22
谢谢指出BUG,我想应该主要是资源泄漏问题吧?修改代码如下,如仍有BUG,望明示

Bitmap* CGdiPlusHelper::CaptureScreen(ScreenType type)
{
HWND wnd;
HDC hDC;
Bitmap* pBitmap = NULL;

if (type == ActiveWindow) {
wnd = ::GetActiveWindow();
hDC = ::GetWindowDC(wnd);
}
else {
wnd = ::GetDesktopWindow();
hDC = ::GetDC(NULL);
}

ATLASSERT(::IsWindow(wnd));

CRect rect;

if(type==WorkArea )
{
::SystemParametersInfo(SPI_GETWORKAREA,0,&rect,0);
}
else
{
::GetWindowRect(wnd, &rect);
}

//most of the remaining code is normal GDI stuff
HDC hMemDC = ::CreateCompatibleDC(hDC);
HBITMAP hBitmap = ::CreateCompatibleBitmap(hDC, rect.Width(), rect.Height());

if (hBitmap)
{
HBITMAP hOld = (HBITMAP) ::SelectObject(hMemDC, hBitmap);
BitBlt(hMemDC, 0, 0, rect.Width(), rect.Height(), hDC, 0, 0, SRCCOPY);
SelectObject(hMemDC, hOld);
//Create GDI+ Bitmap from HBITMAP
pBitmap = Bitmap::FromHBITMAP(hBitmap, NULL);
pBitmap = Bitmap::FromHBITMAP(hBitmap, NULL);

DeleteObject(hBitmap);
}

DeleteDC(hMemDC);
ReleaseDC(wnd,hDC);

return pBitmap;
}
回复
FengYuanMSFT 2004-06-22
Buggy code. Fix it.
回复
发帖
图形处理/算法
创建于2007-09-28

1.9w+

社区成员

VC/MFC 图形处理/算法
申请成为版主
帖子事件
创建了帖子
2004-06-21 04:34
社区公告
暂无公告