Jump to content



Photo

"Function Expected" IE10 Script Error on Disposing of WebBrowser ST

c# forms ie10 vs2012 w8

  • Please log in to reply
2 replies to this topic

#1 +_Alexander

_Alexander

    Neowinian

  • Tech Issues Solved: 1
  • Joined: 21-January 13
  • Location: USA
  • OS: W8.1 u1
  • Phone: Nokia 521

Posted 13 May 2013 - 00:26

Point of application,
Render Provided html string and print it using Internet Explorer, quickly in the background.

Problem,
WebBrowser control requires to be run in an STA thread stopping this thread (after Disposing the Web Browser) causes an IE script error.



I have a class which starts a WebBrowser Control in an STA thread,
    class RenderHtml : System.Windows.Forms.ApplicationContext
    {
// stuff
        private WebBrowser browser = null;
// more stuff
        public RenderHtml()
        {
            renderThread = new Thread(Run);
            renderThread.SetApartmentState(System.Threading.ApartmentState.STA);
            renderThread.Start();
        }

        private void Run()
        {
// Error Checking Omitted
                    browser = new WebBrowser();
                    browser.DocumentCompleted += HasRendered;
                    browser.ScriptErrorsSuppressed = true;
            Application.Run(this);

Then, I give pipe it Html to load,

        public void RenderNew(string html, AutoResetEvent resultEvent)
        {
// Error Checking Omitted
                    browser.DocumentText = html;
                    browser.Refresh();
// AutoResetEvent Logic Omitted
        }

Then after it finished rendering, I tell it to print to a specific virtual printer.
That all works.

So now, I need to dispose of it.
This is what I have right now

        protected override void Dispose(bool disposing)
        {
            lock (locko)
            {
                if (renderThread != null)
                {
                    renderThread.Abort();
                    renderThread = null;
                    return;
                }
            }
            browser.DocumentCompleted -= HasRendered;
            System.Runtime.InteropServices.Marshal.Release(browser.Handle);
            browser.Dispose();
            base.Dispose(disposing);
        }

        public void Stop()
        {
            resultEvent.Dispose();

            browser.Stop();
            ExitThread();
            Dispose();
        }

The problem is with both renderThread.Abort() and ExitThread() - I have to commit both out not to get an error.
Both of these fail due to the WebBrowser control.
I don't feel like providing a screenshot, so here is the text,

The error comes from IE itself,
Script Error
An error has occurred in the script on this page.
Line: 289
Char: 1
Error: Function Expected
Code: 0
URL: res://ieframe.dll/preview.js

Do you want to continue running scripts on this page?

So, after calling Dispose methods including the WebBrowsers dispose method.
I try to stop the thread. Which gives me this error.


If I did NOT provide any html to render I think (been a long time) there is no Script Error.
But then, it completely kills the point of the application!

Note I do suppress Script Errors - when I created the WebBrowser.
                    browser.ScriptErrorsSuppressed = true;

Also, does anyone know of a way to provide images to the WebBrowser control without going through saving them to disk?


#2 OP +_Alexander

_Alexander

    Neowinian

  • Tech Issues Solved: 1
  • Joined: 21-January 13
  • Location: USA
  • OS: W8.1 u1
  • Phone: Nokia 521

Posted 14 May 2013 - 05:09

EDIT: Code is Available if you have no f-ing idea what I am talking about.

Now to dispose a WebBrowser,
I can do it from the STA Thread Which runs the Web Browser using Application.run(this).
So I can dispose the WebBrowser when it triggers an even such as a DocumentCompleted event.
Additionally, I can dispose of the Web Browser by using .Invoke(...) method.
Sadly WebBrowser cannot be disposed of from another thread by simply calling the Dispose() method.

I can also set the reference to the STA thread to null as well well as the reference to the WebBrowser
and set the thread to be a background thread.
The issue is, the thread does not terminate.
And to release all memory I have to terminate the damn thread.

Whether before or after dispose, whether from a separate thread or the STA Thread itself (during the DocumentCompleted event)
I get this error.

I guess I am asking is, how am I supposed to properly stop a ApplicationContext with a Web Browser Control properly and dispose of it WITHOUT any errors?

1. Why doesn't the renderThread exit itself (Abort is bad from what I understand)?
2. From where do I Abort the renderThread (STA Thread)? The calling thread (which uses the code)? renderThread itself?
3. Do I abort the thread before or after disposing of the WebBrowser?
4. Is the most proper way to dispose of a WebBrowser from another (calling) thread is to use browser.Invoke(...)?
5. Now there is Exit() ExitThread() renderThread.Abort() ExitThreadCore() - they all give me the same error... where am I supposed to call them to avoid bull?

Also this error may help,
A first chance exception of type 'System.Runtime.InteropServices.InvalidComObjectException' occurred in System.Windows.Forms.dll

Edited by _Alexander, 14 May 2013 - 05:24.


#3 OP +_Alexander

_Alexander

    Neowinian

  • Tech Issues Solved: 1
  • Joined: 21-January 13
  • Location: USA
  • OS: W8.1 u1
  • Phone: Nokia 521

Posted 18 May 2013 - 01:52

Workaround Solution

Even after calling browser.Dispose(),
waiting for the browser.Disposed event handler to finish,
and checking browser.IsDisposed before calling renderThread.Abort()
I still got the dreaded Script Error - An error has occurred in the script on this page Line 289 Char 1 Function Expected Code 0 URL res://ieframe.dll/preview.js

You may also get Script Error - An error has occurred in the script on this page Line 518 Char 1 Error The callee (server [not server application]) is not available and disappeared; all connections are invalid. The call did not execute. Code 0 URL res://ieframe.dll/preview.js

Create Separate Thread Which Waits 1 Second After The WebBrowser is Disposed and the calls renderThread.Abort()

Here is the code for this,
        public void TryDispose()
        {
            //
            // Do not Dispose Twice
            //

            if (_doDispose)
                return;

            _doDispose = true;

            //
            // Dispose Browser
            //

            browser.Invoke(
                new MethodInvoker
                    (
                        () =>
                        {
                            browser.Stop();

                            browser.DocumentCompleted -= HasRendered;
                            browser.Disposed += SetDisposeEvent;

                            if (browser.ActiveXInstance != null)
                                Marshal.ReleaseComObject(browser.ActiveXInstance);
                            if (browser.Handle != null)
                                Marshal.Release(browser.Handle);

                            browser.Dispose();

                        }
                    )
                );

            //
            // Hack Around - Create a Thread which Waits for 1 second before disposing the Web Browser
            //

            var browserDisposeThread = new Thread
                (
                    () =>
                    {
                        disposeEvent.WaitOne(1000);
                        Thread.Sleep(1000); // Wait to Avoid Script Error <:::::::::::::::::::::::
                        if (renderThread.IsAlive)
                        {
                            renderThread.Abort();
                            renderThread = null;
                            browser = null;

                            _applicationThreadReady.Dispose();
                            disposeEvent.Dispose();
                            if (resultEvent != null) resultEvent.Dispose();
                        }
                    }
                );
            browserDisposeThread.Name = "Browser Dispose Thread For " + renderThread.Name;
            browserDisposeThread.IsBackground = true;
            browserDisposeThread.Start();

            Console.WriteLine("TryDispose");
        }

Suggestions, Comments welcome.

Edited by _Alexander, 18 May 2013 - 01:58.