0

这是我试图用我的应用程序完成的任务——当一个 HTML 文件被添加到一个目录时,(使用 FileSystemWatcher),我想立即使用 WebBrowser 打印它。除了发生这种情况时我遇到以下错误:

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

这是我正在使用的遇到此问题的代码。(我在 Main() 之前有 [STAThread])

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();
        }  
    }

如果你们对此有任何见解,我将不胜感激!也许我不应该使用 WebBrowser 来打印 HTML 文件?

4

1 回答 1

2

FSW 引发的事件(如 Created)在工作线程上触发。这是相当重要的,它确保通知尽可能快地传递,并且 FSW 使用的内部缓冲区不会溢出。顺便说一句,您应该检查一下,不要忘记订阅 Error 事件。

您在事件处理程序中可以做的事情是有限的,但是您始终需要注意您的代码在另一个线程中运行。线程安全始终是一个问题。您在这里遇到的具体错误是由此引起的,WebBrowser 不是线程安全的类。您实际上收到警告说您正在使用不支持线程的类的少数情况之一,更常见的问题是随机错误行为。

Anyhoo,FSW 使解决这个问题变得非常简单。您可以要求它在 UI 线程上引发事件。它需要一行代码:

  fsw.SynchronizingObject = this;

请注意,这会使 FSW 陷入困境,如果事件频率很高,您必须保持 UI 线程响应。不要跳过那个错误事件。使用线程安全队列在技术上可以将 FSW 与浏览器分离,您将在此答案中找到在单独线程上运行 WebBrowser 所需的代码类型。

于 2013-02-27T21:51:54.783 回答