|
Rank: Newbie Groups: Member
Joined: 12/11/2013 Posts: 5
|
Hi, I am trying to capture the image of a web page with this simple code, yet my image is always null when I call webView.Capture
Code:
var webView = new EO.WebBrowser.WebView(); var navTask = webView.LoadUrl("www.google.com"); var img = webView.Capture(); img.Save("c:\temp\test.png", ImageFormat.Png);
Any thoughts on why this is?
|
|
Rank: Administration Groups: Administration
Joined: 5/27/2007 Posts: 24,275
|
Hi, There are two problems in your code. 1. You must initialize the WebView. See here for more details about how to initialize WebView: http://www.essentialobjects.com/doc/6/start/webview.aspx2. You have to wait until the page finishs loading before you can capture the image. Change your second line to this:
Code: C#
webView.LoadUrl("http://www.google.com").WaitOne();
Note the "WaitOne" call. This waits until the page finish loading. After these changes you can call Capture to capture page image. Thanks!
|
|
Rank: Newbie Groups: Member
Joined: 12/11/2013 Posts: 5
|
Hi,
Thanks for the reply.
So I need to put it inside a form for it to work properly, will this have any impact if the code is being run in a background task on a server?
James.
|
|
Rank: Administration Groups: Administration
Joined: 5/27/2007 Posts: 24,275
|
Hi, Technically it does not need a form. WebView needs at least a message pump to function. The message pump is the loop that dispatches all the windows messages (keyboard, mouse, paint, background tasks, etc). Additionally if you use WebView for any rendering related functions (for example, to display the page onscreen, or to capture output like you do), then you need a window handle. If you use .NET, A Windows Form is probably the easiest way to provide you both (you can use WPF too). When you use a Windows Form, the "parent control" in your form provide the window handle and the Application.Run call in your main thread provides the message pump. If you intend to use it as a background ground task, then you will want to run your WebView inside a dedicated thread. Your code would then need to communicate to that thread to perform any action that you wish it to perform. All WebView related actions must occurs in that thread and that thread must run a message pump. In order to simplify such task, we are introducing a ThreadRunner class for you to perform such tasks easily. The code is not finalized yet but here is a copy of the code that you can use:
Code: C#
using System;
using System.Threading;
using System.Windows.Forms;
namespace EO.WebBrowser
{
public delegate object ThreadRunnerAction(WebView webView, object args);
public class ThreadRunner
{
private Thread m_Thread;
private Form m_MainForm;
private ManualResetEvent m_InitEvent = new ManualResetEvent(false);
private SynchronizationContext m_SyncContext;
public WebView CreateWebView(int width, int height)
{
lock (typeof(ThreadRunner))
{
if (m_Thread == null)
{
m_Thread = new Thread(ThreadProc);
m_Thread.SetApartmentState(ApartmentState.MTA);
m_Thread.IsBackground = true;
m_Thread.Start();
}
}
return (WebView)Send((WebView webView, object args) =>
{
Panel panel = new Panel();
panel.Left = 0;
panel.Top = 0;
panel.Width = width;
panel.Height = height;
m_MainForm.Controls.Add(panel);
webView = new WebView();
webView.Closed += (object sender, EventArgs e) =>
{
m_MainForm.Controls.Remove(panel);
};
webView.Create(panel.Handle);
return webView;
}, null, null);
}
private void ThreadProc()
{
m_MainForm = new Form();
m_MainForm.ShowInTaskbar = false;
m_MainForm.FormBorderStyle = FormBorderStyle.None;
m_MainForm.WindowState = FormWindowState.Minimized;
m_MainForm.Size = new System.Drawing.Size(0, 0);
m_MainForm.Load += (object sender, EventArgs e) =>
{
m_SyncContext = SynchronizationContext.Current;
m_InitEvent.Set();
};
System.Windows.Forms.Application.Run(m_MainForm);
}
public object Send(ThreadRunnerAction action, WebView webView, object args)
{
if (!m_InitEvent.WaitOne())
return null;
object result = null;
m_SyncContext.Send((object state) =>
{
result = action(webView, args);
}, null);
return result;
}
public void Post(ThreadRunnerAction action, WebView webView, object args)
{
if (!m_InitEvent.WaitOne())
return;
m_SyncContext.Post((object state) =>
{
action(webView, args);
}, null);
}
}
}
Once you have the above code, you can do something like this:
Code: C#
private ThreadRunner m_Runner = new ThreadRunner();
//Create the WebView inside the ThreadRunner thread
WebView webView = m_Runner.CreateWebView(300, 300);
//Post the action to the runner's thread to load a page
//and capture page image
m_Runner.Post((WebView webView2, object args) =>
{
webView.LoadUrlAndWait("http://www.google.com");
webView.Capture().Save("c:\\1.bmp");
return null;
}, webView, null);
Hope this helps. Please feel free to let us know if you still have any questions. Thanks!
|
|
Rank: Newbie Groups: Member
Joined: 7/2/2013 Posts: 7
|
I believe this line is wrong:
m_Thread.SetApartmentState(ApartmentState.MTA);
AFAIK WinForms threads should be STA.
|
|
Rank: Administration Groups: Administration
Joined: 5/27/2007 Posts: 24,275
|
Yes. You are correct. Thanks for pointing that out!
|
|
Rank: Member Groups: Member
Joined: 8/10/2014 Posts: 15
|
Hi,
this code works, but how to get full page image? After loading url, you cannot resize webview.
|
|
Rank: Administration Groups: Administration
Joined: 5/27/2007 Posts: 24,275
|
Hi, I believe you can resize the WebView with this method: http://www.essentialobjects.com/doc/6/eo.webbrowser.webview.resize.aspxMake sure you download the latest build from our download page. This method was added very recently. So if you have an older build it may not exist in your build. Thanks
|
|
Rank: Member Groups: Member
Joined: 8/10/2014 Posts: 15
|
Hi,
i saw "resize" function, but it needs off-screen mode. In your code i think there is no off-screen mode (when the WebView's was created without a window handle) and if i include resize function, i get error that function needs off-screen mode.
Can you write me sample or how to write off-screen mode that works, i will be very grateful?
|
|
Rank: Administration Groups: Administration
Joined: 5/27/2007 Posts: 24,275
|
Hi Matjaz, You can change the original code from:
Code: C#
webView.Create(panel.Handle);
To:
Code: C#
webView.Create(IntPtr.Zero);
Then the WebView will be created in off-screen mode and you will be able to call Resize. Thanks!
|
|
Rank: Member Groups: Member
Joined: 8/10/2014 Posts: 15
|
Hi, now i can call Resize function without error, but nothing happens. Created image is not big as whole page. Did i miss something?
Code: C#
ThreadRunnerEo m_Runner = new ThreadRunnerEo();
//Create the WebView inside the ThreadRunner thread
EO.WebBrowser.WebView webView = m_Runner.CreateWebView();
//Post the action to the runner's thread to load a page
//and capture page image
m_Runner.Send((WebView webView2, object args) =>
{
webView.LoadUrlAndWait("http://www.page.com");
webView.Resize(new Size(1300, webView.GetContentAreaSize(true).Height));
webView.Capture().Save(Server.MapPath("~/images/ParsedImages/" + Guid.NewGuid() + ".jpg"), ImageFormat.Jpeg);
webView.Dispose();
return null;
}, webView, null);
|
|
Rank: Administration Groups: Administration
Joined: 5/27/2007 Posts: 24,275
|
Hi, We have posted a new build that should fix this problem. Please download the new build from our download page. Also ThreadRunner is built-in in the new build. So you do not need to have your own ThreadRunner class. You would call this method to create the WebView in off-screen mode: http://www.essentialobjects.com/doc/6/eo.webbrowser.threadrunner.createwebview_overload_1.aspxSorry about the delay. Please take a look and let us know whether the new build works for you. Thanks!
|
|
Rank: Member Groups: Member
Joined: 8/10/2014 Posts: 15
|
Hi, now it works, but not all the time. After few retries a get "Object reference not set to an instance of an object." error at
Code: C#
webView.Capture().Save(Server.MapPath("~/images/ParsedImages/" + Guid.NewGuid() + ".jpg"), ImageFormat.Jpeg);
The funny thing is that after error, image is saved successfully in debug mode. I also notice that after each capture a get rundll32.exe process running with 100MB of memory and not deleted after finish. How to solve this? Full code
Code: C#
ThreadRunner m_Runner = new ThreadRunner();
EO.WebBrowser.WebView webView = m_Runner.CreateWebView();
m_Runner.Send((WebView webView2, object args) =>
{
webView.LoadUrlAndWait("http://www.google.com");
webView.Resize(new Size(1300, webView.GetPageSize().Height));
webView.Capture().Save(Server.MapPath("~/images/ParsedImages/" + Guid.NewGuid() + ".jpg"), ImageFormat.Jpeg);
return null;
}, webView, null);
return View();
Thanks, Matjaž
|
|
Rank: Member Groups: Member
Joined: 8/10/2014 Posts: 15
|
Hi i found out, that
with Rectangle parameter not working correctly. Cropped image size is OK, but start point of image is (0,0) not the Point I set. Matjaz
|
|
Rank: Administration Groups: Administration
Joined: 5/27/2007 Posts: 24,275
|
Matjaz wrote: with Rectangle parameter not working correctly. Cropped image size is OK, but start point of image is (0,0) not the Point I set.
We have not been able to reproduce this problem. Can you isolate the problem into a test app and send us the test app? See here for more information on how to send a test app: http://www.essentialobjects.com/forum/test_project.aspxThanks!
|
|
Rank: Administration Groups: Administration
Joined: 5/27/2007 Posts: 24,275
|
Hi,
We have received your project and posted a new build that should fix the problem. You can download the new build from our download page.
In your test code, you call webView.Close and WebView.Dispose inside your ThreadRunner.Send. You can not do that. Doing that will cause a deadlock because the WebView can not be destroyed from inside Send (it is intentionally kept alive inside the Send call). So you can only destroy it after the Send call.
Thanks!
|
|
Rank: Member Groups: Member
Joined: 8/10/2014 Posts: 15
|
Hi, i made the corrections you suggested, but still errors occur on
function. The error is the same "Object reference not set to an instance of an object." Crop function now works, but after finish, some times rundll32.exe stays in process list. Any suggestion? Thanks, Matjaž
|
|
Rank: Administration Groups: Administration
Joined: 5/27/2007 Posts: 24,275
|
Hi,
Please check whether you call ThreadRunner.Post or ThreadRunner.Send. In your case you should call ThreadRunner.Send since you need to destroy the webView after you have captured the image. However because ThreadRunner.Post returns immediately, so if you use Post then it will proceed to webView.Destroy immediately. If this occurs before Capture call then Capture will return null and you will get the null reference exception.
Thanks!
|
|
Rank: Administration Groups: Administration
Joined: 5/27/2007 Posts: 24,275
|
Update:
We have encountered null reference exception in our test environment even if we use Send instead of Post. We are investigate this issue and will post again as soon as we have an update.
Thanks!
|
|
Rank: Member Groups: Member
Joined: 8/10/2014 Posts: 15
|
Hi, in last update CreateWebView not working, when it is used in ThreadRunner. CreateWebView never finishes.
Code: C#
ThreadRunner m_Runner = new ThreadRunner();
WebView webView = m_Runner.CreateWebView();
Matjaž
|
|