kogh
kogh

Reputation: 1015

ThreadStateException when printing from WebBrowser

Here's what I'm trying to accomplish with my application - when an HTML file is added to a directory, (using FileSystemWatcher), I want to immediately print this using a WebBrowser. Except I'm running into the following error when this happens:

System.Threading.ThreadStateException: ActiveX control GUID cannot be instantiated because the current thread is not in a single-threaded apartment

Here's the code I'm using that's experiencing this issue. (I have [STAThread] before Main())

public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        //  Create a FileSystemWatcher to monitor all files on drive C.
        FileSystemWatcher fsw = new FileSystemWatcher("C:\\Test");

        //  Watch for changes in LastAccess and LastWrite times, and 
        //  the renaming of files or directories. 
        fsw.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
            | NotifyFilters.FileName | NotifyFilters.DirectoryName;

        //  Register a handler that gets called when a  
        //  file is created, changed, or deleted.
        //fsw.Changed += new FileSystemEventHandler(OnChanged);

        fsw.Created += new FileSystemEventHandler(OnChanged);

        //fsw.Deleted += new FileSystemEventHandler(OnChanged);
        fsw.EnableRaisingEvents = true;

        PrinterSettings settings = new PrinterSettings();
        label2.Text = settings.PrinterName;

        Thread.CurrentThread.SetApartmentState(ApartmentState.STA);
    }

    private void OnChanged(object source, FileSystemEventArgs e)
    {
        notifyIcon1.BalloonTipText = "Printing document " + e.Name + "...";
        notifyIcon1.BalloonTipTitle = "Printing Application";
        notifyIcon1.BalloonTipIcon = ToolTipIcon.Info;
        notifyIcon1.ShowBalloonTip(500);

        PrintCOAPage(e.Name);
    }

    private void PrintCOAPage(string name)
    {
        try
        {
            // Create a WebBrowser instance. 
            WebBrowser webBrowserForPrinting = new WebBrowser();

            // Add an event handler that prints the document after it loads.
            webBrowserForPrinting.DocumentCompleted +=
                new WebBrowserDocumentCompletedEventHandler(PrintDocument);

            // Set the Url property to load the document.
            webBrowserForPrinting.Url = new Uri(@"C:\\Test\\" + name);
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.ToString());
        }
    }

    private void PrintDocument(object sender,
        WebBrowserDocumentCompletedEventArgs e)
    {
        try
        {
            // Print the document now that it is fully loaded.
            ((WebBrowser)sender).Print();

            // Dispose the WebBrowser now that the task is complete. 
            ((WebBrowser)sender).Dispose();
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.ToString());
        }

    }

    private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e)
    {
        this.Show();
        this.Activate();
        if (this.WindowState == FormWindowState.Minimized)
        {
            this.WindowState = FormWindowState.Normal;
        }
    }

    private void Form1_Resize(object sender, EventArgs e)
    {
        if (FormWindowState.Minimized == WindowState)
        {
            Hide();
        }  
    }

If you guys have any insight on this, I would greatly appreciate the help! Maybe I shouldn't be using WebBrowser to print the HTML files?

Upvotes: 0

Views: 897

Answers (1)

Hans Passant
Hans Passant

Reputation: 942010

The events that are raised by FSW, like Created, are fired on a worker thread. That's fairly important, it ensures that the notifications are delivered as quickly as possible and that the internal buffer that FSW uses doesn't overflow. Something you should check btw, don't forget to subscribe the Error event.

What you can do in your event handler is limited however, you always need to be aware that your code runs in another thread. Thread safety is always a concern. The specific error you are getting here is caused by this, WebBrowser is not a thread-safe class. One of the few cases where you actually get a warning that you are using a class that doesn't support threading, the more common problem is random mis-behavior.

Anyhoo, FSW makes this very simple to solve. You can ask it to raise the events on the UI thread instead. It takes a single line of code:

  fsw.SynchronizingObject = this;

Do note that this will bog FSW down and you must keep your UI thread responsive if the frequency of events is high. Don't skip that Error event. Decoupling the FSW from the browser is technically possible with a thread-safe queue, you'll find the kind of code you need to run WebBrowser on a separate thread in this answer.

Upvotes: 2

Related Questions