C ++ / CLI WinForms:BeginInvoke错误

时间:2015-03-17 02:22:27

标签: .net multithreading winforms c++-cli begininvoke

我无法找出导致此错误的原因:

  

在创建窗口句柄之前,无法在控件上调用Invoke或BeginInvoke。

这是我的(已剥离)代码:

private: delegate void MyDelegate(Object^ openFileDialog1);
ListView^ myDelegate;

private: void import_links(Object^ openFileDialog1) {
            myDelegate = (gcnew System::Windows::Forms::ListView());
            myDelegate->BeginInvoke( gcnew MyDelegate( this, &Form1::import_links ), openFileDialog1);

            //do some work here
}
private: System::Void Import_LinkClicked(System::Object^  sender, System::Windows::Forms::LinkLabelLinkClickedEventArgs^  e) {
        OpenFileDialog^ openFileDialog1 = gcnew OpenFileDialog;

        if ( openFileDialog1->ShowDialog() == System::Windows::Forms::DialogResult::OK )
        {
            Thread^ importThread = gcnew Thread(gcnew ParameterizedThreadStart(this,&Form1::import_links));
            importThread->Start(openFileDialog1);
        }
    }

请让我知道解决方案。

1 个答案:

答案 0 :(得分:2)

        myDelegate = (gcnew System::Windows::Forms::ListView());

本声明的基本问题:

  • 它不是委托,它是ListView对象
  • 控件与创建它们的线程有很强的关联。像你一样在工作线程上创建控件永远不会正确
  • 控件需要父可见且有用,它没有
  • 控件要求线程运行调度程序循环,以便它可以获取消息。这样的线程必须调用Application :: Run()。你的工作线程没有
  • 当需要变得可见时,懒惰地创建控件的基础窗口。由于它没有父项且不可见,因此无需创建窗口。所以它没有有效的Handle属性,因为异常告诉你
  • BeginInvoke()确保调用的代码在拥有该控件的线程上运行。由于它是拥有它的工作线程,因此BeginInvoke()永远不会实际调用另一个线程

您已经拥有对正确线程所拥有的对象的引用。它是this。所以正确的代码看起来像:

void import_links(Object^ openFileDialog1) {
    if (this->InvokeRequired) {
       this->BeginInvoke( gcnew MyDelegate( this, &Form1::import_links ), openFileDialog1);
    }
    else {
        //do some work here
    }
}

但请注意最终的谬误,你创建了一个工作线程,而它所做的就是调用this->BeginInvoke()。这需要几分之一微秒。创建一个线程来做这么少的工作永远不会有用。

重构您的代码,使用BackgroundWorker。让其DoWork事件处理程序执行昂贵的操作,例如导入文件。让其RunWorkerCompleted事件执行需要在UI线程上发生的事情,例如显示导入的结果并隐藏“我正在处理它”通知。