[OLE/GDI+] Save Thumbnail of a Web Page To File

Using the technique introduced here you can generate a thumbnail of a web page (or part of it) and save it to an image file.

IHTMLElementRender interface allows users to draw the content of an element (usually IHTMLElement) to a device context (DC). The Image class in GDI+ provides a uniform way to save the image as a file.

The code is quite simple and self-explanatory.

IWebBrowser2 *wb = NULL;
IHTMLDocument2 *pDoc = NULL;
IHTMLElement *pBody = NULL;
IHTMLElement2 *pBody2 = NULL;
IHTMLElementRender *pRender = NULL;
IDispatch *pDisp;
 
// ...............
// get the IWebBrowser2 interface from the ActiveX control
// ...............
 
if (wb) {
    // get the <body> element via IHTMLDocument2 interface
    if (SUCCEEDED(wb->get_Document(&pDisp)) && pDisp) {
        if (SUCCEEDED(pDisp->QueryInterface(IID_IHTMLDocument2, (void **) &pDoc)) && pDoc) {
            if (SUCCEEDED(pDoc->get_body(&pBody)) && pBody) {
 
                // get IHTMLElementRender for rendering, IHTMLElement2 for the the dimensions
                if (SUCCEEDED(pBody->QueryInterface(IID_IHTMLElementRender, (void **) &pRender)) && pRender
                        && SUCCEEDED(pBody->QueryInterface(IID_IHTMLElement2, (void **) &pBody2)) && pBody2) {
 
                    // get width & height
                    long width, height;
                    pBody2->get_clientWidth(&width);
                    pBody2->get_clientHeight(&height);
 
                    // create a bitmap with the same dimensions
                    Image *bitmap = new Bitmap(width, height);
 
                    // create a Graphics from Image
                    Graphics *g = Graphics::FromImage(bitmap);
 
                    HDC hDC = g->GetHDC();  // get the DC
                    pRender->DrawToDC(hDC); // draw to DC
                    g->ReleaseHDC(hDC);     // IMPORTANT: MUST RELEASE DC before calling any other GDI+ methods
 
                    CLSID bmpClsid;
                    GetEncoderClsid(L"image/bmp", &bmpClsid);  // helper function by MS
                    bitmap->Save(L"thumbnail.bmp", &bmpClsid); // save to "thumbnail.bmp"
 
                    pBody2->Release();
                    pRender->Release();
                }
 
                pBody->Release();
            }
 
            pDoc->Release();
        }
 
        pDisp->Release();
    }
 
    wb->Release();
}

Of course you need to initialize GDI+ by calling GdiplusStartup and call GdiplusShutdown when terminating.

A sample project can be downloaded from http://playground.softboysxp.org/IHTMLElementRenderDemo.zip

I used code from Michael Chourdakis in his codeproject article

Search MSDN Library for detailed documents on GDI+ and MSHTML.

[Delphi] Get the Raw HTML Source from TWebBrowser

TWebBrowser is Delphi’s wrapper for IWebBrowser2, so the technique covered here also applies to other languages that supports COM.

IWebBrowser2.Document implements IHTMLDocument, IHTMLDocument2 and IHTMLDocument3. Those interfaces, however, did not provide any method to get the source. The closest you can get is by getting the outterHTML property of the IHTMLElement that presents the ‘html’ tag. i.e.

source := ((((WebBrowser1.Document as IHTMLDocument2).all.tags('html') as IHTMLElementCollection).item(0, '')) as IHTMLElement).outerHTML;

However via this method the HTML you get is generated by MSHTML, via the DOM tree, not the original HTML file you downloaded from the server. And all the tags become ugly UPPERCASE.

To get the raw source, we have to turn to another interface, IPersistStreamInit, which provide support for stream-based persistence. Using the Save method of IPersistStreamInit, we can save the raw source to a stream and read it to a string. (Here I took advantage of Delphi’s various wrapper and helper class. Sometimes you can’t help but feel sorry that VCL and Delphi’s nearly dead)

function GetRawHTML(WB: TWebBrowser);
var
stream: IPersistStreamInit;
buffer: TStringStream;
begin
   buffer := TStringStream.Create('');
   try
      stream := WB.Document as IPersistStreamInit;
      if not Assigned(stream) then
         result := ''
      else
         if Succeeded(stream.Save(TStreamAdapter.Create(buffer), true)) then
            result := buffer.DataString;
   finally
        FreeAndNil(buffer);
   end;
end;

More about IPersistStreamInit on MSDN :
http://msdn.microsoft.com/en-us/library/ms682273(VS.85).aspx