Click here to Skip to main content
15,881,092 members
Articles / Programming Languages / C++
Article

XRES, a Utility for Translating EXEs and DLLs

Rate me:
Please Sign up or sign in to vote.
3.89/5 (5 votes)
4 Jan 2010CPOL4 min read 20.3K   949   17  
Take text resources out of an EXE/DLL- translate - take them back in.
XRES_DEMO

Introduction

Have you ever needed to use an app that was written in a language other than your own? Then this is for you.

This project grew out of a simple need. At the begining I needed to modify a menu in a DLL; I solved it; then I thought I could do something more with the code. And I coded some more.

Basically it's an specialised resource editor. You choose an EXE or DLL (whatever extension it has) and extract three types of resources to a script file. (Menus, Dialogs, and StringTables). Normally used you should use the 'create XML' option which leaves all strings of text in a separate XML:

For example:

XML
<STR ID="0088">"&amp;Add to Favorites..."</STR>

or:

XML
<STR ID="0974">"OEM (DOS) conversion for filenames and descriptions"</STR>

Once the strings are translated you simply put them back into the app to which they belong.

I'd like to try it on RTL languages (Arabic, Hebrew, Farsi..) but Microsoft Windows discards any RTL flag if you are working on a Western language platform. (I tried to write strings backwards, but letters do not connect properly) If someone wants to do it, then RTL flag should be ORred on Extended Style Flags.

Background

There are many resource editors (some of then in Code Project). Great if what you want is to move a control in a dialog. But painful if you have to select -one by one- every portion of text. That is why I created this project.

Using the Code

Select an EXE/DLL file, push the 'extract text resources' button. Edit (translate) strings in the xml. have a preview of de dlg (and mabe retouch something). Put it all back with the 'apply changes' button (it will save a backup copy before modifyen anything). If something's wrong push the 'vade retro' button.

It does some transliteraton back and forth (for &, ",<,>, CR/LF) so that these chars do not interfere in a XML file. It should be respected.

Caution should be payed when you translate a word in a stringtable to discover later that it was a folder name. Or you forget a percent (%) sign and a message does not showup properly formated. Or you change hotkeys in a menu. etc.

It allows some limited editing of dialogs, to account for all changes in text len. For every dialog in the EXE/DLL you are working on, you can select every item by clicking on it and change size with arrows, and/or change position with Ctrl Arrows.

The apps that were designed to be translated, have their resources in separate DLL and, by config you select the appropiate one. It should be easier to get the originally translated DLL. On the other hand, some apps are poorly coded just embed every message within code:

C++
if (so and so..) MesageBox( NULL, "a message to show", "warning", MB_OK);

XRES does not help with this. You'd have to go with an hex editor (provided your translation has equal or less length). There are also apps which perform a selftest to verify they have not been modified (all AntiVirus software do this). There is nothing that can be changed with them.

Points of Interest

For any EXE or DLL you can load it into the app memory with the LoadLibrary function. Once you have a handle on it, you can list all of the resources; this is a multistep procedure:

C++
hModule = LoadLibrary(pFileName);
EnumResourceTypes(hModule, (ENUMRESTYPEPROC)EnumTypesFunc, 0); 
FreeLibrary(hModule);

BOOL EnumTypesFunc(HMODULE hModule, LPTSTR lpType, LONG lParam) {
    EnumResourceNames(hModule, lpType, (ENUMRESNAMEPROC)EnumNamesFunc, 0);
    return TRUE;
}

BOOL EnumNamesFunc(HMODULE hModule, LPCTSTR lpType, LPTSTR lpName, LONG lParam) {
   EnumResourceLanguages(hModule, lpType, lpName, (ENUMRESLANGPROC)EnumLangsFunc, 0);
   return TRUE;
}

// here we process resources
BOOL EnumLangsFunc(HMODULE hModule, LPCTSTR lpType, LPCTSTR lpName, WORD wLang, LONG lParam) {
   if (lpType ==RT_STRING) {
    //process_stringtable
   } else if (lpType ==RT_MENU) {
    // process_menu 
   } else if (lpType ==RT_DIALOG) {
    // process_dialog
   }
}

Windows executables pack all resources in tight variable length structs, a legacy from times when saving a byte was worth lots of effort. All this structs have to be followed down to the bottom, even when you have not interest on what's in between; there's no other way to know its real length. To make it more interesting, different versions of the same type of resource exist and can cohabit the same module. As it sometimes happens when you work with Microsoft products, real formats disagree with documentation, like the 'id' field in the DLGITEMTEMPLATEEX struct, wich IS a DWORD, modern documentation says so, but the first doc I used said it was WORD.

For every resource, all of its data are kept in a script file (in a form easy to understand if you are aware of the struct it is describing):

C++
DIALOG 132 'L:1033' (Version:1, Size:1840)
HID:00000000, XST:00000400, STY:80C800C0 nitems:36, x=0, y=0, cx=400, cy=258
MNU:'?' WCL:'?' TIT:"Database maintenance"
FNT:'MS Sans Serif', PNT:8, WGT:0, IT:0, CH:1

(I keep the original size just as a clue to how much memory should be allocated when dealing with it)

With de XML option, instead of

C++
MNU:'?' WCL:'?' TIT:"Database maintenance"

we keep

C++
MNU:'?' WCL:'?' TIT:"STR ID=0562"

and in the XML file we keep

XML
<STR ID="0562">"Database maintenance"</STR>

Once the strings are translated, we have to do all the way back, from script file to windows variable size structs, and put them into its module with:

C++
HANDLE hdlResourceUpdate =BeginUpdateResource( strFileNameDEST, FALSE);
UpdateResource(hdlResourceUpdate, pcTypeResource, MAKEINTRESOURCE(wIDResource),
    nLangResource, ptrPackedResource, sizeofresource);
(loop for as many resources you've got)
EndUpdateResource(hdlResourceUpdate, FALSE);

Some useful macros are provides to help in this procedure.

Acknowledgements

// Parts of the code were taken from others. (beggining with Microsofts MSDN)
// For some of them, it was straight copy-paste; for others, it was just taking an idea. 
// Hyperlinks.cpp / Copyright 2002 Neal Stublen All rights reserved.
// http://www.awesoftware.com
// (minor changes to adapt it here)

// ShellOpenFile.cpp / ExecApp.cpp Version 1.0 (see article at CodeProject.com)
// Author: Hans Dietrich hdietrich@gmail.com
// (minor changes to adapt it here)

// portions of XTRACT.cpp where taken from ListResources
// by Roman Ryltsov, roman@alax.info


// dictum:
// "every advancement is done by climbing upon other's shoulders"
//

Why do I still use VC6 ? see link http://www.codeproject.com/Lounge.aspx?msg=2486354#xx2486354xx

License

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


Written By
Web Developer
Spain Spain
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
-- There are no messages in this forum --