4

In WinAPI, you can access resources via FindResource and LoadResource.

According to the documentation for FindResource, you can specify the name of the resource:

lpName [in]

Type: LPCTSTR

The name of the resource. Alternately, rather than a pointer, this parameter can be MAKEINTRESOURCE(ID), where ID is the integer identifier of the resource. For more information, see the Remarks section below.

I have two questions:

First, this doesn't even seem to be accurate because neither specifying the ID or file name worked. What is the proper value to input for the lpName argument?
This other question seemed to have this issue as well

Second, I want to know if it is possible to retrieve the file name for the resource at run-time. Is this possible? Or is the file name discarded once the file is packaged as a resource?

Test Code

#include <Windows.h>
#include <tchar.h>
#include "resource.h"

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    //This is the only test that succeeds.
    if (!FindResource(hInstance, MAKEINTRESOURCE(IDR_DRAWING1), _T("BINARY")))
    {
        MessageBox(NULL, _T("MAKEINTRESOURCE(IDR_DRAWING1); BINARY"), _T(""), MB_ICONERROR);
    }

    //This one fails.
    if (!FindResource(hInstance, _T("IDR_DRAWING1"), _T("BINARY")))
    {
        MessageBox(NULL, _T("\"IDR_DRAWING1\"; BINARY"), _T(""), MB_ICONERROR);
    }

    //ICON - Each fails.
    if (!FindResource(hInstance, MAKEINTRESOURCE(IDI_ICON1), _T("ICON")))
    {
        MessageBox(NULL, _T("MAKEINTRESOURCE(IDI_ICON1); ICON"), _T(""), MB_ICONERROR);
    }
    if (!FindResource(hInstance, _T("IDI_ICON1"), _T("ICON")))
    {
        MessageBox(NULL, _T("\"IDI_ICON1\"; ICON"), _T(""), MB_ICONERROR);
    }
    if (!FindResource(hInstance, MAKEINTRESOURCE(IDI_ICON1), RT_ICON))
    {
        MessageBox(NULL, _T("MAKEINTRESOURCE(IDI_ICON1); RT_ICON"), _T(""), MB_ICONERROR);
    }
    if (!FindResource(hInstance, _T("IDI_ICON1"), RT_ICON))
    {
        MessageBox(NULL, _T("\"IDI_ICON1\"; RT_ICON"), _T(""), MB_ICONERROR);
    }

    //HTML - Each fails.
    if (!FindResource(hInstance, MAKEINTRESOURCE(IDR_HTML1), _T("HTML")))
    {
        MessageBox(NULL, _T("MAKEINTRESOURCE(IDR_HTML1); HTML"), _T(""), MB_ICONERROR);
    }
    if (!FindResource(hInstance, _T("IDR_HTML1"), _T("HTML")))
    {
        MessageBox(NULL, _T("\"IDR_HTML1\"; HTML"), _T(""), MB_ICONERROR);
    }
    if (!FindResource(hInstance, MAKEINTRESOURCE(IDR_HTML1), RT_HTML))
    {
        MessageBox(NULL, _T("MAKEINTRESOURCE(IDR_HTML1); RT_HTML"), _T(""), MB_ICONERROR);
    }
    if (!FindResource(hInstance, _T("IDR_HTML1"), RT_HTML))
    {
        MessageBox(NULL, _T("\"IDR_HTML1\"; RT_HTML"), _T(""), MB_ICONERROR);
    }
    return 0;
}

Resource.rc
This isn't the entire file because it contains a lot of boiler-plate code. Here are the relevant resource declarations.

IDR_DRAWING1            BINARY                  "Drawing1.dwg"
IDI_ICON1               ICON                    "icon1.ico"
IDR_HTML1               HTML                    "html1.htm"

The .ico and the .htm were created automatically using Visual Studio; by adding new resources of the corresponding type. So their format shouldn't be messing up the FindResource statement.

resource.h

#define IDR_DRAWING1                    101
#define IDI_ICON1                       102
#define IDR_HTML1                       103

EDIT:

Per Ben Voigt's comment, I've gone ahead and changed the Resource.rc file so non-numeric names are used:

DWG1                    BINARY                  "Drawing1.dwg"
ICON1                   ICON                    "icon1.ico"
HTML1                   HTML                    "html1.htm"

Now, the resource.h file isn't used at all. Following are the new relevant tests:

FindResource(hInstance, _T("DWG1"), _T("BINARY")); //Succeeds now.
FindResource(hInstance, _T("ICON1"), _T("ICON")); //Still fails.
FindResource(hInstance, _T("ICON1"), RT_ICON); //Still fails.
FindResource(hInstance, _T("HTML1"), _T("HTML")); //Still fails.
FindResource(hInstance, _T("HTML1"), RT_HTML); //Still fails.

So, my expectations are met for my binary resource, but what is happening with the ICON and HTML?

Community
  • 1
  • 1
Nicholas Miller
  • 4,205
  • 2
  • 39
  • 62

2 Answers2

5

Solution and Results:

Okay, so I've finally got a predictable results.

The way you find the resource is partially dependent on the resource type. In this case, DWG1 is a BINARY resource which is a custom type. There does not exist a predefined RT_* among the resource types, so you will need to specify the type as an LPCTSTR:

FindResource(hInstance, _T("DWG1"), _T("BINARY"));

For a predefined resource type, you cannot specify the type name as a LPCTSTR. Instead you must use the corresponding RT_* value. This is probably because each RT_* value corresponds to a MAKEINTRESOURCE(WORD), and if my understanding is correct, that macro result points to an invalid address within the portable executable file, not to a textual representation of the resource type.

This questions addresses MAKEINTRESOURCE

The issue with the ICON1 resource was that the type should have been RT_GROUP_ICON instead of RT_ICON. The difference of these two types is that the former is hardware-independent and the latter is hardware-dependent. Though, I do not know why RT_ICON didn't work.

Last, the issue with HTML1 was a fault on my part because I hadn't made sure that the referenced file actually had data. During the build, it was probably omitted since it was essentially a null resource. The correct type to use for this resource is RT_HTML.

Now, in regards to the names. As mentioned by Ben Voigt in a comment to his answer, non-numeric names are required in order to specify the name as a LPCTSTR. If numeric names are used, then one must use MAKEINTRESOURCE instead.

Visual Studio's resource editor makes it a bit cumbersome to name resources with strings instead of numbers, since by default it creates macros for each resource. These macros then replace the resource name with a number during the preprocessing phase.

In order to change the name into a string, you have 2 options:

  • Surround the ID of the resource (found in the properties) with double quotes. This prevents the resource editor from creating a macro for the name. However, a macro might already exist if quotations were placed around a previously set ID.

                                                   Resource Properties

  • Alternatively, open the resource script file in any text editor and choose the name like that. Again the idea is to prevent a macro from being generated. So detaching the the resource script and resource header would work, or simply make sure the macro doesn't exist in the header.
Community
  • 1
  • 1
Nicholas Miller
  • 4,205
  • 2
  • 39
  • 62
  • "I do not know why RT_ICON didn't work" The resources are arranged in a nested structure, think of it as a directory for each type, with named/numbered resources inside. If you look in the wrong directory, you won't find the resource. This allows giving the same name/number to related resources, such as main window icon, main window menu, main window accelerators. If it went the other way, and the name/number identified the resource, then an attempt could be made to convert to the requested type. But that design decision is ancient history now. – Ben Voigt Feb 26 '15 at 22:17
  • "that macro result points to an invalid address within the PE file"... well, sort of. Windows tests whether the high bits are all zero, and if they are, treats it as a numeric resource. By a happy coincidence, pointer values with all-zero high bits are made illegal to help catch NULL pointer errors. So there is no ambiguity. – Ben Voigt Feb 26 '15 at 22:18
2

is the file name discarded once the file is packaged as a resource?

Yes, exactly.

For example, in the ICON statement the first column is the resource name, and the third column is the filename..

nameID ICON filename

And some resources don't come from external files at all, for example the MENU statement gets its data from nested MENU and MENUITEM statements directly in the resource script. DIALOG, STRINGTABLE, VERSION, and VERSIONINFO are just a few of the other common resources that don't exist as separate files.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • I've posted some test code to my OP demonstrating what works and what doesn't. Apparently, none of the statements accept the `nameID` as described in your answer. – Nicholas Miller Feb 26 '15 at 19:28
  • 1
    @NickMiller: After preprocessing, your file is `101 BINARY "Drawing1.dwg"` etc. You've used numeric IDs without any name. So no name is available at runtime. If you didn't have a macro definition, then the resource would actually be named `IDR_DRAWING1` – Ben Voigt Feb 26 '15 at 19:45
  • Again thanks for the help, however that doesn't seem to be sufficient. I've added more tests to the OP and only the `BINARY` resource is found. The `ICON` and `HTML` still fail. – Nicholas Miller Feb 26 '15 at 20:18
  • @NickMiller: Well, open your module (EXE or DLL) up in the resource editor and see what the actual resource names are. (Or use `EnumResourceTypes` and `EnumResourceNames`, but for a quick test it's a lot easier to use an existing tool) – Ben Voigt Feb 26 '15 at 20:22
  • Thanks for all your help Ben! I have learned quite a bit on how resources work. – Nicholas Miller Feb 26 '15 at 22:08