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

来源:互联网 发布:有没有优步抢单软件 编辑:程序博客网 时间:2024/04/29 10:56

先说一点题外话,将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<IWebBrowser2> pWebBrowser2(pDisp);if( pWebBrowser2 ){CComPtr<IDispatch> pDispatch;if( S_OK == pWebBrowser2->get_Document(&pDispatch) ){CComQIPtr<IHTMLDocument2> pDoc2(pDispatch);if( pDoc2 ){BSTR bstrReadyState;if( S_OK == pDoc2->get_readyState(&bstrReadyState) ){if( 0 == _wcsicmp( bstrReadyState, L"complete") ){CComPtr<IHTMLWindow2> pWnd2;if( S_OK == pDoc2->get_parentWindow(&pWnd2) ){CComPtr<IHTMLWindow2> pTopWnd2;if( S_OK == pWnd2->get_top(&pTopWnd2) ){CComQIPtr<IHTMLWindow3> 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<IHTMLDocument2> pDoc2 = this->get_Document();CComQIPtr<IHTMLDocument3> pDoc3(pDoc2);if( pDoc2 ){CComPtr<IHTMLElement> pBodyElem;CComPtr<IHTMLWindow2> 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<IHTMLElement> pDocElem;pDoc3->get_documentElement(&pDocElem);CComQIPtr<IHTMLElement2> pDocElem2(pDocElem);CComQIPtr<IHTMLElement2> 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压缩的不成样子了。。



原创粉丝点击