Search code examples
cvisual-studioembedded-resource

How do I embed and use resource files in a C program using VS11?


This is my first attempt at using a resource file. I have seen a lot of answers that apply to C# but not C. Any suggestions?


Solution

  • Assuming you mean the method used by Sysinternals and others to carry the drivers or needed DLLs (or even the x64 version of the program itself) in the resource section of a program (e.g. Sysinternals' Process Explorer), using Microsoft Visual C you can use this code:

    BOOL ExtractResTo(HINSTANCE Instance, LPCTSTR BinResName, LPCTSTR NewPath, LPCTSTR ResType)
    {
        BOOL bResult = FALSE;
        HRSRC hRsrc;
        if(hRsrc = FindResource(HMODULE(Instance), BinResName, ResType))
        {
            HGLOBAL hGlob
            if(HGLOBAL hGlob = LoadResource(Instance, hRsrc))
            {
                DWORD dwResSize = SizeofResource(Instance, hRsrc);
                HANDLE hFileWrite = CreateFile(NewPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_ARCHIVE, 0);
                if(hFileWrite != INVALID_HANDLE_VALUE)
                __try
                {
                    DWORD dwSizeWritten = 0;
                    bResult = (WriteFile(hFileWrite, LockResource(hGlob), dwResSize, &dwSizeWritten, NULL) && (dwSizeWritten == dwResSize));
                }
                __finally
                {
                    CloseHandle(hFileWrite);
                }
            }
        }
        return bResult;
    }
    

    This saves the given resource (BinResName) of resource type (ResType) from the module (e.g. a DLL) Instance to file NewPath. Obviously if your C doesn't understand __try and __finally you'll have to adjust the code accordingly.

    Taken from here (in SIDT.rar) and adjusted for C. The code is under the liberal BSD license according to the website.

    Now if you wanted to get the pointer to the data (ppRes) and its size (pwdResSize):

    BOOL GetResourcePointer(HINSTANCE Instance, LPCTSTR ResName, LPCTSTR ResType, LPVOID* ppRes, DWORD* pdwResSize)
    {
        // Check the pointers to which we want to write
        if(ppRes && pdwResSize)
        {
            HRSRC hRsrc;
            // Find the resource ResName of type ResType in the DLL/EXE described by Instance
            if(hRsrc = FindResource((HMODULE)Instance, ResName, ResType))
            {
                HGLOBAL hGlob;
                // Make sure it's in memory ...
                if(hGlob = LoadResource(Instance, hRsrc))
                {
                    // Now lock it to get a pointer
                    *ppRes = LockResource(hGlob);
                    // Also retrieve the size of the resource
                    *pdwResSize = SizeofResource(Instance, hRsrc);
                    // Return TRUE only if both succeeded
                    return (*ppRes && *pdwResSize);
                }
            }
        }
        // Failure means don't use the values in *ppRes and *pdwResSize
        return FALSE;
    }
    

    Call like this:

    LPVOID pResource;
    DWORD pResourceSize;
    if(GetResourcePointer(hInstance, _T("somename"), _T("sometype"), &pResource, &pResourceSize))
    {
        // use pResource and pResourceSize
        // e.g. store into a string buffer or whatever you want to do with it ...
    }
    

    Note, this also works for resources with integer IDs. You can detect these by using the macro IS_INTRESOURCE.

    The resource script, e.g. myresources.rc, itself is trivial:

    #include <winnt.rh>
    "somename" "sometype" "path\to\file\to\embed"
    

    RCDATA instead of sometype is a reasonable choice

    #include <winnt.rh> // for RCDATA to be known to rc.exe
    "somename" RCDATA "path\to\file\to\embed"
    

    ... in which case you would adjust the above call to be:

    GetResourcePointer(hInstance, _T("somename"), RT_RCDATA, &pResource, &pResourceSize)
    

    Complete example making use of GetResourcePointer above. Let's say you have a pointer variable char* buf and you know your resource is actual text, then you need to make sure that it is zero-terminated when used as a string, that's all.

    Resource script:

    #include <winnt.rh>
    
    "test" RCDATA "Test.txt"
    

    The code accessing it

    char* buf = NULL;
    LPVOID pResource;
    DWORD pResourceSize;
    if(GetResourcePointer(hInstance, _T("test"), RT_RCDATA, &pResource, &pResourceSize))
    {
        if(buf = calloc(pResourceSize+1, sizeof(char)))
        {
            memcpy(buf, pResource, pResourceSize);
            // Now use buf
            free(buf);
        }
    }
    

    Unless, of course you meant to simply link a .res file which works by passing it to the linker command line.