I Read a few threads on how passing data from Workerthread to Main window(Dialog), but I still don't understand and still need help.
My workerthread should process some calculations and display the result during each loop to an edit on the GUI. I know, I should use PostMessage but since the calculation I'm doing implies a control element, I don't know how to solve this problem ...
//CWorkerThreadMgr.h manages the second thread
HRESULT Start (HWND hWnd);// from where the workerthread will be started
HWND m_hWnd ; //Window handle to the UI ;
HANDLE m_hTread; //handle of the worker thread
static UINT WINAPI ThreadProc( LPVOID lptest );
static UINT WINAPI ThreadProc( LPVOID lptest )
{
CWorkerThreadMgr* pCalculateMgr = reinterpret_cast< CWorkerThreadMgr*(lptest);
//The following operation:rand() *m_Slider.GetPos() should
//should be calculated and the result displayed each time in the edit box in the gui
for( UINT uCount = 0; uCount < 40; uCount++ ){
pCalculateMgr->rand() *m_Slider.GetPos();//?don't allowed to touch the gui!!
PostMessage(pCalculateMgr-> m_hWnd, WM_SENDCALCULATED_VALUE,wparam(rand() *m_Slider.GetPos(),0);
}
}
LRESULT CStartCalculationDlg::OnSendCalculatedValue( WPARAM Result, LPARAM )
{
// The resut should be displayed in the edit box
m_Calculation.Format(_T("%d"),???);
SetDlgItemText(IDC_CALCULATION, m_Calculation);
return 1;
}
void CStartCalculationDlg::OnHScroll(UINT nSBCode, UINT nPos,CScrollBar* pScrollBar)
{
m_SliderValue.Format(_T("%d"),m_Slider.GetPos());
SetDlgItemText(IDC_SLIDER_VALUE,m_SliderValue);
}
// Implementation in the CStartCalculationDlg.h
CWorkerThreadMgr m_WorkerThreadMgr //instance of the WorkerThreadMgr
CSliderCtrl m_Slider //member variable of the slider control
CString m_SliderValue // member variable of the edit box, where the current value of the
//slider will be displayed
CString m_Calculation // member variable of the edit box where the calculated
//result from the workerthread will be displayed via PostMessage
afx_msg LRESULT OnSendCalculatedValue( WPARAM, LPARAM );
afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
The next probem is that, when my slider control is moved and gets a new value, the thread procedure should know it and update the value of the slider. How can I manage to do it?
Instead of reading the slider position from the worker thread, read it in the UI thread and make it available to the worker thread.
I'm really no expert in this field, but I've done some worker threads as explained here.
What I would do in your case is create a static variable, like the "running" flag used in the article linked above, to hold the current position of the slider. Then, in the OnHScroll handler set it to the appropriate value.
As long as you only write to that variable from one thread, you should not have synchronization issues.
From worker thread:
m_data = foo();
PostMessage(hWndMain, UWM_SOME_CUSTOM_MESSAGE, 0, 0);
From UI thread:
LRESULT CMainFrame::OnSomeCustomMessage(WPARAM wParam, LPARAM lParam)
{
CMyData data = m_pWorker->GetData();
// Do stuff...
return 0;
}
GetData must be guarded by a critical section:
CMyData CMyWorker::GetData()
{
// This critical section is used in the worker thread too, whenever m_data is accessed.
m_lock.Lock();
CMyData data = m_data;
m_lock.Unlock();
return data;
}
See CCriticalSection on MSDN.
Related
In my MFC application I have a worker thread that listens on a network connection and as soon as some information has arrived I call SendMessageToDescendants to send that information via the lparam parameter. So every frame window will get the message and possibly process it via a message handler for the WM_SYNCHRONOTIFICATION message (WM_SYNCHRONOTIFICATION is a WM_APP+x message).
Code in the worker thread: (simplified for brevity)
while (TRUE)
{
CNotificationMessage notificationmessage;
Listen(¬ificationmessage); // blocking until some information arrives
m_pMainWnd->SendMessageToDescendants(WM_SYNCHRONOTIFICATION, NULL, (LPARAM)(newnotif));
// have all OnSynchroNotification handlers been called here ?
}
Message handler in the main thread:
LRESULT CMyFrame::OnSynchroNotification(WPARAM p1, LPARAM p2)
{
CNotificationMessage *pNotification = (CNotificationMessage*)p2;
// process message (ommited for brevity)
}
The code works fine, but I'm not sure if upon return from SendMessageToDescendants all OnSynchroNotification have been called.
The simplest solution to use is a counter. Before calling SendMessage, initialize a shared counter to the number of child windows you would like to have process the message. Each message handler is then responsible for decrementing the counter when it completes its work, and your event loop can check that the counter is 0 before generating more events. In pseudo C++:
unsigned int sharedCount; // global/static shared value
while (TRUE)
{
CNotificationMessage notificationmessage;
Listen(¬ificationmessage); // blocking until some information arrives
unsigned int current = InterlockedCompareExchange(&sharedCount, activeWindowCount, 0);
if (current == 0)
{
m_pMainWnd->SendMessageToDescendants(WM_SYNCHRONOTIFICATION, NULL, (LPARAM)(newnotif));
}
else
{
// Still processing the last message.
}
while (InterlockedCompareExchange(&sharedCount, 0, 0) != 0)
{
Sleep(100);
}
}
LRESULT CMyFrame::OnSynchroNotification(WPARAM p1, LPARAM p2)
{
// Processing
InterlockedDecrement(&sharedCount);
}
The slightly more complex solution, but the one I personally prefer, since you don't have to burn CPU waiting on completion, is to create an event for each message processing window, and then use WaitForMultipleObjects (or the Ex version) to halt the event loop until completion. Again, in pseudo C++:
while (TRUE)
{
CNotificationMessage notificationmessage;
Listen(¬ificationmessage); // blocking until some information arrives
m_pMainWnd->SendMessageToDescendants(WM_SYNCHRONOTIFICATION, NULL, (LPARAM)(newnotif));
DWORD waitResult = WaitForMultipleObjects(activeWindowCount, FrameEvents, TRUE, INFINITE);
if (waitResult == WAIT_OBJECT_0)
{
// Success
}
else if (waitResult == WAIT_FAILED)
{
// Failure: Use GetLastError() to figure out why the function call failed.
}
// Reset the events
}
LRESULT CMyFrame::OnSynchroNotification(WPARAM p1, LPARAM p2)
{
// Processing
SetEvent(thisFramesEvent);
}
This example uses an infinite timeout, but you can always set a reasonable timeout, and check for the return value WAIT_TIMEOUT to see if the time elapsed.
(Required Disclaimer: Error checking and variable initialization have been removed from both of these for the sake of brevity and readability. See the documentation for how to check for errors.)
I want to catch the tab change event of a CMFCTabCtrl. Below is the code I'm trying to do that. But it does not catch the change event.
BOOL SurvChatDlg::OnNotify( WPARAM wParam, LPARAM lParam, LRESULT*
pResult )
{
if(((LPNMHDR)lParam)->code==TCN_SELCHANGE)
{
int i = m_TabControl.GetActiveTab();
AfxMessageBox("Changed");
}
return CDialog::OnNotify( wParam, lParam, pResult );
}
According to this forum thread, you need to handle the AFX_WM_CHANGING_ACTIVE_TAB message sent to the parent window.
This forum thread has more code samples.
If you want to catch the post tab change, the tab that will be active, need AFX_WM_CHANGE_ACTIVE_TAB ie;
ON_REGISTERED_MESSAGE(AFX_WM_CHANGE_ACTIVE_TAB,OnTabSetActive)
LRESULT CYourClass::OnTabSetActive(WPARAM wParam, LPARAM lParam)
{
const int iActiveTab = (int)wParam;
int iCheckActiveTab = m_wndTabs.GetActiveTab(); //CMFCTabCtrl m_wndTabs;
m_wndTabs.SetActiveTab(iActiveTab); //good idea to also add this depending on usage.
return 0;
}
And if you require manually changing the tab call using;
SendMessage(AFX_WM_CHANGE_ACTIVE_TAB, iTabNum2ChangeTo, 0);
Posted the above after trying to find a solution to my problem where using
CMFCTabCtrl::SetActiveTab()
would crash but in debug mode only. And this OP was googles top answer.
AFX_WM_CHANGING_ACTIVE_TAB appears to catch the event prior to the actual tab change, hence why hasn't worked for the OP, and can be checked by;
int iCheckActiveTab = m_wndTabs.GetActiveTab();
I have a project in Visual C++ 2010 where I have to draw some circles and lines. The coordinates of the circles depend on two global variables. The global variables are modified from two functions, each running in their own thread. Boost is used for multi-threading.
However, as soon as I run the threads, my main thread is blocked, thus preventing me from drawing the shapes and using the global variables. How can I get around this? What I ultimately want to achieve is, modify the global variables from two seperate functions running in their own thread and simultaneously draw the circles using the said global varibales
global_variable_1
global_variable_2
void function_1()
{
while(true)
{
//modifies global_variable_1
}
}
void function_2()
{
while(true)
{
//modifies global_variable_2
}
}
void MyOnPaint(HDC hdc)
{
Graphics graphics(hdc);
Pen pen(Color::Blue);
/* Uses global_variable_1 & global_variable_2 to
draw circles */
}
int APIENTRY _tWinMain(......)
{
/* Some initial code */
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_GAZEPOINTEVALUATION));
/*Start threads*/
using namespace boost;
thread thread_1(function_1);
thread thread_2(function_2);
//Start threads
thread_1.join()
thread_2.join()
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_COMMAND:
/* Some code */
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
/*CALL MY DRAWING METHOD*/
MyOnPaint(hdc);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
/* Some code */
default:
/* Some code */
}
return 0;
}
join calls will never return because your threads loop for ever. From the docs:
In order to wait for a thread of execution to finish, the join(),
__join_for or __join_until ( timed_join() deprecated) member functions of the boost::thread object must be used. join() will block the
calling thread until the thread represented by the boost::thread
object has completed.
You never enter your message loop, therefore.
If you remove the join calls, this should do something more like what you expect - in a more complex application you'd need to properly design thread scheduling and exit handling. Even as it stands, you will likely have to add some delay into the spawned threads to avoid pegging the CPU and possibly seeing other weirdness you are not expecting.
I'm stuck with a strange problem. I'm making a Win32 application in VC++ 2008, making a class to encapsulate most of the work for easy repetition when calling a MessageBox. The message box` is created (I think) but doesn't show up unless I press the Alt key!
What happen exactly is :
I run the program
press Enter
the main window lose focus
give beep sound when i click on the main window as if a modal MessageBox is present
either press Escape... focus is gained OR press Alt then the MessageBox appear with alt key pressed (i.e. menu will drop )!!!!!!
P.S. It was working fine but suddenly this happened. I didn't find any difference - I even made a new project!
This is supposed the Main program:
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
MSG msg;
CWnd cMainWindow(TEXT("DentoMan"), TEXT("Bejkoman")); // pass The class name and window name to the constructor
cMainWindow.CreateDef(); //Create the Window
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
While This is the Class file
CWnd::CWnd() {
};
CWnd::CWnd(LPTSTR lpszClassName, LPTSTR lpszWindowName) {
CWnd::lpszClassName = lpszClassName;
CWnd::lpszWindowName = lpszWindowName;
};
CWnd::~CWnd() {
};
// Create the window with default parameters
HWND CWnd::CreateDef(void) {
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = StaticWndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = (HINSTANCE)GetModuleHandle(NULL);
wcex.hIcon = 0;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 4);
wcex.lpszMenuName = 0;
wcex.lpszClassName = lpszClassName;
wcex.hIconSm = 0;
RegisterClassEx(&wcex);
g_hWnd = CreateWindowEx(0,lpszClassName, lpszWindowName, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, wcex.hInstance, this);
hInst = wcex.hInstance; //Store hInstance in the class hInst variable
if (!g_hWnd) return false;
ShowWindow(g_hWnd, SW_SHOW);
UpdateWindow(g_hWnd);
return g_hWnd;
}
LRESULT CALLBACK CWnd::StaticWndProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam) {
/* The Only Message we take here so we store the 'this' pointer within the window to identify messages
comming from it by the 'this' pointer*/
if ( Message == WM_CREATE ) {
SetWindowLong( hWnd, GWL_USERDATA, (LONG)((CREATESTRUCT FAR *)lParam)->lpCreateParams);
}
/* Store the window pointer in the class pointer we just created in order to run the right public WndPRoc */
CWnd *Destination = (CWnd*)GetWindowLong( hWnd, GWL_USERDATA );
// If the hWnd has a related class, pass it through
if (Destination) {
return Destination->WndProc( hWnd, Message, wParam, lParam );
}
// No destination found, defer to system...
return DefWindowProc( hWnd, Message, wParam, lParam );
};
LRESULT CWnd::WndProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam) {
// Determine message type
switch (Message) {
case WM_LBUTTONDOWN:
{
/* this is a common trick for easy dragging of the window.this message fools windows telling that the user is
actually dragging the application caption bar.*/
SendMessage(hWnd, WM_NCLBUTTONDOWN, HTCAPTION,NULL);
break;
}
/*case WM_CREATE:
break;
*/
case WM_CLOSE:
PostQuitMessage(0);
break;
case WM_DESTROY:
UnregisterClass(lpszClassName, hInst);
PostQuitMessage(0);
break;
case WM_KEYDOWN: //KeyBoard keys
// Which key was pressed?
switch (wParam) {
case VK_ESCAPE: //close through escape key
PostQuitMessage(0);
return 0;
case VK_RETURN:
MessageBox(hWnd, TEXT("DFGDGD"), TEXT("DFGDFG"), NULL);
return 0;
} // End Switch
break;
case WM_COMMAND:
/*switch(LOWORD(wParam))
{
}*/
break;
case WM_PAINT:
break;
default:
return DefWindowProc(hWnd, Message, wParam, lParam);
} // End Message Switch
return 0;
};
The Class Header:
class CWnd {
public:
CWnd();
CWnd(LPTSTR lpszClassName, LPTSTR lpszWindowName);
virtual ~CWnd();
virtual HWND CreateDef(void); // Create the window with default parameters
virtual LRESULT WndProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam );
private:
static LRESULT CALLBACK StaticWndProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam);
HWND g_hWnd; //Global window handle for this window
HINSTANCE hInst; //Global instance for this window
LPTSTR lpszClassName;
LPTSTR lpszWindowName;
};
P.S. I included all needed header files, everything goes fine except MessageBox
This is also a link to the code on here
Ohhhhhhh finally i found the solution of this problem ... and for everyone to benefit the problem was in WndProc(.......) at the WM_PAINT Message i wrote some code in it and removed all the code along with BeginPaint and EndPaint functions so the program enter a freeze period once anything is being painted over it including that MessageBox but it only show when i press Alt i think coz the control is transfered to the system in that step to show the system Menu (i think)
the solution either remove the WM_PAINT message handler or add the normal BeginPaint and EndPaint functions
Thanks for everyone who passed on my question
If anyone is still interested, this method works:
MessageBox(NULL,L"error",L"Error",MB_ICONERROR|MB_DEFAULT_DESKTOP_ONLY);
I had a similar problem that was cause by WM_PAINT as somebody above mentioned. Solved it by adding return DefWindowProc(hWnd, Message, wParam, lParam); there.
When you create your MessageBox you should pass WS_CHILD in CreateWindowEx.
EDIT 2:
Ok try this.
MessageBox(hWnd, TEXT("DFGDGD"), TEXT("DFGDFG"), MB_OK);
Try using MB_TOPMOST:
MessageBox(hWnd, TEXT("DFGDGD"), TEXT("DFGDFG"), MB_TOPMOST);
I' am working on a small example and am a bit of curious using criticalsection in my example.
What I'am doing is,I have a CStringArray(which has 10 elements added to it).I want to copy
these 10 elements(string) to another CStringArray(am doing this to understand threading and Critical section),I have created 2 threads,Thread1 will copy the first 5 element to another CStringArray and Thread2 will copy the rest.Here two CStringArray are being used,I know only 1 thread can access it at a time.I wanted to know how this can be solved by using criticalsection or any other method.
void CThreadingEx4Dlg::OnBnClickedOk()
{
// TODO: Add your control notification handler code here
thread1 = AfxBeginThread((AFX_THREADPROC)MyThreadFunction1,this);
thread2 = AfxBeginThread((AFX_THREADPROC)MyThreadFunction2,this);
}
UINT MyThreadFunction1(LPARAM lparam)
{
CThreadingEx4Dlg* pthis = (CThreadingEx4Dlg*)lparam;
pthis->MyFunction(0,5);
return 0;
}
UINT MyThreadFunction2(LPARAM lparam)
{
CThreadingEx4Dlg* pthis = (CThreadingEx4Dlg*)lparam;
pthis->MyFunction(6,10);
return 0;
}
void CThreadingEx4Dlg::MyFunction(int minCount,int maxCount)
{
for(int i=minCount;i<=maxCount;i++)
{
CString temp;
temp = myArray.GetAt(i);
myShiftArray.Add(temp);
}
}
The way I'd use a CriticalSection is:
Declare a member variable in your CThreadingEx4Dlg class:
CCriticalSection m_CriticalSection;
Enclose your not thread safe code in a Lock-Unlock block of this CriticalSection:
void CThreadingEx4Dlg::MyFunction(int minCount,int maxCount)
{
m_CriticalSection.Lock();
for(int i=minCount;i<=maxCount;i++)
myShiftArray.Add(myArray.GetAt(i));
m_CriticalSection.Unlock();
}
Consider using CSingleLock so that the constructor takes care of the locking and the destructor automatically takes care of the unlocking
void CThreadingEx4Dlg::MyFunction(int minCount,int maxCount)
{
CSingleLock myLock(&m_CriticalSection, TRUE);
// do work here.
// The critical section will be unlocked when myLock goes out of scope
}