C++/CLI WinForms:BeginInvoke 错误

C++/CLI WinForms: BeginInvoke Error

我无法找出这个错误的原因:

Invoke or BeginInvoke cannot be called on a control until the window handle has been created.

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

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

请告诉我解决方法。

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

此语句的基本问题:

  • 不是委托,是ListView对象
  • 控件与创建它们的线程有很强的关联。像你这样在工作线程上创建控件永远不会正确
  • 一个控件需要父级才能变得可见和有用,它没有
  • 控件需要线程 运行 调度程序循环,以便它可以获取消息。这样的线程必须调用Application::Run()。你的工作线程没有
  • 控件的基础 window 是在需要时延迟创建的,以使其可见。由于它没有父对象且不可见,因此还没有创建 window 的任何必要。所以它不会有一个有效的句柄属性,因为异常告诉你
  • 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 事件 only 做那种需要在 UI 线程上发生的事情,比如显示导入的结果和隐藏 "I'm working on it" 通知.