Posts tagged ‘Screen Shot’

Programmatically Taking a Full Web Page Screenshot

I needed to programmatically take a screenshot of a full web page, and not just the visible area. When I set out to accomplish this task, I didn’t realize how tough it was actually going to be.

The reason it was tough wasn’t because it was hard to do, but that there were many different ways of doing it, and each had different results. Some ways would only capture the visible screen. Some would be missing backgrounds. Some wouldn’t capture pages that loaded with JavaScript, such as google.com.

I was able to find a way that has worked on every site I’ve tested so far that is a combination of many of the other methods I’ve found. This way also does not require any assembly outside of the System namespace.

The code is pretty small and self explanatory, so here is how I did it.

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

public class ScreenShot
{
[ComImport]
[InterfaceType( ComInterfaceType.InterfaceIsIUnknown )]
[Guid( "0000010d-0000-0000-C000-000000000046" )]
    private interface IViewObject
    {
[PreserveSig]
        int Draw( [In] [MarshalAs( UnmanagedType.U4 )] int dwDrawAspect, int lindex, IntPtr pvAspect,
[In] /*tagDVTARGETDEVICE*/ IntPtr ptd, IntPtr hdcTargetDev, IntPtr hdcDraw,
[In] /*COMRECT*/ Rectangle lprcBounds, [In] /*COMRECT*/ IntPtr lprcWBounds, IntPtr pfnContinue,
[In] int dwContinue );

[PreserveSig]
        int GetColorSet( [In] [MarshalAs( UnmanagedType.U4 )] int dwDrawAspect, int lindex, IntPtr pvAspect,
[In] /*tagDVTARGETDEVICE*/ IntPtr ptd, IntPtr hicTargetDev, [Out] /*tagLOGPALETTE*/ IntPtr ppColorSet );

[PreserveSig]
        int Freeze( [In] [MarshalAs( UnmanagedType.U4 )] int dwDrawAspect, int lindex, IntPtr pvAspect, [Out] IntPtr pdwFreeze );

[PreserveSig]
        int Unfreeze( [In] [MarshalAs( UnmanagedType.U4 )] int dwFreeze );

        void SetAdvise( [In] [MarshalAs( UnmanagedType.U4 )] int aspects, [In] [MarshalAs( UnmanagedType.U4 )] int advf,
[In] [MarshalAs( UnmanagedType.Interface )] /*IAdviseSink*/ IntPtr pAdvSink );

        void GetAdvise( [In] [Out] [MarshalAs( UnmanagedType.LPArray )] int[] paspects,
[In] [Out] [MarshalAs( UnmanagedType.LPArray )] int[] advf,
[In] [Out] [MarshalAs( UnmanagedType.LPArray )] /*IAdviseSink[]*/ IntPtr[] pAdvSink );
    }

    public static Bitmap Create( string url )
    {
        using( var webBrowser = new WebBrowser() )
        {
            webBrowser.ScrollBarsEnabled = false;
            webBrowser.ScriptErrorsSuppressed = true;
            webBrowser.Navigate( url );

            while( webBrowser.ReadyState != WebBrowserReadyState.Complete )
            {
                Application.DoEvents();
            }

            webBrowser.Width = webBrowser.Document.Body.ScrollRectangle.Width;
            webBrowser.Height = webBrowser.Document.Body.ScrollRectangle.Height;

            var bitmap = new Bitmap( webBrowser.Width, webBrowser.Height );
            var graphics = Graphics.FromImage( bitmap );
            var hdc = graphics.GetHdc();

            var rect = new Rectangle( 0, 0, webBrowser.Width, webBrowser.Height );

            var viewObject = (IViewObject)webBrowser.Document.DomDocument;
            viewObject.Draw( 1, -1, (IntPtr)0, (IntPtr)0, (IntPtr)0, hdc, rect, (IntPtr)0, (IntPtr)0, 0 );

            graphics.ReleaseHdc( hdc );

            return bitmap;
        }
    }
}
view raw ScreenShot.cs This Gist brought to you by GitHub.

From here, it’s pretty simple to start adding features like min/max width/height, a capture delay for pages that have Flash on them, etc.

Note: For Flash to work properly, you need to set the project type to x86. If running on a 64-bit system and the project is set to “Any CPU”, Flash won’t work because it’s not compatible with 64-bit systems.