2

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?

1 Answer 1

5

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.

Sign up to request clarification or add additional context in comments.

7 Comments

I simply want to have access to an xml file, text file, and ps1 file. In the C program I call on theses files. I would like to have them embedded into the exe. I am new to VS and c programming. In fact I have my first class on it next semester in college. I am just anxious to learn so I decided to write a program.
@arynhard: Well, the above function does what you want then. Let me add a little derived version that allows you to read it into a buffer instead ...
I don't know if it is worth noting, but I am not using Microsoft Visual C, I am using Visual Studio Pro 11.
@arynhard: that is Microsoft Visual C, or rather contains it. I was merely hinting at the fact that __try is not supported in all C compilers (and not the same everywhere). Also, above are the C variants, but should work for the C++ compiler, too.
I have to admit, it seems a little over my head, but I will try to figure it out. Any tutorials you might suggest that could help me understand what this code is doing exactly? I appreciate your help.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.