DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
Note the new set of curly braces {} . These are required when declaring variables inside a switch() statement. This should be basic C knowledge but I thought I should point it out anyway for those of you doing things the hard way.
So if you've added in that code, compile it now. If it works, click on the window and you should see a box with the name of the .exe pop up.
You'll notice we've added two variables, hInstance and szFileName. Look up GetModuleFileName() and you will see that the first parameter is a HINSTANCE refering to the executable module (our program, the .exe file). Where do we get such a thing? GetModuleHandle() is the answer. The references for GetModuleHandle() indicate that passing in NULL will return us "a handle to the file used to create the calling process ", which is exactly what we need, the HINSTANCE just mentioned. Putting all this information together we end up with the following declaration:
HINSTANCE hInstance = GetModuleHandle(NULL);
Now on to the second parameter, again turning to our trusty reference manual, we see that it is "a pointer to a buffer that receives the path and file name of the specified module " and the data type is LPTSTR (or LPSTR if your references are old). Since LPSTR is equivalent to char* we can declare an array of char 's like this:
char szFileName[MAX_PATH];
MAX_PATH is a handy macro included via <windows.h> that is defined to the maximum length of a buffer needed to store a filename under Win32. We also pass MAX_PATH to GetModuleFileName() so it knows the size of the buffer.
After GetModuleFileName() is called, the buffer szFileName will be filled with a null terminated string containing the name of our .exe file. We pass this value to MessageBox() as an easy way of displaying it to the user.
So if you've added in that code, compile it now. If it works, click on the window and you should see a box with the name of the .exe pop up.
If it doesn't work, here's the full code to the program. Compare it to what you have and see what, if any, mistakes you made.
#include <windows.h>
const char g_szClassName[] = "myWindowClass";
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch(msg) {
case WM_LBUTTONDOWN:
{
char szFileName[MAX_PATH];
HINSTANCE hInstance = GetModuleHandle(NULL);
GetModuleFileName(hInstance, szFileName, MAX_PATH);
MessageBox(hwnd, szFileName, "This program is:", MB_OK | MB_ICONINFORMATION);
}
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
WNDCLASSEX wc;
HWND hwnd;
MSG Msg;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszMenuName = NULL;
wc.lpszClassName = g_szClassName;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if (!RegisterClassEx(&wc)) {
MessageBox(NULL, "Window Registration Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK);
return 0;
}
hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, g_szClassName, "The title of my window", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 240, 120, NULL, NULL, hInstance, NULL);
if (hwnd == NULL) {
MessageBox(NULL, "Window Creation Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK);
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while(GetMessage(&Msg, NULL, 0, 0)> 0) {
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
Understanding the Message Loop
Understanding the message loop and entire message sending structure of windows programs is essential in order to write anything but the most trivial programs. Now that we've tried out message handling a little, we should look a little deeper into the whole process, as things can get very confusing later on if you don't understand why things happen the way they do.
What is a Message?
A message is an integer value. If you look up in your header files (which is good and common practice when investigating the workings of API's) you can find things like:
#define WM_INITDIALOG 0x0110
#define WM_COMMAND 0x0111
…
#define WM_LBUTTONDOWN 0x0201
…and so on. Messages are used to communicate pretty much everything in windows at least on basic levels. If you want a window or control (which is just a specialized window) to do something you send it a message. If another window wants you to do something it sends you a message. If an event happens such as the user typing on the keyboard, moving the mouse, clicking a button, then messages are sent by the system to the windows affected. If you are one of those windows, you handle the message and act accordingly.
Each windows message may have up to two parameters, wParam and lParam. Originally wParam was 16 bit and lParam was 32 bit, but in Win32 they are both 32 bit. Not every message uses these parameters, and each message uses them differently. For example the WM_CLOSE message doesn't use either, and you should ignore them both. The WM_COMMAND message uses both, wParam contains two values, HIWORD(wParam) is the notification message (if applicable) and LOWORD(wParam) is the control or menu id that sent the message. lParam is the HWND (window handle) to the control which sent the message or NULL if the messages isn't from a control.