解决办法是建立一个 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
});
}
}
}
Node.JS console app
=> Node DLL addon
=>.我的控制台应用程序特别是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在用户与窗口交互之前不会被执行(简单地将光标移到窗口上就足够了)。
这是我的完整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);
}
}
}
}
}
});
这就好像当没有用户交互时,主应用程序循环不会运行一样。
其实不是的。 嗯,更准确的说,是在根本没有待处理的窗口消息时。 一旦主线程的消息队列被清空,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
事件等。
你说的没错,一个典型的Windows程序的主循环在有某种输入(通常是用户输入,但也有其他种类)之前不会运行。
我不熟悉C++ Builder框架。
如果你控制了使主循环的代码,你可以修改它来处理额外的信息源,比如观察另一个线程是否发出同步对象的信号。
其他选项。
让正在向队列中添加项目的线程每当进行一次常规更新时,就在主线程的窗口中发布一条自定义消息(或者只是一条线程消息)。
在主线程上设置一个定时器。 它将周期性地 "唤醒 "主循环,就像用户输入一样。