I love your thumbnail utility and it works great.
I actually started to use it as a basis for a project that I'm working on where I'm instantiating a WebBrowser control to be used in a Web Application or Web Service to automate the filling in off online forms. I chose this approach rather than using the HttpWebRequest and HttpWebResponse objects because some of the forms we need to fill in our AJAX based and doing that with HttpWebRequest of HttpWebResponse is almost impossible.
I have the code working for the first page, but when I try to navigate to a second page in the process on the same new thread, the DocumentCompleted event never fires. I put in some debugging code and it shows me exactly what happens, basically it just hangs up on my second Navigate() method call and it eventually times out after my 10 second timeout is reached.
Below you will find the results of my debugging, as you can see, it just hangs after Step 8 and then eventually times out, it's supposed to be going to the DocumentCompleted event handler but never does. The code for this class is below, I'm actually using this WebControl in a web application and that's how you would run it, actually in the same way you run the Thumbnail control.
CODE: (I also uploaded the file as well), I left the debugging code in, just comment it out when you try to run.
using System;
using System.Collections.Specialized;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Reflection;
using System.ComponentModel;
using System.Windows.Forms;
using System.Threading;
namespace WebAutomator {
public class OnlineForm : System.Web.UI.Control {
//Control Properties:
public string DocumentText = "";
public int Timeout = 10; //Timeout in seconds for the entire process
private void OnlineForm_Init(EventArgs e){
//Start by going to the Home Page:
string otherTestUrl = "http://www.myfreecorporation.com/";
string otherTestUrl2 = "http://www.myfreecorporation.com/contact-us.aspx";
string[] urlActionArray = new string[2];
urlActionArray[0] = otherTestUrl;
urlActionArray[1] = otherTestUrl2;
//Instantiate the WebBrowserAutomationProcess object:
WebBrowserAutomationProcess ap = new WebBrowserAutomationProcess(urlActionArray, this.Timeout);
//Call the method to start the process:
this.DocumentText = ap.StartProcess();
}
#region Control Init and Render Event Handler
protected override void OnInit(EventArgs e){
OnlineForm_Init(e);
}
protected override void Render(HtmlTextWriter output){
output.Write(this.DocumentText);
}
#endregion
#region Web Browser Automation Section
public class WebBrowserAutomationProcess {
private string _documenttext = "";
private string[] _urlActionArray;
private int _urlActionIndex = 0;
private int _timeout;
private ManualResetEvent _mre = new ManualResetEvent(false);
public WebBrowserAutomationProcess(string[] urlActionArray, int timeout){
this._urlActionArray = urlActionArray;
this._timeout = timeout;
}
//To user the System.Windows.Forms.WebBrowser control in a WebApp or WebService, it must be instantiate and accessed on a new
//STA (Single Threaded Apartment) thread, without doing this, it would never work:
public string StartProcess(){
//Clear all existing steps:
DbHelper.DeleteSteps();
//Instantiate the new thread and set it's apartment state to STA:
Thread th = new Thread(new ThreadStart(_ProcessWebPageAction));
th.SetApartmentState(ApartmentState.STA);
//Start processing the web page action by starting the thread:
th.Start();
DbHelper.InsertStep("After th.Start() in StartProcess method");
//Tell the ManualResetEvent to wait, this freezes the main thread until the new thread has finished it's task:
_mre.WaitOne();
DbHelper.InsertStep("After _mre.WaitOne() in StartProcess method");
//Abort the new thread once the ManualResetEvent has been set (this occurs in the DocumentCompleted event handler):
th.Abort();
DbHelper.InsertStep("After th.Abort() in StartProcess method");
//Finally, return the document text to show we had a successful process which was set in the DocumentCompleted event handler as well:
return this._documenttext;
}
//1st Overloaded Method - Called for the first action:
private void _ProcessWebPageAction(){
//Instantiate the webbrowser object:
WebBrowser wb = new WebBrowser();
wb.AllowNavigation = true;
DbHelper.InsertStep("After wb instanitated in _ProcessWebPageAction() method");
//Set the DocumentCompleted event handler:
wb.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(WebBrowser_DocumentCompleted);
//Navigate to the url for this process:
string url = this._urlActionArray[this._urlActionIndex];
wb.Navigate(url);
DbHelper.InsertStep("After wb.Navigate() to " + url);
//Get the start time of navigate method:
DateTime startTime = DateTime.Now;
//This loop serves 2 very important purposes, the first it so keep things going with the DoEvents() call so the
//DcoumentCompleted event is actually trigger and the second purpose is that it provides a scheduled timeout if
//the process as a whole is running longer than it should:
while (true){
Thread.Sleep(0);
TimeSpan elapsedTime = DateTime.Now - startTime;
if (elapsedTime.Seconds >= this._timeout){
DbHelper.InsertStep("Before _mre.Set() after TimeOut in _ProcessWebPageAction method");
_mre.Set();
}
//Call DoEvents() to ensure that the DocumentCompleted event fires:
Application.DoEvents();
}
}
//2nd Overloaded Method - Called for the second and all subsequent actions:
private void _ProcessWebPageAction(object sender){
//Cast the sender to a WebBrowser, essentially it's the same object instantiated at the very start of the thread:
WebBrowser wb = (WebBrowser)sender;
wb.AllowNavigation = true;
DbHelper.InsertStep("After wb instanitated in _ProcessWebPageAction(object sender) method");
//Remove the previous DocumentCompletedEventHandler and add it again:
wb.DocumentCompleted -= new WebBrowserDocumentCompletedEventHandler(WebBrowser_DocumentCompleted);
wb.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(WebBrowser_DocumentCompleted);
//Navigate to the url for this process:
string url = this._urlActionArray[this._urlActionIndex];
wb.Navigate(url);
DbHelper.InsertStep("After wb.Navigate() to " + url);
//Get the start time of navigate method:
DateTime startTime = DateTime.Now;
//This loop serves 2 very important purposes, the first it so keep things going with the DoEvents() call so the
//DcoumentCompleted event is actually trigger and the second purpose is that it provides a scheduled timeout if
//the process as a whole is running longer than it should:
while (true){
Thread.Sleep(0);
TimeSpan elapsedTime = DateTime.Now - startTime;
if (elapsedTime.Seconds >= this._timeout){
DbHelper.InsertStep("Before _mre.Set() after TimeOut in _ProcessWebPageAction(object sender) method");
_mre.Set();
}
//Call DoEvents() to ensure that the DocumentCompleted event fires:
Application.DoEvents();
}
}
private void WebBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e){
//Cast the sender to a WebBrowser, essentially it's the same object instantiated in the _ProcessWebPageAction method:
WebBrowser wb = (WebBrowser)sender;
DbHelper.InsertStep("After wb object instantiated from sender in DocumentCompleted Event");
//Set the class's _documenttext property to the DocumentText of the WebBrowser control:
this._documenttext = wb.DocumentText;
DbHelper.InsertStep("After this._documenttext set in DocumentCompleted Event");
//Increment the urlIndex:
this._urlActionIndex++;
//Check to see if there are more actions to process:
if (this._urlActionIndex < this._urlActionArray.Length){
DbHelper.InsertStep("Before calling of next action in DocumentCompleted Event");
//Start the next action in the process and pass the WebBrowser object as the sender arg:
_ProcessWebPageAction(wb);
}
else {
//Cleanup the WebBrowser object:
wb.Dispose();
DbHelper.InsertStep("After WebBrowser Dispose() in DocumentCompleted Event");
//If the ManualResetEvent exists, then set it so the WebBrowser thread is aborted and the control is rendered:
if (_mre != null){
DbHelper.InsertStep("Before _mre.Set() in DocumentCompleted Event");
_mre.Set();
}
}
}
}
#endregion
}
}