So I made a GUI in C++ which calls a child process every time a button is clicked twice. The StandardOutput is redirected, doesn't use ShellExecute.
I made a simple dummy process to test it, let's say dummy.exe, which basically just do this :
void() {
printf("0");
}
And that's all. The process will exit itself after 0 is plotted.
The process is started when a button is clicked, which does this :
private: System::Void bt_getData_Click(System::Object^ sender, System::EventArgs^ e) {
if (bt_getData->Text == "Get Data") {
proc->Start();
bt_getData->Text = "Stop";
}
else if (bt_getData->Text == "Stop") {
bt_getData->Text = "Get Data";
}
}
Then it will read the output using the OutputDataReceived EventHandler.
The problem is when I clicked the button again, the process will be Restarted, but the GUI can't read the new Output.
Case 1 : I cancelled the output read in the OutputDataReceived EventHandler then restart the process, but the next restarted process output can't be read.
private: System::Void outputData(System::Object^ sender, System::Diagnostics::DataReceivedEventArgs^ e) {
x0 = xt;
xt += 1;
if (xt*x_scale > pb_Graph->Width) {
x0 = 0;
xt = 0;
imgTemp = gcnew Bitmap(pb_Graph->Image, 460, 460);
gpcGraph->Clear(Color::Transparent);
}
y0 = yt;
yt = Convert::ToInt16(e->Data);
ret_index++;
if (ret_index > 2047) ret_index = 0;
gpcGraph->DrawLine(greenPen,(float)x0*x_scale,pb_Graph->Height - (float)y0/y_scale - y_null,(float)xt*x_scale,pb_Graph->Height - (float)yt/y_scale - y_null);
pb_Graph->Refresh();
}
After three times restart, this error shows :
An unhandled exception of type 'System.InvalidOperationException' occurred in System.dll
Additional information: An async read operation has already been started on the stream.
Case 2 : I didn't cancel the output read. The same error with case 1 shows, but it is still understandable to me.
Case 3 : I didn't redo the BeginOutputReadLine() when restarting. The error doesn't shown, but the restarted process output can't be read.
My actual goal is to restart the process periodically using a 1 mS timer, so I tested the restart process first using button. But it seems that the newly generated output can't be read.
Any help would be appreciated :)
Okay... I've managed to prevent the errors using different method, which is to call
process->StandardOutput->ReadLine();
This way the async stream reading won't happen.
Related
I am testing application's latency during UDP communication on windows 10.
I tried to send a message every 1 second and receive a response sent immediately from the remote.
Send thread
It works every 1 second.
auto start = std::chrono::system_clock::now();
unsigned int count = 1;
while (destroyFlag.load(std::memory_order_acquire) == false)
{
if (isReady() == false)
{
break;
}
/*to do*/
worker_();
std::this_thread::sleep_until(start + std::chrono::milliseconds(interval_)* count++);
}
worker_()
Send thread call this. just send message and make log string.
socket_.send(address_);
logger_.log("," + std::string("Send") + "\n");
Receiver
When message arrives, it creates a receive log string and flushes it to a file.
auto& queueData = socket_.getQueue();
while (queueData.size() > 0)
{
auto str = queueData.dequeue();
logger_.log(",Receive" + str + "\n");
logger_.flush();
}
I've been testing it overnight and I can't figure out why I got this result.
chart for microseconds
x-axis : Hour_Minute_second
y-axis : microseconds
For a few hours it seemed to work as expected. But after that, the time gradually changed and went to a different time zone.
Does anyone know why this is happening?
std::chrono::steady_clock is working.
It made my charts straight.
And another way, turn off the windows automatically time synchronize.
I have to get the output of a QProcess while it is running. Therefore I have written the following Code:
CommandExecutor_C::CommandExecutor_C():
mProcessStatus(AI_UNKNOWN),
mOnTdiActiveCallback(),
mTdiProcess(new QProcess)
{
connect(mTdiProcess, SIGNAL(readyReadStandardOutput()), this, SLOT(CheckOutput()));
connect(mTdiProcess, SIGNAL(readyReadStandardError()), this, SLOT(CheckOutput()));
}
void CommandExecutor_C::ExecuteCommand(QString &aCommand)
{
mTdiProcess->start(aCommand, QProcess::Unbuffered | QProcess::ReadWrite);
LOGINFO(FB_TDI,"Launch command: " + aCommand.toStdString());
}
void CommandExecutor_C::CheckOutput()
{
QString StdOut = QString(mTdiProcess->readAllStandardOutput());
QString StdErr = QString(mTdiProcess->readAllStandardError());
mProcessStatus = CheckTdiAutomationInterface(StdOut.toStdString(), StdErr.toStdString());
if(mProcessStatus != AI_UNKNOWN)
{
OnTdiActive(mProcessStatus);
}
}
This works fine if QProcess gets finished but in my case the Process starts an automation interface which should run in background permanently. Therefore I have used "readyReadStandardOutput" and connect it to the slot CheckOutput(). CheckOutput() is getting called just if the process has been finished. Otherwise I am waiting endless.
I have googled a lot about the problem but nothing worked. I am very sure that the output is getting buffered and does just return if the Process has finished. Therefore I have started the Process in Unbuffered-Mode. I have also tried to forward the channels of mTdiProcess. Here the Code:
void CommandExecutor_C::ExecuteCommand(QString &aCommand)
{
mTdiProcess->setProcessChannelMode(QProcess::ForwardedChannels);
mTdiProcess->start(aCommand, QProcess::Unbuffered | QProcess::ReadWrite);
LOGINFO(FB_TDI,"Launch command: " + aCommand.toStdString());
}
But nothing worked. I hope you can help me.
I am using Qt 5.4.2 if that's important.
I usually check the output in regular intervals like this:
bool returnBool = false;
while (returnBool == false)
{
/*! Wait one second if the process finishes. Then read all output to
* stdout and stderr and redo. */
returnBool = process.waitForFinished(1000);
QString outputStdOut = process.readAllStandardOutput();
QString outputStdErr = process.readAllStandardError();
}
I am addind a python editor to a C++ application. In order to be able to stop the python process when the user presses ctrl-c, I have 2 threads:
1- the first one runs the python string;
2- the second one polls the keyboard to check for a keyboard interupt.
I use microsoft concurrency library to create the threads:
auto task1 = Concurrency::make_task([&]
{
task1_id = GetCurrentThreadId();
Run(Script);
bFinish = true;
});
auto task2 = Concurrency::make_task([&]
{
while(true)
{
if ((GetKeyState (0x43) < 0 && (GetKeyState (VK_CONTROL) < 0)) && task1_id != 0)
{
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
int t = PyThreadState_SetAsyncExc(task1_id,PyExc_KeyboardInterrupt);
PyGILState_Release(gstate);
break;
}
else if(bFinish)
{
break;
}
else
{
Sleep(10);
}
}
});
m_tasks.run(task2);
Concurrency::task_group_status test = m_tasks.run_and_wait(task1);
This usually works pretty well. If a ctrl-c event happens, a keyboardinterrupt event will happend on the 'script thread' and everything will stop.
A problem happends when I use matplotlib to create some figure, and send a ctrl-c event while the figures are still open. The event will stop everything like before, but the tkinter windows will not be destroyed and clicking on them will give a run time error:
Fatal Python error: PyEval_RestoreThread: NULL tstate
Is there any way I could force those windows to be properly destroyed by the keybordinterrupt exception?
I like to use the Sleep() function in a Windows Forms project, but the Sleep() is executed before anything else. I read that i should flush using fflush(), but i don't know what to flush. Can someone help me?
The code:
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) {
this->label1->Visible= false;
this->button1->Visible= false;
r = (float)rand()/(float)RAND_MAX;
r = r*100000;
i = r;
r = r - i;
String^ strR = "" + r;
this->label2->Text = strR;
if(r >= 0.5)
{
this->pictureBox1->Visible= true;
this->pictureBox1->BackColor = System::Drawing::Color::Blue;
}
else
{
this->pictureBox1->Visible= true;
this->pictureBox1->BackColor = System::Drawing::Color::Red;
}
Sleep(500);
}
The call to Sleep() is blocking your main (UI) thread, which prevents the message pump from updating your controls.
In this case, it doesn't appear that the call to Sleep really serves a purpose, other than blocking your UI - if you want to prevent the button from being pressed again, a better option would be to disable it, then use a timer (System::Windows::Forms::Timer) with a 500 ms interval to re-enable the button.
By using a timer, you won't block the UI thread, which allows your controls to stay active, but you still prevent the user from pressing the button.
This is a Visual Studio Express C++ Windows Forms application.
I want to play a WAV file strFileName.WAV in a WAV play wavPlayer every 10 seconds from the time I press the "Start" button until such time as I press the "Stop" button.
When each 10 second interval is up, TimerEventProcessor plays the WAV file.
The problem is that I have to press "Stop" twice to get it to work. The first press of "Stop" seems to be ignored.
Why is btnStop_Click not executing the first time I press "Stop"?
private: System::Void bntStart_Click(System::Object^ sender, System::EventArgs^ e) {
if (String::IsNullOrEmpty(strFileName)) {
lblRunning->Text = L"Cannot Start Until File Is Loaded";
}
else {
lblRunning->Text = L"Running";
myTimer->Interval = iIntervalSeconds * 1000;
myTimer->Tick += gcnew EventHandler( TimerEventProcessor );
myTimer->Enabled = true;
while (lblRunning->Text == L"Running") {
Application::DoEvents();
}
}
}
private: System::Void btnStop_Click(System::Object^ sender, System::EventArgs^ e) {
lblRunning->Text = L"Stopped";
myTimer->Enabled = false;
wavPlayer->Stop();
}
Get rid of the
while (lblRunning->Text == L"Running") {
Application::DoEvents();
}
loop. When you return from bntStart_Click the form will return to dispatching messages and the timer will tick as expected. There is no need for you to create a manual Application::DoEvents() loop, which is probably the cause of your problem.
By calling Application::DoEvents(); in a loop you are creating a pooling loop for window messages. Without any sleep call this loop cause 100% CPU usage. By letting your bntStart_Click return the WinForms runtime will sleep your process until a message is ready - resulting in very low CPU usage.
This high CPU usage is probably making your application unresponsive which is why it seems that clicks are not being processed.