Topic : Using Resources in Win32 Programs
Author : Joseph Farrell
Page : << Previous 3  Next >>
Go to page :

  // handle to application instance
    LPCTSTR lpMenuName    // menu name string or menu-resource identifier

You should be used to these parameters by now. The first one is the instance of your application, and the second is the identifier you assigned to the menu. Remember to use MAKEINTRESOURCE() if you used a numerical constant. Now, to attach a menu to a window, you have two options. The first is to set the menu as the default for your window class, like this:
sampleClass.lpszMenuName = LoadMenu(hinstance, MAKEINTRESOURCE(MAIN_MENU));
The second option is to leave lpszMenuName equal to NULL, and attach a menu yourself later. This can be useful if you want to create two windows with different menus, but don't want to define two separate window classes. To attach a menu, use the SetMenu() function:

BOOL SetMenu(
    HWND hWnd,   // handle to window
    HMENU hMenu, // handle to menu

The return value is TRUE if the function succeeds, or FALSE if it fails. The parameters are pretty easy to figure out:
HWND hWnd: This is the handle to the window to which you want to attach the menu. Pass the handle that was returned when you called CreateWindowEx().
HMENU hMenu: To identify the menu, pass the handle returned by LoadMenu(). If you pass NULL, the specified window's menu is removed.
This resource is particularly nice because all the functionality of the menu is defined by that simple scripting. But what happens when the user selects a menu option? The answer is that Windows sends a WM_COMMAND message informing the program that it must take action. Let's pay a visit to our message-handling function and see if we can't figure out how to handle this.

Handling Menu Events
As you probably remember, Windows messages are handled by a special callback function usually called WindowProc() or something similar. The simple one we wrote last time was called MsgHandler(), and its prototype looked like this:

    HWND hwnd,     // window handle
    UINT msg,      // the message identifier
    WPARAM wparam, // message parameters
    LPARAM lparam, // more message parameters

When a menu message is sent, msg will be WM_COMMAND, and the menu item that was selected will be contained in wparam. This is why menu item identifiers can't be strings; they need to fit into the wparam parameter. More specifically, the menu item identifier is the low word of wparam. To extract the low or high word of a 32-bit variable type like WPARAM, LPARAM, int, etc. Windows provides macros called LOWORD() and HIWORD() that do the job. They are shown here:
#define LOWORD(l)   ((WORD) (l))
#define HIWORD(l)   ((WORD) (((DWORD) (l) >> 16) & 0xFFFF))

In the case of LOWORD(), the typecast to WORD simply truncates the value to the lower 16 bits. HIWORD() shifts the upper 16 bits to the right, then performs a logical AND with 0xFFFF just to be sure any bits above the lower 16 are all set to zero. If you're not familiar with the >> and << operators, they are bit shifts. The << operator shifts all the bits of a variable a number of positions to the left, and the >> operator shifts to the right. For example, suppose you had a 16-bit variable x whose value was 244. In binary this is 0000 0000 1111 0100. The following example shows a bit shift, and the effect on x:
short int x = 244, y;
y = x << 4;
Contents of x: 0000 0000 1111 0100
Contents of y: 0000 1111 0100 0000
Anyway, use LOWORD() to extract the low word of wparam, and you have the ID of the menu item that was selected. So, somewhere in your MsgHandler() function, you should have something like this:

// handle menu selections
if (msg == WM_COMMAND)
    switch (LOWORD(wparam))
    case MENUID_NEW:
        // code to handle File->New goes here
    case MENUID_OPEN:
        // code to handle File->Open goes here

    // the rest of the option handlers go here


    // tell Windows you took care of it

Make sense? Good. That about wraps it up for the specific resource types I'm going to cover. There are others, such as accelerator tables (tables full of keyboard shortcuts), HTML pages, WAV files etc. but I think these are the most useful. Before I wrap this up, though, there's one more very powerful feature of Windows programs I'm going to show you, and that's defining a custom resource type.

Custom Resources
The standard Windows resources are those which have special functions for loading and handling them, but they are not the only types you can use. Resources can be any data you want them to be! Working with custom resources requires a little more work since you must locate and read the resource data manually, but it's not too bad. The script file entry for a custom type follows the basic format you're already used to:
[identifier]  [resource type name]  [filename]
The resource type name is a string that defines your custom resource, and can be whatever you want. For the purposes of this example, let's say you want to include a data file called p1config.dat that contains information necessary to initialize a character in a game program. We'll call the custom resource type CHARCONFIG. With that in mind, here's an example of what the script file entry might look like for your data file:
Pretty simple, hey? Now that you've included your file, there are three steps you must take in order to retrieve a pointer to the resource data. Each involves calling a function we haven't talked about yet, so let's go through them one at a time. The first thing you must do is to find the resource with a call to FindResource(). Here's the prototype:

HRSRC FindResource(
    HMODULE hModule, // module handle
    LPCTSTR lpName,  // pointer to resource name
    LPCTSTR lpType   // pointer to resource type

The return value is a handle to the resource's information block, or NULL if the function fails. The parameters are as follows:
HMODULE hModule: The HMODULE data type is simply an HINSTANCE. Don't ask me why they felt they needed another name for it, but you should simply pass the instance of your application. You don't even need a typecast because the data types are exactly the same.
LPCTSTR lpName: This is the resource identifier. Remember to use MAKEINTRESOURCE() on this one if you're using numeric constants to define your resources.
LPCTSTR lpType: This is the resource type, so pass the string you used to define your resource type. In our case, this is CHARCONFIG.
A sample function call looks like this:


This is a handle to the info block the resource resides in. The next step to getting a pointer to the data is to take this handle and pass it to LoadResource() to actually load the data. This yields a handle to the resource itself. Here is the function prototype:

HGLOBAL LoadResource(
    HMODULE hModule, // resource-module handle
    HRSRC hResInfo   // resource handle

The return type, HGLOBAL, is a pretty general handle type, as opposed to the other load functions we've covered, which returned specific handle types like HBITMAP or HICON. If the function fails, this value will be NULL. The parameters are straightforward:
HMODULE hModule: Again, simply the application instance.
HRSRC hResInfo: Pass the handle that was returned by FindResource().
Now that you have a handle to the resource, you can finally get a pointer to the data that was in the resource file you included. This is achieved with a call to LockResource(), shown here:
LPVOID LockResource(HGLOBAL hResData);
Simply pass the handle that was returned by LoadResource(). If the return value is NULL, the function call failed. If not, you've got your pointer! Now you're free to do whatever you like with the data. Note that the return type is LPVOID (Windows-speak for void*), so if you want to use array notation on the pointer, you need to cast it to something like a BYTE*. Now that we've gone through all the steps, I'll show you an example of a function you might write to return a pointer to a specified resource:

UCHAR* LoadCustomResource(int resID)
    HRSRC hResInfo;
    HGLOBAL hResource;

    // first

Page : << Previous 3  Next >>