从不同线程调用 Form 方法 (Invoke)

Call a Form method from a different thread (Invoke)

我在主线程上有一个 WinForm 运行,在单独的线程上有一个 while(true) 循环 运行。 while(true) 的每个循环都会创建一个新的 System::String^,我想将 String 粘贴到我的 UI.

上的 TextBox

我的文件结构包括 GUI.hGUI.cppOther.cpp

GUI.h 包含所有自动创建的主(也是唯一)表单代码。它还具有一些 GetSetButtonClick 方法。

//GUI.h
#pragma once

#include <string>
#include <vector>
#include <cliext\vector>
#include <conio.h>
#include <list>
#include <iostream>

extern void BufferRecieveLoop();

namespace GUI_Example_Receive { 

    static bool loopFlag = true;

    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;

    /// <summary>
    /// Summary for GUI
    /// </summary>
    public ref class GUI : public System::Windows::Forms::Form
    {
    public:
        GUI(void)
        {
            InitializeComponent();
        }
        std::vector<std::string> CollectText();
        void ContinueNormally(); // Object^);
        void DisableAllTextboxes();
        void EnableAllTextboxes();

    protected:
        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        ~GUI()
        {
            if (components)
            {
                delete components;
            }
        }

    private:
        //Labels
        //GroupBoxes
        //Buttons
        //SaveFile

    public:
        //TextBoxes
        System::Windows::Forms::TextBox^  consoleTextBox;

    private:
        System::ComponentModel::Container ^components;

#pragma region Windows Form Designer generated code
        void InitializeComponent(void)
        {
            //automatically made, lightly edited
        }
#pragma endregion

    public: 
        void SetConsoleTextBoxText(System::String^ input)
        {
            this->consoleTextBox->Text = input;
            this->consoleTextBox->Refresh();
        }

        void ClearConsoleTextBoxText()
        {
            this->consoleTextBox->Clear();
        }

        delegate void MyDelegate(System::String ^ str);

        void ClearAndSetConsoleTextBoxText(System::String ^ input)
        {
            /***************************************************
            if (InvokeRequired)
            {
                this->BeginInvoke(gcnew MyDelegate(this, &ClearAndSetConsoleTextBoxText), { input });
            }
            ***************************************************/
            ClearConsoleTextBoxText();
            SetConsoleTextBoxText(input);
        }

        System::Void startButton_Click(System::Object^  sender, System::EventArgs^  e)
        {
            loopFlag = true; //able to loop through ContinueNormally()

            ContinueNormally(); //method in GUI.cpp
        }

    };

    //https://social.msdn.microsoft.com/Forums/vstudio/en-US/4da834f0-d8f8-4abb-a655-ef9e99d51eb2/how-to-create-a-global-object-of-a-ref-class-type?forum=vcgeneral
    ref struct Globals {
        static GUI ^gui; //using Globals::gui everywhere to access the one Form
    };

}

Gui.cpp 包含 Run() 表单、启动线程并永远循环的代码。

//GUI.cpp
void BufferRecieveLoop()
{
    while (true)
    {
        size_t bytes_read = multicast.Receive(buffer, Example::MTU_SIZE);

        incoming.Process(buffer, bytes_read, endian); //method in Other.cpp
    }
}

void GUI::ContinueNormally()
{
    System::Threading::Thread ^loopThread = gcnew System::Threading::Thread(gcnew System::Threading::ThreadStart(BufferRecieveLoop));
    loopThread->Start();
    loopThread->Join();
}

static void Start()
{
    Globals::gui = gcnew GUI;
    System::Windows::Forms::Application::Run(Globals::gui);
}

int __cdecl main(int argc, char* argv[])
{
    System::Windows::Forms::Application::EnableVisualStyles();
    System::Windows::Forms::Application::SetCompatibleTextRenderingDefault(false);

    Start();

    return 0;
}

Other.cpp 创建一个 String^ 并在 GUI.h 中调用一个方法来更改文本框中的文本。

//Other.cpp
void Process(const DIS::Pdu& packet)
{
    System::String^ sysStr2 = "stuff";

    GUI_Example_Receive::Globals::gui->ClearAndSetConsoleTextBoxText(sysStr2);

    //GUI_Example_Receive::Globals::gui->BeginInvoke(gcnew MyStringDelegate(GUI_Example_Receive::Globals::gui, &GUI_Example_Receive::GUI::ClearAndSetConsoleTextBoxText), { sysStr2 });
}

我不知道在哪里可以正确地 Invoke 我的方法。我也不知道如何 Invoke 我的方法。我发现的很多东西都是 C# 的,但对我不起作用。

我是从 Other.cpp 调用还是在 GUI.h 调用的方法内部调用?

如果以后有人遇到这个问题,并且像我一样找不到很多 C++ 代码示例,我将 post 我的解决方案。

在我的 GUI.h 文件中,我有一个 SetConsoleTextBoxText() 的方法。这只能由拥有 consoleTextBox 的线程使用。因此,任何其他尝试调用该方法的线程都需要 Invoke() 该方法(这是将控制权交还给拥有线程)。

//GUI.h
delegate void MyDelegate(System::String ^ text);

void SetConsoleTextBoxText(System::String^ input)
{
    if (this->consoleTextBox->InvokeRequired) //is a thread other than the owner trying to access?
    {
        MyDelegate^ myD = gcnew MyDelegate(this, &GUI::SetConsoleTextBoxText); 
        //GUI is the ref class. Replace with wherever your function is located.
        this->Invoke(myD, gcnew array<Object^> { input }); //Invoke the method recursively
    }
    else
    {
        //Normal function of this method. This will be hit after a recursive call or from the owning thread
        this->consoleTextBox->Text = input;
        this->consoleTextBox->Refresh();
    }
}