我怎样才能在不同的线程上听这个事件?

时间:2011-06-14 15:59:59

标签: c# multithreading sta

我做了一个小测试程序,尝试使用制造商提供的ActiveX控件来使用USB读卡器。

只要不使用单独的线程,表单就可以正常工作。我创建了Reader的新实例,并听取Read事件,一切正常。我刷卡,事件触发,文本框更新。

private Reader _reader;

public Form1()
{
    InitializeComponent();
    CreateScanner();
}

public void CreateScanner()
{
    _reader = new Reader();
    _reader.Read += Read;
}

void Read(object sender, EventArgs e)
{
    CardData.Text = "Card Read";
}

Reader类,以防它有用:

public class Reader
{
    private AxUSBHIDInsert _axUsbHid;
    public event EventHandler Read;

    public Reader()
    {
        _axUsbHid = new AxUSBHIDInsert();
        _axUsbHid.CreateControl();
        _axUsbHid.BeginInit();
        _axUsbHid.MSRReadDir = MSRReadDirection.ReadWithdrawal;
        _axUsbHid.EndInit();

        _axUsbHid.PortOpen = true;

        _axUsbHid.CardDataChanged += CardDataChanged;
    }

    void CardDataChanged(object sender, EventArgs e)
    {
        if (Read != null)
            Read(this, new EventArgs());
    }
}

但是,我需要在一个单独的线程上运行它。所以我将构造函数更改为

Thread thread = new Thread(CreateScanner);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();

它必须是一个STA线程,否则ActiveX控件会抱怨它无法实例化。无论如何,事件不会再发生了。我不熟悉线程的工作原理,所以我不确定原因。

有什么想法吗?请注意,它必须以这种方式工作(单独的线程,连接到Read事件),因为代码最终将驻留在使用我无法更改的应用程序部署的类库中。

1 个答案:

答案 0 :(得分:2)

您的COM对象将消息发送到第二个线程,这意味着它必须在应用程序运行时始终处于活动状态。

尝试这样做:

public class Reader
{
  public Reader()
  {
    // leave empty
  }

  public Read() {
    _axUsbHid = new AxUSBHIDInsert();
    ...
  }
}

public Form1()
{
  InitializeComponent();
  _reader = new Reader();
  _reader.Read += Read;
  StartRead(_reader);
}

void StartRead(Reader reader) {
  Thread thread = new Thread(ReadRoutine);
  thread.SetApartmentState(ApartmentState.STA);
  thread.Start(reader);
}

void ReadRoutine(object param) {
  Reader reader = (Reader)param;
  reader.Read();
  while (AppIsAlive) { // add logic here
    // bad code, import GetMessage from user32.dll
    Application.DoEvents();
    Thread.Sleep(100);
  }
}

但必须同步处理Read事件:

void Read(object sender, EventArgs e)
{
  if (this.InvokeRequired)
    this.BeginInvoke(new EventHandler(Read), new object[2] { sender, e } );
  else {
    CardData.Text = "Card Read";
  }
}