Using Resources
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.
Now an example of how to use a resource in your program.
HICON hMyIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MYICON));
The first parameter of LoadIcon() and many other resource using functions is the
handle to the current instance (which we are given in WinMain() and can also be retreived by
using GetModuleHandle() as demonstrated in previous sections). The
second is the identifier of the resource.
You're probably wondering what's up with MAKEINTRESOURCE() and possibly wondering
why LoadIcon() takes a parameter of type LPCTSTR instead of say UINT
when we're passing it an ID. All MAKEINTRESOURCE() does is cast from an integer (what
our ID is) to LPCTSTR , which LoadIcon() expects. This brings us
to the second way of identifying resources, and that's with strings. Almost nobody does this
any more, so I won't go into details, but basically if you don't use #define to assign an integer
value to your resources then the name is interpreted as a string, and can be referenced in
your program like this:
HICON hMyIcon = LoadIcon(hInstance, "MYICON");
LoadIcon() and other resource loading APIs can tell the difference between an
integer passed in and a pointer to a string passed in by checking the high word of the value.
If it's 0 (as would be the case of any integer with a value less than or equal
to 65535) then it assumes it is a resource ID. This effectively limits your resources to
using IDs below 65535, which unless you have a whole lot of resources, should not be a problem.
If it's not 0 then it assumes the value is a pointer, and looks up the resource
by name. Never rely on an API to do this unless it is explicitely stated in the documentation.
For example, this doesn't work for menu commands like ID_FILE_EXIT ,
since they can only be integers.
|