最近在写程序的时候,突然觉得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")));
- //hidescrollbars
- HRTEST_E(pBody->put_scroll(bsScrollStyle=::SysAllocString(L"no")));
- //resizethebrowsercomponenttothesizeoftheHTMLcontent
- IHTMLElement2*pBodyElement2;
- HRTEST_E(pBody->QueryInterface(IID_IHTMLElement2,(void**)&pBodyElement2));
- longiScrollWidth=0;
- HRTEST_E(pBodyElement2->get_scrollWidth(&iScrollWidth));
- longiScrollHeight=0;
- HRTEST_E(pBodyElement2->get_scrollHeight(&iScrollHeight));
- RECTrc={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;
- returnS_OK;
- }
其中DOCHOSTUIFLAG_NO3DBORDER和DOCHOSTUIFLAG_SCROLL_NO正是我们需要的效果. 接下来我们要画出整个网页,只需要设置WebBrowser的容器大小为html的大小, 就可以完满的画出网页缩略图.
当然, 在下强烈推荐应当使用一个看不见的WebBrowser来实现化网页缩略图, 毕竟, 一个窗口忽大忽小,不是一个好的用户体验.
最后整理一下代码:
我使用一个叫CShadowWebWindow类实现WebBrowser, 这是一个不可见的窗口.
以下是关键代码, (请忽略奇怪的宏 )
- void
- CShadowWebWindow::DocumentComplete(IDispatch*pDisp,VARIANT*URL)
- {
- WebBrowserWindow::DocumentComplete(pDisp,URL);
- PWSTRnpwNewBmpFilePath=NULL;
- IWebBrowser2*pWB2=GetWebBrowser2();
- IViewObject2*pViewObject2=NULL;
- HRTEST_E(pWB2->QueryInterface(IID_IViewObject2,(void**)&pViewObject2));
- IHTMLElement*pBodyElem;
- HRTEST_E(GetHTMLDocument2()->get_body(&pBodyElem));
- //resizethebrowsercomponenttothesizeoftheHTMLcontent
- IHTMLElement2*pBodyElement2;
- HRTEST_E(pBodyElem->QueryInterface(IID_IHTMLElement2,(void**)&pBodyElement2));
- longiScrollWidth=0;
- HRTEST_E(pBodyElement2->get_scrollWidth(&iScrollWidth));
- longiScrollHeight=0;
- HRTEST_E(pBodyElement2->get_scrollHeight(&iScrollHeight));
- //调整WebBrowser大小和网页大小一致
- RECTrc={0,0,iScrollWidth,iScrollHeight};
- ::SetWindowPos(GetHWND(),NULL,0,0,iScrollWidth,iScrollHeight,SWP_NOREDRAW);
- SetWebRect(&rc);
- HBITMAPhBitmap=::CreateCompatibleBitmap(GetDC(GetHWND()),RECTWIDTH(rc),RECTHEIGHT(rc));
- HDChBitmapDC=::CreateCompatibleDC(GetDC(GetHWND()));
- ::SelectObject(hBitmapDC,hBitmap);
- RECTLrctl={rc.left,rc.top,rc.right,rc.bottom};
- //绘画WebBrowser中的网页内容
- //GetHWND()返回WebBrowser容器窗口句柄
- HRTEST_E(pViewObject2->Draw(DVASPECT_CONTENT,1,NULL,NULL,::GetDC(GetHWND()),hBitmapDC,&rctl,NULL,NULL,0));
- //写bmp文件
- BITMAPstBitmap;
- PBYTEnpData;
- DWORDdwDataSize;
- DWORDdwBmpDataSize;
- HANDLEhBmpFile;
- DWORDdwWritten;
- NULLTEST_E(::GetObjectW(hBitmap,sizeof(stBitmap),(PVOID)&stBitmap));
- NULLTEST_E(npData=
- newBYTE[dwDataSize=(sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+(dwBmpDataSize=((stBitmap.bmWidth*8+31)/32)*4*stBitmap.bmHeight*3))]);
- BITMAPFILEHEADER*pBmpFileHeader=(BITMAPFILEHEADER*)(npData);
- pBmpFileHeader->bfType=0x4D42;//"BM"
- pBmpFileHeader->bfSize=dwDataSize;//sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+0+dwBmpDataSize;
- 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;
- returnS_OK;
- }
至此,我们已经实现了网页的快照(或者缩略图). 完全可以媲美Chrome的实现. 希望给大家有帮助,我在网上看到的都有这样那样的瑕疵, 在下的实现应该稍微完美一点, 希望对大家有帮助.
如果我的文章对您有帮助,请您留言说说您的感想,这是对我莫大的鼓励,谢谢!