先说一点题外话,将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;
}
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
}
首先创建一个大小等于 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);
}
}
}
}
}
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
}