Click here to Skip to main content
Email Password   helpLost your password?

Sample Image - capture.gif

Introduction

This article presents a C# routine for capturing an entire web page as an image. Many capture examples show how to grab a screen shot, but do not show how to gather information that is below the scrolling region of an application. The most common example of a scrolling problem or �run-over� program is a web page.

This application grabs the page, plus, as a bonus, it demonstrates how to let the client adjust the size of the image and the quality of the JPEG. It shows how to write the name of the webpage onto the image, draw Standard Resolution Guides, save a bitmap as a JPEG and open the directory where the captures are stored.

Background

In a recent application, I wanted to provide our Quality Assurance testers the ability to capture an entire web page. I wanted them to do this by clicking a button from within a BHO (Browser Helper Object) that is used for another testing task. I also wanted to reduce the size of the capture, because the images are e-mailed and can quickly fill up our mailbox quotas.

Using the code

The easiest way to use this code is to download the source, trim out the code functions that may not be wanted (quality of capture, size of image, URL writing, guides, or the open directory function). After the code is trimmed down and the program can compile without errors, copy the source and its dependencies into the desired project.

The first issue to face when copying the source code into a project is the need to refer SHDocVw.dll and MSHTML.dll. In Visual Studio, go to Project, Add Reference, and then select the COM tab. Now, go down to the Microsoft section and look for "Microsoft Internet Controls". Select it, and then find "Microsoft HTML Object Library" (see the above image).

After adding the references, add these necessary directives into the project. (A few other directives are needed, if the code is not loaded into a form.)

using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.IO;
using System.Drawing.Imaging;
using SHDocVw;
using mshtml;

Import user32 functions

[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr FindWindowEx(IntPtr parent /*HWND*/, 
  IntPtr next /*HWND*/, string sClassName, IntPtr sWindowTitle);

[DllImport("user32.dll", ExactSpelling=true, CharSet=CharSet.Auto)] 
public static extern IntPtr GetWindow(IntPtr hWnd, int uCmd); 

[DllImport("user32.Dll")]
public static extern void GetClassName(int h, StringBuilder s, int nMaxCount);

[DllImport("user32.dll")]
private static extern bool PrintWindow(IntPtr hwnd, IntPtr hdcBlt, uint nFlags);

public const int GW_CHILD = 5; 
public const int GW_HWNDNEXT = 2;

Find an open browser and assign a browser document for it.

 SHDocVw.WebBrowser m_browser = null;
 SHDocVw.ShellWindows shellWindows = new SHDocVw.ShellWindowsClass();
 
 //Find first availble browser window.

 //Application can easily be modified to loop through and 

 //capture all open windows.

 string filename;
  foreach (SHDocVw.WebBrowser ie in shellWindows)
  {
      filename = Path.GetFileNameWithoutExtension(ie.FullName).ToLower();
      if (filename.Equals("iexplore"))
      {
          m_browser = ie;
          break;  
      }
  }
  if (m_browser == null)
  {   
      MessageBox.Show("No Browser Open");
      return;
  }

  //Assign Browser Document

  mshtml.IHTMLDocument2 myDoc = (mshtml.IHTMLDocument2)m_browser.Document;

The width and height of the web page must be determined along with the resolution settings of the clients screen.

 //Set scrolling on.

 myDoc.body.setAttribute("scroll", "yes", 0);
 
 //Get Browser Window Height

 int heightsize = (int)myDoc.body.getAttribute("scrollHeight", 0);
 int widthsize = (int)myDoc.body.getAttribute("scrollWidth", 0);
 
 //Get Screen Height

 int screenHeight = (int)myDoc.body.getAttribute("clientHeight", 0);
 int screenWidth = (int)myDoc.body.getAttribute("clientWidth", 0);

To capture the whole web page, fragments of the page will have to be grabbed and stitched together to make the whole page. After the first fragment is captured, the browser is scrolled down for the next capture. As the fragments are captured, they are stitched into a target bitmap. The process is repeated until the whole page is captured. For pages that are wider than the clients screen, the page gets scrolled over horizontally, and then the above process is repeated.

 //Get bitmap to hold screen fragment.

 Bitmap bm = new Bitmap(screenWidth, screenHeight, 
    System.Drawing.Imaging.PixelFormat.Format16bppRgb555);
 
 //Create a target bitmap to draw into.

 Bitmap bm2 = new Bitmap(widthsize + URLExtraLeft, heightsize + 
    URLExtraHeight - trimHeight, 
         System.Drawing.Imaging.PixelFormat.Format16bppRgb555);
 Graphics g2 = Graphics.FromImage(bm2);
 
 Graphics g = null;
 IntPtr hdc;
 Image screenfrag = null;
 int brwTop = 0;
 int brwLeft = 0;
 int myPage = 0;
 IntPtr myIntptr = (IntPtr)m_browser.HWND;
 
 //Get inner browser window.

 int hwndInt = myIntptr.ToInt32();
 IntPtr hwnd = myIntptr;
 hwnd = GetWindow(hwnd, GW_CHILD); 
 StringBuilder sbc = new StringBuilder(256);
 
 //Get Browser "Document" Handle

 while (hwndInt != 0) 
 { 
     hwndInt = hwnd.ToInt32();
     GetClassName(hwndInt, sbc, 256);
 
     if(sbc.ToString().IndexOf("Shell DocObject View", 0) > -1)
     {
         hwnd = FindWindowEx(hwnd, IntPtr.Zero, 
             "Internet Explorer_Server", IntPtr.Zero);
         break;
     }                
     hwnd = GetWindow(hwnd, GW_HWNDNEXT);
  } 
 
 //Get Screen Height (for bottom up screen drawing)

 while ((myPage * screenHeight) < heightsize)
 {
     myDoc.body.setAttribute("scrollTop", (screenHeight - 5) * myPage, 0);
     ++myPage;
 }
 
 //Rollback the page count by one

 --myPage;
 
 int myPageWidth = 0;
  while ((myPageWidth * screenWidth) < widthsize)
 {
     myDoc.body.setAttribute("scrollLeft", (screenWidth - 5) * myPageWidth, 0);
     brwLeft = (int)myDoc.body.getAttribute("scrollLeft", 0);
     for (int i = myPage; i >= 0; --i)
     {
         //Shoot visible window

         g = Graphics.FromImage(bm);
         hdc = g.GetHdc();
         myDoc.body.setAttribute("scrollTop", (screenHeight - 5) * i, 0);
         brwTop = (int)myDoc.body.getAttribute("scrollTop", 0);
         PrintWindow(hwnd, hdc, 0);
         g.ReleaseHdc(hdc);
         g.Flush();
         screenfrag = Image.FromHbitmap(bm.GetHbitmap());
         g2.DrawImage(screenfrag, brwLeft + URLExtraLeft, brwTop + 
            URLExtraHeight);
     }
     ++myPageWidth;
 }

Finally, save the above target to a time stamped JPEG file.

Points of Interest

I had a lot of fun and suffered a lot of frustration with this project. The captures are really nice. Try it out on one of the "Code Project" pages.

Not shown in this article, but available in the source is the saving of the file to JPEG. I tried GIF and bitmap, but settled on JPEG for size. The main goal was to be able to e-mail these files without taking up a lot of our mailbox quota.

In the actual application, I have an option to copy the file to the clipboard. I never was able to get the clipboard image into a "device dependent bitmap" state that didn't take up much size. I would copy the image, and then paste it into my Outlook e-mail, only to have the e-mail be about a MB big. When I would open the JPEG in Photoshop, then select it, copy it and paste it into Outlook, the Adobe device dependent bitmap was under 100 KB. The same happened with the simple Windows Paintbrush application.

Because of time constraints, I settled on just copying the JPEG file to Outlook. Any solutions on how to turn a large device independent bitmap into a bitmap with a small memory footprint would be welcomed.

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
Questionurgent****unable to capture some websites***need help***
miteshh
5:25 3 Mar '10  
Hi Douglas,

thank you so much for your code on site snap-shot.... i have modified it so that it can caputre the site you want by chooosing from list of opened sites, with thumbnails...Smile

Frown ... <b> but i have very serious problem.....</b>
it dont caputres some sites...i mean it dont scroll down for google search result...and many sites like orkut it dont work Dead ....please sugesst me some way out Confused .. i need this v urgent dear... .

loadsof thanks in adavance..

regards
Mack
India
GeneralUsing without the ActiveX references
Dunstad
16:09 23 Feb '10  
Cool code Douglas, it's helped me a lot.
However I've found a way to do the same kind of thing just using the .NET web browser control instead of the ActiveX references:
I just use the PrintWindow reference and then the following code will get back the image of the web browser document:
Bitmap myImage = new Bitmap(this.picBack.Width, this.picBack.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
using (Graphics g = Graphics.FromImage(myImage))
{
IntPtr hdc = g.GetHdc();
PrintWindow(mWebBrowserMain.Handle, hdc, 0);
g.ReleaseHdc(hdc);
g.Flush();
this.picBack.Image = myImage;
}

QuestionImage cant open.
Hardikasd
2:55 16 Feb '10  
When i run this project. It is giving me image but image is whole black and webpage is something different
GeneralCode used in Firefox
Member 3828123
23:53 22 Jan '10  
Thanks Douglas, this article is very helpful. But I have a question need suggestions: how can we make the same with Firefox (if can, this project will be very powerful)?

Any suggestion is appreciate.

Regards,
Danh.
Generalnice one
Josh Andy
2:18 10 Dec '09  
Helpful,
Thank you.Thumbs Up
GeneralThe article was truly truly amazing.
chowdarysway
10:46 2 Nov '09  
Wow!
Let me say this, this was the most amazing and fun filled article i found on code project.
I hope the comments section comes back to life, so that the article can get more improvements.

If anyone is there reading this, i would like to know is there a way to determine whether the IE is minimized or not, so that instead of getting a black image, the app can just show a message, like maximize IE to get a screen shot.

I would once again like to thank the author Douglas M.Weems for such a beautiful article.
GeneralGetting vector-based enhanced metafile from WebBrowser?
jasonharrison
16:14 21 Aug '09  
I see lots of this code around which calls Print or PrintWindow to a Bitmap based HDC. Is it possible to get an enhanced metafile from a WebBrowser object with vector contents. So far all I'm able to get is a BitBlt record wrapped in an EMF using OleDraw or IViewObject::Draw.

-Jason
GeneralWay to Modify This to Mimic 'ALT+PrintScreen'?
eggnturtle
13:48 12 Aug '09  
Hi,

Thanks for the code! I was able to successfully do a screen shot of my browser window. However this seems to identical to User32.GetDesktopWindow() that I had also used. I dont want anything but the actual browser window (just like ALT+PrintScreen) Is there a way to modify variables/function parameters or use a different dll function to just capture the window without anything else in background? Please advise, thanks so much!
QuestionProbelm with some Google services, got the incorrect value of scroll height and width
Biii
22:15 14 Jun '09  
as title, always got the same value which equals the client height and width.

cannot scroll:
http://www.google.com/mail
http://www.google.com/reader

can be scrolled:
http://www.google.com/nwshp?tab=mn
http://www.google.com/calendar/render?tab=oc

Do you have any idea? Thumbs Up
GeneralThere is a vertical line on the picture if browser window has horizontal scrollbar
Shuriken87
3:03 17 May '09  
There is a vertical line on the picture if browser window has horizontal scrollbar.
GeneralFull Featured CodeTested In XP With IE7 (Solution From Navid Akhtar)
jsezar
1:54 8 Apr '09  
Copy These Lines to Form1.cs ...

/* ---------------------------------------------------------------------------
*
* Copyright (c) Doug Weems.
*
* You may use this code for fun and knowledge.
* You can compile and use the application as is or copy out what you need.
* This code makes for a really useful tool.
*
* ---------------------------------------------------------------------------
*/

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.IO;
using System.Drawing.Imaging;
using SHDocVw;
using mshtml;


namespace IEWindowCapture
{
/// <summary>
/// This is the working code for "IE Complete Web Window Image Capture". IECWWIC for short.
/// Just kidding. Wink Call it, "WebPageToImage".
/// This tool will pick up one open IE window and capture the entire web page to a single jpeg.
/// It is best if you only are running the IE instance that you are interested in.
/// The quality and size of the image can be adjusted and standard resolution screen sizes and web page name
/// can be added to the image.
/// Author: Doug Weems
/// </summary>
public class frmMain : System.Windows.Forms.Form
{
private System.Windows.Forms.GroupBox grpWebCapture;
private System.Windows.Forms.LinkLabel lnkOpenCapture;
private System.Windows.Forms.CheckBox chkShowGuides;
private System.Windows.Forms.GroupBox groupBox1;
private System.Windows.Forms.ComboBox cmbResolution;
private System.Windows.Forms.Label lblResolution;
private System.Windows.Forms.Label lblQuality;
private System.Windows.Forms.ComboBox cmbQuality;
private System.Windows.Forms.CheckBox chkWriteURL;
private System.Windows.Forms.Button button1;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;



public frmMain()
{
InitializeComponent();
}

/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}

#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(frmMain));
this.grpWebCapture = new System.Windows.Forms.GroupBox();
this.lnkOpenCapture = new System.Windows.Forms.LinkLabel();
this.chkShowGuides = new System.Windows.Forms.CheckBox();
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.cmbResolution = new System.Windows.Forms.ComboBox();
this.lblResolution = new System.Windows.Forms.Label();
this.lblQuality = new System.Windows.Forms.Label();
this.cmbQuality = new System.Windows.Forms.ComboBox();
this.chkWriteURL = new System.Windows.Forms.CheckBox();
this.button1 = new System.Windows.Forms.Button();
this.grpWebCapture.SuspendLayout();
this.groupBox1.SuspendLayout();
this.SuspendLayout();
//
// grpWebCapture
//
this.grpWebCapture.Controls.Add(this.lnkOpenCapture);
this.grpWebCapture.Controls.Add(this.chkShowGuides);
this.grpWebCapture.Controls.Add(this.groupBox1);
this.grpWebCapture.Controls.Add(this.chkWriteURL);
this.grpWebCapture.Controls.Add(this.button1);
this.grpWebCapture.Location = new System.Drawing.Point(28, 12);
this.grpWebCapture.Name = "grpWebCapture";
this.grpWebCapture.Size = new System.Drawing.Size(256, 208);
this.grpWebCapture.TabIndex = 41;
this.grpWebCapture.TabStop = false;
this.grpWebCapture.Text = "Capture Web Page";
//
// lnkOpenCapture
//
this.lnkOpenCapture.Location = new System.Drawing.Point(16, 136);
this.lnkOpenCapture.Name = "lnkOpenCapture";
this.lnkOpenCapture.Size = new System.Drawing.Size(128, 16);
this.lnkOpenCapture.TabIndex = 36;
this.lnkOpenCapture.TabStop = true;
this.lnkOpenCapture.Text = "Open Capture Directory";
this.lnkOpenCapture.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.lnkOpenCapture_LinkClicked);
//
// chkShowGuides
//
this.chkShowGuides.Location = new System.Drawing.Point(16, 112);
this.chkShowGuides.Name = "chkShowGuides";
this.chkShowGuides.Size = new System.Drawing.Size(200, 16);
this.chkShowGuides.TabIndex = 34;
this.chkShowGuides.Text = "draw Standard Resolution Guides";
//
// groupBox1
//
this.groupBox1.Controls.Add(this.cmbResolution);
this.groupBox1.Controls.Add(this.lblResolution);
this.groupBox1.Controls.Add(this.lblQuality);
this.groupBox1.Controls.Add(this.cmbQuality);
this.groupBox1.ForeColor = System.Drawing.Color.Black;
this.groupBox1.Location = new System.Drawing.Point(16, 16);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(200, 80);
this.groupBox1.TabIndex = 39;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "Reduce File Size by reducing Quality and/or Resolution";
//
// cmbResolution
//
this.cmbResolution.Items.AddRange(new object[] {
"100",
"90",
"80",
"70",
"60",
"50",
"40"});
this.cmbResolution.Location = new System.Drawing.Point(120, 32);
this.cmbResolution.Name = "cmbResolution";
this.cmbResolution.Size = new System.Drawing.Size(48, 21);
this.cmbResolution.TabIndex = 31;
this.cmbResolution.Text = "90";
//
// lblResolution
//
this.lblResolution.Location = new System.Drawing.Point(24, 32);
this.lblResolution.Name = "lblResolution";
this.lblResolution.Size = new System.Drawing.Size(88, 16);
this.lblResolution.TabIndex = 35;
this.lblResolution.Text = "% Capture Size";
this.lblResolution.TextAlign = System.Drawing.ContentAlignment.TopRight;
//
// lblQuality
//
this.lblQuality.Location = new System.Drawing.Point(24, 56);
this.lblQuality.Name = "lblQuality";
this.lblQuality.Size = new System.Drawing.Size(88, 16);
this.lblQuality.TabIndex = 38;
this.lblQuality.Text = "Quality";
this.lblQuality.TextAlign = System.Drawing.ContentAlignment.TopRight;
//
// cmbQuality
//
this.cmbQuality.Items.AddRange(new object[] {
"100",
"90",
"80",
"70",
"50",
"30",
"10"});
this.cmbQuality.Location = new System.Drawing.Point(120, 56);
this.cmbQuality.Name = "cmbQuality";
this.cmbQuality.Size = new System.Drawing.Size(48, 21);
this.cmbQuality.TabIndex = 37;
this.cmbQuality.Text = "70";
//
// chkWriteURL
//
this.chkWriteURL.Location = new System.Drawing.Point(16, 96);
this.chkWriteURL.Name = "chkWriteURL";
this.chkWriteURL.Size = new System.Drawing.Size(176, 16);
this.chkWriteURL.TabIndex = 32;
this.chkWriteURL.Text = "write URL name on Image";
//
// button1
//
this.button1.Cursor = System.Windows.Forms.Cursors.Hand;
this.button1.ForeColor = System.Drawing.Color.White;
this.button1.Image = ((System.Drawing.Image)(resources.GetObject("button1.Image")));
this.button1.Location = new System.Drawing.Point(176, 144);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(48, 40);
this.button1.TabIndex = 29;
this.button1.TextAlign = System.Drawing.ContentAlignment.TopCenter;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// frmMain
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(304, 238);
this.Controls.Add(this.grpWebCapture);
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.Name = "frmMain";
this.Text = "Capture";
this.grpWebCapture.ResumeLayout(false);
this.groupBox1.ResumeLayout(false);
this.ResumeLayout(false);

}
#endregion

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new frmMain());
}


//We need some system dll functions.
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr FindWindowEx(IntPtr parent /*HWND*/, IntPtr next /*HWND*/, string sClassName, IntPtr sWindowTitle);

[DllImport("user32.dll", ExactSpelling=true, CharSet=CharSet.Auto)]
public static extern IntPtr GetWindow(IntPtr hWnd, int uCmd);

[DllImport("user32.Dll")]
public static extern void GetClassName(int h, StringBuilder s, int nMaxCount);

[DllImport("user32.dll")]
private static extern bool PrintWindow(IntPtr hwnd, IntPtr hdcBlt, uint nFlags);

public const int GW_CHILD = 5;
public const int GW_HWNDNEXT = 2;
int heightsize;
int widthsize;
int screenHeight;
int screenWidth;
public bool IsDTDDocument(object document)
{
// XHtml declare flag string
string DocTypeContent = @"-//W3C//DTD";
mshtml.IHTMLDocument3 document3 = (mshtml.IHTMLDocument3)document;
mshtml.IHTMLDOMChildrenCollection domChilds = (mshtml.IHTMLDOMChildrenCollection)document3.childNodes;
mshtml.IHTMLDOMNode domNode = (mshtml.IHTMLDOMNode)domChilds.item(0);
return domNode.nodeValue.ToString().Contains(DocTypeContent);
}


private void button1_Click(object sender, System.EventArgs e)
{
//TODO In Next Version:
//Add cursor capture
//Add file naming option
//Add visible screen capture
//Make captured image a DDB not a DIB bitmap.

Cursor.Current = Cursors.WaitCursor;

SHDocVw.WebBrowser m_browser = null;

SHDocVw.ShellWindows shellWindows = new SHDocVw.ShellWindowsClass();

//Find first availble browser window.
//Application can easily be modified to loop through and capture all open windows.
string filename;
foreach (SHDocVw.WebBrowser ie in shellWindows)
{
filename = Path.GetFileNameWithoutExtension(ie.FullName).ToLower();

if (filename.Equals("iexplore"))
{
m_browser = ie;
break;
}
}
if (m_browser == null)
{
MessageBox.Show("No Browser Open");
return;
}

//Assign Browser Document
mshtml.IHTMLDocument2 myDoc = (mshtml.IHTMLDocument2)m_browser.Document;
mshtml.IHTMLDocument3 doc3 = (mshtml.IHTMLDocument3)myDoc;


//URL Location
string myLocalLink = myDoc.url;
int URLExtraHeight = 0;
int URLExtraLeft = 0;

//Adjustment variable for capture size.
if (chkWriteURL.Checked == true)
URLExtraHeight = 25;

//TrimHeight and TrimLeft trims off some captured IE graphics.
int trimHeight = 3;
int trimLeft = 3;

//Use UrlExtra height to carry trimHeight.
URLExtraHeight = URLExtraHeight - trimHeight;
URLExtraLeft = URLExtraLeft - trimLeft;
if (!IsDTDDocument(myDoc))
{
myDoc.body.setAttribute("scroll", "yes", 0);

//Get Browser Window Height
heightsize = (int)myDoc.body.getAttribute("scrollHeight", 0);
widthsize = (int)myDoc.body.getAttribute("scrollWidth", 0);

//Get Screen Height
screenHeight = (int)myDoc.body.getAttribute("clientHeight", 0);
screenWidth = (int)myDoc.body.getAttribute("clientWidth", 0);
}
else
{
doc3.documentElement.setAttribute("scroll", "Yes", 0);

//Get Browser Window Height
heightsize = (int)doc3.documentElement.getAttribute("scrollHeight", 0);
widthsize = (int)doc3.documentElement.getAttribute("scrollWidth", 0);

//Get Screen Height
screenHeight = (int)doc3.documentElement.getAttribute("clientHeight", 0);
screenWidth = (int)doc3.documentElement.getAttribute("clientWidth", 0);

}
//Get bitmap to hold screen fragment.
Bitmap bm = new Bitmap(screenWidth, screenHeight, System.Drawing.Imaging.PixelFormat.Format16bppRgb555);

//Create a target bitmap to draw into.
Bitmap bm2 = new Bitmap(widthsize + URLExtraLeft, heightsize + URLExtraHeight - trimHeight, System.Drawing.Imaging.PixelFormat.Format16bppRgb555);
Graphics g2 = Graphics.FromImage(bm2);

Graphics g = null;
IntPtr hdc;
Image screenfrag = null;
int brwTop = 0;
int brwLeft = 0;
int myPage = 0;
IntPtr myIntptr = (IntPtr)m_browser.HWND;
//Get inner browser window.
int hwndInt = myIntptr.ToInt32();
IntPtr hwnd = myIntptr;
hwnd = GetWindow(hwnd, GW_CHILD);
StringBuilder sbc = new StringBuilder(256);

//Get Browser "Document" Handle
while (hwndInt != 0) {
hwndInt = hwnd.ToInt32();
GetClassName(hwndInt, sbc, 256);
if(sbc.ToString().IndexOf("Shell DocObject View", 0) > -1) //IE6
{
hwnd = FindWindowEx(hwnd, IntPtr.Zero, "Internet Explorer_Server", IntPtr.Zero);
break;
}
if (sbc.ToString().IndexOf("TabWindowClass", 0) > -1) //IE7
{
hwnd = FindWindowEx(hwnd, IntPtr.Zero, "Shell DocObject View", IntPtr.Zero);
hwnd = FindWindowEx(hwnd, IntPtr.Zero, "Internet Explorer_Server", IntPtr.Zero);
break;
}
if (sbc.ToString().IndexOf("Frame Tab", 0) > -1) // IE8
{
hwnd = FindWindowEx(hwnd, IntPtr.Zero, "TabWindowClass", IntPtr.Zero);
hwnd = FindWindowEx(hwnd, IntPtr.Zero, "Shell DocObject View", IntPtr.Zero);
hwnd = FindWindowEx(hwnd, IntPtr.Zero, "Internet Explorer_Server", IntPtr.Zero);
break;
}
hwnd = GetWindow(hwnd, GW_HWNDNEXT);
}

//Get Screen Height (for bottom up screen drawing)
while ((myPage * screenHeight) < heightsize)
{
if (!IsDTDDocument(myDoc))
myDoc.body.setAttribute("scrollTop", (screenHeight - 5) * myPage, 0);
else
doc3.documentElement.setAttribute("scrollTop", (screenHeight - 5) * myPage, 0);
++myPage;
}
//Rollback the page count by one
--myPage;

int myPageWidth = 0;

while ((myPageWidth * screenWidth) < widthsize)
{
if (!IsDTDDocument(myDoc))
myDoc.body.setAttribute("scrollLeft", (screenWidth - 5) * myPageWidth, 0);
else
doc3.documentElement.setAttribute("scrollLeft", (screenWidth - 5) * myPageWidth, 0);
if (!IsDTDDocument(myDoc))
brwLeft = (int)myDoc.body.getAttribute("scrollLeft", 0);
else
brwLeft = (int)doc3.documentElement.getAttribute("scrollLeft", 0);
for (int i = myPage; i >= 0; --i)
{
//Shoot visible window
g = Graphics.FromImage(bm);
hdc = g.GetHdc();
if (!IsDTDDocument(myDoc))
myDoc.body.setAttribute("scrollTop", (screenHeight - 5) * i, 0);
else
doc3.documentElement.setAttribute("scrollTop", (screenHeight - 5) * i, 0);

if (!IsDTDDocument(myDoc))
brwTop = (int)myDoc.body.getAttribute("scrollTop", 0);
else
brwTop = (int)doc3.documentElement.getAttribute("scrollTop", 0);
PrintWindow(hwnd, hdc, 0);
g.ReleaseHdc(hdc);
g.Flush();
screenfrag = Image.FromHbitmap(bm.GetHbitmap());
g2.DrawImage(screenfrag, brwLeft + URLExtraLeft, brwTop + URLExtraHeight);
}
++myPageWidth;
}

//Draw Standard Resolution Guides
if(chkShowGuides.Checked == true)
{
// Create pen.
int myWidth = 1;
Pen myPen = new Pen(Color.Navy, myWidth);
Pen myShadowPen = new Pen(Color.NavajoWhite, myWidth);
// Create coordinates of points that define line.
float x1 = -(float)myWidth - 1 + URLExtraLeft;
float y1 = -(float)myWidth - 1 + URLExtraHeight;

float x600 = 600.0F + (float)myWidth+1;
float y480 = 480.0F + (float)myWidth+1;

float x2 = 800.0F + (float)myWidth+1;
float y2 = 600.0F + (float)myWidth+1;

float x3 = 1024.0F + (float)myWidth+1;
float y3 = 768.0F + (float)myWidth+1;

float x1280 = 1280.0F + (float)myWidth+1;
float y1024 = 1024.0F + (float)myWidth+1;

// Draw line to screen.
g2.DrawRectangle(myPen, x1, y1, x600+myWidth, y480+myWidth);
g2.DrawRectangle(myPen, x1, y1, x2+myWidth, y2+myWidth);
g2.DrawRectangle(myPen, x1, y1, x3+myWidth, y3+myWidth);
g2.DrawRectangle(myPen, x1, y1, x1280+myWidth, y1024+myWidth);

// Create font and brush.
Font drawFont = new Font("Arial", 12);
SolidBrush drawBrush = new SolidBrush(Color.Navy);
SolidBrush drawBrush2 = new SolidBrush(Color.NavajoWhite);

// Set format of string.
StringFormat drawFormat = new StringFormat();
drawFormat.FormatFlags = StringFormatFlags.FitBlackBox;
// Draw string to screen.
g2.DrawString("600 x 480", drawFont, drawBrush, 5, y480 - 20 + URLExtraHeight, drawFormat);
g2.DrawString("800 x 600", drawFont, drawBrush, 5, y2 - 20 + URLExtraHeight, drawFormat);
g2.DrawString("1024 x 768", drawFont, drawBrush, 5, y3 - 20 + URLExtraHeight, drawFormat);
g2.DrawString("1280 x 1024", drawFont, drawBrush, 5, y1024 - 20 + URLExtraHeight, drawFormat);
}

//Write URL
if (chkWriteURL.Checked == true)
{ //Backfill URL paint location
SolidBrush whiteBrush = new SolidBrush(Color.White);
Rectangle fillRect = new Rectangle(0, 0, widthsize, URLExtraHeight+2);
Region fillRegion = new Region(fillRect);
g2.FillRegion(whiteBrush, fillRegion);

SolidBrush drawBrushURL = new SolidBrush(Color.Black);
Font drawFont = new Font("Arial", 12);
StringFormat drawFormat = new StringFormat();
drawFormat.FormatFlags = StringFormatFlags.FitBlackBox;

g2.DrawString(myLocalLink, drawFont, drawBrushURL, 0, 0, drawFormat);
}

//Reduce Resolution Size
double myResolution = Convert.ToDouble(cmbResolution.Text) * 0.01;
int finalWidth = (int)((widthsize + URLExtraLeft) * myResolution);
int finalHeight = (int)((heightsize + URLExtraHeight) * myResolution);
Bitmap finalImage = new Bitmap(finalWidth, finalHeight, System.Drawing.Imaging.PixelFormat.Format16bppRgb555);
Graphics gFinal = Graphics.FromImage((Image)finalImage);
gFinal.DrawImage( bm2, 0, 0, finalWidth, finalHeight);

//Get Time Stamp
DateTime myTime = DateTime.Now;
String format = "MM.dd.hh.mm.ss";

//Create Directory to save image to.
Directory.CreateDirectory("C:\\IECapture");

//Write Image.
EncoderParameters eps = new EncoderParameters(1);
long myQuality = Convert.ToInt64(cmbQuality.Text);
eps.Param[0] = new EncoderParameter( System.Drawing.Imaging.Encoder.Quality, myQuality);
ImageCodecInfo ici = GetEncoderInfo("image/jpeg");
finalImage.Save(@"c:\\IECapture\Captured_" + myTime.ToString(format) + ".jpg", ici, eps);


//Clean Up.
myDoc = null;
g.Dispose();
g2.Dispose();
gFinal.Dispose();
bm.Dispose();
bm2.Dispose();
finalImage.Dispose();

Cursor.Current = Cursors.Default;
}

private static ImageCodecInfo GetEncoderInfo(String mimeType)
{
int j;
ImageCodecInfo[] encoders;
encoders = ImageCodecInfo.GetImageEncoders();
for(j = 0; j < encoders.Length; ++j)
{
if(encoders[j].MimeType == mimeType)
return encoders[j];
}
return null;
}

private void lnkOpenCapture_LinkClicked(object sender, System.Windows.Forms.LinkLabelLinkClickedEventArgs e)
{
Process.Start("explorer.exe", "C:\\IECapture");
}



}
}
GeneralRe: Full Featured CodeTested In XP With IE7 (Solution From Navid Akhtar)
morrischen
9:35 13 Apr '09  
It works fine in some cases but it also failed sometimes. I am still finding th root cause.
Failed Url: http://www.mobile01.com/newsdetail.php?id=7299
Please check!
GeneralRe: Full Featured CodeTested In XP With IE7 (Solution From Navid Akhtar)
wel come to our site
3:02 18 Aug '09  
This does not allow to take screen shot of scrollable pages.
GeneralThe code does not capture the scrolling part.
sumeet1977
22:28 24 Mar '09  
//Get Browser Window Height
int heightsize = getAttribute("scrollHeight", myDoc, doc3);
int widthsize = getAttribute("scrollWidth", myDoc, doc3);

//Get Screen Height
int screenHeight = getAttribute("clientHeight", myDoc, doc3);
int screenWidth = getAttribute("clientWidth", myDoc, doc3);

the heightsize and screenHeight returns the same value
the widthsize and screenWidth returns the same value

so the loop does not count the number of pages

//Get Screen Height (for bottom up screen drawing)
while ((myPage * screenHeight) < heightsize)
{
setAttribute("scrollTop", (screenHeight - 5) * myPage, myDoc, doc3);
++myPage;
}
//Rollback the page count by one
--myPage;

Please let us know the problem ASAP

ff

GeneralBlack Image Problem in Vista
rbunn83815
11:32 12 Mar '09  
You cannot have the browser window minimized when taking the screenshot. Just thought I would share this in case someone else is making the same mistake.
GeneralIE7 and IE8 Compatibility Solution
NealSchafer
17:59 6 Mar '09  
Building on an earlier comment by Daniel Landers, I was able to make this app run with with IE versions 6,7, and 8. Here is the modification to the code to make it work. Find the comment to Get browser "Document" handle and replace the code between that and the "Get Screen Height (for bottom up screen drawing)" comment with the code below.

//Get Browser "Document" Handle
while (hwndInt != 0)
{
hwndInt = hwnd.ToInt32();
GetClassName(hwndInt, sbc, 256);
if(sbc.ToString().IndexOf("Shell DocObject View", 0) > -1) //IE6
{
hwnd = FindWindowEx(hwnd, IntPtr.Zero, "Internet Explorer_Server", IntPtr.Zero);
break;
}
if (sbc.ToString().IndexOf("TabWindowClass", 0) > -1) //IE7
{
hwnd = FindWindowEx(hwnd, IntPtr.Zero, "Shell DocObject View", IntPtr.Zero);
hwnd = FindWindowEx(hwnd, IntPtr.Zero, "Internet Explorer_Server", IntPtr.Zero);
break;
}
if (sbc.ToString().IndexOf("Frame Tab", 0) > -1) // IE8
{
hwnd = FindWindowEx(hwnd, IntPtr.Zero, "TabWindowClass", IntPtr.Zero);
hwnd = FindWindowEx(hwnd, IntPtr.Zero, "Shell DocObject View", IntPtr.Zero);
hwnd = FindWindowEx(hwnd, IntPtr.Zero, "Internet Explorer_Server", IntPtr.Zero);
break;
}

hwnd = GetWindow(hwnd, GW_HWNDNEXT);
}

AnswerRe: IE7 and IE8 Compatibility Solution
Rolf Barbakken
3:39 1 Apr '09  
This works better than the previous code, but does not solve all. Some pages now work fine in IE8 and some are still black.

For instance:
www.alpakka.org works kinda ok. Most of the page is rendered just fine
www.alpakkanorge.no is all black
www.vg.no is fine
www.abb.com mostly renders ok, but a part at the bottom is gone (blackened) like www.alpakka.org
GeneralRe: IE7 and IE8 Compatibility Solution
NealSchafer
10:57 10 Apr '09  
This is really fits more in line with the black images comment below, but another user sent me a new function to replace my isDTDDocument function. This one seems to work a bit more reliably and reslved the problem on the pages you listed.

        public bool IsDTDDocument(object document)
{
bool retVal = false;
IHTMLDocument5 doc5 = (IHTMLDocument5)document;
IHTMLDocument3 doc3 = (IHTMLDocument3)document;
//compatibility mode affects how height is computed
if ((doc3.documentElement != null) && (!doc5.compatMode.Equals("BackCompat")))
{
retVal = true;
}
return retVal;
}


GeneralRe: IE7 and IE8 Compatibility Solution
michael. zhao
5:13 18 May '09  
Actually, there are many software for this purpose, like easyWeb2Pic Pro at www.easywebaction.com, IE7Pro at http://www.ie7pro.com/...
GeneralSolution for Black Images [modified]
NealSchafer
15:18 6 Mar '09  
Navid Akhtar showed a solution for the Black Image problem in his comment titled "Capturing entire web page like msn.com" unfortunately he ended his comment with "Similarly update other location where we are trying to get/set attributes." This may have left some people with the code still not working for pages that have a Strict DTD set.

So to Recap what Navid said, the problem with Black images is that pages with a Strict DTD set have a different structure for accessing the document attributes. Instead of myDoc.body.getAttribute("attributeName") you have to create an object as an IHTMLDocument3 object and access the attributes with thes new object named "doc3" in the example as doc3.documentElement.getAttribute("attributeName").

So Here's a more complete set of code for performing this:

Step 1: This code corresponds to the bottom of the third code section in the article or line 289 of Form1.cs in the provided source code.
  //Assign Browser Document
mshtml.IHTMLDocument2 myDoc = (mshtml.IHTMLDocument2)m_browser.Document;
mshtml.IHTMLDocument3 doc3 = (mshtml.IHTMLDocument3)myDoc ;

Step 2: Add the following Function to your code. I'm adding it to the bottom to not interfere with the line numbers I'll be mentioning later. This code is from Navid Akhtar's comment. I adjusted ths function to return false for documents with a Transitional DTD. Pages with a Transitional DTD work the same as those without a DTD.
public bool IsDTDDocument(object document)
{
bool retVal = false;
// XHtml declare flag string
string DocTypeContent = @"-//W3C//DTD";
mshtml.IHTMLDocument3 document3 = (mshtml.IHTMLDocument3)document;
mshtml.IHTMLDOMChildrenCollection domChilds = (mshtml.IHTMLDOMChildrenCollection)document3.childNodes;
mshtml.IHTMLDOMNode domNode = (mshtml.IHTMLDOMNode)domChilds.item(0);
if (domNode.nodeValue.ToString().Contains(DocTypeContent) && !domNode.nodeValue.ToString().Contains("Transitional"))
retVal = true;
return retVal;
}


Step 3: I've created two more Functions to handle getting and setting attributes so we don't have to have a bunch of if ... else statements throughout the code. Again, I've added these to the end of the class.
public void setAttribute(string attribute, int value, mshtml.IHTMLDocument2 myDoc, mshtml.IHTMLDocument3 doc3)
{
if (!IsDTDDocument(myDoc))
myDoc.body.setAttribute(attribute, value, 0);
else doc3.documentElement.setAttribute(attribute, value, 0);
}

public int getAttribute(string attribute, mshtml.IHTMLDocument2 myDoc, mshtml.IHTMLDocument3 doc3)
{
int retVal = 0;
if (!IsDTDDocument(myDoc))
retVal = (int)myDoc.body.getAttribute(attribute, 0);
else retVal = (int)doc3.documentElement.getAttribute(attribute, 0);
return retVal;
}

Step 4. We go in and change all of the calls to get or set attributes to use the new functions.
change Line 310 - 318
From:
myDoc.body.setAttribute("scroll", "yes", 0);

//Get Browser Window Height
int heightsize = (int)myDoc.body.getAttribute("scrollHeight", 0);
int widthsize = (int)myDoc.body.getAttribute("scrollWidth", 0);

//Get Screen Height
int screenHeight = (int)myDoc.body.getAttribute("clientHeight", 0);
int screenWidth = (int)myDoc.body.getAttribute("clientWidth", 0);
To:
setAttribute("scroll", 1, myDoc, doc3);

//Get Browser Window Height
int heightsize = getAttribute("scrollHeight", myDoc, doc3);
int widthsize = getAttribute("scrollWidth", myDoc, doc3);

//Get Screen Height
int screenHeight = getAttribute("clientHeight", myDoc, doc3);
int screenWidth = getAttribute("clientWidth", myDoc, doc3);
  
Change Line 357
From:
myDoc.body.setAttribute("scrollTop", (screenHeight - 5) * myPage, 0);
To:
setAttribute("scrollTop", (screenHeight - 5) * myPage, myDoc, doc3);
  
Change Lines 367 - 381
From:
myDoc.body.setAttribute("scrollLeft", (screenWidth - 5) * myPageWidth, 0);
brwLeft = (int)myDoc.body.getAttribute("scrollLeft", 0);
for (int i = myPage; i >= 0; --i)
{
//Shoot visible window
g = Graphics.FromImage(bm);
hdc = g.GetHdc();
myDoc.body.setAttribute("scrollTop", (screenHeight - 5) * i, 0);
brwTop = (int)myDoc.body.getAttribute("scrollTop", 0);
PrintWindow(hwnd, hdc, 0);
g.ReleaseHdc(hdc);
g.Flush();
screenfrag = Image.FromHbitmap(bm.GetHbitmap());
g2.DrawImage(screenfrag, brwLeft + URLExtraLeft, brwTop + URLExtraHeight);
}
To:
setAttribute("scrollLeft", (screenWidth - 5) * myPageWidth, myDoc, doc3);
brwLeft = getAttribute("scrollLeft", myDoc, doc3);
for (int i = myPage; i >= 0; --i)
{
//Shoot visible window
g = Graphics.FromImage(bm);
hdc = g.GetHdc();
setAttribute("scrollTop", (screenHeight - 5) * i, myDoc, doc3);
brwTop = getAttribute("scrollTop", myDoc, Doc3);
PrintWindow(hwnd, hdc, 0);
g.ReleaseHdc(hdc);
g.Flush();
screenfrag = Image.FromHbitmap(bm.GetHbitmap());
g2.DrawImage(screenfrag, brwLeft + URLExtraLeft, brwTop + URLExtraHeight);
}

This should make the code in this article work for all pages whether Strict DTD or not.

modified on Thursday, March 19, 2009 1:24 PM

GeneralRe: Solution for Black Images
LiShuangJiang
22:47 6 Apr '09  
Hi, I tried to follow your instruction step by step, but it still not work for me, the tool always return black image to me Cry I'm sure the targe window(IE7) are not minize in taskbar.
GeneralRe: Solution for Black Images
NealSchafer
11:00 10 Apr '09  
Did you look at the IE7 & IE8 compatibility solution listed here?
GeneralRe: Solution for Black Images
Shuriken87
21:53 27 Apr '09  
Thank you for your patch! =) But some web-pages don't scrolling. For example www.microsoft.com. Frown
GeneralRe: Solution for Black Images
NealSchafer
6:31 28 Apr '09  
Try replacing the isDTDDocument function with the following. It seems to be more reliable at determining which pages need to use which method for calculating page size.

public bool IsDTDDocument(object document)        
{
bool retVal = false;
IHTMLDocument5 doc5 = (IHTMLDocument5)document;
IHTMLDocument3 doc3 = (IHTMLDocument3)document;
//compatibility mode affects how height is computed
if ((doc3.documentElement != null) && (!doc5.compatMode.Equals("BackCompat")))
{
retVal = true;
}
return retVal;
}

GeneralRe: Solution for Black Images
Shuriken87
2:31 17 May '09  
Thank you! It's a good code. Smile


Last Updated 22 Jun 2005 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010