[ contents | #winprog ]

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.


Copyright © 1998-2003, Brook Miles (theForger). All rights reserved.