validating UpdateData(true) return value in MFC OnTimer routine - c++

I am working on an MFC application(VS2017).
In that I have a made one second timer. Inside the OnTimer() routine along with my business logic I am calling UpdateData(FALSE) to show some runtime info back to UI.
Also, I have to get some user input from the UI for that inside the user Input Event handler, I am calling UpdateData(TRUE) and checking its return value in OnTimer routine.
Problem is since the timer is an independent entity here. Even though I have guarded the UpdateData(FALSE) inside OnTimer the UpdateData(FALSE) is getting executed for a blank user input, which crashes the program by calling assert.
/***SAMPLE Problem CODE****/`
void abc::OnEnUserInput()
{
IsvalidInput = UpdateData(TRUE);
}
void abc::OnTimer(UINT_PTR nIDEvent)
{
if (IsvalidInput == true)
{
UpdateData(FALSE);
}
}
Any help would be greatly appreciated!!!
Thanks.

Got my solution earlier itself but due to the busy schedule couldn't get time to share it.
for avoiding this issue I opened another dialogue box to receive the user input, since UpdatedData(FALSE) in OnTimer() is in background dialogue box so the user input in child dialog UpdateData(TRUE) will not interfare with background UpdatedData(FALSE).

Related

Callback of request is never called when I put std::this_thread::sleep_for(1)

I am trying to prevent entering in scene if some stuff from server are not loaded, I have flag bool
done = false;
CCHttpRequest *pRequest;
// initialization ....
pRequest->setResponseCallback(this, httpresponse_selector(SceneController::on_response));
CCHttpClient::getInstance()->send(pRequest);
pRequest->release();
while(!done) {
std::chrono::milliseconds duration(1);
std::this_thread::sleep_for(duration);
}
in function callback on_response I set
done = true;
what should ( but doesn't work ) break out of infinite loop.
Problem with this approach is that callback is never called ( breakpoint at the first line in callback is never reached ). When I comment while loop it called callback.
Does anyone know what is a problem and how to prevent further execution until I fetch data from server ?
Callstack when it works on break point inside callback
Callback is NOT another thread as you might be thinking. You trying hold current function execution 'till callback gets called'. That's not correct. You should return from it and allow callback to be called. This should do:
done = false;
CCHttpRequest *pRequest;
// initialization ....
pRequest->setResponseCallback(this, httpresponse_selector(SceneController::on_response));
CCHttpClient::getInstance()->send(pRequest);
pRequest->release();
if (!done)
{
return;
}
else
{
// Do here whatever you want to do after callback changes flag
}
All your code would be executed in Cocos thread, CCHttpRequest has its own thread to fetch data from server then when all data was fetched the callback you registered before would be called in cocos thread, too.
After the registration of callback, cocos thread continues to execute to your while, now the callback is not called (data isn't completed) your while will run forever. When the data is completed, callback need to be executed in cocos thread but you're seeing the while keep blocking it.
In async world (see here, and here, you must understand async), you cannot "prevent further execution until you fetch data from server" with the pattern you are using. Try to prevent it by your done variable in places that you want it does not be executed.
E.g:
done = false;
CCHttpRequest *pRequest;
// initialization ....
pRequest->setResponseCallback(this, httpresponse_selector(SceneController::on_response));
CCHttpClient::getInstance()->send(pRequest);
pRequest->release();
and then in any you don't want user to interact with your game interface, as in Touch:
void onTouchBegan(Event *pEvent, Touch *pTouch)
{
if (!done)
{
return false;
}
// code when `done == true`
}
void onTouchEnded(Event *pEvent, Touch *pTouch)
{
// Code here will not be executed if `onTouchBegan` return `false`
}
whenever onTouchBegan return false, no more of touch code will be executed then prevent your user from interacting with game's element.
Also to prevent buttons menu clicking, set disable to their parent Menu container: menu->setEnabled(false) then set back to enabled when fetching done.

MFC - Changing dialog item focus programmatically

I have a Modeless dialog which shows a bunch of buttons; some of these are customized to draw stuff with GDI.
Now, when the user clicks on a customized one under certain conditions, a message box appears to alert user of the error and this is fine.
The problem is that after accepting the Message Box (showed as MB_ICON_ERROR), everywhere I click in the dialog, I always get the error message as if the whole dialog send the message to the customized button and the only way to get rid this is to press tab and give the focus to another control.
This is a strange behaviour and knowing why happens wouldn't be bad, but a simple workaround for now should do the job.
Since the moment that is probably a matter of focus, I've tried to set it on another control (in the owner dialog) by doing:GetDlgItem( IDC_BTN_ANOTHER_BUTTON )->SetFocus();
and then, inside the customized control by adding:KillFocus( NULL );but had no results.
How should I use these functions?
Thanks in advance.
PS: if I comment the AfxMessageBox, the control does not show this bizarre behaviour.
EDITI'll show some code as requested.
// This is where Message Box is popping out. It is effectively inside the dialog code.
void CProfiloSuolaDlg::ProcessLBtnDownGraphProfilo(PNT_2D &p2dPunto)
{
// m_lboxProfiles is a customized CListBox
if(m_lboxProfiles.GetCurSel() == 0)
{
// This profile cannot be modified.
/*
CString strMessage;
strMessage.Format( _T("Default Profile cannot be edited.") );
AfxMessageBox( strMessaggio, MB_ICONERROR );
*/
return;
}
// Selecting a node from sole perimeter.
SelectNodo(p2dPoint);
}
Actually, the message is commented to keep the dialog working.
// This is inside the customization of CButton
void CMyGraphicButton::OnLButtonDown(UINT nFlags, CPoint point)
{
PNT_2D p2dPunto;
CProfiloSuolaDlg* pDlg = (CProfiloSuolaDlg*)GetParent();
m_pVD->MapToViewport(point,p2dPunto);
switch(m_uType)
{
case GRF_SEZIONE:
pDlg->ProcessLBtnDownGraphProfilo(p2dPunto);
break;
case GRF_PERIMETRO:
pDlg->ProcessLBtnDownGraphPerimetro(p2dPunto);
break;
}
CButton::OnLButtonDown(nFlags, point);
}
Since you are handling the button down event in the button handler for the custom control, you don't need to call the base class. Just comment out CButton::OnLButtonDown(nFlags, point).

deadlock problem with threads in GTK

In my GUI, I have a list store treeview in my main window. When the user double clicks a row, a dialog pops up. The problem is that the data I populate in the dialog box takes a while to process, so what I've done is started a thread (using boost threads) to do the dialog box calculations.
In main:
.......
g_signal_connect (G_OBJECT (m_treeview), "row_activated", G_CALLBACK (m_row_activated),
(gpointer) main_window);
.......
In m_row_activated:
.........
// combo_box and dialog are GtkWidget* global variables
create_dialog(dialog, combo_box); // function creates the combobox
set_combo_box_with_loading_message;
gtk_widget_show_all (dialog);
thread m_thread (bind (&do_dialog_calculations, data1, data2, combobox));
.........
In do_dialog_calculations:
.........
// do_calculations takes about 15 seconds to complete
do_calculations(MyData data1, MyData data2, combobox);
gdk_threads_enter();
gtk_combo_box_append_text(...);
gdk_threads_leave()
Everything works fine (i.e. when the user double clicks a row, a dialog pops up immediately with a loading message and it is populated eventually when the thread returns), but my problem is when the user closes the dialog before do_calculations in do_dialog_calculations completes. If the dialog is destroyed, my combobox within it will be destroyed and my call to gtk_combo_box_append_text will seg fault.
I tried to test the combo box before updating it:
In do_dialog_calculations:
.........
do_calculations(MyData data1, MyData data2, combobox);
gdk_threads_enter();
if (GTK_IS_COMBO_BOX (combobox))
gtk_combo_box_append_text(...);
gdk_threads_leave()
but this results in a deadlock at the call to GTK_IS_COMBO_BOX. I think this is beause GTK_IS_COMBO_BOX probably calls gdk_threads_enter(). I've also tried testing for NULL
if (combobox == NULL)
but that doesn't seem to work either. Any suggestions on how to get around this problem?
UPDATE: The deadlock at GTK_IS_COMBO_BOX only occurs if I close the dialog immediately after it opens (i.e. before do_calculations() completes. If I just let the dialog sit, it will eventually update. Also, if I switch the combobox check before writing calling gdk_threads_enter():
if (GTK_IS_COMBO_BOX (combobox)
{
gdk_threads_enter();
gtk_combo_box_append_text(...);
gdk_threads_leave();
}
No deadlock occurs when I destroy the dialog before this code executes. However, I'm afraid of the rare possibility that the user will close the dialog after the GTK_IS_COMBO_BOX check completes.
PS - I use threads to do my dialog box calculations because the dialog boxes are non modal, and I want the user to be able to do other things with the main UI while the dialog boxes populate.
I think this is beause GTK_IS_COMBO_BOX probably calls gdk_threads_enter()
I don't think this is the case. These macros are generally pretty simple and I wouldn't expect it to take a lock. In fact, as far as I can tell the whole idea of gdk_threads_enter is that the library itself shouldn't call this, only code that knows it's running in another thread should.
Here's my idea: Did you forget to call g_thread_init and gdk_threads_init?
Also, one thing to keep in mind... By default, gdk_threads_enter is not using a recursive mutex. Though some people have religious objections to recursive mutexes, it's possible to have gdk_threads_enter use one:
static GStaticRecMutex my_gdk_lock;
static void my_gdk_lock_enter() {g_static_rec_mutex_lock(&my_gdk_lock);}
static void my_gdk_lock_leave() {g_static_rec_mutex_unlock(&my_gdk_lock);}
// ...
g_thread_init(NULL);
g_static_rec_mutex_init(&my_gdk_lock);
gdk_threads_set_lock_functions(G_CALLBACK(my_gdk_lock_enter),
G_CALLBACK(my_gdk_lock_leave));
gdk_threads_init();
// ...
Update: From your comment it sounds like you have a race condition between destroying the dialog and populating the combo box. One potential solution is that you bump up the reference count of the combo box (i.e. gtk_widget_ref) so that it doesn't get freed while your asynchronous worker is doing something. Then release it with gtk_widget_unref when the other thread no longer needs the pointer.

wxProgressDialog somehow keeping app alive after death?

I'm having a strange problem with wxWidgets. I have the following code
MyFrame::OnDoSomeLongThing(...) {
progScreen = new wxProgressDialog(text,text,number,this,wxPD_AUTO_HIDE); // wxProgressDialog *progScreen is class member
doPartOfThing() // calls the update method at the end of it
....
doLastPartOfThing() // again calls update method that pushes value to 100/100
progScreen->Destroy();
}
MyFrame::update() {
progScreen->Update(newValue);
}
Now here's the thing. I can literally comment out the lines relating to progScreen, just let the process go without using a progress dialog, after all is said and done, my apps exits gracefully when I close the main window.
However, just the use of the progress dialog is somehow extending the life of the application. I've tried Destroy(), I've tried simply 'delete progScreen', and both, every time: I'll close the main frame, the process keeps running, and at some point exits with some astronomical number. The only thing I could think might be relevant, is that the doPartsOfThings methods may call boost::this_thread::sleep, because it involves waiting and whatnot down in my model class. But this shouldn't have anything to do with my problem. Or maybe it does... EDIT: I do want to emphasize that progScreen->Update() IS being called from the main (GUI) thread.
So I ask, am I using a wxProgressDialog correctly? If not, how should it be used?
Thanks for your help!
EDIT:
Well... it turns out that removing wxPD_AUTO_HIDE fixed the problem. I'm still not quite sure what the problem is, but the dialog even still behaves as before. App closes as expected.
I think that you need to override the wxApp method that closes the application so that it closes the wxProgressDialog object before it quits.
wxApp::OnExit
virtual int OnExit()
Override this member function for any processing which needs to be
done as the application is about to exit. OnExit is called after
destroying all application windows and controls, but before wxWidgets
cleanup. Note that it is not called at all if OnInit failed.
The return value of this function is currently ignored, return the
same value as returned by the base class method if you override it.
You will need something like, assuming progScreen is a public attribute of your frame
int myApp::OnExit()
{
(MyFrame*)(GetTopWindow())->progScreen->Destroy()
return wxApp::OnExit();
}

CDockingManager GetPaneList() causes assertion failure in wincore.cpp?

So I thought this would be pretty simple, but I forgot it's MFC. Instead of registering a notification listener for data model changes that would possibly require a GUI update on each individual control I figure why not register it once and then send a message to all the open dock panes and allow them to update their controls as needed on their own terms for efficiency.
My callback function for handling the notification from the server looks something like this:
void CMainFrame::ChangeCallback(uint32_t nNewVersion, const std::vector<uint32_t>& anChangedObjectTypes)
{
CObList panes;
GetDockingManager()->GetPaneList(panes); // assert failure
if (!panes.IsEmpty())
{
POSITION pos = panes.GetHeadPosition();
while (pos)
{
CDockablePane* pPane = dynamic_cast<CDockablePane*>(panes.GetNext(pos));
if (pPane)
pPane->PostMessage(DM_REFRESH, nNewVersion);
}
}
}
The error I am getting is an assertion failure on line 926 of wincore.cpp
CHandleMap* pMap = afxMapHWND();
ASSERT(pMap != NULL); // right here
There is a comment below this saying this can happen if you pass controls across threads however this is a single threaded MFC application and this is all being done from the main frame.
Does anyone know what else can cause this?
If there is another way to go about sending a message to all the open CDockablePane derived windows in MFC that works as well ...
Here's the obvious workaround that I didn't want to have to do but after hours of debugging and no response here I guess this is a viable answer:
I added std::vector<CDockPane*> m_dockList; to the members of CMainFrame
Now after each call to AddPane in various places that can create and open new dock panes I make a subsequent call to push_back and then I override CDockablePane::OnClose like so:
CMainFrame* pMainFrame = reinterpret_cast<CMainFrame*>(AfxGetMainWnd());
if (pMainFrame)
{
std::vector<CDockPane*>::const_iterator found(
std::find(pMainFrame->DockList()->begin(), pMainFrame->DockList()->end(), this));
if (found != pMainFrame->DockList()->end())
pMainFrame->DockList()->erase(found);
}
CDockablePane::OnClose();
Now this list will only contain pointers to open dock panes which allows me to handle the event notification in my callback and simply do a for loop and PostMessage to each.