【问题标题】:C++/CLI WinForms: BeginInvoke ErrorC++/CLI WinForms:BeginInvoke 错误
【发布时间】:2015-05-19 08:40:05
【问题描述】:

我无法弄清楚这个错误的原因:

在创建窗口句柄之前,不能对控件调用 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);
        }
    }

请告诉我解决方案。

【问题讨论】:

  • 如果我添加这个条件:if(myDelegate->InvokeRequired),然后我得到跨线程操作不允许错误。
  • 调用myDelegate->CreateControl() 来强制创建句柄,或者只是将控件添加到父控件,确保它是可见的。顺便说一句,你为什么把它命名为myDelegate?这是ListView,当然不是代表。
  • @LucasTrzesniewski 我正在尝试执行跨线程操作,即更新我刚刚在上面的代码中创建的另一个线程中的主线程元素。我知道我的代码有问题。调用 CreateControl() 后,我现在面临此错误:跨线程操作无效。你能告诉我我的代码有什么问题吗?

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


【解决方案1】:
        myDelegate = (gcnew System::Windows::Forms::ListView());

此语句的基本问题:

  • 它不是一个委托,它是一个 ListView 对象
  • 控件与创建它们的线程有很强的关联。像你这样在工作线程上创建控件是不正确的
  • 一个控件需要一个 Parent 变得可见和有用,它没有一个
  • 控件需要线程运行调度程序循环,以便它可以获取消息。这样的线程必须调用 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 线程上发生的事情,例如显示导入的结果和隐藏“我正在处理它”通知。

【讨论】:

  • 感激不尽。你的回答让我学到了很多东西。学习 C++/CLI 才 3 天,我对代表的概念还不清楚。在您的回答的帮助下,我能够更正我的代码,并且现在可以使用线程正常工作。我创建了这个应用程序。出于学习的目的,所以我必须学习如何使用线程。我已经知道如何使用 BackgroundWorker。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-05
  • 1970-01-01
  • 1970-01-01
  • 2018-03-15
  • 1970-01-01
相关资源
最近更新 更多