By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
440,213 Members | 2,108 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 440,213 IT Pros & Developers. It's quick & easy.

A design problem: how to represent resource id's

P: n/a
[I'm cross-posting to comp.lang.c++ since any solution is constrained by
that language, i.e. this is much about how to use that language]

<what to design a C++ solution for>
A Windows API /resource/ is data embedded in the executable, accessed
via API functions. A resource is completely identified by the quadruple
(id, type, language, file), where (1) id is what, for want of a better
name, I'll call a /kludgeon/, (2) type is also a kludgeon, (3) language
is a small integer identifying a concrete national language or one of a
set of pseudo-languages such as the Windows installation's language or
the language implied by the locale associated with the execution thread,
or it is an empty specification, resulting in the API level's
unspecified best match with the thread locale's implied language, and
(4) file is the executable file or DLL the resource resides in.

A kludgeon, used for id and type, can be a 16-bit integer or a general
identifier represented as a string. Vis-a-vis API functions it must be
specified as (1a) a pointer to a general identifier string, or (1b) a
pointer to a string of the form "#1234" for integer id 1234, or (1c) a
pseudo-pointer with all bits zero except the 16 least significant bits,
which then specify an integer kludgeon. An API function kludgeon
argument is typewise just a pointer to char or wchar_t (call that TChar
for simplicity, i.e., argument type TChar const*), and accepts any of
the forms (1a), (1b) and (1c) -- the latter being most efficient.

For naming kludgeons, if that cannot be avoided, one relevant fact is
that the same representation convention is used for Windows "atoms", but
atoms are dynamically allocated things, whereas kludgeons are not.

The language identification is constrained by the set of languages
supported by Windows, i.e., generalizing it can be problematic.

The file specification can be (3a) some identification of a file system
file, e.g. a path (there are also other ways to identify a file, but
probably irrelevant), or (3b) a 32-bit module identifier, called a
"module handle", where a /module/ is the loaded image of an executable
or DLL, such as the loaded image of the currently executing program.

Ordinarily one wants the file to default to the currently executing
program (a module handle is very easily obtained from the API level),
and the language specification to default to the empty specification.
</what to design a C++ solution for>

The main requirements are (1) correctness and safety, (2) clarity, and
(3) efficiency, since this is for my third installment about Windows
programming (for the two first installments, see <url:
http://groups.google.com/group/comp.os.ms-windows.programmer.win32/browse_thread/thread/fc61acc7316d9257/892100ca99fe8200>
or, as tinyurl, <url: http://tinyurl.com/pgzlq>). Functionality,
especially general file specification functionality, can suffer.

It's no big deal to design a "working" solution; in fact, the API level
itself is a "working" solution, so for that, nothing more is needed.

What I'm up against is a conflict between the three main requirements;
it's difficult to satisfy all three.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Apr 30 '06 #1
Share this Question
Share on Google+
9 Replies


P: n/a
"Alf P. Steinbach" <al***@start.no> wrote in message
news:4b*************@individual.net...
It's no big deal to design a "working" solution; in fact, the API level
itself is a "working" solution, so for that, nothing more is needed.


If the API consists of kludgeons put together by a couple of kludgers would
that make us poor users 'kludgees'?

[jw]
May 1 '06 #2

P: n/a
Could someone translate that to English?

But seriously, as far as I understand you're interested in a solution for including data into executable that would be: 1)correct and safe, 2)clare and 3)efficient.
And for some obvious reasons using default windows resources and loading them using a bunch of deprecated functions like the code below (no error checking):

HRSRC hres;
HGLOBAL hglob;
void* p;

hres = FindResource(GetModuleHandle(NULL),id,type);
hglob = LoadResource(GetModuleHandle(NULL),hres);
p = LockResource(hglob);
FreeResource(hRes);

does not meet at least one of the three established conditions (and perhaps does not meet any). I assume here simplified version of the problem when resources are within executable itself and there is only one language version of the resource.

I believe resources are commonly used because it's easy to both create and include them, though loading is not very elegant. Also the example above refers to the custom types resources, using some predefined types is often easier.

As to more general C++ approach, the best (fully portable and easy to access from within a code) way of including data is simply to incude the data into the code as table or some custom struct in a dedicated header/header+code file(s). The disadvantage here is that the first step (creating and including) requires more effort, because any data needs to be converted to C++ readable format.

This approach fulfils 1) and also 3) if by efficiency is meant executing the code (if creating then not necessary).
I would say it fulfils 2) as well but clarity is a subjective thing.

--
677265676F727940346E6575726F6E732E636F6D
May 1 '06 #3

P: n/a
Grzegorz Wróbel </dev/nu**@localhost.localdomain> wrote:
HRSRC hres;
HGLOBAL hglob;
void* p;
hres = FindResource(GetModuleHandle(NULL),id,type);
hglob = LoadResource(GetModuleHandle(NULL),hres);
p = LockResource(hglob);
FreeResource(hRes);


You're not meant to use FreeResource. It does nothing. So just

HRSRC hres = FindResource(hInstance,id,type);
hglob = LoadResource(hInstance,hres);
void *p = LockResource(hglob)

and no need to free anything. I actually find these three lines rather
beautiful, clean, understandable and easy! Alf, in your quest to make
a framework/wrapper, I think that these three should be at the BOTTOM
of your priority list.

--
Lucian
May 1 '06 #4

P: n/a
Lucian Wischik wrote:
Grzegorz Wróbel </dev/nu**@localhost.localdomain> wrote:
HRSRC hres;
HGLOBAL hglob;
void* p;
hres = FindResource(GetModuleHandle(NULL),id,type);
hglob = LoadResource(GetModuleHandle(NULL),hres);
p = LockResource(hglob);
FreeResource(hRes);

You're not meant to use FreeResource. It does nothing. So just

HRSRC hres = FindResource(hInstance,id,type);
hglob = LoadResource(hInstance,hres);
void *p = LockResource(hglob)

and no need to free anything. I actually find these three lines rather
beautiful, clean, understandable and easy! Alf, in your quest to make
a framework/wrapper, I think that these three should be at the BOTTOM
of your priority list.


You right FreeResource() is obsolete and along with UnlockResource() left only for backward compatibility reasons. I just copied and paste some code from one of my old projects.

And what I meant by deprecated was that LoadResource() return HGLOBAL type although handle it returns is not really a pointer to a global memory block. This is also for backward compatibility with Win16 code.
So what beatiful you see in this remnant of ancient times? I quess you are a romantic soul.

I personally dislike this weirdness when you need LoadSomething() which returns HGLOBAL type and then LockSomething(), which still resides in some parts of win32 (and win64 now) code for backward compatibility with 16bit code. Examples are some resources functions and clibboard access functions. But of course I still use it :) I take Alf's question as more theoretical than practical.

--
677265676F727940346E6575726F6E732E636F6D
May 1 '06 #5

P: n/a
Grzegorz Wróbel </dev/nu**@localhost.localdomain> wrote:
So what beatiful you see in this remnant of ancient times? I quess you are a romantic soul.


Sure, the return types are weird and wrong as you say. And the
FindResource/LoadResource could reasonably be rolled into a single
call. What I like about it is that it's three lines, you don't need to
free it, and it's small+compact to look at, and you know exactly what
it's doing (i.e. no hidden background machinery)

--
Lucian
May 1 '06 #6

P: n/a
* Lucian Wischik:
Grzegorz Wróbel </dev/nu**@localhost.localdomain> wrote:
HRSRC hres;
HGLOBAL hglob;
void* p;
hres = FindResource(GetModuleHandle(NULL),id,type);
hglob = LoadResource(GetModuleHandle(NULL),hres);
p = LockResource(hglob);
FreeResource(hRes);


You're not meant to use FreeResource. It does nothing. So just

HRSRC hres = FindResource(hInstance,id,type);
hglob = LoadResource(hInstance,hres);
void *p = LockResource(hglob)

and no need to free anything. I actually find these three lines rather
beautiful, clean, understandable and easy! Alf, in your quest to make
a framework/wrapper, I think that these three should be at the BOTTOM
of your priority list.


Perhaps. But add error checking and MAKEINTRESOURCE calls (converting
integers to kludgeon pointers). However, wrapping these three calls in a
single function is not a big deal; the problem lies not with
encapsulating the three calls above in a single function, i.e. not with
the procedural aspect, but with representing id+type as a cohesive unit
that can be safely passed around and that centralizes the
MAKEINTRESOURCE calls, perhaps also dealing with type safety of
resources, while not needlessly sacrificing efficiency.

At the lowest level, tackling just the kludgeon representation, I'm
thinking something like

// The intent of this class is to provide safety & clarity
// (especially wrt. string lifetime), while preserving efficiency
// for integer atom-like names.
class AtomLikeName
{
private:
std::wstring myIdentifier;
unsigned short myIntValue;

public:
AtomLikeName( std::wstring const& value )
: myIdentifier( value )
, myIntValue( 0 )
{
assert( myIdentifier.length() > 0 );
}

AtomLikeName( unsigned short value, bool isAtom = false )
: myIntValue( value )
{
assert( 0 < myIntValue );
assert( myIntValue <= (isAtom? 0xBFFF : 0x7FFF) );
}

bool isPseudoPointer() const { return (myIntValue != 0); }

// Can be a pseudo-pointer:
wchar_t const* value() const
{
return (isPseudoPointer()?
MAKEINTRESOURCE( myIntValue ) : myIdentifier.c_str()
);
}

// Comparision operations can be defined in terms of asString().
std::wstring asString() const
{
return (isPseudoPointer()?
L"#" + stringFrom( myIntValue ) : myIdentifier
);
}
};

Then an id+type+(possible language)+(more remotely possible file spec)
can be added on top.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
May 1 '06 #7

P: n/a
Lucian Wischik wrote:
Sure, the return types are weird and wrong as you say. And the
FindResource/LoadResource could reasonably be rolled into a single
call. What I like about it is that it's three lines, you don't need to
free it, and it's small+compact to look at, and you know exactly what
it's doing (i.e. no hidden background machinery)


True..

But I would add at least one check at the end to see if resource has been correctly loaded. This would render 5 lines and 3 variables. Not that bad as many windows specific stuff requires much more.
Now compare this to the pure C++ alternative I described: 0 lines and 1 variable declared/defined in header file, ready to use.
I don't count here dedicated header/code files as real code since they are practicaly generated and I treat them as equivalent of .rc and "resource.h" files. Of course the cost for that is additional work with generating those from files that normaly would be included as a resource. So running external progam and perhaps some copy/paste "labour".
Also it is worth to mention that resources are easily crackable by PE-format editors unless file has been compressed using a custom compressing routine. So in case it does matter different approach is worth considerating.

I quess most find resources more comfortable to use, especially if their IDE provides build in resource editor/wizard and that they are already familiar with resources since those are used for dialog templates, icons, etc., but it's good to be aware there are alternatives.
But now back to the OP's problem. As I gather it Alf is witing some paper - a tutorial for windows programming and wants to provide a chapter about way of including external data into the program.
So, is the resource approach
1) correct and safe?
Loading resource may fail, though in case it's included in the very same executable that loads it, it's very unlikely unless resource section has been corrupted or edited by some PE format editor.
2) clear?
From my experience, when I had to use it for the first time - no. I was confused by this return types. I was really suprised Microsoft didn't provide additionally, valid non-backward compatible method(s) except this backward compatible weirdiness.
3)efficient?
Since you generally load resource only once (at least custom one) it doesn't matter that much.

If Alf's concern is only about identifiers then with pure C++ approach he can decide and define the way "resources" are identified by himself.

--
677265676F727940346E6575726F6E732E636F6D
May 1 '06 #8

P: n/a
On Mon, 01 May 2006 20:51:35 +0200, "Alf P. Steinbach"
<al***@start.no> wrote:
* Lucian Wischik:
Grzegorz Wróbel </dev/nu**@localhost.localdomain> wrote:
HRSRC hres;
HGLOBAL hglob;
void* p;
hres = FindResource(GetModuleHandle(NULL),id,type);
hglob = LoadResource(GetModuleHandle(NULL),hres);
p = LockResource(hglob);
FreeResource(hRes);


You're not meant to use FreeResource. It does nothing. So just

HRSRC hres = FindResource(hInstance,id,type);
hglob = LoadResource(hInstance,hres);
void *p = LockResource(hglob)

and no need to free anything. I actually find these three lines rather
beautiful, clean, understandable and easy! Alf, in your quest to make
a framework/wrapper, I think that these three should be at the BOTTOM
of your priority list.


Perhaps. But add error checking and MAKEINTRESOURCE calls (converting
integers to kludgeon pointers). However, wrapping these three calls in a
single function is not a big deal; the problem lies not with
encapsulating the three calls above in a single function, i.e. not with
the procedural aspect, but with representing id+type as a cohesive unit
that can be safely passed around and that centralizes the
MAKEINTRESOURCE calls, perhaps also dealing with type safety of
resources, while not needlessly sacrificing efficiency.

At the lowest level, tackling just the kludgeon representation, I'm
thinking something like

// The intent of this class is to provide safety & clarity
// (especially wrt. string lifetime), while preserving efficiency
// for integer atom-like names.
class AtomLikeName
{
private:
std::wstring myIdentifier;
unsigned short myIntValue;

public:
AtomLikeName( std::wstring const& value )
: myIdentifier( value )
, myIntValue( 0 )
{
assert( myIdentifier.length() > 0 );
}

AtomLikeName( unsigned short value, bool isAtom = false )
: myIntValue( value )
{
assert( 0 < myIntValue );
assert( myIntValue <= (isAtom? 0xBFFF : 0x7FFF) );
}

bool isPseudoPointer() const { return (myIntValue != 0); }

// Can be a pseudo-pointer:
wchar_t const* value() const
{
return (isPseudoPointer()?
MAKEINTRESOURCE( myIntValue ) : myIdentifier.c_str()
);
}

// Comparision operations can be defined in terms of asString().
std::wstring asString() const
{
return (isPseudoPointer()?
L"#" + stringFrom( myIntValue ) : myIdentifier
);
}
};

Then an id+type+(possible language)+(more remotely possible file spec)
can be added on top.


Don't forget that resources in Windows can be created on the fly. They
don't have to be embedded in the executable or a DLL, although that is
a popular alternative.

Just an idea I have been playing with:
One could have a factory class for each kind of resource and let the
factory be a template taking a parameter of different kinds of
resource managers. In the debugging phase, for example, resource data
could be kept in a configuration file (e.g. XML, or even an INI file)
which would be read by the resource manager as text. The binary
resource would be created on the fly by the resource manager. Slow, of
course, but a whole lot easier to edit during development. No
recompiling, no linking, just edit the configuration and run.

Once everything is satisfactory, the configuration file could be
parsed and compiled as a RES file by an external tool and linked to
the executable. The template resource manager used for debugging would
be swapped for one that loads resources the "normal" way.

I believe this is similar to how some GUI libraries manage resources
on Linux, isn't it?

--
Bob Hairgrove
No**********@Home.com
May 1 '06 #9

P: n/a
[Bob, I think the discussion you aim for is off-topic in clc++ since it
doesn't involve C++, so I'm setting follow-ups; please honor.]

* Bob Hairgrove:

Don't forget that resources in Windows can be created on the fly.
Depends what you mean: obviously they can be created, and hence, on the fly.

They
don't have to be embedded in the executable or a DLL, although that is
a popular alternative.
Classic API-level resources have to be embedded in files, mainly because
they're used without regard for deallocation.

Just an idea I have been playing with:
One could have a factory class for each kind of resource and let the
factory be a template taking a parameter of different kinds of
resource managers. In the debugging phase, for example, resource data
could be kept in a configuration file (e.g. XML, or even an INI file)
which would be read by the resource manager as text. The binary
resource would be created on the fly by the resource manager. Slow, of
course, but a whole lot easier to edit during development. No
recompiling, no linking, just edit the configuration and run.
XML resources like you describe are supported by many environments,
including Java and Windows .NET.

It's not a case of edit-and-go, however.

On the contrary, for all but the most trivial kinds of resources it
typically involves a lot of obscure under-documented or undocumented
tools and API's, e.g. conversion to and back from Base-64. It's IMHO an
over-generalization that only barely delivers the basic functionality,
and then in an inefficient and needlessly complex way. Unfortunately,
those who make the decisions about what we should use in the future, by
backing this or that approach so that support becomes available, have a
vested interest in selling tools, seminars and so on, and are perhaps
not so much engineers as buzz-word compliant managers with $$$ eyes.

Once everything is satisfactory, the configuration file could be
parsed and compiled as a RES file by an external tool and linked to
the executable. The template resource manager used for debugging would
be swapped for one that loads resources the "normal" way.
Uh. Microsoft is introducing something like that in Windows Vista.
It's ugly, and it's over-generalized. It tries to fix that which
doesn't need fixing, and solve that which is already solved, instead of
fixing the things that are broken (such as the documentation...).

I believe this is similar to how some GUI libraries manage resources
on Linux, isn't it?


Everywhere, unfortunately.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
May 1 '06 #10

This discussion thread is closed

Replies have been disabled for this discussion.