2013-02-27 66 views
0

下面是我试图用我的应用程序完成的工作 - 将HTML文件添加到目录(使用FileSystemWatcher)时,我想立即使用WebBrowser打印此文件。除非我遇到以下错误时,发生这种情况:从WebBrowser打印时出现ThreadStateException

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文件?

回答

1

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

但是,您可以在事件处理程序中执行的操作是有限的,但您始终需要知道您的代码在另一个线程中运行。线程安全始终是一个问题。你在这里得到的具体错误是由此造成的,WebBrowser不是一个线程安全的类。在少数情况下,如果您实际发出警告,指出您使用的是不支持线程的类,则更常见的问题是随机错误行为。

Anyhoo,FSW使得这个很容易解决。您可以要求它在UI线程上提升事件。这需要一个单一的代码行:

fsw.SynchronizingObject = this; 

请注意,这将陷入FSW下来,你必须保持您的UI线程响应,如果事件的频率高。不要跳过该错误事件。在线程安全队列中技术上可以将FSW与浏览器解耦,您可以在this answer的单独线程中找到运行WebBrowser所需的代码类型。

+0

非常感谢您提供丰富的答案,它有助于一吨。我也会确保处理错误事件。你摇滚,汉斯。 – kogh 2013-02-27 21:59:19