What is the expression language used by the Resource Compiler for non-preprocessor expressions?
I noted some time ago that the Resource Compiler’s preprocessor is not the same as the C preprocessor: Although it supports the same expression language, it does not support directives like
There is a second expression language used by the Resource Compiler, and that’s the one used to define resources. Surprisingly, this expression language is different from the preprocessor expression language.
For one thing, it has a much reduced set of operators, and no operator precedence. All binary operators are left-associative, and parentheses can be used for grouping.
|Unary bitwise negation|
|a ||Binary addition|
|a ||Binary subtraction|
|a ||Binary bitwise OR|
|a ||Binary bitwise AND|
|a ||Binary bit clear (a |
NOT operator looks weird, but the idea is that you can write something like
WS_OVERLAPPEDWINDOW | NOT WS_MINIMIZEBOX
WS_ style is a composite style:
#define WS_OVERLAPPEDWINDOW (WS_OVERLAPPED | \ WS_CAPTION | \ WS_SYSMENU | \ WS_THICKFRAME | \ WS_MINIMIZEBOX | \ WS_MAXIMIZEBOX)
| NOT WS_MINIMIZEBOX means that you want all of the styles that come with
WS_ except for
Note that all operators have equal precedence, so
1 | 1 - 1 parses as
(1 | 1) - 1, which is 0. On the other hand, in C, the
- operator has higher precedence than
|, so the C expression
1 | 1 - 1 parses as
1 | (1 - 1), which is 1.
Since the preprocessor directives follow C expression rules, you can get into an odd situation where the same expression comes out to a difference value depending on the context in which you use it:
#if 1 | 1 - 1 DLG_MYDIALOG DIALOG 1 | 1 - 1, 0, 42, 42 BEGIN END #endif
1 | 1 - 1 in the
#if statement is a preprocessor directive, and it evaluates to 1, so the
#if condition is truthy, and the contents generate a dialog resource. On the other hand, when the compiler sees the
1 | 1 - 1 in the
DIALOG statement, it is evaluated as a resource constant, which produces zero.
Note also that the resource expression language lacks many operators, most notably multiplication.
Another oddity is that the resource expression language uses
0o as the octal prefix rather than a simple
0 like in C. This means that
010 evaluates to 8 when used in a preprocessor expression, but it evaluates to 10 when used in a resource expression. This is particularly troublesome if you put the definition in a header file that is shared with C/C++ code:
// resource.h #define DLG_AWESOME 010 // contoso.rc #include "resource.h" DLG_AWESOME DIALOG ... // contoso.cpp #include "resource.h" DialogBox(instance, MAKEINTRESOURCE(DLG_AWESOME), ...);
Even though they are both using
DLG_AWESOME, the resource compiler sees it as a resource expression, and the
010 is the decimal constant 10. On the other hand, the C++ compiler sees it as as a C++ expression, and the
010 is an octal constant which evaluates to 8.
Moral of the story: Don’t use leading zeroes.
There’s another quirk of the resource compiler expression language, and that’s the concept of the “initial value”.
In many cases, the value you are specifying comes with an initial value, and the expression you provide is implicitly treated as combining with the initial value as if by logical “or” (
|). In other words, if you write some expression
e, the Resource Compiler acts as if you had actually written
initial | e
Note the absence of parentheses around the
Here is the table of initial values for various control types:
|Control||Underlying control||With initial style|
These initial styles are documented in the Resource Compiler documentation. For each control, look at the section of the documentation that says “If you do not specify a style, the default style is…” That default style is the value that your custom style is combined with.
It didn’t fit in the above table, but dialogs themselves have an initial style of
WS_CAPTION if you gave the dialog a
The “initial value” trick makes the
NOT behavior a little more comprehensible, because it lets you write things like
LTEXT "Hello", -1, 4, 80, 160, 10, NOT WS_GROUP | SS_SUNKEN
NOT WS_GROUP combines with the initial value to produce a net result of
SS_LEFT | WS_GROUP | NOT WS_GROUP | SS_SUNKEN
Translating this into C, you get
((SS_LEFT | WS_GROUP) & ~WS_GROUP) | SS_SUNKEN
SS_LEFT | SS_SUNKEN. The
NOT WS_GROUP removes the
WS_GROUP style that came by default with the
Bonus chatter: Why are there two different expression languages?
The resource language is designed for use in resource statements. The
NOT syntax, in particular, is handy for removing styles from the initial styles that come with certain controls.
The preprocessor language is designed for use in preprocessor statements. And the preprocessor language matches the C programming language because preprocessor statements commonly occur in header files which are consumed by both the Resource Compiler and the C/C++ compiler. It would be a very difficult bug to track down if a
#if statement in a header file evaluated differently depending on whether you included it from a C/C++ file or from a resource file.
I don’t quite remember the details now, but I struggled a bit with the stringizing operator # in .rc files. I had the project’s version number kept as a MSBuild prop, then I passed that prop to the resource compiler as a preprocessor define (through the command-line). Somehow the rc.exe preprocessor didn’t quite do its work. Probably I had to use a separate .rc2 file that I then included in the main .rc file.
I have some .rc files that do this, and they use a two-step stringizing operation, i.e.
I’m pretty sure Resource Compiler supports