c# thread bitmap
I would like to know if instead of void function in thread, I can pass bitmap function. When I am passing void function there is no error reported but when I am passing the bitmap function I am getting an error. Here is the code:
protected void btnSubmit_Click(object sender, EventArgs e)
{
Thread thr = new Thread(new ThreadStart(OptimizeWebBrowser));
thr.SetApartmentState(ApartmentState.STA);
thr.Start();
}
public System.Drawing.Bitmap CaptureWebPage(string url)
{
System.Windows.Forms.WebBrowser browser = new System.Windows.Forms.WebBrowser();
browser.ScrollBarsEnabled = false;
browser.ScriptErrorsSuppressed = true;
browser.Navigate(url);
while (browser.ReadyState != WebBrowserReadyState.Complete)
System.Windows.Forms.Application.DoEvents();
System.Threading.Thread.Sleep(1500);
int width = browser.Document.Body.ScrollRectangle.Width;
int height = browser.Document.Body.ScrollRectangle.Height;
browser.Width = width;
browser.Height = height;
System.Drawing.Bitmap bmp = new Bitmap(width, height);
browser.DrawToBitmap(bmp, new System.Drawing.Rectangle(0, 0, width, height));
return bmp;
}
protected void OptimizeWebBrowser()
{
System.Windows.Forms.WebBrowser browser = new System.Windows.Forms.WebBrowser();
browser.ScrollBarsEnabled = false;
browser.ScriptErrorsSuppressed = true;
browser.Navigate(currentPageUrl);
while (browser.ReadyState != WebBrowserReadyState.Complete)
System.Windows.Forms.Application.DoEvents();
System.Threading.Thread.Sleep(1500);
int width = browser.Document.Body.ScrollRectangle.Width;
int height = browser.Document.Body.ScrollRectangle.Height;
browser.Width = width;
browser.Height = height;
System.Drawing.Bitmap bmp = new Bitmap(width, height);
browser.DrawToBitmap(bmp, new System.Drawing.Rectangle(0, 0, width, height));
Response.ContentType = "image/jpeg";
Response.AppendHeader("Content-Disposition", "attachment; filename=screenshot.jpg");
bmp.Save(Response.OutputStream, ImageFormat.Jpeg);
bmp.Dispose();
bmp.Dispose();
Response.End();
}
When I am trying now to put CAptureWebPage instead of Optimize开发者_JAVA百科WebBrowser in the ThreadStart, I am getting an error: 'No overload for 'CaptureWebPage' matches delegate 'System.Threading.ThreadStart'.
To pass a parameter to a Thread you should use ParameterizedThreadStart
delegate which has single parameter of object
type. So you should use it as shown below:
System.Drawing.Bitmap bitmapObject;
private static readonly bitmapLock = new object();
// change parameter type to object and int he method just cast it to string
public void CaptureWebPage(object rawUrl)
{
string url = rawUrl.ToString();
lock(bitmapLock)
{
// create a bitmap object
bitmapObject = ...;
}
}
// start thread
Thread thr = new Thread(CaptureWebPage);
thr.Start("url");
PS: code which you've posted is very hard to read, consider some code refactoring and style changes
Haven't had a chance to try this, but the out keyword might work. Set the bitmap as a parameter to your function (keep it as a void function) and pass one in when you create the threadstart by using a ParameterizedThreadStart. As the out keyword is in the function declaration the value will be returned when the function is completed.
If not I would suggest having the bitmap that you output be a global variable that you can access from either thread. This can be accessed as you would normally, though I would suggest using the lock() keyword just in case.
Additionally the BackgroundWorker class in c# allows you to get a return result when the thread is complete by adding a function to the RunWorkerCompleted event
No, you can't pass a method that has a return value. As the ThreadStart
method doesn't wait for the thread to complete, it would not make sense to return anything from the thread as there is nothing waiting for the result.
There are several ways of returning data from a thread, for example using a member variable in the class where the method is declared, but the tricky part is that you won't get the result immediately after starting the thread, so you have to wait for it. Typically you would use a callback method that the thread can call when the data is ready to be used.
Note that your code won't work reliably, ever, because the Response might end before your code is done (this is called a race condition). You should investigate the use of IAsyncHttpHandler.
However, for future reference, this is how you would solve this if there wasn't a race condition.
You need to use a context object. First you need to declare a class that contains all the information required by the method (as well as its return value, or, more ideally something to do when the result is ready - a.k.a. a delegate). E.g.:
class CaptureWebPageContext
{
public string Url;
public Action<Bitmap> ResultDelegate;
}
Once you have this you can initialize it and send it to the void (object)
delegate (ParameterizedThreadStart
):
var context = new CaptureWebPageContext() { Url = url, ResultDelegate = BitmapRendered };
var thr = new Thread(context);
thr.Start(context);
protected void BitmapRendered(Bitmap result)
{
// ... Do stuff with the final bitmap.
}
protected void CaptureWebPage(object contextObject)
{
var context = (CaptureWebPageContext)contextObject;
var url = context.Url;
//... Your code to capture the web page.
context.ResultDelegate(bmp);
bmp.Dispose();
}
Ideally you would use the .Net Async pattern, but I see you need a STA thread.
精彩评论