FindWindow() fails occasionally (attempting IPC) - c

my first post on stackoverflow.
I'm not much of a coder, I dabble in coding sometimes for fun and as such I don't invest much time in understanding the fundamentals, but rather find whatever solution that works, even if it's a little "ugly".
Which brings me to my problem: I wrote a simple winapi program in C with one dialog box and one DlgProc.
It accepts files and does something with them, lets say, for the sake of simplification that all it does is create a copy of the file with the extension *.BAK.
I've added a key to the registry (HKEY_CLASSES_ROOT*\shell\BKUP\command) so that I can select several files in windows explorer and have the option "Create Backup" to send all their names to my program but that calls my program for each file separately. So I googled, did some reading, turns out I need something called IPC (Interprocess Communications), read some options, WM_COPYDATA message looked like it was the simplest and easiest solution, so I used it and it works like a charm, BUT, sometimes it just doesn't... First I'll explain what I do:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
HWND hwnd;
COPYDATASTRUCT dsIPC;
hwnd=FindWindow("#32770","Backup program");
if(hwnd)
{
// send "__argv[1]" via SendMessage(hwnd,WM_COPYDATA... etc.
return(0);
}
return DialogBox(hInstance, MAKEINTRESOURCE(ID_DIALOG), NULL, DlgProc);
}
I use FindWindow() to check if there is an instance of the program running, if not it runs normally, if yes I send the file name to the window found by FindWindow() and exit that instance of the program completely.
Inside the dialog process I have code to fill an array with the names of the files and for each set a short timer with SetTimer() and when all the file names are received the timer goes off and I start copying the files.
Again, all this works great but on occasion, two or even 3 instances of the program are opening and the files are split between them, which means FindWindow() sometimes fails to find the 1st window.
example:
I select 10 files in windows explorer, right click them and choose "Create Backup".
2 windows of my program open.
1st window output:
"File 00.DAT" Backed up successfully.
"File 01.DAT" Backed up successfully.
2nd window output:
"File 02.DAT" Backed up successfully.
"File 03.DAT" Backed up successfully.
"File 04.DAT" Backed up successfully.
"File 05.DAT" Backed up successfully.
"File 06.DAT" Backed up successfully.
"File 07.DAT" Backed up successfully.
"File 08.DAT" Backed up successfully.
"File 09.DAT" Backed up successfully.
And then, I close the 2 windows and again select the same 10 files and again choose "Create Backup" but this time and for the next several attempts I get only one window:
1st window output:
"File 00.DAT" Backed up successfully.
"File 01.DAT" Backed up successfully.
"File 02.DAT" Backed up successfully.
"File 03.DAT" Backed up successfully.
"File 04.DAT" Backed up successfully.
"File 05.DAT" Backed up successfully.
"File 06.DAT" Backed up successfully.
"File 07.DAT" Backed up successfully.
"File 08.DAT" Backed up successfully.
"File 09.DAT" Backed up successfully.
Can anyone explain why does it happen?
P.S. If it matters, I'm testing on Windows 7 x64.
[Edit - May 16th 2017]
Here is the dumbed down version of zett42's code, it works great for testing but on my to-do-list I wrote down to read and understand the rest of zett42's code because mine's probably flawed in some way.
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
HWND hwnd;
COPYDATASTRUCT dsIPC;
int i;
DWORD err;
HANDLE hMutex;
hMutex = CreateMutex(NULL, TRUE, "56f0e348-2c1a-4e01-a98e-3e6c8198f9aa");
err = GetLastError();
if(!hMutex)
{
MessageBox(NULL, "Cannot create mutex object. Click OK to exit.", "Error:", MB_OK | MB_ICONSTOP);
return (1);
}
if(err == ERROR_ALREADY_EXISTS)
{
for(i=0 ; i<1000 ; i++)
{
hwnd=FindWindow("#32770","Backup program");
if(hwnd) break;
Sleep(30);
}
if(i==1000) return (1);
dsIPC.dwData=666;
dsIPC.cbData=lstrlen(__argv[1])+1;
dsIPC.lpData=__argv[1];
SendMessage(hwnd, WM_COPYDATA, (WPARAM)hInstance, (LPARAM)&dsIPC);
return(0);
}
return DialogBox(hInstance, MAKEINTRESOURCE(ID_DIALOG), NULL, DlgProc);
}

As commenter treintje suggested:
There could be a race condition happening where two of your program
instances are started at the same time and neither of them had the
chance to create a dialog window yet, causing FindWindow to return
NULL in both cases. You could prevent this by using a mutex object to
check if another instance is already running.
Here is a code sample to show how such mutex could be used to avoid the race condition:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
// Try to create a mutex. Replace the string by something globally unique,
// for instance a GUID created by using the GuidGen utility, that comes with
// Visual Studio (look in the "Extras" menu).
HANDLE hMutex = CreateMutexW(nullptr, TRUE, L"REPLACE-WITH-YOUR-GUID");
// Make sure to put no other code in between the CreateMutex() and the
// GetLastError() calls to prevent the last error value from being messed up.
DWORD err = GetLastError();
if(!hMutex)
{
// TODO: error handling
return 1;
}
if(err == ERROR_ALREADY_EXISTS)
{
// An instance of this process is already running, but it might not
// have created the window yet, so FindWindow() could still fail.
// You could call FindWindow() in a loop but that would waste resources.
// So I'm using an event object to wait until the window has been created.
// This event object must be set to "signaled" state in WM_INITDIALOG
// handler of the dialog.
HANDLE hWindowCreatedEvent = CreateEventW(nullptr, TRUE, FALSE,
L"PUT-ANOTHER-GUID-HERE");
if(hWindowCreatedEvent)
{
// Wait with timeout of 30s because the 1st process might have failed
// to create the window for some reason.
DWORD waitRes = WaitForSingleObject(hWindowCreatedEvent, 30 * 1000);
if(waitRes == WAIT_OBJECT_0)
{
// The event is signaled so now we know for sure that the window
// has been created.
HWND hwnd;
COPYDATASTRUCT dsIPC;
hwnd=FindWindow("#32770","Backup program");
if(hwnd)
{
// send "__argv[1]" via SendMessage(hwnd,WM_COPYDATA... etc.
}
}
else
{
// TODO: error handling
}
CloseHandle(hWindowCreatedEvent);
}
else
{
// TODO: error handling
}
}
else
{
// This is the first instance of this process.
return DialogBox(hInstance, MAKEINTRESOURCE(ID_DIALOG), NULL, DlgProc);
}
CloseHandle(hMutex);
}
Edit your DialogProc to set the event object that signals other instances of the process that the window has been created:
INT_PTR CALLBACK DialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
switch(uMsg)
{
case WM_INITDIALOG:
{
// Use the same event name as in WinMain()!
HANDLE hWindowCreatedEvent = CreateEventW(nullptr, TRUE, FALSE,
L"PUT-GUID-FROM-WINMAIN-HERE");
if(hWindowCreatedEvent)
{
SetEvent(hWindowCreatedEvent);
CloseHandle(hWindowCreatedEvent);
}
// other initialization code...
return TRUE;
}
}
return FALSE;
}
Another suggestion: Don't search for the window caption because "Backup program" is way to generic and could be used by other applications. Then you would send WM_COPYDATA to wrong process.
Instead, register a window class with a globally unique name and only search for the class name (call FindWindow() with NULL as the argument for lpWindowName). You must also specify the class name in the dialog template.

Related

Dont know how to correctly edit this code

Well i am still slightly new in C.
Lets say i have this code:
source.c
#include "logger.c"
int main{
FILE *myfile1;
fileX = fopen("myfile.txt, a+);
SetHook(fileX);
}
and then i have very simple keylogger
logger.c
HHOOK _hook;
KBDLLHOOKSTRUCT kbdStruct;
LRESULT __stdcall HookCallback(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode >= 0)
{
if (wParam == WM_KEYDOWN)
{
// PRINT INTO THE FILE
kbdStruct = *((KBDLLHOOKSTRUCT*)lParam);
fprintf(fileX, "%c", kbdStruct.vkCode);
}
}
return CallNextHookEx(_hook, nCode, wParam, lParam);
}
void SetHook(fileX)
{
_hook = SetWindowsHookEx(WH_KEYBOARD_LL, HookCallback, NULL, 0)
}
Basically i want to have separated code like this.
But i think this is wrong i need to pass that "fileX" not to function setHOOK() but to that "LRESULT __stdcall..." and i dont know how to do that.
I will be thankfull for any help.
WH_KEYBOARD_LL is global hook you need place its hook procedure in a DLL separate from
the application installing the hook procedure. (For simple purpose you can put hook procedure in your application and it is also working. But this is not suggested way.)
(SetWindowsHookExA function Using Hooks)
This hook is called in the context of the thread that installed it.
The call is made by sending a message to the thread that installed the
hook. Therefore, the thread that installed the hook must have a
message loop. (In another word, the application need have a window so that it can keep pumping messages.)
(LowLevelKeyboardProc callback function)
For learning purpose of hook, my suggestion is getting started using Visual Studio Windows Desktop Application template (C++).
It is simple to register the keyboard hook and receive message in the hook.
And for writing to a file in the hook producer you can create a named file map so that you don't need to pass a file handle to the hook. You can open this file map using its name and write to the file using this file map. (Creating Named Shared Memory)
static HHOOK hhookKeyPress;
hhookKeyPress = SetWindowsHookEx(
WH_KEYBOARD_LL,
KeyboardHookCallback,
NULL,
0);
// Main message loop:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
Sharing above information to help you get started and then you can find C programming way later. Hope it helps.

Key Press to inactive window

I'm trying to make a program that sends key presses to an inactive window, I'm using "SendMessageCallback()" but it isn't giving an error, or sending the key press.
typedef enum {false,true} bool;
bool KeyDown(HWND hWnd, WORD dKey){
if(!SendMessageCallback(hWnd, WM_KEYDOWN, 0, dKey, (SENDASYNCPROC)NULL, (ULONG_PTR)NULL))
return false;
return true;
}
bool KeyUp(HWND hWnd, WORD uKey){
if(!SendMessageCallback(hWnd, WM_KEYDOWN, 0, uKey, (SENDASYNCPROC)NULL, (ULONG_PTR)NULL))
return false;
return true;
}
then, I simply call it like this:
HWND FOO = FindWindow("FOO", NULL );
if( FOO == NULL )
MessageBox(NULL, "FOO Not Found", "ERROR", MB_OK);
if (!KeyDown(FOO, VK_SPACE))
MessageBox(NULL, "KeyDown failed to execute!", "ERROR", MB_OK);
if (!KeyUp(FOO, VK_SPACE))
MessageBox(NULL, "KeyUp failed to execute!", "ERROR", MB_OK);
I substituted What the real HWND is for FOO, If more information is needed please tell me.
The problem is it doesn't send the key press or show a message box.
Edit: I tried adding a delay between pressing the key down and releasing it and that didn't solve it.
Without knowing anything about the window you are working with, I can only surmise that it is not coded to process keystrokes in its WndProc. You can use Spy++ or similar tool to verify that the messages are actually being sent, but that is no guarantee that they will be processed. You cannot send keystrokes to arbitrary windows that are not expecting them. And BTW, keystroke messages are typically posted to a window's message queue, not sent directly to the window's WndProc, so try using PostMessage() instead.

How would I go about disabling the close button?

First of all, let me get my point for this:
This is an emergency alert system, it pulls from a website which we manipulate in the back office, the program can be minimized but cannot be closed (easily, at least none of the people that will use it will know how). As you can see, if the program was closed the Emergency Alerts wouldn't be seen/heard....that's an issue. I have to deploy this application on over 200 computers so I want it simple, I don't want to create a scheduled task, etc.. to keep it running. I simply want it to not be easy to close.
See my code, below:
/* example.c
This is a Win32 C application (ie, no MFC, WTL, nor even any C++ -- just plain C) that demonstrates
how to embed a browser "control" (actually, an OLE object) in your own window (in order to display a
web page, or an HTML file on disk). The bulk of the OLE/COM code is in DLL.c which creates a DLL that
we use in this simple app. Furthermore, we use LoadLibrary and GetProcAddress, so our DLL is not
actually loaded until/unless we need it.
NOTE: The DLL we create does not normally use UNICODE strings. If you compile this example as UNICODE,
then you should do the same with DLL.C.
*/
#include <windows.h>
#include "..\CWebPage.h" /* Declarations of the functions in DLL.c */
// A running count of how many windows we have open that contain a browser object
unsigned char WindowCount = 0;
// The class name of our Window to host the browser. It can be anything of your choosing.
static const TCHAR ClassName[] = "EAS";
// Where we store the pointers to CWebPage.dll's functions
EmbedBrowserObjectPtr *lpEmbedBrowserObject;
UnEmbedBrowserObjectPtr *lpUnEmbedBrowserObject;
DisplayHTMLPagePtr *lpDisplayHTMLPage;
DisplayHTMLStrPtr *lpDisplayHTMLStr;
/****************************** WindowProc() ***************************
* Our message handler for our window to host the browser.
*/
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_CREATE)
{
// Embed the browser object into our host window. We need do this only
// once. Note that the browser object will start calling some of our
// IOleInPlaceFrame and IOleClientSite functions as soon as we start
// calling browser object functions in EmbedBrowserObject().
if ((*lpEmbedBrowserObject)(hwnd)) return(-1);
// Another window created with an embedded browser object
++WindowCount;
// Success
return(0);
}
if (uMsg == WM_DESTROY)
{
// Detach the browser object from this window, and free resources.
(*lpUnEmbedBrowserObject)(hwnd);
// One less window
--WindowCount;
// If all the windows are now closed, quit this app
if (!WindowCount) PostQuitMessage(0);
return(TRUE);
}
// NOTE: If you want to resize the area that the browser object occupies when you
// resize the window, then handle WM_SIZE and use the IWebBrowser2's put_Width()
// and put_Height() to give it the new dimensions.
return(DefWindowProc(hwnd, uMsg, wParam, lParam));
}
/****************************** WinMain() ***************************
* C program entry point.
*
* This creates a window to host the web browser, and displays a web
* page.
*/
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hInstNULL, LPSTR lpszCmdLine, int nCmdShow)
{
HINSTANCE cwebdll;
MSG msg;
WNDCLASSEX wc;
// Load our DLL containing the OLE/COM code. We do this once-only. It's named "cwebpage.dll"
if ((cwebdll = LoadLibrary("cwebpage.dll")))
{
// Get pointers to the EmbedBrowserObject, DisplayHTMLPage, DisplayHTMLStr, and UnEmbedBrowserObject
// functions, and store them in some globals.
// Get the address of the EmbedBrowserObject() function. NOTE: Only Reginald has this one
lpEmbedBrowserObject = (EmbedBrowserObjectPtr *)GetProcAddress((HINSTANCE)cwebdll, "EmbedBrowserObject");
// Get the address of the UnEmbedBrowserObject() function. NOTE: Only Reginald has this one
lpUnEmbedBrowserObject = (UnEmbedBrowserObjectPtr *)GetProcAddress((HINSTANCE)cwebdll, "UnEmbedBrowserObject");
// Get the address of the DisplayHTMLPagePtr() function
lpDisplayHTMLPage = (DisplayHTMLPagePtr *)GetProcAddress((HINSTANCE)cwebdll, "DisplayHTMLPage");
// Get the address of the DisplayHTMLStr() function
lpDisplayHTMLStr = (DisplayHTMLStrPtr *)GetProcAddress((HINSTANCE)cwebdll, "DisplayHTMLStr");
// Register the class of our window to host the browser. 'WindowProc' is our message handler
// and 'ClassName' is the class name. You can choose any class name you want.
ZeroMemory(&wc, sizeof(WNDCLASSEX));
wc.cbSize = sizeof(WNDCLASSEX);
wc.hInstance = hInstance;
wc.lpfnWndProc = WindowProc;
wc.lpszClassName = &ClassName[0];
RegisterClassEx(&wc);
// Create another window with another browser object embedded in it.
if ((msg.hwnd = CreateWindowEx(0, &ClassName[0], "Emergency Alert System", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
HWND_DESKTOP, NULL, hInstance, 0)))
{
// For this window, display a URL. This could also be a HTML file on disk such as "c:\\myfile.htm".
(*lpDisplayHTMLPage)(msg.hwnd, "http://www.google.com");
// Show the window.
ShowWindow(msg.hwnd, nCmdShow);
UpdateWindow(msg.hwnd);
}
// Do a message loop until WM_QUIT.
while (GetMessage(&msg, 0, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// Free the DLL.
FreeLibrary(cwebdll);
return(0);
}
MessageBox(0, "Can't open cwebpage.dll! You are not protected by Emergency Alerting System. Click OK to terminate this application. Contact the developer, Scott Plunkett.", "ERROR", MB_OK);
return(-1);
}
It took this from an example tutorial I found, I have never done any windows programming before so I had to figure out a quick solution.
I appreciate any and all help on this.
If you create a handler for the WM_SYSCOMMAND message and check for the SC_SYSCLOSE parameter, you can stop it from executing the default action of closing the window.
I noticed it's not an MFC application, so you have to call Win32 Library to enable the feature.
Try this out:http://www.davekb.com/browse_programming_tips:win32_disable_close_button:txt
---EDIT---
Sorry the code did not work out. I did not have resources to debug it.
To disable the X of the window, you can either set CS_NOCLOSE property of WNDCLASSEX:
wc.style = CS_NOCLOSE;//in your code
or rewrite the WM_CLOSE message handler function. Thanks.

How do I use SetWindowsHookEx to filter low-level key events?

I have this code which sets up a keyboard hook for low-level events, then displays a message box.
HHOOK keyboardHook = SetWindowsHookEx (WH_KEYBOARD_LL, HookKey, hInstance, 0);
MessageBox(NULL, L"Click to exit", L"hook test", NULL);
UnhookWindowsHookEx(keyboardHook);
How do I run the application's main loop without creating a foreground window, and how do I set hInstance to capture global events?
The answer is all contained on the MSDN docs page for SetWindowsHookEx. To experiment with a sample application, create a new solution, add two projects, a console app and a DLL. Each project just needs one file. The DLL will contain the hook, and the console app installs the hook.
The code for the hook must live inside a DLL, as the MSDN docs very clearly say:"If the dwThreadId parameter is zero or specifies the identifier of a thread created by a different process, the lpfn parameter must point to a hook procedure in a DLL." Indeed, the hook clearly has to be in a DLL, or how could the shell run the code during another application's message loop? The DLL installs the hook using its HINSTANCE to identify where the function pointer lives.
The main application runs starts the hooks, and pumps messages, again just following the instructions in the MSDN docs: "...the hooking application must continue to pump messages or it might block the normal functioning of the 64-bit processes."
Note that this is very niche application — hardly ever will you need to inject a DLL like this. Please, please, think twice about doing this. For many applications, it's not necessary to capture low-level events, although immersive applications that suppress the Start menu are a reasonable use-case. More importantly though, you very rarely need to capture input sent to other applications. Check before using this code that you really need to do this. For example, recording a macro can be done just by hooking the input to your own application's window.
main.cpp
#include <windows.h>
#include <tchar.h>
__declspec(dllimport) void (__stdcall runHook)(BOOL startOrStop);
__declspec(dllimport) LRESULT (CALLBACK hookProc)(int code, WPARAM wParam, LPARAM lParam);
int _tmain(int argc, _TCHAR* argv[])
{
runHook(true);
MSG Msg;
while(GetMessage(&Msg, NULL, 0, 0) > 0 && Msg.message != WM_QUIT) {
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
runHook(false);
return 0;
}
Dll.cpp
#include <assert.h>
#include <windows.h>
static HINSTANCE dllInst = 0;
static HHOOK hookSelf = 0;
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
dllInst = (HINSTANCE)dllInst;
return TRUE;
}
__declspec(dllexport) LRESULT (CALLBACK hookProc)(int code, WPARAM wParam, LPARAM lParam)
{
if (code >= 0) {
UINT msgType = wParam;
KBDLLHOOKSTRUCT* msgInfo = (KBDLLHOOKSTRUCT*)lParam;
// The msgInfo contains the VK_CODE and flags.
}
return hookSelf ? CallNextHookEx(hookSelf, code, wParam, lParam) : 0;
}
__declspec(dllexport) void (__stdcall runHook)(BOOL startOrStop)
{
assert((bool)hookSelf != (bool)startOrStop);
if (startOrStop)
hookSelf = SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, dllInst, 0);
else {
UnhookWindowsHookEx(hookSelf);
hookSelf = 0;
}
}
One way to do this is creating a TSR(Terminate and Stay Resident) program on Windows.
On Windows, a typical TSR program consists of the following components :
Creating and hiding the main window.
Registering hot keys.
The loop testing events.
Functions responding to hot keys.
Functions invoked periodically by timers.
Here is a good article with code-snippets and detailed explanations about writing TSRs on Windows.

Where do I add my actual program after I establish a Window using WinMain() and WindowProc()? (C++)

First of all, sorry for my ignorance of the window creation process. Today is actually the first day of my experimenting with it.
I started to code a text based game a few days ago and I have the main menu, and 3 or 4 different functions that control various things with text. I was then advised to look into Windows API and create a window for the program. I have created the window which can be seen here:
#include <Windows.h>
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, PWSTR nCmdLine, int nCmdShow)
{
const wchar_t CLASS_NAME[] = L"WindowClass";
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProc;
wc.lpszClassName = CLASS_NAME;
wc.hInstance = hInstance;
RegisterClass(&wc);
HWND hwnd = CreateWindowEx( //This creats a new instance of a window
0,
CLASS_NAME,
L"MyFirstWindow",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
500,
500,
NULL,
NULL,
hInstance,
NULL);
if(hwnd == 0)
return 0;
ShowWindow(hwnd,nCmdShow);
nCmdShow = 1;
MSG msg = { };
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_DESTROY:PostQuitMessage(0); return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd,&ps);
FillRect(hdc,&ps.rcPaint,(HBRUSH)(COLOR_WINDOW+5));
EndPaint(hwnd, &ps);
}return 0;
case WM_CLOSE:
{
if(MessageBox(hwnd,L"Do you want to exit?",L"Exit",MB_OKCANCEL)==IDOK)
DestroyWindow(hwnd);
} return 0;
}
return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
This looks a bit messy, but you probably will not need it anyway.
So at this point I have my original program and this program that creates a window. My question is how, or even where, do I put my original program's code so that it can be incorporated into the window.
If you are reading this and thinking I'm a total moron for doing it this way, I'm open to ideas that are a lot simpler than what I'm doing right now.
Your code is the standard boilerplate for creating a window using c and win32 API functions. I recommend that you modify the message pump (it's the while loop in middle calling GetMessage). Instead do this:
Run an infinite loop
Peek a message
If the message is not there, execute your code
Else process messages including the quit message
Here's what the code should look like:
while (1)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
//Your game code
}
}
I also want to point, that while learning game programming using C and calling Win32 API is a worthy goal, you might want to look at XNA game studio.
The reason I am recommending it is because it is easier to learn and you can make much more interesting games faster. Here are a few links to get you started if you are interested.
http://joshua-w-wise78.livejournal.com/
http://www.krissteele.net/blogdetails.aspx?id=72
http://www.codeproject.com/KB/game/xna1.aspx
http://msdn.microsoft.com/en-us/library/bb203894.aspx
If your original program was a console app, that read input and printed output, then you will probably want to get input from your window to implement your game.
Instead of looking at it from the perspective of read user input from stdin then generate output to stdout, you have to think of it from the view of window messaging. So you need to process the WM_KEYDOWN messages, you can then use DrawText() to show the user input in your client area, or you could use a c++ RichEdit control. Once you process the WM_KEYDOWN messages you know what the user has pressed and then your program can do it's thing (maybe being triggered to process an accumalated buffer of characters whenever the WM_KEYDOWN key is equal to the enter key?) and write the output to your client area using DrawText() or send WM_KEYDOWN messages to your richedit window using SendMessage().
I think this is what you meant by a text based game, anyway, you just have to start thinking of doing everything by monitoring windows messages. Anything that the user does to your window, Windows will send a message to it to let it know.

Resources