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 ENDAgain
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.