以 Windows 形式通过线程传递参数 C++/CLI
Passing Arguments Through Threads in Windows Form C++/CLI
我真的很难做一些可能很简单的事情。基本上,我试图编写一个 Windows Form C++/CLI 程序来从串行端口读取 arduino 流,并且至少在最初,将每个值添加到列表框。 Arduino 代码没问题,它每 2 秒给我一个字符串,当我调试带断点的 Windows 表单时,我可以看到字符串变量 "Message" 的值很好。但是我 运行 遇到了这个 "thread" 问题,我知道我必须创建一个不同的线程来更新列表框...好的。现在,我找不到办法将 "message" 字符串传递给函数 "UpdateList"。我见过很多关于传递对象、class 等的事情……但无法设法将其放入我的代码中。有人可以帮忙吗?
完整代码如下:
#pragma once
namespace ArduComm {
using namespace System;
using namespace System::ComponentModel;
using namespace System::Collections;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Drawing;
using namespace System::Threading;
public ref class MyForm : public System::Windows::Forms::Form
{
public:
MyForm(void)
{
InitializeComponent();
}
protected:
~MyForm()
{
if (components)
{
delete components;
}
}
private: System::IO::Ports::SerialPort^ Arduino;
private: System::Windows::Forms::Button^ StartButton;
private: System::Windows::Forms::Button^ StopButton;
private: System::Windows::Forms::ListBox^ ListBox;
private: System::ComponentModel::IContainer^ components;
#pragma region Windows Form Designer generated code
void InitializeComponent(void)
{
this->components = (gcnew System::ComponentModel::Container());
this->Arduino = (gcnew System::IO::Ports::SerialPort(this->components));
this->StartButton = (gcnew System::Windows::Forms::Button());
this->StopButton = (gcnew System::Windows::Forms::Button());
this->ListBox = (gcnew System::Windows::Forms::ListBox());
this->SuspendLayout();
//
// Arduino
//
this->Arduino->PortName = L"COM3";
this->Arduino->DataReceived += gcnew System::IO::Ports::SerialDataReceivedEventHandler(this, &MyForm::ArduinoDataReceived);
//
// StartButton
//
this->StartButton->Location = System::Drawing::Point(12, 12);
this->StartButton->Name = L"StartButton";
this->StartButton->Size = System::Drawing::Size(75, 23);
this->StartButton->TabIndex = 3;
this->StartButton->Text = L"Start";
this->StartButton->UseVisualStyleBackColor = true;
this->StartButton->Click += gcnew System::EventHandler(this, &MyForm::StartReadingSerial);
//
// StopButton
//
this->StopButton->Location = System::Drawing::Point(12, 41);
this->StopButton->Name = L"StopButton";
this->StopButton->Size = System::Drawing::Size(75, 23);
this->StopButton->TabIndex = 4;
this->StopButton->Text = L"Stop";
this->StopButton->UseVisualStyleBackColor = true;
this->StopButton->Click += gcnew System::EventHandler(this, &MyForm::StopReadingSerial);
//
// ListBox
//
this->ListBox->FormattingEnabled = true;
this->ListBox->Location = System::Drawing::Point(117, 12);
this->ListBox->Name = L"ListBox";
this->ListBox->Size = System::Drawing::Size(273, 277);
this->ListBox->TabIndex = 5;
//
// MyForm
//
this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
this->ClientSize = System::Drawing::Size(409, 303);
this->Controls->Add(this->ListBox);
this->Controls->Add(this->StopButton);
this->Controls->Add(this->StartButton);
this->Name = L"MyForm";
this->Text = L"ArduComm";
this->ResumeLayout(false);
}
#pragma endregion
/* My functions */
private: System::Void UpdateList() {
ListBox->Items->Add("xxx");
}
private: System::Void InvokeThread() {
this->Invoke(gcnew MethodInvoker(this, &MyForm::UpdateList));
}
private: System::Void ArduinoDataReceived(System::Object^ sender, System::IO::Ports::SerialDataReceivedEventArgs^ e) {
String^ Message;
Message = Arduino -> ReadLine();
Thread^ oThread = gcnew Thread(gcnew ThreadStart(this, &MyForm::InvokeThread));
oThread->Start();
}
private: System::Void StartReadingSerial(System::Object^ sender, System::EventArgs^ e) {
this->Arduino->Open();
}
private: System::Void StopReadingSerial(System::Object^ sender, System::EventArgs^ e) {
this->Arduino->Close();
}
};
}
非常感谢任何帮助...
谢谢。
不要启动一个线程。 SerialPort::DataReceived 事件处理程序已经在工作线程上运行,添加另一个线程无济于事。您需要编写一个可以从事件处理程序调用的方法,它应该如下所示:
void UpdateList(String^ message) {
ListBox->Items->Add(message);
}
现在您需要声明一个与此方法兼容的委托,这是您忽略的关键步骤,也是您卡住的地方:
delegate void UpdateListDelegate(String^ Message);
或者您可以使用内置的 Action<String^>
通用类型。现在您可以从事件处理程序中调用它。使用 BeginInvoke() 非常重要,使用 Invoke() 在关闭串口时死锁的几率非常高:
System::Void Arduino_DataReceived(System::Object^ sender, System::IO::Ports::SerialDataReceivedEventArgs^ e) {
String^ message = Arduino->ReadLine();
this->BeginInvoke(gcnew UpdateListDelegate(this, &MyForm::UpdateList), message);
}
我真的很难做一些可能很简单的事情。基本上,我试图编写一个 Windows Form C++/CLI 程序来从串行端口读取 arduino 流,并且至少在最初,将每个值添加到列表框。 Arduino 代码没问题,它每 2 秒给我一个字符串,当我调试带断点的 Windows 表单时,我可以看到字符串变量 "Message" 的值很好。但是我 运行 遇到了这个 "thread" 问题,我知道我必须创建一个不同的线程来更新列表框...好的。现在,我找不到办法将 "message" 字符串传递给函数 "UpdateList"。我见过很多关于传递对象、class 等的事情……但无法设法将其放入我的代码中。有人可以帮忙吗?
完整代码如下:
#pragma once
namespace ArduComm {
using namespace System;
using namespace System::ComponentModel;
using namespace System::Collections;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Drawing;
using namespace System::Threading;
public ref class MyForm : public System::Windows::Forms::Form
{
public:
MyForm(void)
{
InitializeComponent();
}
protected:
~MyForm()
{
if (components)
{
delete components;
}
}
private: System::IO::Ports::SerialPort^ Arduino;
private: System::Windows::Forms::Button^ StartButton;
private: System::Windows::Forms::Button^ StopButton;
private: System::Windows::Forms::ListBox^ ListBox;
private: System::ComponentModel::IContainer^ components;
#pragma region Windows Form Designer generated code
void InitializeComponent(void)
{
this->components = (gcnew System::ComponentModel::Container());
this->Arduino = (gcnew System::IO::Ports::SerialPort(this->components));
this->StartButton = (gcnew System::Windows::Forms::Button());
this->StopButton = (gcnew System::Windows::Forms::Button());
this->ListBox = (gcnew System::Windows::Forms::ListBox());
this->SuspendLayout();
//
// Arduino
//
this->Arduino->PortName = L"COM3";
this->Arduino->DataReceived += gcnew System::IO::Ports::SerialDataReceivedEventHandler(this, &MyForm::ArduinoDataReceived);
//
// StartButton
//
this->StartButton->Location = System::Drawing::Point(12, 12);
this->StartButton->Name = L"StartButton";
this->StartButton->Size = System::Drawing::Size(75, 23);
this->StartButton->TabIndex = 3;
this->StartButton->Text = L"Start";
this->StartButton->UseVisualStyleBackColor = true;
this->StartButton->Click += gcnew System::EventHandler(this, &MyForm::StartReadingSerial);
//
// StopButton
//
this->StopButton->Location = System::Drawing::Point(12, 41);
this->StopButton->Name = L"StopButton";
this->StopButton->Size = System::Drawing::Size(75, 23);
this->StopButton->TabIndex = 4;
this->StopButton->Text = L"Stop";
this->StopButton->UseVisualStyleBackColor = true;
this->StopButton->Click += gcnew System::EventHandler(this, &MyForm::StopReadingSerial);
//
// ListBox
//
this->ListBox->FormattingEnabled = true;
this->ListBox->Location = System::Drawing::Point(117, 12);
this->ListBox->Name = L"ListBox";
this->ListBox->Size = System::Drawing::Size(273, 277);
this->ListBox->TabIndex = 5;
//
// MyForm
//
this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
this->ClientSize = System::Drawing::Size(409, 303);
this->Controls->Add(this->ListBox);
this->Controls->Add(this->StopButton);
this->Controls->Add(this->StartButton);
this->Name = L"MyForm";
this->Text = L"ArduComm";
this->ResumeLayout(false);
}
#pragma endregion
/* My functions */
private: System::Void UpdateList() {
ListBox->Items->Add("xxx");
}
private: System::Void InvokeThread() {
this->Invoke(gcnew MethodInvoker(this, &MyForm::UpdateList));
}
private: System::Void ArduinoDataReceived(System::Object^ sender, System::IO::Ports::SerialDataReceivedEventArgs^ e) {
String^ Message;
Message = Arduino -> ReadLine();
Thread^ oThread = gcnew Thread(gcnew ThreadStart(this, &MyForm::InvokeThread));
oThread->Start();
}
private: System::Void StartReadingSerial(System::Object^ sender, System::EventArgs^ e) {
this->Arduino->Open();
}
private: System::Void StopReadingSerial(System::Object^ sender, System::EventArgs^ e) {
this->Arduino->Close();
}
};
}
非常感谢任何帮助...
谢谢。
不要启动一个线程。 SerialPort::DataReceived 事件处理程序已经在工作线程上运行,添加另一个线程无济于事。您需要编写一个可以从事件处理程序调用的方法,它应该如下所示:
void UpdateList(String^ message) {
ListBox->Items->Add(message);
}
现在您需要声明一个与此方法兼容的委托,这是您忽略的关键步骤,也是您卡住的地方:
delegate void UpdateListDelegate(String^ Message);
或者您可以使用内置的 Action<String^>
通用类型。现在您可以从事件处理程序中调用它。使用 BeginInvoke() 非常重要,使用 Invoke() 在关闭串口时死锁的几率非常高:
System::Void Arduino_DataReceived(System::Object^ sender, System::IO::Ports::SerialDataReceivedEventArgs^ e) {
String^ message = Arduino->ReadLine();
this->BeginInvoke(gcnew UpdateListDelegate(this, &MyForm::UpdateList), message);
}