这里是c#的新手,第一次处理事件处理程序。我正在编写的程序的一个步骤是,每次在某个位置创建文件时都要更改剪贴板。以下是我到目前为止的想法:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;
using System.Windows.Forms;
namespace ConsoleApplication1
{
class Program
{
[STAThread]
public static void Main()
{
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = @"C:Input";
watcher.Filter = "*.csv";
watcher.Created += new FileSystemEventHandler(ProcessFile);
watcher.EnableRaisingEvents = true;
Console.ReadLine();
}
public static void ProcessFile(object source, FileSystemEventArgs e)
{
try
{
Clipboard.SetText("text");
}
catch (Exception exc)
{
Console.WriteLine(exc);
Console.ReadKey();
}
}
}
}
抛出了一个异常,即当前线程不处于STA模式,需要在Main方法中进行标记,但我相信我做到了。如何更改此处的剪贴板?
问题是FileSystemWatcher
将在单独的线程上触发事件。您需要做一些事情,以便剪贴板操作与Main
方法在同一个线程中进行。
方法#1:使用Application.Run
,即使您不使用WinForms。它在主线程上旋转一个循环,等待事件发生。类似这样的东西:
static Form InvokerForm;
public static void Main()
{
InvokerForm = new Form();
var dummy = InvokerForm.Handle; // form handle creation is lazy; force it to happen
var watcher = new FileSystemWatcher();
watcher.SynchronizingObject = InvokerForm;
watcher.Path = @"C:Input";
watcher.Filter = "*.csv";
watcher.Created += new FileSystemEventHandler(ProcessFile);
watcher.EnableRaisingEvents = true;
Application.Run();
}
这告诉观察者在您创建的(不可见)表单上调用Invoke
,并将处理程序作为参数传递。这基本上是将其排队等待稍后调用。与此同时,Application.Run
位于主线程上,旋转一个循环,等待这样的事件发生。当它注意到这一点时,它继续并在正确的线程上调用ProcessFile
。
方法#2:如果这看起来太古怪,那是因为它有点古怪。"正确"的方法是使用自定义SynchronizingObject
。不幸的是,似乎没有内置,但有一篇文章描述了如何创建实现ISynchronizeInvoke
的委托队列。
这种方法的净效果是一样的:最终在主线程上有一些东西,等待调用被请求,然后执行它们。只有这一次,你才能对它的所有内部进行编码,同时使程序在系统资源使用方面更加精简。