Undocumented Corner

Windows .RES files, generated by the Windows resource compiler, contain the binary images of Windows menus and dialogs prior to their inclusion in executable files. Alex Fedorov and Dmitry Rogatkin unravel the .RES file format.


August 01, 1993
URL:http://www.drdobbs.com/windows/undocumented-corner/184409061

Figure 5


Copyright © 1993, Dr. Dobb's Journal

Figure 5


Copyright © 1993, Dr. Dobb's Journal

AUG93: UNDOCUMENTED CORNER

UNDOCUMENTED CORNER

The Windows .RES File Format

by Alex G. Fedorov and Dmitry M. Rogatkin

Alex G. Fedorov is a freelance programmer and an executive editor for ComputerPress magazine in Moscow, Russia. Alex can be contacted at [email protected] via the Internet. Dmitry M. Rogatkin, a freelance programmer specializing in Windows software, is a lecturer at the Moscow Institute of Electronic Machinery. Dmitry can be contacted at [email protected].


Introduction

by Andrew Schulman

The "Undocumented Corner" seems to be on a Windows file-format roll. Last month, Mike Maurice explored the .PIF file format. Next month, Peter Davis and Ron Burk will uncover the long-concealed .HLP file format. This month's "Undocumented Corner," which comes to us from Russia (the authors are regular contributors to the Russian-language magazine ComputerPress, published in Moscow), reveals the Windows .RES file format.

Windows .RES files are produced by the Windows resource compiler (RC) and contain the binary images for Windows resources (menus, dialogs, and so on) prior to their inclusion in an executable file. While Microsoft has documented "Resource Formats Within Executable Files" (Microsoft Windows 3.1 SDK, Programmer's Reference, Volume 4: Resources, Chapter 7), it has not publicly documented the .RES format--that is, the resource format outside executable files. While resources within an executable are located using the "Resource Table" (see section 6.2.3 of the SDK, volume 4), .RES files contain no such resource table. As Alex and Dmitry explain, the .RES file format is slightly but fundamentally different from the format of resources within executable files.

Instead of a resource table, a .RES file is simply a concatenation of individual resources, each of which has its own small variable-length header. Alex and Dmitry document the format for this header. Since there's no central header, there's no way to determine with 100 percent reliability that you actually have a genuine .RES file. Yuck! In any case, aside from the header on each individual resource, the resources in a .RES file are identical to those in an .EXE file.

Originally, Alex and Dmitry's article also discussed the format of .BMP (bitmap), .CUR (cursor), and .ICO (icon) files, but I've skipped over this discussion, as this material is already documented by Microsoft in Chapter 1 ("Graphics File Formats") of the 3.1 SDK, volume 4.

The .RES file format makes an interesting case study of why some important interfaces are undocumented. First, Microsoft has already produced a limited-circulation document ("Microsoft Windows 3.0 Internal Resource Formats," November 12, 1990) on this subject, so the lack of public documentation can't be attributed to a lack of sufficient resources (as it were) in Microsoft's documentation department. The documentation already exists, and has simply not been made public. Yet, for reasons that will soon become clear, I don't think this is a simple case of "they're hiding it from us."

The Microsoft "Internal Resource Formats" document was made available to independent software vendors (ISVs) on a limited basis as part of the Microsoft Open Tools effort. Despite its name and good intent, Open Tools appears to be limited to only the largest and most important ISVs. Many of you have complained about never having been able to pry the (therefore somewhat misnamed) Open Tools materials out of Microsoft. I've previously dismissed this as unimportant, since the Windows 3.1 SDK incorporated almost everything previously available only through Open Tools, but I now see that at least the .RES file-format documentation from Open Tools was not brought over to the 3.1 SDK.

The Open Tools document refers to the .RES file format as a "proprietary binary format," and explains that "Until now, the .RES file format has been undocumented. This was mostly because its structure was version-dependent and changed frequently." Fair enough. Apparently, the reason for documenting it as part of Open Tools was that, since much of the .RES file contents are copied directly into .EXE files, this helped document the more-important format of resources within .EXE files. Presumably, because Microsoft later went all the way and properly documented resources within .EXE files, it didn't bother publicly releasing the documentation for resources within .RES files, which it seems to consider merely an "intermediate file" of little intrinsic importance.

My favorite Microsoft product, the Microsoft Developer Network (MSDN) CD-ROM, does provide several crucial snippets of information about the .RES file format, yet still fails to fully document it (rather amazing, considering what we'll see is the utter simplicity of this format). First, the MSDN CD does properly document the Win32 .RES file format, including a brief note on how (using illegal type and name ordinal numbers) to distinguish Win32 from Win16 .RES files. (This same document, by Floyd Rogers, may be found in RESFMT.TXT on the Win32 CD-ROM.) Second, the MSDN CD includes the source code for RESTOOL, a program that reads in Windows .RES files and generates C++ class declarations for any dialog boxes in the .RES. While no other resource types are handled, this demonstrates the basics of walking through a .RES file.

All this is fairly typical of Microsoft's extremely annoying (but not very nefarious), half-hearted approach to documentation. Rather than some deliberate master-minded attempt to deprive software developers of information, we see almost the exact opposite: an almost complete lack of deliberateness. Unlike the example of, say, the MS-DOS network redirector, Microsoft has in the case of the .RES file format made no particular effort to either hide or provide this information. It's not a conspiracy; it's just stupidity.

That Microsoft dances around the issue so much, inadvertently documenting little bits of the .RES file format on a piecemeal basis, but (very likely also inadvertently) not documenting the whole thing once and for all, does indicate that this simple little format is at least somewhat important. Besides its use by programs such as RESTOOL, knowing the .RES file format would also allow interactive programming environments to load .RES files on the fly.

Once a Windows program has located a resource in a .RES file, how could a Windows program load it? The Windows API includes resource-handling functions such as CreateDialog, LoadMenu, FindResource, and LoadResource, but these only work off of executable files. How can a resource be loaded from something other than an executable file? The Windows API includes documented functions such as CreateDialogIndirect and LoadMenuIndirect to create certain resources from binary resource images in memory. For example, Microsoft Excel uses these functions to let a user's macros create menus and dialogs on the fly. Similarly, once a menu or a dialog in a .RES file has been located and read into memory, these same Indirect functions can be used to transform the binary data into a bona fide Windows resource.

Unfortunately, there isn't an equivalent Indirect function for every Windows resource type. However, Alex and Dmitry's sample code (too long to include with this article, but available electronically, see "Availability," page 5) provides functions such as LoadRESAccelerators and LoadRESBitmap that fill in the gaps. This code may also be useful to those working with self-loading Windows executables.

As usual, please me send comments, suggestions, criticisms, and any interesting gossip about Microsoft trade practices. My e-mail address is 76320,302 on CompuServe, and [email protected] on the Internet; you can also reach me on MCI Mail.

While most of the file formats used in the Microsoft Windows environment are documented (although not always adequately), the format of .RES files, which hold resources before their inclusion in a Windows executable file, has been obscured by a curtain--until now! Knowing this format can help developers to load and unload resources on the fly, in interactive programming environments, for example, or in commercial applications with user-programmable macro languages. This article explains how stand-alone Windows resources are organized and provides a set of utilities that shows how to use them.

It is important to note that much of what .RES files contain is already documented. The files contain the binary images of resources, in the format documented in the Windows 3.1 SDK. What needs to be discovered in .RES files are the surroundings for the already-documented binary resource images.

To uncover these magic cookies, we can use a text editor to create a resource script, pass it through the resource compiler (RC), and use a hex-dump utility to study the contents of the resulting .RES file. Listing One (page 153) shows a simple TEST.RES file, and Figure 1 shows a partial hex dump of the resulting TEST.RC file produced by RC. In the ASCII display on the right side of the hex dump, many of the strings from TEST.RC, such as BARFOO and FOOBAR, are plainly visible.

Figure 2 shows an analysis of the hex dump. Each resource within the .RES file may start with the byte 0xFF, which tells us that the next word is a numeric resource type. The predefined resource types, such as RT_CURSOR (1), RT_BITMAP (2), RT_ICON (3), and so on, are listed in WINDOWS.H. (Type 16 is listed in VER.H.) Unlisted resource types are user defined. Any starting byte other than 0xFF is the first character in an ASCIIZ (0-byte terminated) resource-type name.

Following the resource type comes either the resource name, stored as an ASCIIZ string, or an ordinal ID, stored as a number. Just as with the resource type, if the first byte is 0xFF, the resource is identified by number rather than name, and the next word is the ID itself.

The next two fields are memory flags, stored as a WORD, and the resource length, stored as a DWORD. The memory flags are usually 0x1030 indicating (MOVEABLE | PURE | DISCARDABLE), or 0x0x30 indicating (MOVEABLE | PURE). The resource length is for the actual resource, and does not include the header; this length is used to get to the next resource in the file. These two fields complete the resource header. They are followed immediately by the resource data itself, which is in the same format as documented in the Windows 3.1 SDK, Programmer's Reference, Volume 4: Resources, Chapter 7.

The .RES file is simply a collection of resources. There's no signature at the start of the .RES file, so any kind of file can be supplied to a resource utility (and kill it). On the other hand, the absence of a .RES-file signature means that you can simply concatenate .RES files with the COPY /B command. While this allows you to combine a set of small .RES files into a single larger one, a problem can occur: The resulting file may end up with more than one resource of the same type and ID.

While a .RES file is simply a concatenation of individual resources, in Windows 3.0 the resource compiler sometimes builds a "name table" at the end of the file. However, this name table is just another resource type (obsolete in 3.1), and resources can be included in an executable file without it.

To distinguish Win32 .RES files from Win16 .RES files, Microsoft starts off all Win32 .RES files with an entry that is illegal in both Win16 and Win32. For our purposes, we need only note that the first byte is 0: In Win16 terms, the 0 (because it is something other than 0xFF) means that this is a named (rather than numeric) type, but the same 0 then means that the ASCIIZ name has a zero length, which is illegal. This strange method (necessary only because .RES files have no header) is documented in Microsoft's RESFMT.TXT on the Win32 CD-ROM.

The format of a .RES file entry cannot be easily wrapped up in the structure of a programming language such as C or Pascal, because the optional presence of an ASCIIZ string gives the resource-header format a variable length. However, "close-enough" C pseudocode can be presented; see Figure 3. A C "union" (like a variant record in Pascal) is used to indicate that the resource type can be either an ASCIIZ string or a 3-byte 0xFF-prefixed ordinal number.

RESLIST.CPP (Listing Two, page 153) is a short C++ program that walks through a .RES file, displaying the type, name, size, and file offset of each resource. (The size and offset are for the resource itself, not including the header.) For the 16 predefined resource types, RESLIST uses a table of strings indexed by the resource type. Note that RESLIST.CPP does not use the RES_

FILE_ENTRY structure from Figure 3; this is because that structure is merely pseudocode. The variable-length structure means that knowledge of how to get from one field to another, and from one .RES file entry to another, has to be embedded in the code itself.

Sample RESLIST output for TEST.RES is shown in Figure 4; compare this with the original TEST.RC in Figure 1. RESLIST just lists the resources in a .RES file, one per line. Details on each individual resource are not given, as the information necessary to decode these is already documented in the SDK. For example, upon encountering a dialog box (type.id==RT_DIALOG), you'd need to get the number of controls from the DLGHEADER, along with the dialog style and dimensions; you would then walk through each control, using the CTRLHEADER.

The SDK documentation works here because the only significant difference between resources in .RES files and resources within .EXE files is that each resource in the .RES file has one of the headers shown in Figure 3, whereas the .EXE file has a Resource Table (the format of which is documented in the SDK; also see Microsoft Systems Journal, September 1991). The resource data itself is identical between the two formats and is transferred from the .RES to the .EXE without change. Another difference between .RES files and resources in .EXE files is that the .RES file is an unordered collection of resources, whereas RC reorders resources before attaching them to an executable file. Resources in an .EXE file are ordered by type; there is a field in the executable's Resource Table that tells how many resources exist of each type.

There's another small difference: The .RES-file length field is a DWORD, and the resource file offset is only implicit; the .EXE file, on the other hand, has room for both the offset and length of the resource, but uses only a WORD to store them. As Windows executables can be very large, the resource offset and length are stored and shifted by some amount; this resource shift count is stored as the first word in the Resource Table.

The information in this article, together with the information in the SDK, could be used to decompile a .RES file back to the original .RC files. The information in the SDK alone is sufficient to decompile resources in executable files back to the original .RC file.

[Editor's Note: Stan Mitchell of Eclectic Software has written such a resource decompiler. I've written a resource dumper (the RESDUMP program included with my Windows Source tool, distributed by V Communications), but mine doesn't produce an .RC file; Stan's is a genuine resource decompiler. Alex and Dmitry also have such a tool which is available electronically.]

Accessing Resources

Usually, resources are stored in an executable file; the Windows kernel provides us with a set of API functions to load them. But, using the .RES file format presented here, it is possible to implement a set of Windows functions which will instead allow you to load resources from a .RES file. We've created such a set of functions and a test program showing how to use them. Some Windows API functions already exist to load resources "indirectly;" these are very helpful when loading resources from a .RES file. Where such functions do not exist (for BMP, ICO, and CUR resources), we created the closest equivalent.

Our library, READRES.CPP and READRES.H, is too large to include here, but it is available electronically, as is the test program, CDI; see Figure 5. (Note that CDI is examining the .RES file for the Cyrillic version of Program Manager; this .RES file was extracted from PROGMAN.EXE using our resource decompiler.)

First, we need to implement a function which will load a particular resource from a .RES file. As seen in Listing Three (page 153), LoadRESResource() walks through a .RES file, just like RESLIST.CPP. However, instead of printing out information on each resource, it searches for the resource whose type and name the caller specifies. If the requested resource is found, LoadRESResource() calls GlobalAlloc() to allocate sufficient memory for it (the amount of memory comes from the DWORD size found in the .RES-file entry header), GlobalLocks the memory, and then reads in the resource data. It returns to the caller with a handle to the raw resource data.

Okay, here we are with a requested resource loaded in some portion of memory referenced through a global memory handle. What to do next? This depends on the type of resource we've loaded. Some resource types are easy to use from a .RES file, because Windows already provides "indirect" functions. For example, take a look at LoadRESMenu() in Listing Four (page 153), which uses LoadMenuIndirect().

Loading dialog boxes from a .RES file is also fairly easy, except that any program that calls CreateRESDialog() must supply an additional parameter: the address of a dialog proc. All other work is done through CreateDialogIndirectParam(). Supplying a generic dialog proc for someone else's dialog box can be quite difficult. Your dialog proc must handle at least one message, WM_INITDIALOG.

Loading other resource types (such as bitmaps, cursors, icons, and accelerators) from a .RES file is trickier; the code itself may be found in READRES.CPP (available electronically).

As noted in the "Introduction," knowledge of resource formats can be applied to Windows environments based on interpreting languages. You could also apply this knowledge to security: You could scramble your resources with a password or simply XOR them and add some unscrambling features into a FindResource "engine." Another interesting use might be resource packing: You could implement your own resource loading (borrowing from the code in READRES.CPP) and include some unpacking features in it. For example, you can store packed bitmaps to reduce the total size of your application. You don't need to use user-defined RCData resources for that.

Figure 1: Hex dump of TEST.RES.

0000 | FF 03 00 FF 01 00 30 10 E8 02 00 00 28 00 00 00 | ......0.....(...
0010 | 20 00 00 00 40 00 00 00 01 00 04 00 00 00 00 00 |  ...@...........
02f0 | 00 00 00 00 FF 0E 00 42 41 52 46 4F 4F 00 30 10 | .......BARFOO.0.
0300 | 14 00 00 00 00 00 01 00 01 00 20 20 10 00 01 00 | ..........  ....
0310 | 04 00 E8 02 00 00 01 00 FF 05 00 46 4F 4F 42 41 | ...........FOOBA
0320 | 52 00 30 10 4E 00 00 00 00 00 C0 00 02 0A 00 14 | R.0.N...........
0330 | 00 1E 00 28 00 00 46 4F 4F 42 41 52 00 46 4F 4F | ...(..FOOBAR.FOO
0340 | 42 41 52 00 01 00 02 00 03 00 04 00 FF FF 01 00 | BAR.............
0350 | 02 50 82 54 68 69 73 20 69 73 20 61 20 74 65 73 | .P.This is a tes
0360 | 74 00 00 05 00 06 00 07 00 08 00 01 00 01 00 01 | t...............
0370 | 50 80 4F 4B 00 00 FF 04 00 42 41 52 42 41 52 00 | P.OK.....BARBAR.
0380 | 30 10 54 00 00 00 00 00 00 00 10 00 26 54 65 73 | 0.T.........&Tes
0390 | 74 00 00 00 65 00 43 6D 64 20 26 31 00 80 00 66 | t...e.Cmd &1...f
03a0 | 00 43 6D 64 20 26 32 00 10 00 26 41 6E 6F 74 68 | .Cmd &2...&Anoth
03b0 | 65 72 20 74 65 73 74 00 00 00 6F 00 43 6D 64 20 | er test...o.Cmd
03c0 | 26 31 31 00 80 00 70 00 43 6D 64 20 26 31 32 00 | &11...p.Cmd &12.
03d0 | 80 00 C8 00 26 48 65 6C 70 00 FF 09 00 46 4F 4F | ....&Help....FOO
03e0 | 46 4F 4F 00 30 00 05 00 00 00 81 70 00 C8 00 FF | FOO.0......p....
03f0 | 0F 00 FF 01 00 30 10 3A 00 00 00 0E 00 0E 00 01 | .....0.:........
0400 | 80 00 42 41 52 46 4F 4F 00 0E 00 04 00 01 80 00 | ..BARFOO........
0410 | 42 41 52 42 41 52 00 0E 00 05 00 01 80 00 46 4F | BARBAR........FO
0420 | 4F 42 41 52 00 0E 00 09 00 01 80 00 46 4F 4F 46 | OBAR........FOOF
0430 | 4F 4F 00 00 00                                 | OO...

Figure 2: Analysis of the hex dump of TEST.RES.

       +----------------------------------------------Tag1
       |    +-----------------------------------------Res Type (3 = RT_ICON)
       |    |   +-------------------------------------Tag2
       |    |   |    +--------------------------------Resource ID (#1)
       |    |   |    |     +--------------------------Resource Flags
       |    |   |    |     |   +---------+------------Resource Length (0x02E8)
       |  +-+-+ |  +-+-+ +-+-+ |         |
       |  |   | |  |   | |   | |         |            Start of ICON data
0000 | FF 03 00 FF 01 00 30 10 E8 02 00 00 28 00 00 00 | ......0.....(...
0010 | 20 00 00 00 40 00 00 00 01 00 04 00 00 00 00 00 |  ...@...........

skip this hdr size (3+3+2+4=0x0C) + res length (0x02e8) =
next res at 0x02f4

                   +----------------------------------Tag1
                   |    +-----------------------------Res Type (14 =
                   |    |                                RT_GROUP_ICON)
                   |    |            +----------------Res Name ("BARFOO")
                   |    |            |
                   |    |            |              +-Resource Flags
                   |    |            |              |
                   |  +-+-+ +--------+---------+  +-+-+
                   |  |   | |                  |  |   |
02f0 | 00 00 00 00 FF 0E 00 42 41 52 46 4F 4F 00 30 10 | .......BARFOO.0.
0300 | 14 00 00 00 00 00 01 00 01 00 20 20 10 00 01 00 | ..........  ....
       |         | |
       +----+----+ +-------------------------------Resource data
            |
            +--------------------------------------Resource Length (0x14)

       +--------------------------------------------More resource data
       |
       |                       +--------------------Tag1
       |                       |  +---+-------------Res Type (5 = RT_DIALOG)
       |                       |  |   | +-----------Res Name ("FOOBAR")
       |                       |  |   | |
0310 | 04 00 E8 02 00 00 01 00 FF 05 00 46 4F 4F 42 41 | ...........FOOBA
0320 | 52 00 30 10 4E 00 00 00 00 00 C0 00 02 0A 00 14 | R.0.N...........
       |   | |   | |         |                      start of binary dlg data
       |   | |   | +---------+----------------------Res Length (0x4E)
       |   | +---+----------------------------------Res Flags
       +---+----------------------------------------rest of Res Name

Figure 3: C pseudocode for .RES file structure.

typedef struct {
    BYTE ff;              // 0xFF
    WORD id;
    } ID;
typedef struct {
    union {
        ID id;            // 1 = RT_CURSOR, 2 = RT_BITMAP, etc.
        char name[variable_length];     // first byte *not* 0xFF
        } type;
    union {
        ID id;           // ordinal number of resource
        char name[variable_length];     // first byte *not* 0xFF
        } name_or_id;
    WORD mem_flags;       // 0x10=MOVEABLE, 0x20=PURE, 0x40=PRELOAD,
                         // 0x1000=DISCARDABLE
    DWORD size;           // *not* including this header
    BYTE res_data[size];  // the resource data:  see SDK v. 4, ch. 7
    } RES_FILE_ENTRY;     // NOT VALID C!
// RES file is just an accumulation of these entries
RES_FILE_ENTRY RES_FILE[num_resources];

Figure 4: RESLIST output for TEST.RES.

C:\DDJ>reslist test.res
Icon: #1 (744 bytes at ofs cH)
Group icon: BARFOO (20 bytes at ofs 304H)
Dialog box: FOOBAR (78 bytes at ofs 328H)
Menu: BARBAR (84 bytes at ofs 386H)
Accelerator table: FOOFOO (5 bytes at ofs 3eaH)
Name table (obsolete in 3.1): #1 (58 bytes at ofs 3fbH)

Figure 5: CDI loads resources from .RES files, using the READRES library. Here, CDI has loaded a dialog box from PROGMAN.RES, which in turn was extracted from the Cyrillic version of Program Manager using a resource-decompilation tool.

[LISTING ONE]

(Text begins on page 133.)

// TEST.RC -- simple input file
// rc -r test.rc, then dump test.res (see Figure 1)
#include <windows.h>

BARFOO ICON some.ico
FOOBAR DIALOG 10,20,30,40
CAPTION "FOOBAR"
CLASS "FOOBAR"
STYLE WS_BORDER | WS_CAPTION {
    CTEXT "This is a test", -1, 1, 2, 3, 4
    DEFPUSHBUTTON "OK", IDOK, 5, 6, 7, 8
    }
BARBAR MENU {
    POPUP "&Test" {
        MENUITEM "Cmd &1", 101
        MENUITEM "Cmd &2", 102
        }
    POPUP "&Another test" {
        MENUITEM "Cmd &11", 111
        MENUITEM "Cmd &12", 112
        }
    MENUITEM "&Help", 200
    }
FOOFOO ACCELERATORS {
    VK_F1, 200, VIRTKEY
    }

[LISTING TWO]


// RESLIST.CPP -- Reads Windows .RES files from "Undocumented Corner," DDJ,
// August 1993 -- Alex G. Fedorov and Dmitry M. Rogatkin
// [email protected] & datasc@[email protected]

#include <iostream.h>
#include <fcntl.h>
#include <io.h>
#include <stdlib.h>
#include <string.h>

#define LastResNum 16

char *StandardResName[] = {
    /*  1  */ "Cursor",
    /*  2  */ "Bitmap",
    /*  3  */ "Icon",
    /*  4  */ "Menu",
    /*  5  */ "Dialog box",
    /*  6  */ "String table",
    /*  7  */ "Font directory",
    /*  8  */ "Font",
    /*  9  */ "Accelerator table",
    /* 10  */ "RCData (user-defined)",
    /* 11  */ "Not alowed",
    /* 12  */ "Group cursor",
    /* 13  */ "Not alowed",
    /* 14  */ "Group icon",
    /* 15  */ "Name table (obsolete in 3.1)",
    /* 16  */ "Version info"
    };
// read an ordinal number into aId, or an ASCIIZ string into aStr
int RdHeadItem(char *aStr, int &aId, int hFile)
{
    char ch;
    if (_read(hFile, &aStr[0], 1) != 1)
        return 1;
    if (aStr[0] == '\xFF')      // ordinal
    {
        aStr[0] = 0;            // number, not name
        _read(hFile, &aId, sizeof(int));
    }
    else                        // name
    {
        if (aStr[0] == 0)       // invalid magic
        {
            cout << "This is a Win32 .RES file\n";
            exit(1);
        }
        aId = 0;                // name, not number
        signed long pos = lseek(hFile, 0, SEEK_CUR); // where are we?
        _read(hFile, aStr+1, 126);  // probably read too much
        lseek(hFile, pos + strlen(aStr), SEEK_SET); // back up
    }
    return 0;
}
void main(int argc, char *argv[])
{
    int hResFile, RId, Id;
    long ResLen;
    unsigned int Flgs;
    char st[128], sn[128];
    if (argc != 2)
    {
        cout << "Usage: Reslist resfile\n";
        exit(1);
    }
    if ((hResFile = _open(argv[1], O_RDONLY)))
    {
        // get type: ordinal or string
        while (! RdHeadItem(st, RId, hResFile))
        {
            if (st[0] == 0)     // ordinal number, not name
            {
                if (RId <= LastResNum)
                    strcpy(st, StandardResName[RId-1]);
                else
                {
                    itoa(RId, st, 10);
                    strcat(st, " (user defined)");
                }
            }
            else
                ; // already have type string in st
            // get ID: ordinal or string
            RdHeadItem(sn, Id, hResFile);
            if (sn[0] == 0)     // ordinal number, not string
            {
                sn[0] = '#';
                itoa(Id, &sn[1], 10);
            }
            // get memory flags
            _read(hResFile, &Flgs, sizeof(unsigned int));
            // get length in bytes of following resource data
            _read(hResFile, &ResLen, sizeof(long));
            // where are we in the file?
            // so we can output file offset of actual res data
            long pos = lseek(hResFile, 0, SEEK_CUR);
            cout << st << ": " << sn << " (" << ResLen
                 << " bytes at ofs " << hex << pos << dec << "H)\n";
            // in a genuine program, we would read in resource data
            // and switch on resource type in RId
            lseek(hResFile, ResLen, SEEK_CUR);
        };
        _close(hResFile);
    }
}

[LISTING THREE]


HGLOBAL LoadRESResource(LPCSTR ResFName, LPCSTR lpszName, LPCSTR lpszType) {
    char st[128], sn[128];
    void huge *AddrTemp;
    long ResLen;
    HFILE hResFile;
    HGLOBAL hMem=0;
    WORD Flags, RId, Id, fndtyp = 0;

    if (HIWORD(lpszType) == 0) fndtyp = LOWORD(lpszType);
    if ((hResFile = _lopen(ResFName, OF_READ)) == HFILE_ERROR)
        return 0;
    while(! RdHeadItem(st, RId, hResFile))  {   // get type
        RdHeadItem(sn, Id, hResFile);           // get name or ID
        _lread(hResFile, &Flags, sizeof(unsigned int));  // get flags
        _lread(hResFile, &ResLen, sizeof(long));    // get length
        if (fndtyp != 0 && RId == fndtyp ||
            fndtyp == 0 && strcmp(lpszType, st) == 0) { // match type
            if (HIWORD(lpszName) != 0 && strcmp(lpszName, sn) == 0 ||
                Id == LOWORD(lpszName)) {               // match name
                if (! (hMem = GlobalAlloc(GMEM_FIXED, ResLen)))
                    return 0;
                if (! (AddrTemp = GlobalLock(hMem)))    {
                    GlobalFree(hMem);
                    return 0;
                    }
                long count = 0;
                unsigned portion;
                long len = ResLen - count;
                while (len) {
                    portion = (len <= 0xFFFF) ? len : 0xFFFF;
                    if (_lread(hResFile, (char huge *)AddrTemp+count,
                        portion) != portion)    {
                        GlobalUnlock(hMem);
                        GlobalFree(hMem);
                        hMem = 0;
                        }
                    count+=portion;
                    }
                    GlobalUnlock(hMem);
                    break;      // we found it!  done!
                }
            }
    _llseek(hResFile, ResLen, SEEK_CUR); // to next resource entry
    }
    _lclose(hResFile);
    return hMem;
    }

[LISTING FOUR]


// LoadRESMenu() -- excerpted from READRES.CPP
HMENU LoadRESMenu(LPCSTR ResFileName, LPCSTR lpszMenuName)  {
    HGLOBAL hTempMem;
    void far*  AddrTemp;
    HMENU retMnu;
    if ((hTempMem = LoadRESResource(ResFileName, lpszMenuName,
        MAKEINTRESOURCE(RT_MENU))) != 0)  {
        if ((AddrTemp = GlobalLock(hTempMem)) != 0) {
            retMnu = LoadMenuIndirect(AddrTemp);
            GlobalUnlock(hTempMem);
            }
        GlobalFree(hTempMem);
        }
    return retMnu;
    }
End Listings


Copyright © 1993, Dr. Dobb's Journal

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.