Get Access to WndProc Messages

Started by
7 comments, last by LessBread 15 years, 5 months ago
Alright, I am new to posting in this forum, signed up here not too recently and regularly read the articles and such but didn't have anything really to post about until now. I am trying to enable voice recognition in a game I am working on for an assignment. Sound is my task to get working properly and after some scouring of the internet the best solution to voice recognition I could find that was free and worked properly was to use the SpeechSDK from Microsoft. I was quickly able to understand the API and get the tutorials working properly. But as this is part of a much bigger project the main goal was to have it separate from other aspects of the game. So far I have extracted all the initialization code and get to a point where the system is running without error. The problem is that I have done this in a console application, and the SpeechSDK relies on my access to messages that I would receive in the WndProc function. My question is this: Is there some way I can handle WndProc messages in a console application? When I send my work to be integrated with the other assignments I was hoping there to be an initialization function, a function to close everything and a few function calls to be added to the main game update loop. Basically all that's left to do is call a function whenever a certain windows message is received called WM_RECOEVENT. The main function call that would be within the game loop. As far as I can tell I don't have access to do that in a console application, if this problem is only as a result of being in the console would I be better off to try to integrate it with the rest of the project rather than get it working on its own in the console? Thanks in advance, and hopefully this is the first of many posts.
Advertisement
I think you're looking for a hook.

http://msdn.microsoft.com/en-us/library/ms644990(VS.85).aspx
I think this is what I am looking for or at the least very close. I'll try to elaborate a bit because I don't really want to look to far into it, only to find out its not going to work. I've done that a few too many times already.

Here would be the WndProc function in a regular windows application, shrunk for simplicity.

LRESULT WINAPI wndProc(hwndMain, uMsg, wParam, Param)
{
switch (uMsg)
{
case WM_CREATE:
}
}


Now WM_CREATE I dont need to worry about, but the SpeechSDK sends the message WM_RECOEVENT to that same variable, or at least it would if i was using a windows application which I am trying to stay away from as it will create more problems down the line.

If I understand this hook even a little bit, I can hook into thr wndProc function to grab the uMsg variable and check if it WM_RECOEVENT myself? Or am I way off base?

[Edited by - RP520 on November 21, 2008 9:40:20 PM]
A hook would only be necessary if you are trying to attach to an external application and have access to it's WinProc function. To do that you would need to write a dll that attaches to the target application using SetWindowsHookEx() and SetWindowLong() to subclass it's WinProc.

But since this a game your writing you don't need to do this.

Create a window using CreateWindowEx and use it's WndProc. If you don't want the window to show just keep it hidden.

But surely if your writing a game, the game has a window? Every window needs a WndProc so just use that.
I don't think you don't need to use a hook. You can put all of the code that would ordinarily create a window into a console application. Put it into main just like you would WinMain. If you don't want the window to appear, simply toggle away the WS_VISIBLE style. What determines if an app is a console app is a flag set in the header of the executable file (see the bonus function below), not whether the start function is main or WinMain or if it contains code to create and launch a window.

The difficultly with a hidden window is exiting the message loop. If there's no window visible there's no menu to pull down looking for an exit option and no system menu on the title bar to click on the [X] and there's no keyboard focus to accept Alt-F4. With the message loop looping, shutting down the console window becomes an exercise in frustration.

One way to exit a message loop in a console app is to install a signal handler using SetConsoleCtrlHandler, then have the handler function post a WM_QUIT message using PostThreadMessage to exit the message loop. Here's code for a sample handler function. You can look up the remaining details via MSDN.

// this include should be enough, but if it's not consult MSDN#include <windows.h>// place this with your global variables// global thread idstatic DWORD g_tid = 0;// place this in main or WinMain before entering the message loop// without this we can not exit msgloop using signalhandlersg_tid = GetCurrentThreadId();// ---------------------------------------------------------------------------// SetConsoleCtrlHandler handler functionBOOL WINAPI CtrlHandler(DWORD fdwCtrlType){	switch (fdwCtrlType)	{    // uncomment this to capture sysmenu close    //	case CTRL_CLOSE_EVENT:     // Handle the CTRL+C signal.	case CTRL_C_EVENT: 		// Before PostThreadMessage        // this comment stipulates that this call does not work here         // PostQuitMessage(0);         // post a quit message to the queue of the message handling thread        // repeat until PostThreadMessage returns true		if ( g_tid != 0 ) {			while ( !PostThreadMessage(g_tid,WM_QUIT,0,0) ) { Sleep(10); }		}		// After PostThreadMessage        // indicate that this event has been handled		return TRUE;		default:		return FALSE;	}}// ---------------------------------------------------------------------------// BONUS FUNCTIONBOOL _stdcall IsConsoleBasedProcess(void){    IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER *)GetModuleHandle(NULL);    IMAGE_NT_HEADERS *nt = (IMAGE_NT_HEADERS *)( (DWORD)dos + dos->e_lfanew );    return ( nt->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI );}


The Sleep(10) call is there so that your app plays nice with the system. If PostThreadMessage fails, Sleep(10) slows down the posting of quit messages enough to allow other apps access to the cpu.
"I thought what I'd do was, I'd pretend I was one of those deaf-mutes." - the Laughing Man
I will try the suggestion in the morning, but in case someone has another idea overnight. I'd like to add, the game's actual WndProc is likely hidden. The thing is we are using an API to do the graphics of our program. So direct access to the WndProc function may not be available without having to recompile the entire API, which is a last resort. I am trying to make my part of the project as independent as possible.

The mythological solution would be that within the gameloop i can call some GetMessage() function which returns the message id of current system events, and than if its the WM_RECOEVENT then to run my functions to check the recognition.

Thanks for the help.
If your app has to be external to your game and you want to communicate with each other you could use two hidden windows; one in the game and one in your speech recognition app. You could then use RegisterWindowMessage() to create your own custom message and send it using SendMessage(). You will probably need to use FindWindow() to get HWND of the other window. You could infact use FindWindow() to find one window and a custom message to send the HWND to the other in lParam so you can communicate.

If you can't get direct access to the game's main window WndProc you might be better off creating a hidden window and using that. But technically you could subclass the WndProc but you must be in the same process space as the window (either by hooking into it using SetWindowsHookEx() which requires a dll to attach to the game's process space). So it would have to be in game's process where you should already have the HWND to the game's window (if not FindWindow() will get it) then you can use SetWindowLongPtr() with GWLP_WNDPROC to subclass it's WndProc. But I don't really see the point in that you may as well create another window and just keep it hidden and use that instead.
Thanks everyone for the help. In researching the suggestions you all gave I discovered my problem was much simpler to solve than I first expected. All I really needed was a handle to the current window. So I used the following to do that.

char title[500];
GetConsoleTitleA(title, 500);
hWnd = FindWindowA( NULL, title );

From their I could pass that handle around and was able to get everything working properly. I basically created a message loop function in my main function, and dealt with messages as they arrived. Hopefully no more problems come up with it, as I am pretty confident everything is working exactly as it is supposed to.

Once again thanks for the help and pointing me in the right direction.
Doh! That's much simpler.

See also GetConsoleWindow

"I thought what I'd do was, I'd pretend I was one of those deaf-mutes." - the Laughing Man

This topic is closed to new replies.

Advertisement