Sign in to follow this  
psae0001

VC++ multi-thead Input problem

Recommended Posts

I'm trying to run a thread on the background of my window program. I'm using VC++ 6.0 API (not MFC). What I'm trying to do is to create a thead that respond to WM_KEYDOWN message, so the main WndProc doesn't have to waste time doing so. And the program can archieve some concurrency. However, the thread doesn't seem to respond to those A, B, C, D, 1, 2, 3, num_0, num_1 ....keys; yet it works ok to the to F1, F2, up arrow, down arrow... keys. I have to put the WM_CHAR message on in order for the thead respond to a, b, c, d, 1, 2... keys. But if I do so the value on the WParam is different. For example, the numpad keys 0, 1, 2, 3... will be the same as 0, 1, 2, 3... which is different on MS Virtual Keys. I've no idea why the thead doesn't repsond to the WM_KEYDOWN for those keys, but others are ok. While I must put the WM_CHAR message on. Thank you.

Share this post


Link to post
Share on other sites
Well here is the code, thanks for help



DWORD WINAPI input_determine2 ( PVOID p_void )
{

PMSG p_msg = ( PMSG ) p_void ;

int temp = false ;

char test_str [50] = "" ;

while ( p_msg->message != WM_QUIT )
{

switch ( p_msg->message )
{

case WM_KEYDOWN:
case WM_CHAR: // MUST have this messsage otherwise the thread
// WILL NOT capture each input

temp = ( int ) p_msg->wParam ;
sprintf ( test_str, "%X", temp ) ;
MessageBox ( NULL, test_str, "Key Down", MB_OK ) ;

break ;

default:

break ;

}

}

return ( DWORD ) true ;

}

Share this post


Link to post
Share on other sites
Quote:
I've no idea why the thead doesn't repsond to the WM_KEYDOWN for those keys, but others are ok. While I must put the WM_CHAR message on.

Your problem doesn't seem to deal with threading, but character codes. See if this solves your problem.

Share this post


Link to post
Share on other sites
splitting the thead? I just use the CreateThread function to create the thread at the beginning of the program. And the thread never gets call - because it's a thread. It runs on the background until it sees the WM_QUIT message on the main program's message queue.

I guess the problem is that the main program process the WM_KEYDOWN message and remove it on it's message queue, but not yet process the WM_CHAR. That's what I guess why the thread doesn't see the WM_KEYDOWN, but WM_CHAR message. I probably need to use the PeekMessage instead of GetMessage function.

Any idea how to tell the OS not to remove the message?

Thanks,

Share this post


Link to post
Share on other sites
Sorry, I don't think you understood my question.

What is the entry point function you specify when you split (create) the thread? What does the thread's execution loop look like? How and when do you call the input_determine2() function?


I don't know for sure because I don't have a clear idea of how your code is structured, but it sounds to me like you're expecting to have a message pump in a separate thread that handles messages out of the main application message queue. This is not a good idea, and can cause some horrible synchronization bugs.

A better design would be to handle all messages in your main application's pump, and keep a separate queue or buffer of key events that your input thread reads out of. For instance, use a std::deque to store the wParam value each time your message procedure gets a WM_KEYDOWN message. Create a mutex, lock it, add the key value, then unlock the mutex and return 0 from your message procedure to indicate that you handled the message. In your input manager thread, have a loop that locks the mutex, checks the deque for any pending input events, removes one, unlocks the mutex, handles the event, and then repeats. That approach will be safe and fairly efficient, although depending on what exactly your program is doing, you might be making a lot of synchronization headaches for yourself and getting extremely little benefit.

Share this post


Link to post
Share on other sites
Ok, I think I know why. And I kinda sorta the first half of it. Yes, it's having the synchronization problem. The first half was the main messge processed the WM_KEYDOWN message, but the thread doesn't; yet the thead saw the WM_CHAR which has not be processed by the main application (because it comes after it).

Now, I put the main to wait for the thread to see the WM_KEYDOWN message. However, the thread comes back after that, but the main application has not yet removed the message, so sometimes the thread sees the same message twice in a row. I'm now trying to reslove this problem!

I know using mutex or something like is the best way to synchronizate the problem. But I'm try to be use "the most less expensive" way.

I'm trying to find out how to use the thread to remove the message it has seen already.


Ok, my main loop


int execute (void)
{

input_tracker_thread = CreateThread ( NULL, 0, input_determine2,
&main_frame_handler.application_message,
false, &thread_id_input ) ;

while ( GetMessage ( &main_frame_handler.application_message, NULL, 0, 0 ) )
{

TranslateMessage ( &main_frame_handler.application_message ) ;
DispatchMessage ( &main_frame_handler.application_message ) ;

}

ResumeThread ( input_tracker_thread ) ;

WaitForSingleObject ( input_tracker_thread, INFINITE ) ;

return main_frame_handler.application_message.wParam ;

}




and my Windown procedure is


LRESULT CALLBACK app_proc_callback ( HWND h_wnd, UINT u_msg, WPARAM w_par, LPARAM l_par )
{

HDC hdc ;
RECT rt ;
PAINTSTRUCT ps ;

switch ( u_msg )
{

case WM_CHAR: // MUST have this messsage otherwise the thread
// WILL NOT capture each input
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:

case WM_MBUTTONDOWN:
case WM_MBUTTONUP:

case WM_RBUTTONDOWN:
case WM_RBUTTONUP:

case WM_SYSKEYDOWN:
case WM_SYSKEYUP:

case WM_KEYDOWN:
case WM_KEYUP:

MsgWaitForMultipleObjectsEx ( 1, &input_tracker_thread, 10, QS_KEY | QS_MOUSEBUTTON, 0 ) ;

return 0;

case WM_PAINT:

hdc = BeginPaint ( h_wnd, &ps ) ;
// TODO: Add any drawing code here...
GetClientRect ( h_wnd, &rt ) ;
DrawText ( hdc, "This is Main Frame Hello World", -1, &rt, DT_CENTER | DT_VCENTER | DT_SINGLELINE ) ;
EndPaint ( h_wnd, &ps ) ;

return 0 ;

case WM_DESTROY:

PostQuitMessage(0) ;

return 0 ;

}

return DefWindowProc ( h_wnd, u_msg, w_par, l_par ) ;

}



And the thread function is


DWORD WINAPI input_determine2 ( PVOID p_void )
{

PMSG p_msg = ( PMSG ) p_void ;

int temp = false ;

char test_str [50] = "" ;

while ( p_msg->message != WM_QUIT )
{

switch ( p_msg->message )
{

case WM_SYSKEYDOWN:
case WM_KEYDOWN:
// case WM_CHAR: // MUST have this messsage otherwise the thread
// WILL NOT capture each input

temp = ( int ) p_msg->wParam ;
sprintf ( test_str, "%X", temp ) ;
MessageBox ( NULL, test_str, "Key Down", MB_OK ) ;

break ;

default:

break ;

}

}

return ( DWORD ) true ;

}

Share this post


Link to post
Share on other sites
Unfortunately, threads are not something you can do halfway. You either need to do a specific and complete synrchonization model, or stick to single-threading; any compromises will lead to nothing but a lot of hacks and very hard-to-find bugs. Threads are like hand grenades: if you use them well, and properly, you can get very effective results; misuse them, or use them sloppily, and you'll be missing a few body parts - if you're still alive.

Out of curiosity, why would your program need to be handling user input in a separate thread? In most cases, user input will affect such a huge portion of your program that trying to handle it in a separate thread from everything else is a recipe for a lot of headaches. If you really genuinely need to process input asynchronously, I'm afraid the only way to do it well is to do it fully and build in a synchronization system with the rest of your program. Even if you can get a half-baked model to work for a while, I'll guarantee that it will fail eventually - and it will probably be a pretty spectacular failure when it goes.

Anyways, I don't mean to be excessively morbid or put you off threading - just be aware of what you're dealing with and the dangers of not taking proper precautions [smile]

Share this post


Link to post
Share on other sites
Yeah, I understand the issue.

Now, I'm KINDA solve that problem using "SuspendThread" at the end of each loop. Also, "ResumeThread" and "MsgWaitForMultipleObjectsEx" to archieve the classic of Sleep and Awake technique.

Well, it's work ok for now. I'll post again if I have problem again.

Share this post


Link to post
Share on other sites
Why not fix it propertly now so you don't have 10,000 lines of code to fix later? Oh well some people like pain.

Anyways, anyone have any good links to learning threading/syncing? I did a few google searches a few months ago and came up with some pretty lame (read NT4 articles) results.

Thanks.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this