What I need is to insert a custom resource into an executable
or DLL; the EXE or DLL is not running, nor otherwise loaded or
locked, when I do the insertion. Here's the meat of a toy level
test program (a console app, just to do diagnostics with
printf, but I've tried any and all variations!) trying to do this:
HANDLE hUpdate = BeginUpdateResource(exenam, FALSE);
if(!hUpdate) {
printf("Cannot update resources of file (%s), err=%d\n", exenam, GetLastError());
return 1;
}
BOOL result = UpdateResource(hUpdate, // update resource handle
resotyp, // user-specific res type
resonam, // resource name
MAKELANGID(LANG_NEUTRAL,
SUBLANG_NEUTRAL), // neutral language ID
(void*)resoval, // ptr to resource info
1+strlen(resoval)); // size of resource info
if(result==FALSE) {
printf("Cannot update resource (%s/%s->%s), err=%d\n",
resotyp, resonam, resoval,
GetLastError());
return 2;
}
// Write changes to executable file and then close it.
if(!EndUpdateResource(hUpdate, FALSE)) {
printf("Cannot End update resources of file (%s), err=%d\n", exenam, GetLastError());
return 3;
}
printf("OK!\n";
return 0;
This prints OK and returns 0.
I'm running it (for test purposes) on other toy applications that just
enumerate all of their resources and/or locate and print one of them.
Experimental results: when trying to insert a resource into an EXE
that has none to start with, nothing happens -- the executable
file remains bereft of any resources whatsobloodyever.
When trying to insert another resource (same type, different name
and value) into an EXE that already has one custom resource (which
is inserted through visual studio), what seems to happen is that,
instead of inserting the NEW name and value, rather the OLD one
is duplicated (?); this is confirmed both through the toy program that
just enumerates all of its resource types/names/languages, AND by
opening the resulting EXE file (as type "resources") in Visual Studio.
A few posts I've seen seem to confirm that this just plain doesn't
work -- this includes both some people trying to make things work
and getting no results, and answers such as "these API are buggy,
you have to read all resources, delete them all, and reinsert them".
But, no mention of such huge bugs on any Microsoft online
resource that I could find -- and, to be honest, this seems truly
unbelievable, that an important pack of API's could just be
nonworking under any conditions period -- why, I've found on
the net a 1993 version of kernel32 (zipped) documented as
"fixing the UpdateResource bug"... can that bug still be around
5+ years later?!
I'm going crazy around this problem, and will TRULY appreciate
any help -- doubly so if it's also CC'd to my email (al...@magenta.com,
ideally) since my feeds, while many, are all at least somewhat
flaky/unreliable to some extent. I promise to post any resolution
to these groups when reached -- meanwhile, THANKS in advance!!!
Alex
Alex Martelli <mart...@cadlab.it> wrote in article
<F4tzM...@cadlab.it>...
> Win NT 4.0 SP3, VC++5 SP3, and I'm completely unable to
> use the documented functionality of UpdateResource and
> other related API's! I've searched the net far and wide but
> just about all of the many of hundreds of posts etc on the subject
> just remind people that these don't work on Win95 -- but I
> only need them to work on NT, so that's no issue.
I'm sorry no-one else (from MS) has replied.
I KNOW that these api's work - indeed, very few Microsoft applications or
systems would be localized, since all 32-bit executables are localized with
these functions. Sorry I can't help you more than to give you some
encouragement - keep fiddling. Enumerating all the resources, deleting
them, then re-adding everything you want to preserve and adding new stuff
is cleaner. Be sure to look at the language id's - they're often a source
of confusion. There are some weird situations with growing the resource
section that might be causing you problems (esp. adding resources to apps
that don't have them initially) - but you should get an error if it fails
for one of those situations.
App Studio uses these api's, too...
Floyd Rogers
The data you need to pass to UpdateResourceA|W need to be the actual raw
data. Not in .res file format.
BTW, I believe you're using integer resource and type id's, but if you use
strings, be sure to use the correct type: char or WCHAR.
Floyd Rogers
> (void*)resoval, // ptr to resource info
> 1+strlen(resoval)); // size of resource info
If this is a user-defined resource, well, it looks like a string to me
... and that means it lacks a resource header. From the docs:
"Win32 Resource File Formats
This section describes the format of the binary resource file that
the resource compiler creates based on the contents of the
resource-definition file. This file usually has an .res extension.
The linker reformats the .res file into a resource object file and
then links it to the executable file of a Win32-based application.
A binary resource file consists of a number of concatenated
resource entries. Each entry consists of a resource header and the
data for that resource. A resource header is DWORD-aligned in the
file and consists of the following:
A DWORD that contains the size of the resource header
A DWORD that contains the size of the resource data
The resource type
The resource name
Additional resource information
The RESOURCEHEADER structure describes the format of this header.
The data for the resource follows the resource header and is
specific to each type of resource. Some resources also employ a
resource-specific group header structure to provide information
about a group of resources."
Searching MSDN for that title gives you exactly one hit (or look at
Platform SDK, User interface services, Resources, Resources, About
Resources -- that overview section contains the page mentioned above,
plus docs on the resource updating functions.
--
Cheers,
Felix.
If you post a reply, kindly refrain from emailing it, too.
Note to spammers: fel...@mvps.org is my real email address.
No anti-spam address here. Just one comment: IN YOUR FACE!
Thanks for saving me time from trying another won't-work fix!-)
>BTW, I believe you're using integer resource and type id's, but if you use
>strings, be sure to use the correct type: char or WCHAR.
I was using strings earlier (narrow-char ones, I didn't have UNICODE
nor _UNICODE defined in either the toy-inserter application, nor
the toy-indiapig ones into which I was attempting to insert), then I
switched to number codes with different but equally-bad results (I
managed to hose the very format of the indiapig-EXE I was targeting,
so badly the operating system refused to load it...!-).
I have been given another lease of time to work on this problem, so
I shall now try the extract-everything, insert-everything approach... it
IS rather sad that a published SDK sample application (the hand/foot
example, trying to copy the "ABOUTBOX" dialog from hand.exe to
foot.exe) just plain *doesn't work* -- it uses UpdateResource in the
simple way it is documented to work, and which I also was trying, i.e.
not in the enumerate-save-erase-reinsert-everything-else mode, and
it just fails (without any indication that it IS failing).
Again, thanks for the advice and support -- and now, back to work...!
Alex
Thanks for the encouragement -- I will keep fiddling, if I'm able to
convince my manager to let me "throw good time after bad". Unfortunately,
having Win98 and not Win/NT at home, this is not the kind of problem I
can hack at all nights -- it has to be on company time & resources!-)
>them, then re-adding everything you want to preserve and adding new stuff
>is cleaner. Be sure to look at the language id's - they're often a source
Hmmm -- I want to preserve everything and add one new (biggish)
custom resource, so it had not occurred to me to record everything,
erase everything, then push it back in, when, apparently, all I needed
was to just say "and add this one"; maybe I'll pursue this avenue next.
The new resource is language-neutral and none of the old ones needs
to be touched in any way. But, again, thanks for the pointer.
>of confusion. There are some weird situations with growing the resource
>section that might be causing you problems (esp. adding resources to apps
>that don't have them initially) - but you should get an error if it fails
>for one of those situations.
There IS a growth of the resource section involved -- the new, biggish
resource is never present when I need to insert it; but, no errors (I test
them religiously!).
Lately I've been experimenting with numeric ID's (originally I was
using strings, thinking it might eliminate otherwise-possible clashes)
and -- still with no errors -- I managed to completely hose the EXE
whose resources I was supposedly "updating" (the system does not
any more recognize that file as being in a valid EXE format...).
>App Studio uses these api's, too...
Does it? I thought it let the resource section be built afresh by the linker.
How does AppStudio perform its duties under Win95/98 then, since the
API's in question are not implemented there...?
Alex
Thanks for the attempt! Unfortunately, as we've heard from Floyd,
that doesn't seem to be it -- raw data, like I'm passing, is what is
supposed to be passed to UpdateResource (clearly, its other
parameters give it all it needs to build the resource header, at
least in theory).
My "raw data" IS a string in these toy tests, but that is just to ease
my task a bit when I pore over hex dumps of the EXE files, etc --
so I can more easily eyeball whether my data has in fact been
inserted, etc etc. What I'm actually trying to insert is a custom
binary-format resource.
Actually, it is a very compact form of address-to-symbol map -- its
purpose is to allow a symbolic form of stack tracing, rather than
just a list of hex addresses; it is about an order of magnitude
smaller than anything I've been able to squeeze out of .DBG
files, bundled CV debug info, .PDB's, and so forth (the alternative
tack of having the EXE export all functions is unacceptable --
many are static, etc). I build it by processing the linker-produced
.MAP file, which is why I need to insert it afterwards rather than
through the normal .RC -> .RES process (well, I guess I could
systematically re-link the whole EXE every time, but that is a
LONG process -- I was hoping that updating the resource on
the fly would be much faster).
The whole setup is already running fine when the map-info is
in a separate file -- but, I want to insert it as a resource in the
exe for the usual reasons... separate files can be misplaced,
resources are a more "solid" way to operate. Actually, given
the pretty marginal nature of the specific problem all this is
addressing, I suspect I wouldn't have been allowed to spend
as much time on it as I have, were it not for the possible future
side benefits -- mastering resource updating within existing
EXEs and DLLs can obviously be used for other future nifty
tools besides this specific one.
Again, *THANKS* for the attempt at help -- I really appreciate it!
Alex
These APIs are not totally safe, BTW. After many, many hours of fiddling and
support calls to MS, it turns out that they do not always check that they
are doing the right thing to the target Exe/Dll file. They can cause them
randomly not to run. I think actually what it turned out to be was that some
versions of the linker were creating invalid output, which was benign until
you used these APIs to attach data. Then it would do what it thought was the
right thing but hose the Exe/dll module.
The support guy implied that these APIs are NOT what the resource compiler
uses to attach resources. I thought this was kind of questionable, but the
circumstances seem to bear it out. Otherwise, the same problem would afflict
the resource compiler and many programs would get screwed up.
I ended up undoing all this work and going back to a standalone resource
file of my own, which I would have ended up with anyway eventually since my
work is a portable C++ library. But, at the time, I kind of wanted to do it
and it was a PITA to figure out why it was not working. There seemed no way
it could screw up. You tell it how much data to attach and it does it. As
long as you don't step on some other resource, the worst case would seem to
be that you put the wrong data in and later freak out when you try to read
it back and interpret it. But, since it was screwing up the executable
format, the program wouldn't load at all.
BTW, they also implied that they have NO program that can scan a program and
check for a bad Exe format. That seemed awfully wierd to me, since you'd
think it would be relatively common when working on new versions of
compilers and linkers. So I assume that someone had to sit down and go over
the programs I sent them by eye to figure out what was going on.
--------------------------
Dean Roddey
The CIDLib Class Libraries
Charmed Quark Software
dro...@charmedquark.com
http://www.charmedquark.com
"100% Substance Free. More cost, less content"
Problems not solved (I'll just live with them for now):
-- BeginUpdateResource( whatever, FALSE) just doesn't
work -- no error indication, but flaky or no updating/inserting
of resources
-- nothing work reliably if strings are used as resource
types and names -- rather, unsigned short must be used
(again, no error indications, just damages or does not
correctly update the EXE or DLL!)
-- the EXE or DLL seems to get damaged (again, with
NO error indication -- but, it won't start/load any more,
the system loader complaining about invalid format for
the file) if it doesn't have a resource-section to start
with
All in all, a pretty flaky part of the Win32 API implementation
on NT 4.0 SP3, I'd say. But still, with these limitations, something
potentially useful can be done (warning: not tested thoroughly!).
My general approach:
-- encapsulate a resource into a simple C++ class for
ease of use
-- key data structure is a list of resources (std::list)
-- the list is build by sucking in all resources from the
EXE or DLL
-- it can then be altered (additions/deletions/changes)
with normal C++ operations
-- finally it is reinserted into the EXE or DLL replacing
whatever was there before
Here is the code that "sucks in all resources":
static BOOL CALLBACK enumLangs(HINSTANCE hModule,
LPCTSTR lpszType, LPCTSTR lpszName, WORD lang, LONG lParam)
{
Resource r(lpszName, lpszType, lang, hModule);
ResourceList* pList = (ResourceList*)lParam;
pList->push_back(r);
return TRUE;
}
static BOOL CALLBACK enumNames(HINSTANCE hModule,
LPCTSTR lpszType, LPTSTR lpszName, LONG lParam)
{
BOOL ok = EnumResourceLanguages(hModule, lpszType, lpszName, enumLangs, lParam);
if(!ok) throw ResourceException("enumerating langs of name", lpszName);
return TRUE;
}
static BOOL CALLBACK enumTypes(HINSTANCE hModule,
LPTSTR lpszType, LONG lParam)
{
BOOL ok = EnumResourceNames(hModule, lpszType, enumNames, lParam);
if(!ok) throw ResourceException("enumerating names of type ", lpszType);
return TRUE;
}
ResourceList::ResourceList(const char* modName)
{
HINSTANCE hInst = LoadLibraryEx(modName, 0,
DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE);
if(!hInst) throw ResourceException("loading module", modName);
BOOL ok = EnumResourceTypes(hInst, enumTypes, (LPARAM)this);
FreeLibrary(hInst);
if(!ok) throw ResourceException("enumerating types in module", modName);
}
The Resource class is the encapsulation of a resource (it stores the name,
type, language, and a copy of the data obtained via FindResourceEx etc etc);
the ResourceList class is basically a std::list<Resource> plus this ctor, and
the following setInto method, which is what updates (actually, "replaces") the
resources of a module (which MUST already have a .rsrc section, else, with
no error indication, it will just end up damaged, sigh):
void
ResourceList::setInto(const char* modName) const
{
HANDLE hUpdate = BeginUpdateResource(modName, TRUE);
if(!hUpdate) throw ResourceExcepttion("updating module", modName);
const_iterator it = begin();
while(it != end()) {
BOOL result = UpdateResource(hUpdate,
it->getType().self(), it->getName().self(), it->getLang(),
it->data_V(), it->size());
if(!result) throw ResourceException("inserting resource", it->toString());
++it;
}
if(!EndUpdateResource(hUpdate, FALSE))
throw ResourceExcepttion("closing module", modName);
}
So, in this approach, resource alterations are done through changes to
the in-memory ResourceList (additions, deletions, changes), before
calling its setInto method. This, with the already-given caveats, seems
to work somewhat reliably.
Thanks again to both people who helped me get to this point...
Alex