Copyrights @ Journal 2014 - Designed By Templateism - SEO Plugin by MyBloggerLab

Tuesday, August 22, 2006

Create a Thumbnail Image of web page

How do I create cached image of a webpage. I just wanted to create a thumbnail of a webpage based on URL. There is no way, you would find the right stuff to do so. I found Alan Dean's page on Generate an image of a web page. He has explained the technique very well. Described his search for such code. He wrote on his own. There are a very few people got it working. So here is the code, took me a lot of time to get it working. Read through the sample I added here very carefully before you copy the code as is. I tried my best to write all comments to make it easy readable.
Tried it in all possible ways to make it successful the way I wanted. This code works awesome but I wanted to use System.drawing very well to make it work. There is no way I could find to do this without using Win32 Api. Apart from that, I need to add the axWebBrowser control and it needs to be visible on form in order to find the image of the web page. Then how do I do it on the fly on my website. There must be a way to create a thumbnail from webpage with out adding ActiveX controls....
Good Luck!! Let me know if this works for you.
'This is main form Class where you will add the basic functions.

SHDocVw ' Import the shdocvw.dll from Widnows\System32 folder.
Imports mshtml  ' Import the mshtml.dll from Widnows\System32 folder.
Imports System.Runtime.InteropServices ' Need this as we added above two as Interop


Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        'Add the axWebBrowser control from SHDocVw library to your form name it as axWebBrowser
        'Add the Event Handler as DocumentComplete, because we need to capture screen image when document is completely loaded.
        AddHandler axWebBrowser.DocumentComplete, AddressOf Me.OnDocumentComplete
        End Sub
      Private Sub OnDocumentComplete(ByVal sender As Object, ByVal e As AxSHDocVw.DWebBrowserEvents2_DocumentCompleteEvent)
        Dim document As IHTMLDocument2 = CType(Me.axWebBrowser.Document, IHTMLDocument2)
        If Not (document Is Nothing) Then
            Dim element As IHTMLElement = CType(document.body, IHTMLElement)
            If Not (element Is Nothing) Then
                Dim render As IHTMLElementRender = CType(element, IHTMLElementRender)
                If Not (render Is Nothing) Then
                    ' Using
                    Dim graphics As Graphics = Me.pictureBox.CreateGraphics
                        Dim hdcDestination As IntPtr = graphics.GetHdc
                        Dim hdcMemory As IntPtr = gdi32.CreateCompatibleDC(hdcDestination)
                        Dim bitmap As IntPtr = gdi32.CreateCompatibleBitmap(hdcDestination, Me.axWebBrowser.ClientRectangle.Width, Me.axWebBrowser.ClientRectangle.Height)
                        If Not (bitmap = IntPtr.Zero) Then
                            Dim hOld As IntPtr = CType(gdi32.SelectObject(hdcMemory, bitmap), IntPtr)
                            gdi32.BitBlt(hdcMemory, 0, 0, Me.axWebBrowser.ClientRectangle.Width, Me.axWebBrowser.ClientRectangle.Height, hdcDestination, 0, 0, CType(gdi32.TernaryRasterOperations.SRCCOPY, Integer))
                            gdi32.SelectObject(hdcMemory, hOld)

                            'Add a PictureBox control to your form, named pictureBox. This way you can
                            'see the image immediately after it is generated. You can save to FS using Bitmap.Save method.
                            Me.pictureBox.Image = Image.FromHbitmap(bitmap)

                        End If


                        'You must dispose Graphics Object for better use.
                        CType(graphics, IDisposable).Dispose()
                    End Try
                End If
            End If
        End If
    End Sub
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        ' Add a button to you form named Button1.
        ' This way we can control when we want to start Browsing.
        ' Select the URI to be browsed here.
        Me.axWebBrowser.Navigate(New Uri(""))
    End Sub
End Class
'This class for dummy for calling GDI Functions from Win32 Api.
'So that you can encapsulate whole GDI work outside.
Public Class gdi32

    ' I copied all the signatures in this class from 
    Public Declare Function DeleteDC Lib "gdi32.dll" (ByVal hdc As IntPtr) As Boolean
    Public Declare Function SelectObject Lib "gdi32.dll" (ByVal hdc As IntPtr, ByVal hgdiobj As IntPtr) As IntPtr
    Public Declare Function CreateCompatibleDC Lib "gdi32" (ByVal hDC As IntPtr) As IntPtr
    Public Declare Function CreateCompatibleBitmap Lib "gdi32" (ByVal hDC As IntPtr, ByVal nWidth As Integer, ByVal nHeight As Integer) As IntPtr
    Public Declare Function BitBlt Lib "gdi32" (ByVal hDestDC As IntPtr, ByVal x As Integer, ByVal y As Integer, ByVal nWidth As Integer, ByVal nHeight As Integer, ByVal hSrcDC As IntPtr, ByVal xSrc As Integer, ByVal ySrc As Integer, ByVal dwRop As Integer) As Integer
    Enum TernaryRasterOperations As Integer
        SRCCOPY = 13369376      'dest = source
        SRCPAINT = 15597702     'dest = source OR dest
        SRCAND = 8913094        'dest = source AND dest
        SRCINVERT = 6684742     'dest = source XOR dest
        SRCERASE = 4457256      'dest = source AND (NOT dest )
        NOTSRCCOPY = 3342344    'dest = (NOT source)
        NOTSRCERASE = 1114278   'dest = (NOT src) AND (NOT dest)
        MERGECOPY = 12583114    'dest = (source AND pattern)
        MERGEPAINT = 12255782   'dest = (NOT source) OR dest
        PATCOPY = 15728673      'dest = pattern
        PATPAINT = 16452105     'dest = DPSnoo
        PATINVERT = 5898313     'dest = pattern XOR dest
        DSTINVERT = 5570569     'dest = (NOT dest)
        BLACKNESS = 66          'dest = BLACK
        WHITENESS = 16711778    'dest = WHITE
    End Enum
End Class

'Check out Alan Dean's explaination, because I don;t get it. He is smarter than me, so I just believed that it works.
<Guid("3050f669-98b5-11cf-bb82-00aa00bdce0b"), System.Runtime.InteropServices.InterfaceTypeAttribute(System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIUnknown), System.Runtime.InteropServices.ComVisible(True), System.Runtime.InteropServices.ComImport()> _
Interface IHTMLElementRender
    Sub DrawToDC(<System.Runtime.InteropServices.In()> ByVal hDC As IntPtr)
    Sub SetDocumentPrinter(<System.Runtime.InteropServices.In(), System.Runtime.InteropServices.MarshalAs(UnmanagedType.BStr)> ByVal bstrPrinterName As String, <System.Runtime.InteropServices.In()> ByVal hDC As IntPtr)
End Interface


Alan Dean said...

I have been moving my old entries across from dotnetjunkies to my new domain.

As part of that process, I have been reworking some of the content, where it was worth keeping. The "[How To] Generate an image of a web page" has been extensively rewritten and I have also made a Visual Studio 2005 solution available for download as well.


Thanks for the credit given :-)

Megha said...

Hi Vishal,

I want to create a windows service (batch process) which will take the newly added URLs from database and create thumbnails for the URLs. How can I change your code to make a Window's sevice.
Second challege is, if there is popup or confirmation box at the time of navigating to the URL using WebBrowser control, then OnDocumentCompleted event will not be complete and code will get hang at that point. Any solution for this problem?