Nicholas Miller
Nicholas Miller

Reputation: 4410

Using resource names in WinAPI

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?

Upvotes: 4

Views: 5490

Answers (2)

Nicholas Miller
Nicholas Miller

Reputation: 4410

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.

Upvotes: 5

Ben Voigt
Ben Voigt

Reputation: 283803

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.

Upvotes: 2

Related Questions