如何强制我的窗口保持更新?

问题描述 投票:0回答:1

最后更新

解决办法是建立一个 TTimer,设置其 Interval 到任何大于 0,并赋予其 OnTimer 属性到一个空函数。


我有一个 TThread 定期向主窗体添加新控件,通过 Queue(). 但是排队的函数从来没有被执行过,直到表单接收到用户输入,或者光标在上面移动,然后它就会立即执行所有排队的函数。

通过仔细的日志记录,我最终确定这些函数是按照线程的意图被排队的。 它们只是没有被主VCL循环执行,直到表单有用户交互。

这就像当没有用户交互时,主应用程序循环不会运行一样。

如何强制表单立即执行排队的函数?

如果有关系,表单和 TThread 是由一个 .dll,它的另一个名字是 .dll,它本身被一个控制台应用程序调用。

像这样。

console application -> dll -> dll created by C++ Builder

编辑

void __fastcall GirkovArpa::Execute() {
    while (!Terminated) {
        if (GlobalMessageQueue.size() > 0) {
            EnterCriticalSection(&myCritSect);
            std::cout << "";  // this line is required, else thread won't execute
            std::string GlobalMessage = GlobalMessageQueue.at(0);
            std::string copy;
            copy.assign(GlobalMessage);
            GlobalMessageQueue.erase(GlobalMessageQueue.begin());
            LeaveCriticalSection(&myCritSect);
            Queue([&, copy]() {
                // do stuff
            });
        }
    }
}

编辑2

Node.JS console app => Node DLL addon =&gt.我的控制台应用程序特别是NodeJS。C++Builder GUI DLL

我的控制台应用程序是专门的NodeJS。 它加载了一个 "NodeJS addon"(DLL的一种类型),它加载了用C++ Builder创建的DLL,它导出了这个函数。

void myExportedFunction(const char *str) {
    EnterCriticalSection(&myCritSect);
    GlobalMessageQueue.push_back(std::string(str));
    // CheckSynchronize();
    LeaveCriticalSection(&myCritSect);
}

它输出了这个函数: CheckSynchronize() 没有注释出来,我得到一个 Segmentation Fault 错误。

我的 TThread 在一个无限循环中运行,检查 GlobalMessageQueue如果它发现它不是空的,它就会排一个lambda,创建一个 TControl 在主窗体上。

但排队的lambdas在用户与窗口交互之前不会被执行(简单地将光标移到窗口上就足够了)。

编辑3

这是我的完整lambda。

Queue([&, copy]() {
                std::vector<std::string> words;
                boost::split(words, copy, boost::is_any_of(" "));

                // CREATE $TControl $Name $Text $Parent
                if (words.at(0) == "CREATE") {
                    if (words.at(1) == "TEXTBOX") {
                        String formName = stringToString(words.at(4));
                        TForm *form = getFormByName(formName);
                        TEdit *textbox = new TEdit(form);
                        textbox->Parent = form;
                        textbox->Name = words.at(2).c_str();
                        textbox->Text = words.at(3).c_str();
                        textbox->Show();
                        textbox->OnClick = MyForm->OnClick;
                    }
                    if (words.at(1) == "RADIO") {
                        String formName = stringToString(words.at(4));
                        TForm *form = getFormByName(formName);
                        TRadioButton *radio = new TRadioButton(form);
                        radio->Parent = form;
                        radio->Name = words.at(2).c_str();
                        radio->Caption = words.at(3).c_str();
                        radio->Show();
                        radio->OnClick = MyForm->OnClick;
                    }
                    if (words.at(1) == "BUTTON") {
                        String formName = stringToString(words.at(4));
                        TForm *form = getFormByName(formName);
                        TButton *button = new TButton(form);
                        button->Parent = form;
                        button->Name = words.at(2).c_str();
                        button->Caption = words.at(3).c_str();
                        button->Show();
                        button->OnClick = MyForm->OnClick;
                    }
                    if (words.at(1) == "FORM") {
                        createDialog(words.at(2).c_str(), words.at(3).c_str());
                    }
                }

                if (words.at(0) == "CHANGE") {
                    for (int j = 0; j < Screen->FormCount; j++) {
                        TForm *form = Screen->Forms[j];
                        if (form->Name == words.at(1).c_str()) {
                            TRttiContext ctx;
                            TRttiType *type = ctx.GetType(form->ClassInfo());
                            TRttiProperty *prop = type->GetProperty(words.at(2).c_str());
                            TValue value;

                            if (prop->PropertyType->TypeKind == tkUString) {
                                value = TValue::From<UnicodeString>(words.at(3).c_str());
                            } else if (prop->PropertyType->TypeKind == tkInteger) {
                                value = TValue::From<Integer>(StrToInt(words.at(3).c_str()));
                            } else {
                                std::cout << "ERROR" << std::endl;
                            }
                            prop->SetValue(form, value);
                        }

                        for (int i = 0; i < form->ControlCount; i++) {
                            TControl *control = form->Controls[i];
                            if (control->Name == words.at(1).c_str()) {
                                TRttiContext ctx;
                                TRttiType *type = ctx.GetType(control->ClassInfo());
                                TRttiProperty *prop = type->GetProperty(words.at(2).c_str());
                                TValue value;

                                if (prop->PropertyType->TypeKind == tkUString) {
                                    value = TValue::From<UnicodeString>(words.at(3).c_str());
                                } else if (prop->PropertyType->TypeKind == tkInteger) {
                                    value = TValue::From<Integer>(StrToInt(words.at(3).c_str()));
                                } else {
                                    std::cout << "ERROR" << std::endl;
                                }
                                prop->SetValue(control, value);
                            }
                        }
                    }
                }

                // GET NAME PROP
                if (words.at(0) == "GET") {
                    for (int j = 0; j < Screen->FormCount; j++) {
                        TForm *form = Screen->Forms[j];
                        if (form->Name == words.at(1).c_str()) {
                            TRttiContext ctx;
                            TRttiType *type = ctx.GetType(form->ClassInfo());
                            TRttiProperty *prop = type->GetProperty(words.at(2).c_str());

                            TValue result = prop->GetValue(form);
                            if (result.Kind == tkUString) {
                                String leString = result.AsString();
                                std::wstring w(std::wstring(leString.t_str()));
                                std::string STR(w.begin(), w.end());
                                std::string output = words.at(1) + " " + words.at(2);
                                String o = output.c_str();
                                tellJavaScript(AnsiString(o + ": " + leString).c_str());
                            } else if (result.Kind == tkInteger) {
                                int result_int = result.AsInteger();
                                String result_String = IntToStr(result_int);
                                String name = words.at(1).c_str();
                                String prop = words.at(2).c_str();
                                tellJavaScript(AnsiString(name + " " + prop + ": " + result_String).c_str());
                            } else {
                                // assume boolean
                                String result_String = BoolToStr(result.AsBoolean());
                                String name = words.at(1).c_str();
                                String prop = words.at(2).c_str();
                                tellJavaScript(AnsiString(name + " " + prop + ": " + result_String).c_str());
                            }
                        }

                        for (int i = 0; i < form->ControlCount; i++) {
                            TControl *control = form->Controls[i];
                            if (control->Name == words.at(1).c_str()) {
                                TRttiContext ctx;
                                TRttiType *type = ctx.GetType(control->ClassInfo());
                                TRttiProperty *prop = type->GetProperty(words.at(2).c_str());

                                TValue result = prop->GetValue(control);
                                if (result.Kind == tkUString) {
                                    String leString = result.AsString();
                                    std::wstring w(std::wstring(leString.t_str()));
                                    std::string STR(w.begin(), w.end());
                                    std::string output = words.at(1) + " " + words.at(2);
                                    String o = output.c_str();
                                    tellJavaScript(AnsiString(o + ": " + leString).c_str());
                                } else if (result.Kind == tkInteger) {
                                    int result_int = result.AsInteger();
                                    String result_String = IntToStr(result_int);
                                    String name = words.at(1).c_str();
                                    String prop = words.at(2).c_str();
                                    tellJavaScript(AnsiString(name + " " + prop + ": " + result_String).c_str());
                                } else {
                                    // assume boolean
                                    String result_String = BoolToStr(result.AsBoolean());
                                    String name = words.at(1).c_str();
                                    String prop = words.at(2).c_str();
                                    tellJavaScript(AnsiString(name + " " + prop + ": " + result_String).c_str());
                                }
                            }
                        }
                    }
                }

                if (words.at(0) == "DELETE") {
                    for (int j = 0; j < Screen->FormCount; j++) {
                        TForm *form = Screen->Forms[j];
                        if (form->Name == words.at(1).c_str()) {
                            form->Close();
                        }
                        for (int i = 0; i < form->ControlCount; i++) {
                            TControl *control = form->Controls[i];
                            if (control->Name == words.at(1).c_str()) {
                                control->Free();
                            }
                        }
                    }
                }

                if (words.at(0) == "EXECUTE") {
                    for (int j = 0; j < Screen->FormCount; j++) {
                        TForm *form = Screen->Forms[j];
                        if (form->Name == words.at(1).c_str()) {
                            std::cout << "EXECUTE <<" << words.at(2) << ">>" << std::endl;

                            TRttiContext context;
                            TRttiType *rttiType = context.GetType(form->ClassType());
                            TRttiMethod *method = rttiType->GetMethod(words.at(2).c_str());

                            DynamicArray<TRttiParameter *> parameters = method->GetParameters();
                            TValue args[10];
                            if (parameters.Length) {
                                for (int y = parameters.Low; y <= parameters.High; y++) {
                                    String paramType = parameters[y]->ParamType->ToString();
                                    if (paramType == "UnicodeString") {
                                        args[y] = TValue::From<UnicodeString>(stringToString(words.at(y + 3)));
                                    } else if (paramType == "Integer") {
                                        args[y] = TValue::From<Integer>(StrToInt(stringToString(words.at(y + 3))));
                                    }
                                }
                                TValue value = method->Invoke(form, args, parameters.High);
                            } else {
                                TValue value = method->Invoke(form, NULL, -1);
                            }
                        }

                        for (int i = 0; i < form->ControlCount; i++) {
                            TControl *control = form->Controls[i];
                            if (control->Name == words.at(1).c_str()) {
                                std::cout << "EXECUTE <<" << words.at(2) << ">>" << std::endl;

                                TRttiContext context;
                                TRttiType *rttiType = context.GetType(control->ClassType());
                                TRttiMethod *method = rttiType->GetMethod(words.at(2).c_str());

                                DynamicArray<TRttiParameter *> parameters = method->GetParameters();
                                TValue args[10];
                                if (parameters.Length) {
                                    for (int y = parameters.Low; y <= parameters.High; y++) {
                                        String paramType = parameters[y]->ParamType->ToString();
                                        if (paramType == "UnicodeString") {
                                            args[y] = TValue::From<UnicodeString>(stringToString(words.at(y + 3)));
                                        } else if (paramType == "Integer") {
                                            args[y] = TValue::From<Integer>(StrToInt(stringToString(words.at(y + 3))));
                                        }
                                    }
                                    TValue value = method->Invoke(control, args, parameters.High);
                                } else {
                                    TValue value = method->Invoke(control, NULL, -1);
                                }
                            }
                        }
                    }
                }
            });
windows multithreading c++builder
1个回答
3
投票

这就好像当没有用户交互时,主应用程序循环不会运行一样。

其实不是的。 嗯,更准确的说,是在根本没有待处理的窗口消息时。 一旦主线程的消息队列被清空,VCL就会调用Win32的消息队列。WaitMessage() 函数,该函数会阻塞调用线程,直到消息队列中出现新的消息。 即使是传统的非VCL消息循环,当没有消息需要处理时,也会阻塞调用线程。

如何强制表单立即执行排队函数?

你不能这样做 逼迫 它。

如果重要的话,形式和 TThread 是由一个 .dll,它的另一个名字是 .dll它本身是由一个控制台应用程序调用的。

这确实很重要,因为 TThread::Queue()TThread:::Synchronize() 不在DLL里面工作。

TThread::Queue()TThread::Synchronize() 把他们的请求放到RTL内部的队列中,设置一个信号来表示队列中有待处理的请求,然后发布一条消息到 TApplication 窗口来 "唤醒 "主线程(以防消息循环在 "睡觉 "等待新消息的到来)。 该请求队列会在主线程最早方便的时候进行处理。

默认情况下,VCL消息循环会在主线程最方便的时候处理 TThread 队列的时候。

  • 消息循环进入空闲状态,在所有待处理的消息被处理后,消息队列变成空的。

  • 的情况下,才会进入空闲状态。TApplication 窗口收到 "唤醒 "消息。

TThread 队列在一个 DLL 中,并且 DLL 没有与主 EXE 共享同一个 RTL 实例,那么 EXE 中的主消息循环就不会知道这个 TThread 队列中,所以它不能在空闲时间处理待处理的请求。 这就只剩下 "唤醒 "消息了,DLL会把它发布到自己的 TApplication 实例,而不是主EXE的 TApplication. 主线程消息循环仍然会将窗口消息发送到DLL的 TApplication 窗口。

要解决这个问题,你必须:1:

  • 在 DLL 和主 EXE 中启用 Runtime Packages, 或者甚至把 DLL 改成 Package, 这样它们就可以共享 RTL 和 VCL 的共同实例. 这意味着你将不得不部署RTL和VCL。.bpl 文件与您的应用程序,但。

  • 从你的DLL中导出一个函数,调用RTL的 CheckSynchronize() 函数,然后在EXE代码中定期调用该DLL函数,比如在UI定时器中,或者在 TApplication.OnIdle 事件等。


2
投票

你说的没错,一个典型的Windows程序的主循环在有某种输入(通常是用户输入,但也有其他种类)之前不会运行。

我不熟悉C++ Builder框架。

如果你控制了使主循环的代码,你可以修改它来处理额外的信息源,比如观察另一个线程是否发出同步对象的信号。

其他选项。

  • 让正在向队列中添加项目的线程每当进行一次常规更新时,就在主线程的窗口中发布一条自定义消息(或者只是一条线程消息)。

  • 在主线程上设置一个定时器。 它将周期性地 "唤醒 "主循环,就像用户输入一样。

© www.soinside.com 2019 - 2024. All rights reserved.