6. Once you have finished processing the message, your windows procedure returns, DispatchMessage() returns, and we go back to the beginning of the loop.
This is a very important concept for windows programs. Your window procedure is not magically called by the system, in effect you call it yourself indirectly by calling DispatchMessage() . If you wanted, you could use GetWindowLong() on the window handle that the message is destined for to look up the window's procedure and call it directly!
while (GetMessage(&Msg, NULL, 0, 0)> 0) {
WNDPROC fWndProc = (WNDPROC)GetWindowLong(Msg.hwnd, GWL_WNDPROC);
fWndProc(Msg.hwnd, Msg.message, Msg.wParam, Msg.lParam);
}
I tried this with the previous example code, and it does work, however there are various issues such as Unicode/ANSI translation, calling timer callbacks and so forth that this method will not account for, and very likely will break all but trivial applications. So do it to try it, but don't do it in real code :)
Notice that we use GetWindowLong() to retreive the window procedure associated with the window. Why don't we just call our WndProc() directly? Well our message loop is responsible for ALL of the windows in our program, this includes things like buttons and list boxes that have their own window procedures, so we need to make sure that we call the right procedure for the window. Since more than one window can use the same window procedure, the first parameter (the handle to the window) is used to tell the window procedure which window the message is intended for.
As you can see, your application spends the majority of it's time spinning round and round in this message loop, where you joyfully send out messages to the happy windows that will process them. But what do you do when you want your program to exit? Since we're using a while() loop, if GetMessage() were to return FALSE (aka 0 ), the loop would end and we would reach the end of our WinMain() thus exiting the program. This is exactly what PostQuitMessage() accomplishes. It places a WM_QUIT message into the queue, and instead of returning a positive value, GetMessage() fills in the Msg structure and returns 0 . At this point, the wParam member of Msg contains the value that you passed to PostQuitMessage() and you can either ignore it, or return it from WinMain() which will then be used as the exit code when the process terminates.
IMPORTANT: GetMessage() will return -1 if it encounters an error. Make sure you remember this, or it will catch you out at some point… even though GetMessage() is defined as returning a BOOL , it can return values other than TRUE or FALSE , since BOOL is defined as UINT ( unsigned int ). The following are examples of code that may seem to work, but will not process certian conditions correctly:
while (GetMessage(&Msg, NULL, 0, 0))
while (GetMessage(&Msg, NULL, 0, 0) != 0)
while (GetMessage(&Msg, NULL, 0, 0) == TRUE)
The above are all wrong!it may be of note that i used to use the first of these throughout the tutorial, since as I just mentioned, it works fine as long as GetMessage() never fails, which when your code is correct it won't. However I failed to take into consideration that if you're reading this, your code probably won't be correct a lot of the time, and GetMessage() will fail at some point :) I've gone through and corrected this, but forgive me if I've missed a few spots.
while(GetMessage(&Msg, NULL, 0, 0)> 0)
This, or code that has the same effect should always be used.
I hope you now have a better understanding of the windows message loop, if not, do not fear, things will make more sense once you have been using them for a while.
You may also want to refer to the Appendices at the end of this tutorial for more information on resources with VC++ and BC++.
Before we get any deeper I will cover the topic of resources so that I won't have to re-write it for each section. You don't actually need to compile the stuff in this section, it's as example only.
Resources are pre-defined bits of data stored in binary format inside your executable file. You create resources in a resources script, a file with an extension of ".rc". comercial compilers will have a visual resource editor which allows you to create resources without manually editing this file but sometimes editing it is the only way to go, especially if your compiler has no visual editor, it sucks, or doesn't support the exact feature you need.
Unfortunately different compiler suites handle resources differently. I will do the best I can to explain the common features needed to work with resources in general.
The resource editor included with MSVC++ makes it very difficult to edit the resources manually, since it enforces a proprietary format on them, and will totally mangle the file if you save one that you had created by hand. In general you shouldn't bother with creating .rc files from scratch, but knowing how to modify them manually can be very useful. Another annoyance is that MSVC++ will by default name the resource header file "resource.h" even if you wanted to call it something else. I will go with this for the sake of simplicity in this document, but will show you how to change this in the appendix on compilers.
First lets take a very simple resource script, with a single icon.
#include "resource.h"
IDI_MYICON ICON "my_icon.ico"
That's the entire file. IDI_MYICON is the identifier of the resource, ICON is the type and "my_icon.ico" is the name of the external file which contains it. This should work on any compiler.
Now what about this #include "resource.h" ? Well your program needs a way to identify the icon, and the best way to do that is to assign it a unique ID ( IDI_MYICON ). We can do this by creating the file "resource.h" and including it in both our resource script, and our source file.
#define IDI_MYICON 101
As you can see, we've assigned IDI_MYICON the value of 101 . We could just forget about the identifier and use 101 wherever we need to reference the icon, but IDI_MYICON is a lot clearer as to what you are refering too, and easier to remember when you have large number of resources.
Now lets say we add a MENU resource:
#include "resource.h"
IDI_MYICON ICON "my_icon.ico"
IDR_MYMENU MENU
BEGIN
POPUP "&File"
BEGIN
MENUITEM "E&xit", ID_FILE_EXIT
END
END
Again IDR_MYMENU is the name of the resource and MENU is the type. Now a fine point, see the BEGIN and END up there? Some resource editors or compilers use { in place of BEGIN and } in place of END . If your compiler supports both feel free to pick which one you use. If it only supports one or the other, you will need to make the necessary replacements to get it to work.
We've also added a new identifier, ID_FILE_EXIT , so we need to add this to our resource header file, resource.h, in order to use it in our program.
#define IDI_MYICON 101
#define ID_FILE_EXIT 4001
Generating and keeping track of all these ids can become a real chore with large projects, that's why most people use a visual resource editor which takes care of all this for you. They still screw up from time to time, and you could end up with multiple items with the same ID or a similar problem, and it's good to be able to go in and fix it yourself.
Читать дальше