Reputation: 37
I have a problem in using ::AfxBeginThread in MFC.
I tried to make a thread and this is my code.
void CMyoControllerView::OnCbnSelchangeComboFist()
{
int nIndex = m_combo_Fist.GetCurSel();
int INDEX;
if(nIndex != CB_ERR){
CString str;
m_combo_Fist.GetLBText(nIndex, str);
if(str == "Left Click") {
INDEX = 0;
} else if(str == "Double Click") {
INDEX = 1;
} else if(str == "Right Click") {
INDEX = 2;
} else if(str == "Wheel Click") {
INDEX = 3;
} else {
INDEX = 4;
}
pThread_Fist = ::AfxBeginThread(ThreadFunc, &INDEX);
}
}
and ThreadFunc function is...
UINT CMyoControllerView::ThreadFunc(LPVOID pParam)
{
int index= (int)pParam;
if(index == 0) { }
else if(index == 1) { }
...
}
There is a problem in ::AfxBeginThread(ThreadFunc, &INDEX));
I want to pass an argument to "INDEX" (possible value: 0~4).
However, when I debugged this code, there were garbage value in "index" variable in ThreadFunc. (The value should be 0~4.)
I tried to use reinterpret_cast , reinterpret_cast and etc. but I can't get right value.
What should I change to get right value in "index"variable?
(ps. I am not sure but I thought it could be a x64 or x86 problem. so I searched google and got nothing. I am using Windows 8 x64.)
Upvotes: 2
Views: 3633
Reputation: 51413
The way you are passing the argument INDEX
is fine. The problem you run into is one of synchronization: INDEX
is an automatic variable, that becomes invalid, when it goes out of scope, i.e. just before CMyoControllerView::OnCbnSelchangeComboFist
returns. At that point your thread may not have read the variable, and once it does, it sees garbage. Plus, you're re-interpreting the int*
as an int
value, when trying to read it, so your index
variable is the numeric value of the address of the INDEX
variable.
The solution is to implement synchronization. After requesting to create a new thread, you have to wait for it to read the data passed to it. One solution is to use events.
Create a global event object to synchronize thread startup:
HANDLE g_hThreadRunning = ::CreateEvent( NULL, FALSE, FALSE, NULL );
Change your thread creation code to wait for the thread to signal, that it's done reading the data (WaitForSingleObject):
void CMyoControllerView::OnCbnSelchangeComboFist() {
// ...
pThread_Fist = ::AfxBeginThread( ThreadFunc, &INDEX );
::WaitForSingleObject( g_hThreadRunning, INFINITE );
}
In your thread procedure, set the synchronization object to signaled, when done reading the data (SetEvent). Note that you have to interpret pParam
the same way you did, when you passed the parameter, i.e. as an int*
:
UINT CMyoControllerView::ThreadFunc( LPVOID pParam ) {
int index = *static_cast<int*>( pParam );
::SetEvent( g_hThreadRunning );
if(index == 0) { }
else if(index == 1) { }
// ...
}
pParam
pointer argument carry an int
instead:
pThread_Fist = ::AfxBeginThread( ThreadFunc, reinterpret_cast<void*>( INDEX ) );
and inside ThreadFunc
:
int index = reinterpret_cast<int>( pParam );
Since this is passing the argument by value, you don't need any synchronization. However, this is not generally applicable. In particular, it can fail for certain platforms. It is not guaranteed to work (although it will for all platforms running Windows today).
Upvotes: 1
Reputation: 43321
Pass INDEX
by value casting to LPVOID
:
pThread_Fist = ::AfxBeginThread(ThreadFunc, (LPVOID)INDEX);
Your existing thread code is fine to make an opposite casting:
UINT CMyoControllerView::ThreadFunc(LPVOID pParam)
{
int index= (int)pParam;
...
}
LPVOID
size is enough to contain an integer value both for x86 and x64 platforms.
Upvotes: 0