4

I created a basic stringtable resource in Visual C++. I am trying to access that resource. However, my program can't seem to find the resource. Here:

int main(int argc, char* argv[])
{
    HRSRC hRsrc;
    hRsrc = FindResource(NULL, MAKEINTRESOURCE(IDS_STRING102), RT_STRING);
    if (hRsrc == NULL) {
        printf("Not found\n");
    } else {
        printf("Found\n");
    }
}

This program can't find the resource and always returns null.

I created a simple bitmap resource and this new program identifies that just fine. Here:

int main(int argc, char* argv[])
{
    HRSRC hRsrc;
    hRsrc = FindResource(NULL, MAKEINTRESOURCE(IDB_BITMAP1), RT_BITMAP);
    if (hRsrc == NULL) {
        printf("Not found\n");
    } else {
        printf("Found\n");
    }
}

This finds the bitmap.

Do stringtable resources get handled somehow differently?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Steve Quezadas
  • 724
  • 2
  • 11
  • 27
  • [Updating a string table with UpdateResource](https://stackoverflow.com/a/14089163/1889329) explains, how string tables are implemented. [Win32 LoadString wrapper](https://stackoverflow.com/a/33336980/1889329) provides a solution that does not require you to know about those internals. – IInspectable Jul 26 '19 at 10:44

3 Answers3

6

Assuming you do not want to use LoadString() this should help...

Strings and string tables are indeed treated differently when using FindResource() and FindResourceEx(). From this KB article:

String resources are stored as blocks of strings. Each block can have up to sixteen strings and represents the smallest granularity of string resource that can be loaded/updated. Each block is identified by an identifier (ID), starting with one (1). We use this ID when calling the FindResource, LoadResource and UpdateResource functions.

A string with ID, nStringID, is located in the block with ID, nBlockID, given by the following formula:

nBlockID = (nStringID / 16) + 1; // Note integer division.

The lower 4 bits of nStringID indicates which entry in the block contains the actual string. Once you have calculated the block ID to pass to FindResource() and the index in the block where the string exists you have to scan through it's contents to find the string you are looking for.

The following code should get you started.

const WCHAR *stringPtr;
WCHAR stringLen;

//  Get the id of the string table block containing the target string
const DWORD blockID = (nID >> 4) + 1;

//  Get the offset of teh target string in the block
const DWORD itemID = nID % 0x10;

//  Find the resource
HRSRC hRes = FindResourceEx(
    hInst,
    RT_STRING,
    MAKEINTRESOURCE(blockID),
    wLanguage);
if (hRes)
{
    HGLOBAL hBlock = LoadResource(hInst, hRes);
    const WCHAR *tableDataBlock = reinterpret_cast<LPCWSTR>(LockResource(hBlock));
    const DWORD tableBlockSize = SizeofResource(hInst, hRes);
    DWORD searchOffset = 0;
    DWORD stringIndex = 0;

    //  Search through the section for the appropriate entry.
    //  The first two bytes of each entry is the length of the string
    //  followed by the Unicode string itself. All strings entries 
    //  are stored one after another with no padding.
    while(searchOffset < tableBlockSize)
    {
        if (stringIndex == itemID)
        {
            //  If the string has size. use it!
            if (tableDataBlock[searchOffset] != 0x0000)
            {
                stringPtr = &tableDataBlock[searchOffset + 1];
                stringLen = tableDataBlock[searchOffset];
            }
            //  Nothing there -
            else
            {
                stringPtr = NULL;
                stringLen = 0;
            }

            //  Done
            break;
        }

        //  Go to the next string in the table
        searchOffset += tableDataBlock[searchOffset] + 1;

        //  Bump the index
        stringIndex++;
    }
}
Captain Obvlious
  • 19,754
  • 5
  • 44
  • 74
  • The KB entry went offline. Here is the most recent [archive.org snapshot](https://web.archive.org/web/20120912052436/http://support.microsoft.com:80/kb/196774). – IInspectable Jul 26 '19 at 10:17
2

You could use LoadString directly instead. Here's some text from the MSDN FindResource documentation...

An application can use FindResource to find any type of resource, but this function should be used only if the application must access the binary resource data by making subsequent calls to LoadResource and then to LockResource.

To use a resource immediately...

...use LoadString!

Community
  • 1
  • 1
Ben
  • 1,321
  • 15
  • 30
  • 2
    If you must use `FindResource` for strings then you need to pass in the ID `MAKEINTRESOURCE((IDS_STRING102>>4)+1)`. Much easier to use `LoadString` then... `FindResource` just wasn't made for intuitive string loading. :( – Ben Jul 16 '11 at 21:31
0

After 2 days of research I found this(it works!):

#include <atlstr.h>

......

ATL::CString str;
WORD LangID = MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT); 
str.LoadString(NULL,IDS_STRING101, LangID);
neubert
  • 15,947
  • 24
  • 120
  • 212
Nick
  • 1