GUI with c++. lagged when i use button - c++

am new in C++ GUI, am modifying a code sent with a machine to me, i want to make a while loop when i click button, i tried the thread and it is still stuck.
void CDlgWriteEPC::loop()
{
// Do something
}
void CDlgWriteEPC::OnBnClickedOk()
{
std::thread loadingThread(&CDlgWriteEPC::loop, this);
loadingThread.join();
}

join blocks the current thread until the other thread is done, so that's no help.
You should start the worker thread, return immediately and the worker thread
should send some kind of message when it's done.
The function names in your example code seem to look like it's from a MSVC++ MFC
application so we'll work with that.
Simply put, windows GUI applications are event driven, each time a event happens a
WM_MESSAGE is sent. The framework receives these messages and calls the appropriate
functions to handle it. We can define our own messages and message handlers.
This way the worker thread can send such messages to the framework and it will call
our handler function.
WM_APP is defined as a starting point for private user messages, so there won't be
any conflict with already existing system messages.
(https://msdn.microsoft.com/en-us/library/windows/desktop/ms644930%28v=vs.85%29.aspx)
So, imagine we are building a MFC dialog application that searches something in a file.
If the file is big it can take a long time and to prevent blocking the main thread and
getting a 'window is not responding' message we need to do this in a worker thread. We might
also want a progress bar for example.
First, we define our own messages inside our existing dialog class starting from WM_APP + 1
and we add our handler functions, these must be of the following type:
afx_msg LRESULT (CWnd::*)(WPARAM, LPARAM)
WPARAM and LPARAM are parameters passed when posting the message, you can use them to send
custom data. In our example we can use them to send the % progress for our progress bar.
(https://msdn.microsoft.com/en-us/library/k35k2bfs.aspx)
class CMyAppDlg : public CDialogEx
{
public:
//Public so the worker thread class can use these same sames when posting a message.
enum Messages
{
MSG_ThreadProgress = WM_APP + 1,
MSG_ThreadDone
};
private:
afx_msg LRESULT OnThreadProgress(WPARAM wParam, LPARAM lParam)
{
ProgressBar->SetPos(wParam); //Worker thread posts progress as wParam
};
afx_msg LRESULT OnThreadDone(WPARAM wParam, LPARAM lParam)
{
//Get result from worker thread class and use it...
};
};
Then we need to add our messages and handlers to the message map, you should add them to the already
existing message map in the .cpp file for the dialog/document.
BEGIN_MESSAGE_MAP(CMyAppDlg, CDialogEx)
ON_MESSAGE(MSG_ThreadProgress, &CMyAppDlg::OnThreadProgress)
ON_MESSAGE(MSG_ThreadDone, &CMyAppDlg::OnThreadDone)
END_MESSAGE_MAP()
Now we can simply post these messages in our worker thread and the framework main thread will handle
the messages and update the progress bar or use the result:
class ThreadClass
{
public:
//Constructor takes a reference to our dialog class because we need the window handle
//to post a message
ThreadClass(CMyAppDlg& MyAppDlg) : mMyAppDlg(MyAppDlg) {};
void operator()() //Thread worker code...
{
while (what_we_look_for_not_found)
{
int Progress = 0;
//Search for a while and update progress variable...
//Post message to dialog asking it to update the progressbar
PostMessage(mMyAppDlg.m_hWnd, CMyAppDlg::MSG_ThreadProgress, Progress, 0);
}
//Finished searching...
StoreResult();
//Post message to dialog informing it thread is done and result can be retrieved.
PostMessage(mMyAppDlg.m_hWnd, CMyAppDlg::MSG_ThreadDone, 0, 0);
}
private:
CMyAppDlg& mMyAppDlg;
};
QT uses a similar system with SIGNAL and SLOT and other frameworks surely have their own equivalent system.
You should be able to find more information in the manuals if you are using something else then MSVC++ MFC.

Related

Updating an CListViewCtrl from worker threads in WTL and C++

As in title I want to add/remove items to a class derived from the WTL CListViewCtrl class from worker threads, but always get "Unhandled exception thrown: read access violation."
I tried Win32 API PostMessage and SendMessage but once the worker thread touches the HWND of CListViewCtrl I get the same exception.
// CListCtrl member function, calling from worker thread
HWND GetHwnd()
{
return hwndListCtrl; // exception here
}
I tried this SafeQueue but once worker thread touches the mutex or queue then exception again.
// SafeQueue is member variable in CListViewCtrl, created in GUI thread
SafeQueue<T> m_SafeQueue;
. . .
// member function in SafeQueue class, calling from worker thread
void enqueue(T t)
{
std::lock_guard<std::mutex> lock(m); // exception here
q->push(t);
}
I tried to create the mutex and queue with new and HeapAlloc/LocalAlloc but same exception again.
I tried Win32 API CreateMutex but no luck, same exception when accessing mutex handle from worker thread.
It works fine when I add items from the GUI thread.
Only way it works from worker threads if I declare HWND or mutex and queue as static/global but I would avoid this since I want to use more than one instance from this listcontrol and I prefer any more elegant way than global variable.
I want to make this class reusable since I want to use it many times with a few modifications (more columns, different colors).
I appreciate any help and idea how I can make this work.
Environment:
VS2015 Community, WTL/C++ and Win10 Pro 64bit
I found the problem that causes access violation exception:
I declared ThreadProc callback function as static member function in CListViewCtrl class.
// DO NOT USE
// in CListViewCtrl
**static** DWORD WINAPI ThreadProc(LPVOID lp)
{
. . .
}
LRESULT OnStartWorkerThread(WORD /*wNotifyCode*/, WORD /*wID*/, HWND . ..)
{
DWORD dw;
::CreateThread(NULL, 0, this->ThreadProc, NULL, 0, &dw);
}
A working solution:
class CListViewCtrl ...
{
// thread-safe queue to store listctrl items to be added later in GUI thread
SafeQueue<CListCtrlItem<nCols> > m_SafeQueue;
// thread ID of the thread in which listctrl was created, saved in OnCreate
DWORD m_dwGuiTid;
// . . .
Check if SafeAddItem function called from GUI or any other threads
BOOL InvokeRequired()
{
if (m_GuiTid == ::GetCurrentThreadId())
return false;
return true;
}
// ...
SafeAddItem member function can be called from GUI and worker threads
void SafeAddItem(CListCtrlItem<nCols> item)
{
if (!InvokeRequired())
{
// we are in GUI thread so just add listctrl item "normal" way
AddItem(item);
return;
}
// we are in other thread so enqueue listctrl item and post a message to GUI
m_SafeQueue.Enqueue(item);
::PostMessage(m_hWnd, WM_ADD_ITEM, 0, 0);
}
// . . .
Message handler of PostMessage, we are in GUI thread
LRESULT OnAddItem(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
{
CListCtrlItem<nCols> item;
while (!m_SafeQueue.Empty())
{
item = m_SafeQueue.Dequeue();
// we are in GUI thread so we can add list ctrl items normal way
AddItem(item);
}
return 1;
}
// ...
}
And now we can add listctrl items from any threads this way. I pass this pointer to ThreadProc in _beginthreadex
m_ListCtrl.SafeAddItem(item);
The question appears to be not really about UI updates from worker thread, but about proper use of worker threads per se.
There is sufficient amount of comments about dangers of doing UI updates: they are all about potential deadlock problem. Most of the updates involve sending a message, which is a blocking API call. While you do the update from worker thread and the calling thread is blocked, any attempt from the handler in the UI to synchronize or otherwise collaboratively work with the worker may result in a deadlock. The only way around this is to prepare update in the worker thread and signal the UI thread (including by posting a message instead of sending it, in terms of SendMessage, PostMessage API) to take over and complete the updates from UI thread.
Back to original problem: you seem to be having a problem with a static thread procedure. The fourth argument in the CreateThread call is:
lpParameter [in, optional]
A pointer to a variable to be passed to the thread.
You have it NULL and you are typically to use it to pass this value to your thread procedure callback. This way you can pass execution back from static function to your class instance:
DWORD CFoo::ThreadProc()
{
// ThreadProc with proper "this" initialization
// HWND h = GetHwnd()...
}
DWORD WINAPI ThreadProc(LPVOID pvParameter)
{
return ((CFoo*) pvParameter)->ThreadProc();
}
LRESULT CFoo::OnStartWorkerThread(WORD /*wNotifyCode*/, WORD /*wID*/, HWND ...)
{
DWORD dw;
::CreateThread(NULL, 0, this->ThreadProc, (LPVOID) this, 0, &dw);
}
Also note that you are not supposed to use CreateThread directly: you have _beginthreadex and AtlCreateThread (related question).
In Windows you should never directly modify a GUI control via a worker thread. In the .NET world if we want to update a control via a worker thread we have to do a platform invoke on a Delegate which basically performs a context switch.
You have a similar problem in WIN32.
There is an excellent article on this subject I will call your attention to. It also discusses various safe workarounds:
https://www.codeproject.com/Articles/552/Using-Worker-Threads
Worker threads and the GUI II: Don't touch the GUI
"That's right. A worker thread must not touch a GUI object. This means that you should not query the state of a control, add something to a list box, set the state of a control, etc.
Why?
Because you can get into a serious deadlock situation. A classic example was posted on one of the discussion boards, and it described something that had happened to me last year. The situation is this: you start a thread, and then decide to wait for the thread to complete. Meanwhile, the thread does something apparently innocuous, such as add something to a list box, or, in the example that was posted, calls FindWindow. In both cases, the process came to a screeching halt, as all threads deadlocked."

C++ MFC processor thread lagging

I am trying to debug a C++ MFC windows service application as we have found that in high-intensity environment there becomes a lag between the receipt and processing of third-party messages.
The service listens for received messages from a third-party system. On receiving a message, it passed the message to a dedicated processing thread which does all the processing of the command. The problem, as aforementioned, is that we are seeing a significant lag in the receipt and processing. We can see this because all messages include a sequence number. For example, our tracing reports that the main thread receives a message with sequence number 867 at 13:00:00, but we do not see the corresponding trace from the processing thread until 13:00:38 (this is a real-time system, so ideally it would be instantaneous).
I can also see in the tracing that, where on a normal system we would see a clean sequence of Receipt-Process-Receipt-Process-Recepit-Process etc..... on this log we have a block of 4 or 5 receipt messages, followed by 4 or 5 process messages, then another 4 or 5 receipt messages, and so on. Since these are running on different threads this surprises me, as I would expect to see the traces interspersed, even if delayed.
In the code, there is a message map like so:
BEGIN_MESSAGE_MAP(CProcessorThread, CWinThread)
ON_THREAD_MESSAGE(WM_MSG_RCV, OnReceive)
END_MESSAGE_MAP()
The thread class looks like this (slightly abridged):
class CProcessorThread : public CWinThread {
protected:
DECLARE_MESSAGE_MAP()
private:
CAMProcessingObject *obj;
afx_msg void OnReceive(WPARAM wParam, LPARAM lParam) {
CString* strRecData = (CString*)wParam;
CString& sTemp = *strRecData;
// Various checks and filters are done here
if (true) {
obj->ProcessMessage(sTemp);
}
delete strRecData;
}
}
Here is the method that initially receives the message from the third-party system (again, abridged):
DWORD WINAPI CAMReceiving::ThreadReceive( void * args) {
CAMObject * camObject = (CAMObject *) args;
char chTemp[500];
while(true) {
// Get the message from the socket
memset(chTemp, 0, sizeof(chTemp));
iRecvLen = camObject->objsocket.ReceiveData(chTemp, sizeof(chTemp) - 1);
CString strEventNotification = _T("");
// Do various processing to populate the strEventNotification CString and make sure we want to process this message
CString* strEvent = new CString(strEventNotification);
processorThread.PostThreadMessageA(WM_MSG_RCV,(WPARAM)strEvent,0);
continue;
}
return 1;
}
So as you may tell I didn't write this and I'm not particular familiar with the multithreading methods in C++, so my question is really is there something fundamentally wrong with this design? Or is there something I don't know that is going to cause me problems here? Thanks in advance!

Using handler OnBegindrag with CTreeCtrl

I have class MyCTreeCtrl and I want to add message handler like:
void MyCTreeCtrl::OnBegindrag(NMHDR *pNMHDR, LRESULT *pResult)
{
}
What should I write between:
BEGIN_MESSAGE_MAP(MyCTreeCtrl, CTreeCtrl)
END_MESSAGE_MAP()
for creating BEGINDRAG handler.
Can't you advice me some literature about message handling in MFC? Thanks.
You should not deal with message map trying to create handlers yourself. For most messages, wizard is going to add the code for you.
For tree control in the dialog for example, you can select tree control in the resource editor and choose Add Event Handler (There are also other ways of inserting message handler using class view and properties). It is unfortunate that MS named it an event handler while in reality it is notification message handler for control specific notification code; in your case it is TVN_BEGINDRAG.
Wizard inserts appropriate entries into a message map:
ON_NOTIFY(TVN_BEGINDRAG, IDC_TREE_DRAG, &CYourDlg::OnTvnBegindragTreeDrag)
Adds declaration in .h file:
afx_msg void OnTvnBegindragTreeDrag(NMHDR *pNMHDR, LRESULT *pResult);
and implementation (definition) on .cpp file:
void CYourDlg::OnTvnBegindragTreeDrag(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
// TODO: Add your control notification handler code here
*pResult = 0;
}
In the nutshell:
Message map is the way MFC was design for the flexibility of inserting message handlers. As for any Win32 application, message handler is called from windows procedure; in MFC it is MFC window procedure that all controls are subclassed with.
The message map is the static array of AFX_MSGMAP_ENTRY structures:
struct AFX_MSGMAP_ENTRY
{
UINT nMessage; // windows message
UINT nCode; // control code or WM_NOTIFY code
UINT nID; // control ID (or 0 for windows messages)
UINT nLastID; // used for entries specifying a range of control id's
UINT_PTR nSig; // signature type (action) or pointer to message #
AFX_PMSG pfn; // routine to call (or special value)
};
MFC window procedure gets this map, search for an entry for specific signature (nSig) and if signature of the entry matches, calls appropriate function (pfn).
Each message entry in the map uses specific macro that expands to this structure.
In your case it is ON_NOTIFY, since message is MW_NOTIFY. You will also notice the notification code TVN_BEGINDRAG.
In case you want to create message entry for a message that is not in the wizard database, or for custom message, you have couple of choices, ON_MESSAGE you can use in following manner:
Macro goes into a message map and declaration and definition that go into header and cpp files.
ON_MESSAGE(WM_CUSTOM_MESSAGE, OnCustomMessage)
LRESULT CTreeCtrlDragSampleDlg::OnCustomMessage(WPARAM wParam, LPARAM lParam)
{
return 0;
}
afx_msg LRESULT OnCustomMessage(WPARAM wParam, LPARAM lParam);
Other choices: ON_COMMAND, ON_CONTROL that map WM_COMMAND messages from window or windows common control.
More info:
http://msdn.microsoft.com/en-us/library/6d1asasd(v=vs.100).aspx for VS 2010

How can I handle control messages on the parent CDialog?

I've got some control like CtrlTree on CMyDialog.
I want to handle messages like ON_WM_LBUTTONDOWN() from CTreeCtrl in the CMyDialog class.
Is there any way in MFC to redirect message stream to parent?
The simplest way to redirect your messages is just sending a custom (WM_USER + xxx) message from your control's ON_WM_LBUTTONDOWN handler to the parent class.
Place parent's WM_LBUTTONDOWN handler code in a separate method and call this method directly.
Something like that (pseudo code), presuming that your existing code sits in HandleTreeCtrlLBDown()
CMyTreeCtrl::OnLButtonDown(..)
{
pParent ->SendMessage(WM_TREECTRLLBDOWN, 0, (LPARAM)this);
}
CControlParentDialog::OnTreeCtrlLBDown(wParam, lParam)
{
HandleTreeCtrlLBDown();
}

Create a dialog in UI Thread causes crash

I tried to create a dialog in a UI thread(CWinThread).
However, it crashes when the CDialog::Create() is called.
I had verified with previous implementation, the dialog is successfully created in non-threading mode.
Does any guru here know the crash reason of creating a dialog in CWinThread?
Without Threading:
class CProduction : public CDialog{
...
}
class CScrollProductionView : public CScrollView{
CProduction *m_pProduction;
...
}
void CScrollProductionView::OnInitialUpdate(){
m_pProduction = new CProduction(0, *m_pVisionAdapter);
m_pProduction->Create(IDD_DLG_PROD, this); //--> created dialog successfully
m_pProduction->ShowWindow(SW_SHOW);
}
Implement UI Thread:
class CProduction : public CDialog{
...
}
class CScrollProductionView : public CScrollView{
CProductionThread* m_pProdThread;
...
}
class CProductionThread : public CWinThread{
CProduction *m_pProduction;
...
}
void CScrollProductionView::OnInitialUpdate(){
m_pProdThread->PostThreadMessage(WM_INITPRODTHREADMESSAGE, PROD_INIT, (LPARAM)m_pVisionAdapter);
m_pProdThread->PostThreadMessage(WM_INITPRODTHREADMESSAGE, PROD_CREATE_DLG, (LPARAM)this);
}
void CProductionThread::InitMessageHandler(WPARAM wParam, LPARAM lParam)
{
printf("Receiving InitMessageHandler msg %d\n", (UINT)wParam);
switch(wParam)
{
case PROD_INIT:
{
CVisionAdapter* pAdapter = (CVisionAdapter*)lParam;
m_pProduction = new CProduction(NULL, *pAdapter);
}
break;
case PROD_CREATE_DLG:
{
CScrollProductionView* pView = (CScrollProductionView*)lParam;
m_pProduction->Create(IDD_DLG_PROD, pView); //--> Crash here
m_pProduction->ShowWindow(SW_SHOW);
}
break;
default:
break;
}
}
Error message:
Debug Assertion Failed! ..
File: .... wincore.cpp
Line: 9906
Thanks you for viewing this question.
Try not to create CWinThread, instead of creating a worker thread, if you have many communication between classes like passing object pointers, strings etc.
You will feel less headache if update the GUI through message handling.
I suspect that the problem is that your CProduction object has not been created when the PROD_CREATE_DLG message is being handled. This may be because of using PostThreadMessage. Using PostThreadMessage is fraught with problems. In particular, the messages may get lost, so the thread never sees the PROD_INIT message.
In the single-threaded code you create your CProduction object right before the Create call. Why don't you do the same in the multi-threaded code?
If you really want to use windows messages to communicate between your threads, I would create a "message only" window (See http://msdn.microsoft.com/en-us/library/ms632599.aspx#message_only) instead, as the window messages will not get lost in the same way that the thread messages do.
Alternatively, use a thread-safe queue to pass custom messages between threads, such as my example queue at http://www.justsoftwaresolutions.co.uk/threading/implementing-a-thread-safe-queue-using-condition-variables.html
The crash is caused by MFC referring invalid window handle (which passed in as parent) during creation.
As a general rule, a thread can access
only MFC objects that it created. This
is because temporary and permanent
Windows handle maps are kept in thread
local storage to help maintain
protection from simultaneous access
from multiple threads.
http://msdn.microsoft.com/en-us/library/h14y172e(VS.71).aspx
an easy fix stated in the MSDN involves changing the code into
...
m_pProdThread->PostThreadMessage(WM_INITPRODTHREADMESSAGE, PROD_CREATE_DLG, (LPARAM)this->GetSafeHwnd());
...
CScrollProductionView* pView = (CScrollProductionView*)CScrollProductionView::FromHandle((HWND)lParam);
m_pProduction->Create(IDD_DLG_PROD, pView);
m_pProduction->ShowWindow(SW_SHOW);
Edit:
Fixed the link to msdn.