WebBrowser中的网页全身照相

来源:互联网 发布:金山强力卸载软件 编辑:程序博客网 时间:2024/06/10 17:05
最近在写程序的时候,突然觉得google chrome的网页缩略照片挺好玩 ,  可是 chrome 是自己的内核, 自家的东西当然方便.WebBrowser 又怎么办?

     首先想到的是最普通的屏幕复制, 也就是大家熟知的bitblt, 从WebBrowser的dc复制到bitmap的dc. 这种方法有很大的局限性: 1.要正确复制,必须保证WebBrowser在屏幕复制的时候必须处于窗口最前端( 就是没有遮蔽物 ), 否则复制出来的图像是有遮盖物. 2.即使没有遮盖物,复制出来的图像往往包含3D Border , Scroll , 这在标准的WebBrowser是必要的.但是对于网页缩略照片,这可就是恶心的东西了.

      于是有人想到了ActiveX里面的一个接口 IViewObject2 , WebBrowser也实现了这个接口, 你只需要从WebBrowser对象中QueryInterface(...)就可以枚举出这个接口. IWebBrowser2* pWB2 = ...//获取WebBrowser接口, pWB2->QueryInterface( IID_IViewObject2 , (void**)&pViewObj2 ); 然后使用 IViewObject2 中的 Draw 这个方法,就可以绘画WebBrowser中网页的内容, 很好,即使窗口是隐藏的也可以画出, 可是,等等, 只要您尝试这个方法, 您就会发现,这个仍然不尽如人意. 因为你只能获取WebBrowser这个窗口大小的网页缩略图 , 而无法获取整个网页的缩略图. 同时, 3D Border, Scroll 仍然折磨着我们. 有人提出了如下代码 , 可以一定程度缓解, 让我们看看:

 

view plain
  1. IHTMLBodyElement* pBody = 0;  
  2.  IHTMLElement* pBodyElem;  
  3.  HRTEST_E( GetHTMLDocument2()->get_body(&pBodyElem) );  
  4.  HRTEST_E( pBodyElem->QueryInterface(IID_IHTMLBodyElement, (void**)&pBody) );  
  5.   
  6.     IHTMLStyle* pStyle;  
  7.     HRTEST_E ( pBodyElem->get_style(&pStyle) );  
  8.  HRTEST_E (  pStyle->put_borderStyle( bsBorderStyle = ::SysAllocString(L"none")) );  
  9.     
  10.      // hide scrollbars  
  11.      HRTEST_E( pBody->put_scroll( bsScrollStyle = ::SysAllocString(L"no")) );  
  12.   // resize the browser component to the size of the HTML content  
  13.      IHTMLElement2* pBodyElement2;  
  14.      HRTEST_E( pBody->QueryInterface(IID_IHTMLElement2, (void**)&pBodyElement2) );  
  15.      long iScrollWidth = 0;  
  16.      HRTEST_E( pBodyElement2->get_scrollWidth(&iScrollWidth) );  
  17.      long iScrollHeight = 0;  
  18.      HRTEST_E( pBodyElement2->get_scrollHeight(&iScrollHeight) );  
  19.   RECT rc = { 0,0,iScrollWidth , iScrollHeight };  
  20.   ::SetWindowPos( GetHWND() , NULL , 0, 0, iScrollWidth , iScrollHeight , SWP_NOREDRAW );  
  21.   SetWebRect(&rc);  
 

忽略其中奇怪的宏,我们可以发现,这个思想就是使用 Body, 来关闭border, 关闭scroll, 在有些场合下是有用的,可是大部分情况,如果页面过大(比如超过屏幕大小), scroll还是会出现. border也不会消失,因为这和body元素没有关系.

       真的没有办法解决吗? 当然有,否则我也不会写那么多东西了, 有一个很著名的好东西, 接口:IDocHostUIHandler , 他有一个方法: GetHostInfo(DOCHOSTUIINFO *pInfo); 可以改变WebBrowser的显示方式.完全不需要使用body来关闭border和scroll这种吃力不讨好的办法. 所以为了去除border, scroll我们只要这么写:

 

view plain
  1. HRESULT   
  2. CShadowWebWindow::GetHostInfo(DOCHOSTUIINFO *pInfo)  
  3. {  
  4.     pInfo->cbSize = sizeof(DOCHOSTUIINFO);  
  5.     pInfo->dwFlags = DOCHOSTUIFLAG_DIALOG |   
  6.                         DOCHOSTUIFLAG_THEME  |   
  7.                         DOCHOSTUIFLAG_NO3DBORDER |   
  8.                         DOCHOSTUIFLAG_SCROLL_NO;  
  9.     return S_OK;  
  10. }  
 

其中DOCHOSTUIFLAG_NO3DBORDER和DOCHOSTUIFLAG_SCROLL_NO正是我们需要的效果. 接下来我们要画出整个网页,只需要设置WebBrowser的容器大小为html的大小, 就可以完满的画出网页缩略图. 

      当然, 在下强烈推荐应当使用一个看不见的WebBrowser来实现化网页缩略图, 毕竟, 一个窗口忽大忽小,不是一个好的用户体验.

      最后整理一下代码:

      我使用一个叫CShadowWebWindow类实现WebBrowser, 这是一个不可见的窗口. 

      以下是关键代码, (请忽略奇怪的宏 )

view plain
  1. void    
  2. CShadowWebWindow::DocumentComplete( IDispatch *pDisp,VARIANT *URL)  
  3. {  
  4.       
  5.     WebBrowserWindow::DocumentComplete( pDisp, URL );  
  6.       
  7.       
  8.     PWSTR npwNewBmpFilePath = NULL;  
  9.     IWebBrowser2* pWB2 = GetWebBrowser2();  
  10.     IViewObject2* pViewObject2 = NULL;  
  11.     HRTEST_E( pWB2->QueryInterface( IID_IViewObject2 , (void**)&pViewObject2 ) );  
  12.      IHTMLElement* pBodyElem;  
  13.      HRTEST_E( GetHTMLDocument2()->get_body(&pBodyElem) );  
  14.    
  15.       // resize the browser component to the size of the HTML content  
  16.       IHTMLElement2* pBodyElement2;  
  17.       HRTEST_E( pBodyElem->QueryInterface(IID_IHTMLElement2, (void**)&pBodyElement2) );  
  18.       long iScrollWidth = 0;  
  19.       HRTEST_E( pBodyElement2->get_scrollWidth(&iScrollWidth) );  
  20.       long iScrollHeight = 0;  
  21.       HRTEST_E( pBodyElement2->get_scrollHeight(&iScrollHeight) );  
  22.           //调整WebBrowser大小和网页大小一致  
  23.       RECT rc = { 0,0,iScrollWidth , iScrollHeight };  
  24.       ::SetWindowPos( GetHWND() , NULL , 0, 0, iScrollWidth , iScrollHeight , SWP_NOREDRAW );  
  25.       SetWebRect(&rc);  
  26.       
  27.     HBITMAP hBitmap = ::CreateCompatibleBitmap( GetDC(GetHWND()), RECTWIDTH(rc),  RECTHEIGHT(rc) );  
  28.     HDC hBitmapDC = ::CreateCompatibleDC( GetDC(GetHWND()) );  
  29.     ::SelectObject( hBitmapDC , hBitmap );  
  30.    
  31.         
  32.     RECTL rctl = { rc.left , rc.top , rc.right , rc.bottom };  
  33.         //绘画WebBrowser中的网页内容  
  34.         //GetHWND()返回WebBrowser容器窗口句柄  
  35.     HRTEST_E( pViewObject2->Draw(DVASPECT_CONTENT,   1,   NULL,   NULL,   ::GetDC(GetHWND()),   hBitmapDC,   &rctl,  NULL ,   NULL,   0) );  
  36.         //写bmp文件  
  37.     BITMAP stBitmap;  
  38.     PBYTE npData;  
  39.     DWORD dwDataSize;  
  40.     DWORD dwBmpDataSize;  
  41.     HANDLE hBmpFile;  
  42.     DWORD dwWritten;  
  43.     NULLTEST_E( ::GetObjectW( hBitmap, sizeof(stBitmap), (PVOID)&stBitmap ));  
  44.     NULLTEST_E( npData =   
  45.         new BYTE[ dwDataSize = ( sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (dwBmpDataSize = ((stBitmap.bmWidth*8+31)/32)*4*stBitmap.bmHeight*3)) ]  );  
  46.       
  47.     BITMAPFILEHEADER* pBmpFileHeader = (BITMAPFILEHEADER*)(npData);  
  48.     pBmpFileHeader->bfType   =   0x4D42;     //   "BM"     
  49.     pBmpFileHeader->bfSize   =   dwDataSize;//sizeof(BITMAPFILEHEADER)  + sizeof(BITMAPINFOHEADER)  +  0  +  dwBmpDataSize ;     
  50.     pBmpFileHeader->bfReserved1   =   0;     
  51.     pBmpFileHeader->bfReserved2   =   0;     
  52.     pBmpFileHeader->bfOffBits   =   sizeof(BITMAPFILEHEADER)  +  sizeof(BITMAPINFOHEADER)  +  0;     
  53.     BITMAPINFOHEADER* pBmpInfoHeader = (BITMAPINFOHEADER*)(npData+sizeof(BITMAPFILEHEADER));  
  54.     pBmpInfoHeader->biSize  = sizeof(BITMAPINFOHEADER);     
  55.     pBmpInfoHeader->biWidth = stBitmap.bmWidth;     
  56.     pBmpInfoHeader->biHeight = stBitmap.bmHeight;     
  57.     pBmpInfoHeader->biPlanes = 1;     
  58.     pBmpInfoHeader->biBitCount = 24;     
  59.     pBmpInfoHeader->biCompression = BI_RGB;     
  60.     pBmpInfoHeader->biSizeImage = 0;     
  61.     pBmpInfoHeader->biXPelsPerMeter = 0;     
  62.     pBmpInfoHeader->biYPelsPerMeter = 0;     
  63.     pBmpInfoHeader->biClrUsed = 0;     
  64.     pBmpInfoHeader->biClrImportant = 0;     
  65.     NULLTEST_E( ::GetDIBits( hBitmapDC, hBitmap, 0 , stBitmap.bmHeight , npData   + sizeof(BITMAPFILEHEADER)+  sizeof(BITMAPINFOHEADER) , (BITMAPINFO*)(npData+sizeof(BITMAPFILEHEADER)), DIB_RGB_COLORS ) );  
  66.    
  67.     //创建文件,写文件  
  68.     VALUETEST_E( hBmpFile = ::CreateFileW( L"e://test.bmp" ,   GENERIC_WRITE,   0,   NULL,   CREATE_ALWAYS,   FILE_ATTRIBUTE_NORMAL ,   NULL) , INVALID_HANDLE_VALUE);  
  69.     FALSETEST_E( ::WriteFile( hBmpFile, npData, dwDataSize , &dwWritten, NULL ) );  
  70.     ::CloseHandle( hBmpFile );  
  71.       
  72. RETURN:  
  73.     DeleteArray( npwNewBmpFilePath );  
  74.     return;  
  75. }  
  76. HRESULT   
  77. CShadowWebWindow::GetHostInfo(DOCHOSTUIINFO *pInfo)  
  78. {  
  79.     pInfo->cbSize = sizeof(DOCHOSTUIINFO);  
  80.     pInfo->dwFlags = DOCHOSTUIFLAG_DIALOG |   
  81.                         DOCHOSTUIFLAG_THEME  |   
  82.                         DOCHOSTUIFLAG_NO3DBORDER |   
  83.                         DOCHOSTUIFLAG_SCROLL_NO;  
  84.     return S_OK;  
  85. }  

      至此,我们已经实现了网页的快照(或者缩略图). 完全可以媲美Chrome的实现. 希望给大家有帮助,我在网上看到的都有这样那样的瑕疵, 在下的实现应该稍微完美一点, 希望对大家有帮助.

      如果我的文章对您有帮助,请您留言说说您的感想,这是对我莫大的鼓励,谢谢!

原创粉丝点击