将WebBrowser中的页面截屏保存为图片

先说一点题外话,将WEB页面渲染成图片有比较好的开源工具,如CutyCapt ,它使用WebKit渲染,兼容多种操作系统,适合于在服务器上作为后台服务运行。


不过,这里说到的是对WebBrowser内的页面进行截图并保存. WebBrowser本质上就是IE内核的浏览器。使用mshtml来渲染页面的话,依赖GDI,所以不可能作为后台服务运行。

获取WebBrowser截屏的方法很多, PrintWindow / IHTMLElementRender / IViewObject。不管使用哪种方法,都需考虑长页面的问题。因为这些方法都只能截屏clientArea区域,也就是说没显示的部分无法截图,必须通过多次截图完成整个页面截图的拼合。


本文使用的是PrintWindow方式,这种方式原理上能够兼容其它所有的浏览器。


首先通过设置ControlSite将浏览器的边框和滚动条隐藏,这样客户区只有WEB页面。

HRESULT FAR EXPORT  CCustomControlSite::XDocHostUIHandler::GetHostInfo( DOCHOSTUIINFO* pInfo )
{
	METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
	
	pInfo->cbSize = sizeof(DOCHOSTUIINFO);
	pInfo->dwFlags = DOCHOSTUIFLAG_DIALOG | 
		DOCHOSTUIFLAG_DISABLE_HELP_MENU |
		DOCHOSTUIFLAG_ACTIVATE_CLIENTHIT_ONLY |
		DOCHOSTUIFLAG_URL_ENCODING_ENABLE_UTF8 |
		DOCHOSTUIFLAG_NO3DOUTERBORDER |
		DOCHOSTUIFLAG_NO3DBORDER |
		DOCHOSTUIFLAG_SCROLL_NO |
		DOCHOSTUIFLAG_USE_WINDOWLESS_SELECTCONTROL;

	pInfo->dwDoubleClick = DOCHOSTUIDBLCLK_DEFAULT;

	return S_OK;
}

然后挂接 document.body.onload事件,保证页面上的图片都加载完成。

void CWebBrowser::OnDocumentComplete( IDispatch *pDisp, VARIANT *URL)
{
	CComQIPtr pWebBrowser2(pDisp);

	if( pWebBrowser2 )
	{
		CComPtr pDispatch;
		if( S_OK == pWebBrowser2->get_Document(&pDispatch) )
		{
			CComQIPtr pDoc2(pDispatch);
			if( pDoc2 )
			{
				BSTR bstrReadyState;
				if( S_OK == pDoc2->get_readyState(&bstrReadyState) )
				{
					if( 0 == _wcsicmp( bstrReadyState, L"complete") )
					{
						CComPtr pWnd2;
						if( S_OK == pDoc2->get_parentWindow(&pWnd2) )
						{
							CComPtr pTopWnd2;
							if( S_OK == pWnd2->get_top(&pTopWnd2) )
							{
								CComQIPtr pTopWnd3(pTopWnd2);
								if( pTopWnd3 )
								{
									VARIANT_BOOL vbSuccess = VARIANT_FALSE;  

									if( m_pOnPageLoadEvent )
									{
										pTopWnd3->detachEvent( _bstr_t(L"onload"), m_pOnPageLoadEvent);
									}

									m_pOnPageLoadEvent = (CDOMEventHandler*)CDOMEventHandler::CreateEventHandler( &CWebBrowser::OnPageLoad, (LONG_PTR)this);

									pTopWnd3->attachEvent( _bstr_t(L"onload")
										, m_pOnPageLoadEvent
										, &vbSuccess
										);
								}
							}
						}
					}
					SysFreeString(bstrReadyState);
				}
			}// pDoc2
		}// get_Document
	}// pWebBrowser2
	
}


当页面onload后,就可以进行截屏了。下面是关键代码,其实原理很简单:

首先创建一个大小等于 document.body.clientWidth 宽, document.documentElement.scrollHeight 高的 画布。 然后依次滚动页面,每次滚动的距离等于客户区的高度,滚动后截图,依此结束。

void CWebBrowser::CaptureToImage()
{
	
	CComQIPtr pDoc2 = this->get_Document();
	CComQIPtr pDoc3(pDoc2);
	if( pDoc2 )
	{
		CComPtr pBodyElem;
		CComPtr pWnd2, pTopWnd2;
		
		if( S_OK == pDoc2->get_body(&pBodyElem) &&
			S_OK == pDoc2->get_parentWindow(&pWnd2) &&
			S_OK == pWnd2->get_top(&pTopWnd2))
		{	
			long nScrollHeight = 0L, nClientWidth = 0L, nClientHeight = 0L;

			CComPtr pDocElem;
			pDoc3->get_documentElement(&pDocElem);
			CComQIPtr pDocElem2(pDocElem);

			CComQIPtr pBodyElem2(pBodyElem);
			pBodyElem2->get_scrollHeight(&nScrollHeight);

			RECT rect;
			GetClientRect(&rect);
			nClientWidth = rect.right - rect.left;
			nClientHeight = rect.bottom - rect.top;
			if( nScrollHeight > 0 && nClientWidth > 0 && nClientHeight > 0 )
			{
				Bitmap bitmap(nClientWidth, nScrollHeight);
				Graphics g(&bitmap);
				HDC hDC = g.GetHDC();
				if (hDC != NULL)
				{
					long nYPos = nScrollHeight - nClientHeight;

					
					do 
					{
						pTopWnd2->scrollTo( 0, nYPos);

						{
							long y1 = 0, y2 = 0;
							pDocElem2->get_scrollTop(&y1);
							pBodyElem2->get_scrollTop(&y2);
							nYPos = max(y1, y2);
						}

						HDC hMemDC = ::CreateCompatibleDC(hDC);
						HBITMAP hBitmap = ::CreateCompatibleBitmap( hDC, nClientWidth, nClientHeight);
						::SelectObject( hMemDC, hBitmap);
						VERIFY(::PrintWindow( GetSafeHwnd(), hMemDC, PW_CLIENTONLY));
						::SelectObject( hMemDC, NULL);

						::BitBlt( hDC, 0, nYPos, nClientWidth, nClientHeight, hMemDC, 0, 0, SRCCOPY);

						::DeleteDC(hMemDC);
						::DeleteObject(hBitmap);
								
						if( nYPos <= 0)
							break;
						nYPos -= nClientHeight;
						if( nYPos < 0 )
							nYPos = 0;
					
						
					} while (true);

						
					g.ReleaseHDC(hDC);

					CLSID pngClsid;  
					GetEncoderClsid(L"image/png", &pngClsid);  

						 
					bitmap.Save(L"L:\\WebSitesMonitoring\\1.png", &pngClsid);
				}
			}
		}
	}
}

最终通过GDI+保存成PNG格式。下面是 GetEncoderClsid方法

int CWebBrowser::GetEncoderClsid(const WCHAR* format, CLSID* pClsid)  
{  
	UINT  num = 0;          // number of image encoders  
	UINT  size = 0;         // size of the image encoder array in bytes  

	ImageCodecInfo* pImageCodecInfo = NULL;  

	GetImageEncodersSize(&num, &size);  
	if(size == 0)  
		return -1;  // Failure  

	pImageCodecInfo = (ImageCodecInfo*)(malloc(size));  
	if(pImageCodecInfo == NULL)  
		return -1;  // Failure  

	GetImageEncoders(num, size, pImageCodecInfo);  

	for(UINT j = 0; j < num; ++j)  
	{  
		if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )  
		{  
			*pClsid = pImageCodecInfo[j].Clsid;  
			free(pImageCodecInfo);  
			return j;  // Success  
		}      
	}  

	free(pImageCodecInfo);  
	return -1;  // Failure  
}  

下面是效果,图片已经被CSDN压缩的不成样子了。。



你可能感兴趣的:(将WebBrowser中的页面截屏保存为图片)