Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

An Alternative to Using LoadString to Access String Resources

0.00/5 (No votes)
11 Jan 2015CPOL2 min read 19.3K   71  
This is an alternative to LoadString for accessing string resources.

Introduction

Recently, I created a program and decided to store some literal strings in its resources. I found the LoadString function somewhat lacking. The main thing that bugged me is that you had to play a guessing game with the buffer size since there is no way from the function of knowing how long the string actually is! I thought ok there has to be a better solution and of course there is. All of the Loadxxx functions basically wrap raw access. So with a little research, I created rcgets.

You have a block of data called a string table that holds your string literals. This always stores 16 strings. It’s basically an array of wchar_t elements that hold indexes and unicode string data. An element in the array will be an index between 0 – 15 and the length of its following string is contained inside the index. If the index is 0 then it has no associated string in which case you just jump to the next element in the array for the following index. If the index is not zero, then it is followed by a string of that length. The next index will follow immediately after the aforementioned string until the last index in the array.

The Code

C++
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>

// constants from resource header file
#define IDS_HELLO        1001
#define IDS_GOODBYE        1002

wchar_t* rcgets( unsigned int id );

wchar_t* rcgets( unsigned int id )
{
    unsigned int block = 0, index = 0, i = 0;
    HRSRC info = NULL;
    HGLOBAL resc = NULL;
    LPVOID data = NULL;
    DWORD size = 0;
    wchar_t *p = NULL;
    wchar_t *s = NULL;
    
    // convert the resource id to required values
    block = ((id >> 4) + 1);
    index = (id & 0xF);
    
    info = FindResourceEx( NULL, RT_STRING, MAKEINTRESOURCE(block), 
        MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL) );
    
    // exit function is various conditions are not met
    if ( info == NULL ) { return NULL; }
    size = SizeofResource( NULL, info );
    if ( size == 0 ) { return NULL; }
    
    resc = LoadResource( NULL, info );
    if ( resc != NULL )
    {
    
    data = LockResource( resc );
    if ( data != NULL )
    {
    
    //
    //

    p = (wchar_t*) data;
    
    for ( unsigned int i = 0; i < 16; i++ )
    {
        if ( i == index )
        {

        // create string buffer based on the length in the index
        s = new wchar_t[ *p + 1 ];
        if ( s != 0 )
        {
            // copy string to buffer then null terminate
            memcpy( s, &p[1], *p * sizeof(wchar_t) );
            s[ *p ] = 0;
        }
            // index was processed so break for loop
            break;
        }
        
        // increment the pointer based on length in the index 
        // + 1 default increment if zero length or more
        p += *p;
        p++;
    }
    
    //
    //
    
    }
    
        // unlockresource function is obsolete but here for completeness
        UnlockResource( data );
        FreeResource( resc );
    }

    return s;
}

// test run the rcgets function
int wmain()
{
    wchar_t *s = NULL;

    s = rcgets( IDS_GOODBYE );
    if ( s != NULL )
    {
        _putws( s );
        delete[] s;
        s = NULL;
    }

    s = rcgets( IDS_HELLO );
    if ( s != NULL )
    {
        _putws( s );
        delete[] s;
        s = NULL;
    }

    // getchar is just to allow a pause to view the output to the console
    getchar();
    return EXIT_SUCCESS;
}

//
//

Notes

The variable β€˜p’ in the function could possibly run pass the end of the data block as it assumes the data in the block is correct. This scenario could lead to read errors or corruption. For example, if an index reports that its string is 7845 in length but the data block is only 512. It might be wise to use the size variable returned by SizeOfResource to test the pointers offset and prevent overrun errors.

I believe that literal strings can be used to identify resources rather than numbered ids. I am still researching how to process these. I believe it is related to the top bits of the id string.

The function can easily be modified to pass extra parameters to FindResourceEx to for example access another module or use another language.

Anyway feedback on these points or any improvement of the code would be appreciated.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)