|
Altering File Resources When you compile a VC++ application you will more often than not compile in resources. Resources can be icons, string tables, version information or even HTML pages and can be used by the actual executable, or by another executable. The linker puts resources into a section of the PE executable file called .rsrc. This means that one process can load the resources of another executable file (EXE or DLL) by loading this section and searching for the resource. Since an executable may have more than one resource, of more than one type, and formatted for more than one locale, the .rsrc segment contains a resource directory that collates this information. In this article I will not consider such low level access, instead I will show you how to access resources using the Win32 API. Win32 also allows you to change the resources in another file and I will show you how to use these APIs and explain some of the problems that you need to overcome. Loading a ResourceGetting access to a resource involves multiple stages. Firstly, you have to load the resource file (EXE or DLL) and obtain a HMODULE, next you have to search for the resource using the resource name and type, then you have to load the resource and finally you have to ‘lock’ the resource in memory to get a valid pointer through which you can access the resource. If the resource type is an accelerator table, a bitmap, a cursor, an icon, a menu or a string in the string table then there are APIs which will locate, load and lock the resource in one go. The resource type and resource name are passed to the APIs as strings, however they can be stored in the .rsrc section as either strings or integers. Typically, when you add a resource to a VC++ project the resource editor will give the resource a numeric ID, and so to convert this to a string to pass to the resource APIs you have to use the MAKEINTRESOURCE() macro. This creates a LPCSTR pointer that has the resource ID as the lower word and zero for the upper word. The following shows you examples of accessing a resource
// load an icon
HICON hIcon;
hIcon = LoadIcon(hInstance,
MAKEINTRESOURCE(IDC_MYICON));
// load a HTML resource
HRSRC hR = FindResource(hMod,
“MYPAGE”, RT_HTML)
HGLOBAL hG = LoadResource(hMod, hR);
char* p = (char*)LockResource(hG);
// use p
UnlockResource(hG);
Note that in the second example although the HTML is character data, and the code attempts to cast the locked resource pointer to char*, this will cause problems. The reason is that the resource will not be terminated with a NUL character, so if you want to treat the data as a string you first have to make a copy and then add a NUL character. If you do not know the name of the resource, you can enumerate all the names of a specified resource using EnumResourceNames which requires a callback function, for example the following
BOOL CALLBACK CallbackResName(HINSTANCE, LPCTSTR,
LPTSTR lpszName, LONG lParam)
{
std::vector< std::basic_string<TCHAR>
>* pvec;
pvec =
reinterpret_cast<StrVector*>(lParam);
if (HIWORD(lpszName) ==
0)
{
TCHAR str[20];
wsprintf(str,
"%ld", LOWORD(lpszName));
pvec->push_back(str);
}
else
{
pvec->push_back(lpszName);
}
return TRUE;
}
This is called for every resource of the type that EnumResourceNames() can find. If the top 16 bits are zero, then the resource has an ID and not an name, this code just stores the names and IDs in a STL vector passed in through the LPARAM parameter. The function is called like this:
EnumResourceNames(hModule, "TYPELIB",
CallbackResName,
reinterpret_cast<LPARAM>(&vec));
std::vector<
std::basic_string<TCHAR>
>::iterator it;
for (it = vec.begin(); it < vec.end(); it++)
{
printf("%s\n", (*it).c_str());
}
This code asks for all of the TYPELIB resources in the code module hModule. This can be an EXE or DLL as both can be loaded with LoadLibrary(). In addition, Win32 has two other APIs: EnumResourceTypes() and EnumResourceLanguages(). The first enumerates all the resource types and passes these to the callback function, the second is the most specific because it enumerates the languages used for a specific resource of a specific type. This means that you could have two HTML resources both called MYPAGE, but each one localized to a different locale. Changing ResourcesThe Win32 API gives you the ability to change the resources in an existing code module. The process that calls this API must have write access to the file, which causes a problem if you want to change the resources of a module that is executing. I will explain later one way to solve this problem. Changing resources involves you ‘locking’ the resources with a call to BeginUpdateResource(), then updating the actual resource with UpdateResource() and finally ‘unlocking’ the resources with EndUpdateResource() which does the actual work of writing the resource to the file. The call to BeginUpdateResource() takes the name of a code module that you want to change and a flag. The flag determines if the API should delete all resources in the file or modify existing resources. If you choose to delete all resources then only those resources added with a subsequent call to UpdateResource() will be in the file after the update has been finished. BeginUpdateResource() returns a handle which you must pass to UpdateResource() to change a resource and to EndUpdateResource() to commit the changes. UpdateResource() looks like this:
BOOL UpdateResource(HANDLE hUpdate,
LPCTSTR lpType, LPCTSTR
lpName,
WORD wLanguage, LPVOID
lpData,
DWORD cbData);
The second and third parameters are the type and name of the resource, the fourth parameter is a language ID that you can use to localise the resource. The last two parameters give the new resource and the size of this resource. If lpData is NULL then the resource is deleted from the code module. After you have made the changes you must commit them by calling EndUpdateResource(), which takes the handle obtained earlier and a Boolean. If you choose you can abort these changes by passing TRUE in the Boolean. Note that when you compile resources using the resource compiler it appears to make the compile binary quite compact (some NULs are placed in there for padding). When you update resources in a file with UpdateResource(), the API does not appear to compact the resources. Indeed, it appears to create a memory page and bind it in its entirety to the file. The space in this page that is not filled with resources contains padding with the string “PADDINGPADDINGXX”. What if the Module is Running?If the code module is running then you cannot obtain write access to it. This is true even if the code module is the actual process trying to change the resources. To solve this I wrote a function called MakeFileWriteable(). This works by renaming the file. In my code I rename the file to a file in the temp folder and then copy this file back to the original location. This means that the executing file is now running from the temp folder and the file that is in the original location of that file is a new copy, which you can open for write access. Any changes that you make to this new version of the file does not affect the running instance, so if the code in the file depends on those changes you must stop the executing file and restart it from the original location. The only problem with this technique is that the temp file will now contain the old copy of the file, so you have to make sure that you clear out the temp folder temporarily. One solution to this is to use MoveFileEx() on the file in the temp folder and indicate that the change should occur when the system reboots (and hence the file will not be executing):
MoveFileEx(strFile, NULL,
MOVEFILE_DELAY_UNTIL_REBOOT);
Since the second parameter is NULL this indicates that the file indicated by strFile should be deleted, the flag indicates that this should only occur when the system reboots. The complete function looks like this:
BOOL MakeFileWriteable(LPCTSTR strFile)
{
TCHAR str[MAX_PATH];
LPCTSTR strName =
strFile;
if (strName == NULL)
{
if
(GetModuleFileName(NULL,
str,
sizeof(str)) == 0)
return FALSE;
strName = str;
}
TCHAR
strTempPath[MAX_PATH];
GetTempPath(sizeof(strTempPath), strTempPath);
// rename the original file so that we
// can overwrite it
TCHAR strNew[MAX_PATH];
GetTempFileName(strTempPath, _T("TMP"),
0, strNew);
if (!MoveFileEx(strName, strNew, MOVEFILE_REPLACE_EXISTING))
return FALSE;
// make a copy to the original location
if (!CopyFile(strNew, strName, FALSE))
return FALSE;
// indicates that the original (moved) file
// should be deleted when the system reboots
if (!MoveFileEx(strNew, NULL,
MOVEFILE_DELAY_UNTIL_REBOOT))
return FALSE;
return TRUE;
}
ExampleThis is all you need to know to change the resources in a code module. The example for this article is a EXE that has a HTML page as a resource that shows the number of times the EXE has been run. Each time the EXE is run the HTML page is updated. The example accesses the resource and prints it to the command line before changing the resource. Internet Explorer allows you to show a HTML resource bound to an executable file with this syntax:
res://C:\PathToFile\file.exe/101
C:\PathToFile\file.exe is the path to the EXE or DLL that has the resource, the actual resource ID or name is passed at the end of the URL (in this case an ID of 101). If you give your HTML resource a string name rather than an ID then place the name at the end of the URL. For example:
res://C:\PathToFile\file.exe/MYRES
I have found that some of the resource APIs convert the resource name to uppercase, while others are case sensitive, so if you use string names for resources it is best to make sure that the names only have uppercase letters. The example code has a HTML resource called MYCOUNT that initially looks like this:
<html>
<body>
Count 0
</body>
</html>
When the EXE is run it first loads this resource and prints it to the console. After that it makes a writeable copy of the EXE, loads the resource again, updates the count by one and writes it back to the file. Finally, it calls ShellExecute() to run IE and shows the resource:
BOOL ShowMe()
{
TCHAR strURL[MAX_PATH];
lstrcpy(strURL, _T("res://"));
if
(GetModuleFileName(NULL, strURL + 6,
sizeof(strURL) - 6) == 0)
{
return FALSE;
}
lstrcat(strURL, "/MYCOUNT");
ShellExecute(NULL, "open", strURL, NULL,
NULL, SW_SHOWNORMAL);
return TRUE;
}
The code has a class called Resource that does all the work of locating and loading the resource for you. The public interface
template<class T>
class Resource
{
HGLOBAL m_hG;
// cannot call default constructor
Resource(){}
public:
// access the resource and lock it
Resource(HMODULE hMod, LPCTSTR strRes,
LPCTSTR strType);
// access the resource as if it is a
// pointer to type T
operator T*();
// get the size of the resource
DWORD GetSize();
T* m_data;
DWORD m_dwSize;
};
The template parameter allows you to treat an instance of Resource as if it is a pointer to type T, so for a HTML resource you can treat it as a pointer to char*. If the resource does not exist then m_data and m_dwSize will both be zero. This class is used in the example code like this:
BOOL ShowHTMLPage()
{
Resource<char>
res(NULL,
_T("MYCOUNT"),
RT_HTML);
if (res.GetSize() == 0)
return FALSE;
// resource is not NUL terminated, so we
// need to copy it add the NUL
char* pv = new
char[res.GetSize() + 1];
memcpy(pv, (char*)res,
res.GetSize());
pv[res.GetSize()] = 0;
puts(pv);
delete [] pv;
return TRUE;
}
In this code we load the MYCOUNT HTML resource, this could be accessed by treating res as a char* pointer, however, there will no NUL termination character, so the rest of the code makes a copy I memory and adds this character. SummaryThe Win32 API has a flexible API for accessing resources and for updating them. If you are willing to do a little bit of file renaming you are able to change the resources of a file even when it is running. An examples of this is shown in the download for this article. Contribute to IDR: To contribute an article to IDR, a click here.
|