最近在写程序的时候,突然觉得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 仍然折磨着我们. 有人提出了如下代码 , 可以一定程度缓解, 让我们看看:
- IHTMLBodyElement* pBody = 0;
- IHTMLElement* pBodyElem;
- HRTEST_E( GetHTMLDocument2()->get_body(&pBodyElem) );
- HRTEST_E( pBodyElem->QueryInterface(IID_IHTMLBodyElement, (void**)&pBody) );
-
- IHTMLStyle* pStyle;
- HRTEST_E ( pBodyElem->get_style(&pStyle) );
- HRTEST_E ( pStyle->put_borderStyle( bsBorderStyle = ::SysAllocString(L"none")) );
-
-
- HRTEST_E( pBody->put_scroll( bsScrollStyle = ::SysAllocString(L"no")) );
-
- IHTMLElement2* pBodyElement2;
- HRTEST_E( pBody->QueryInterface(IID_IHTMLElement2, (void**)&pBodyElement2) );
- long iScrollWidth = 0;
- HRTEST_E( pBodyElement2->get_scrollWidth(&iScrollWidth) );
- long iScrollHeight = 0;
- HRTEST_E( pBodyElement2->get_scrollHeight(&iScrollHeight) );
- RECT rc = { 0,0,iScrollWidth , iScrollHeight };
- ::SetWindowPos( GetHWND() , NULL , 0, 0, iScrollWidth , iScrollHeight , SWP_NOREDRAW );
- SetWebRect(&rc);
忽略其中奇怪的宏,我们可以发现,这个思想就是使用 Body, 来关闭border, 关闭scroll, 在有些场合下是有用的,可是大部分情况,如果页面过大(比如超过屏幕大小), scroll还是会出现. border也不会消失,因为这和body元素没有关系.
真的没有办法解决吗? 当然有,否则我也不会写那么多东西了, 有一个很著名的好东西, 接口:IDocHostUIHandler , 他有一个方法: GetHostInfo(DOCHOSTUIINFO *pInfo); 可以改变WebBrowser的显示方式.完全不需要使用body来关闭border和scroll这种吃力不讨好的办法. 所以为了去除border, scroll我们只要这么写:
- HRESULT
- CShadowWebWindow::GetHostInfo(DOCHOSTUIINFO *pInfo)
- {
- pInfo->cbSize = sizeof(DOCHOSTUIINFO);
- pInfo->dwFlags = DOCHOSTUIFLAG_DIALOG |
- DOCHOSTUIFLAG_THEME |
- DOCHOSTUIFLAG_NO3DBORDER |
- DOCHOSTUIFLAG_SCROLL_NO;
- return S_OK;
- }
其中DOCHOSTUIFLAG_NO3DBORDER和DOCHOSTUIFLAG_SCROLL_NO正是我们需要的效果. 接下来我们要画出整个网页,只需要设置WebBrowser的容器大小为html的大小, 就可以完满的画出网页缩略图.
当然, 在下强烈推荐应当使用一个看不见的WebBrowser来实现化网页缩略图, 毕竟, 一个窗口忽大忽小,不是一个好的用户体验.
最后整理一下代码:
我使用一个叫CShadowWebWindow类实现WebBrowser, 这是一个不可见的窗口.
以下是关键代码, (请忽略奇怪的宏 )
- void
- CShadowWebWindow::DocumentComplete( IDispatch *pDisp,VARIANT *URL)
- {
-
- WebBrowserWindow::DocumentComplete( pDisp, URL );
-
-
- PWSTR npwNewBmpFilePath = NULL;
- IWebBrowser2* pWB2 = GetWebBrowser2();
- IViewObject2* pViewObject2 = NULL;
- HRTEST_E( pWB2->QueryInterface( IID_IViewObject2 , (void**)&pViewObject2 ) );
- IHTMLElement* pBodyElem;
- HRTEST_E( GetHTMLDocument2()->get_body(&pBodyElem) );
-
-
- IHTMLElement2* pBodyElement2;
- HRTEST_E( pBodyElem->QueryInterface(IID_IHTMLElement2, (void**)&pBodyElement2) );
- long iScrollWidth = 0;
- HRTEST_E( pBodyElement2->get_scrollWidth(&iScrollWidth) );
- long iScrollHeight = 0;
- HRTEST_E( pBodyElement2->get_scrollHeight(&iScrollHeight) );
-
- RECT rc = { 0,0,iScrollWidth , iScrollHeight };
- ::SetWindowPos( GetHWND() , NULL , 0, 0, iScrollWidth , iScrollHeight , SWP_NOREDRAW );
- SetWebRect(&rc);
-
- HBITMAP hBitmap = ::CreateCompatibleBitmap( GetDC(GetHWND()), RECTWIDTH(rc), RECTHEIGHT(rc) );
- HDC hBitmapDC = ::CreateCompatibleDC( GetDC(GetHWND()) );
- ::SelectObject( hBitmapDC , hBitmap );
-
-
- RECTL rctl = { rc.left , rc.top , rc.right , rc.bottom };
-
-
- HRTEST_E( pViewObject2->Draw(DVASPECT_CONTENT, 1, NULL, NULL, ::GetDC(GetHWND()), hBitmapDC, &rctl, NULL , NULL, 0) );
-
- BITMAP stBitmap;
- PBYTE npData;
- DWORD dwDataSize;
- DWORD dwBmpDataSize;
- HANDLE hBmpFile;
- DWORD dwWritten;
- NULLTEST_E( ::GetObjectW( hBitmap, sizeof(stBitmap), (PVOID)&stBitmap ));
- NULLTEST_E( npData =
- new BYTE[ dwDataSize = ( sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (dwBmpDataSize = ((stBitmap.bmWidth*8+31)/32)*4*stBitmap.bmHeight*3)) ] );
-
- BITMAPFILEHEADER* pBmpFileHeader = (BITMAPFILEHEADER*)(npData);
- pBmpFileHeader->bfType = 0x4D42;
- pBmpFileHeader->bfSize = dwDataSize;
- pBmpFileHeader->bfReserved1 = 0;
- pBmpFileHeader->bfReserved2 = 0;
- pBmpFileHeader->bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 0;
- BITMAPINFOHEADER* pBmpInfoHeader = (BITMAPINFOHEADER*)(npData+sizeof(BITMAPFILEHEADER));
- pBmpInfoHeader->biSize = sizeof(BITMAPINFOHEADER);
- pBmpInfoHeader->biWidth = stBitmap.bmWidth;
- pBmpInfoHeader->biHeight = stBitmap.bmHeight;
- pBmpInfoHeader->biPlanes = 1;
- pBmpInfoHeader->biBitCount = 24;
- pBmpInfoHeader->biCompression = BI_RGB;
- pBmpInfoHeader->biSizeImage = 0;
- pBmpInfoHeader->biXPelsPerMeter = 0;
- pBmpInfoHeader->biYPelsPerMeter = 0;
- pBmpInfoHeader->biClrUsed = 0;
- pBmpInfoHeader->biClrImportant = 0;
- NULLTEST_E( ::GetDIBits( hBitmapDC, hBitmap, 0 , stBitmap.bmHeight , npData + sizeof(BITMAPFILEHEADER)+ sizeof(BITMAPINFOHEADER) , (BITMAPINFO*)(npData+sizeof(BITMAPFILEHEADER)), DIB_RGB_COLORS ) );
-
-
- VALUETEST_E( hBmpFile = ::CreateFileW( L"e://test.bmp" , GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL , NULL) , INVALID_HANDLE_VALUE);
- FALSETEST_E( ::WriteFile( hBmpFile, npData, dwDataSize , &dwWritten, NULL ) );
- ::CloseHandle( hBmpFile );
-
- RETURN:
- DeleteArray( npwNewBmpFilePath );
- return;
- }
- HRESULT
- CShadowWebWindow::GetHostInfo(DOCHOSTUIINFO *pInfo)
- {
- pInfo->cbSize = sizeof(DOCHOSTUIINFO);
- pInfo->dwFlags = DOCHOSTUIFLAG_DIALOG |
- DOCHOSTUIFLAG_THEME |
- DOCHOSTUIFLAG_NO3DBORDER |
- DOCHOSTUIFLAG_SCROLL_NO;
- return S_OK;
- }
至此,我们已经实现了网页的快照(或者缩略图). 完全可以媲美Chrome的实现. 希望给大家有帮助,我在网上看到的都有这样那样的瑕疵, 在下的实现应该稍微完美一点, 希望对大家有帮助.
如果我的文章对您有帮助,请您留言说说您的感想,这是对我莫大的鼓励,谢谢!