Welcome Guest Search | Active Topics | Sign In | Register

WebView - Create and Capture Image Options
James Summerton
Posted: Saturday, May 31, 2014 9:55:30 PM
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?

eo_support
Posted: Saturday, May 31, 2014 11:16:02 PM
Rank: Administration
Groups: Administration

Joined: 5/27/2007
Posts: 24,066
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.aspx

2. 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!
James Summerton
Posted: Sunday, June 1, 2014 7:12:19 AM
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.
eo_support
Posted: Monday, June 2, 2014 12:07:02 AM
Rank: Administration
Groups: Administration

Joined: 5/27/2007
Posts: 24,066
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!
fm
Posted: Friday, July 18, 2014 4:52:03 AM
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.
eo_support
Posted: Friday, July 18, 2014 3:43:55 PM
Rank: Administration
Groups: Administration

Joined: 5/27/2007
Posts: 24,066
Yes. You are correct. Thanks for pointing that out!
Matjaz
Posted: Sunday, August 10, 2014 3:14:02 PM
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.
eo_support
Posted: Sunday, August 10, 2014 3:22:08 PM
Rank: Administration
Groups: Administration

Joined: 5/27/2007
Posts: 24,066
Hi,

I believe you can resize the WebView with this method:

http://www.essentialobjects.com/doc/6/eo.webbrowser.webview.resize.aspx

Make 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
Matjaz
Posted: Monday, August 11, 2014 2:04:07 AM
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?
eo_support
Posted: Monday, August 11, 2014 9:31:07 AM
Rank: Administration
Groups: Administration

Joined: 5/27/2007
Posts: 24,066
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!
Matjaz
Posted: Tuesday, August 12, 2014 9:01:19 AM
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);
eo_support
Posted: Monday, August 18, 2014 9:59:02 PM
Rank: Administration
Groups: Administration

Joined: 5/27/2007
Posts: 24,066
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.aspx

Sorry about the delay. Please take a look and let us know whether the new build works for you.

Thanks!
Matjaz
Posted: Tuesday, August 19, 2014 3:25:07 AM
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ž
Matjaz
Posted: Tuesday, August 19, 2014 7:14:19 AM
Rank: Member
Groups: Member

Joined: 8/10/2014
Posts: 15
Hi

i found out, that

Code: C#
WebView.Capture()

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
eo_support
Posted: Tuesday, August 19, 2014 4:27:32 PM
Rank: Administration
Groups: Administration

Joined: 5/27/2007
Posts: 24,066
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.aspx

Thanks!
eo_support
Posted: Wednesday, August 20, 2014 7:13:56 PM
Rank: Administration
Groups: Administration

Joined: 5/27/2007
Posts: 24,066
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!
Matjaz
Posted: Thursday, August 21, 2014 9:45:24 AM
Rank: Member
Groups: Member

Joined: 8/10/2014
Posts: 15
Hi,

i made the corrections you suggested, but still errors occur on

Code: C#
WebView.Capture()


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ž
eo_support
Posted: Thursday, August 21, 2014 2:34:30 PM
Rank: Administration
Groups: Administration

Joined: 5/27/2007
Posts: 24,066
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!
eo_support
Posted: Thursday, August 21, 2014 5:21:42 PM
Rank: Administration
Groups: Administration

Joined: 5/27/2007
Posts: 24,066
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!
Matjaz
Posted: Wednesday, September 10, 2014 3:28:46 AM
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ž


You cannot post new topics in this forum.
You cannot reply to topics in this forum.
You cannot delete your posts in this forum.
You cannot edit your posts in this forum.
You cannot create polls in this forum.
You cannot vote in polls in this forum.