{"id":107928,"date":"2023-03-13T07:00:00","date_gmt":"2023-03-13T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=107928"},"modified":"2023-03-10T17:52:33","modified_gmt":"2023-03-11T01:52:33","slug":"20230313-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230313-00\/?p=107928","title":{"rendered":"What is the expression language used by the Resource Compiler for non-preprocessor expressions?"},"content":{"rendered":"<p>I noted some time ago that <a title=\"The Resource Compiler's preprocessor is not the same as the C preprocessor\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20171004-00\/?p=97126\"> the Resource Compiler&#8217;s preprocessor is not the same as the C preprocessor<\/a>: Although it supports the same expression language, it does not support directives like <code>#pragma<\/code>.<\/p>\n<p>There is a second expression language used by the Resource Compiler, and that&#8217;s the one used to define resources. Surprisingly, this expression language is different from the preprocessor expression language.<\/p>\n<p>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.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td><code>+<\/code><var>a<\/var><\/td>\n<td>Unary plus<\/td>\n<\/tr>\n<tr>\n<td><code>-<\/code><var>a<\/var><\/td>\n<td>Unary minus<\/td>\n<\/tr>\n<tr>\n<td><code>~<\/code><var>a<\/var><\/td>\n<td>Unary bitwise negation<\/td>\n<\/tr>\n<tr>\n<td><var>a<\/var> <code>+<\/code> <var>b<\/var><\/td>\n<td>Binary addition<\/td>\n<\/tr>\n<tr>\n<td><var>a<\/var> <code>-<\/code> <var>b<\/var><\/td>\n<td>Binary subtraction<\/td>\n<\/tr>\n<tr>\n<td><var>a<\/var> <code>|<\/code> <var>b<\/var><\/td>\n<td>Binary bitwise OR<\/td>\n<\/tr>\n<tr>\n<td><var>a<\/var> <code>&amp;<\/code> <var>b<\/var><\/td>\n<td>Binary bitwise AND<\/td>\n<\/tr>\n<tr>\n<td><var>a<\/var> <code>| NOT<\/code> <var>b<\/var><\/td>\n<td>Binary bit clear (<var>a<\/var> <code>&amp; ~<\/code><var>b<\/var>)<\/td>\n<\/tr>\n<tr>\n<td><code>0x<\/code><var>dddd<\/var><\/td>\n<td>Hexadecimal constant<\/td>\n<\/tr>\n<tr>\n<td><code>0o<\/code><var>dddd<\/var><\/td>\n<td>Octal constant<\/td>\n<\/tr>\n<tr>\n<td><var>dddd<\/var><\/td>\n<td>Decimal constant<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The <code>NOT<\/code> operator looks weird, but the idea is that you can write something like<\/p>\n<pre>WS_OVERLAPPEDWINDOW | NOT WS_MINIMIZEBOX\r\n<\/pre>\n<p>The <code>WS_<wbr \/>OVERLAPPED\u00adWINDOW<\/code> style is a composite style:<\/p>\n<pre>#define WS_OVERLAPPEDWINDOW (WS_OVERLAPPED     | \\\r\n                             WS_CAPTION        | \\\r\n                             WS_SYSMENU        | \\\r\n                             WS_THICKFRAME     | \\\r\n                             WS_MINIMIZEBOX    | \\\r\n                             WS_MAXIMIZEBOX)\r\n<\/pre>\n<p>Appending a <code>| NOT WS_MINIMIZEBOX<\/code> means that you want all of the styles that come with <code>WS_<wbr \/>OVERLAPPED\u00adWINDOW<\/code> except for <code>WS_<wbr \/>MINIMIZE\u00adBOX<\/code>.<\/p>\n<p>Note that all operators have equal precedence, so <code>1 | 1 - 1<\/code> parses as <code>(1 | 1) - 1<\/code>, which is 0. On the other hand, in C, the <code>-<\/code> operator has higher precedence than <code>|<\/code>, so the C expression <code>1 | 1 - 1<\/code> parses as <code>1 | (1 - 1)<\/code>, which is 1.<\/p>\n<p>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:<\/p>\n<pre>#if 1 | 1 - 1\r\nDLG_MYDIALOG DIALOG 1 | 1 - 1, 0, 42, 42\r\nBEGIN\r\nEND\r\n#endif\r\n<\/pre>\n<p>The <code>1 | 1 - 1<\/code> in the <code>#if<\/code> statement is a preprocessor directive, and it evaluates to 1, so the <code>#if<\/code> condition is truthy, and the contents generate a dialog resource. On the other hand, when the compiler sees the <code>1 | 1 - 1<\/code> in the <code>DIALOG<\/code> statement, it is evaluated as a resource constant, which produces zero.<\/p>\n<p>Note also that the resource expression language lacks many operators, most notably multiplication.<\/p>\n<p>Another oddity is that the resource expression language uses <code>0o<\/code> as the octal prefix rather than a simple <code>0<\/code> like in C. This means that <code>010<\/code> 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:<\/p>\n<pre>\/\/ resource.h\r\n#define DLG_AWESOME 010\r\n\r\n\/\/ contoso.rc\r\n#include \"resource.h\"\r\n\r\nDLG_AWESOME DIALOG ...\r\n\r\n\/\/ contoso.cpp\r\n#include \"resource.h\"\r\n\r\nDialogBox(instance, MAKEINTRESOURCE(DLG_AWESOME), ...);\r\n<\/pre>\n<p>Even though they are both using <code>DLG_AWESOME<\/code>, the resource compiler sees it as a resource expression, and the <code>010<\/code> is the decimal constant 10. On the other hand, the C++ compiler sees it as as a C++ expression, and the <code>010<\/code> is an octal constant which evaluates to 8.<\/p>\n<p>Moral of the story: Don&#8217;t use leading zeroes.<\/p>\n<p>There&#8217;s another quirk of the resource compiler expression language, and that&#8217;s the concept of the &#8220;initial value&#8221;.<\/p>\n<p>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 &#8220;or&#8221; (<code>|<\/code>). In other words, if you write some expression <code>e<\/code>, the Resource Compiler acts as if you had actually written<\/p>\n<pre>initial | e\r\n<\/pre>\n<p>Note the absence of parentheses around the <code>e<\/code>.<\/p>\n<p>Here is the table of initial values for various control types:<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th style=\"border: solid 1px black;\">Control<\/th>\n<th style=\"border: solid 1px black;\">Underlying control<\/th>\n<th style=\"border: solid 1px black;\" colspan=\"2\">With initial style<\/th>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\"><code>PUSHBUTTON<\/code><\/td>\n<td style=\"border: solid 1px black;\">Button<\/td>\n<td style=\"border: solid 1px black; border-right: transparent;\"><code>BS_PUSHBUTTON<code><\/code><\/code><\/td>\n<td style=\"border: solid 1px black; border-left: transparent;\"><code>| WS_TABSTOP<\/code><\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\"><code>DEFPUSHBUTTON<\/code><\/td>\n<td style=\"border: solid 1px black;\">Button<\/td>\n<td style=\"border: solid 1px black; border-right: transparent;\"><code>BS_DEFPUSHBUTTON<code><\/code><\/code><\/td>\n<td style=\"border: solid 1px black; border-left: transparent;\"><code>| WS_TABSTOP<\/code><\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\"><code>CHECKBOX<\/code><\/td>\n<td style=\"border: solid 1px black;\">Button<\/td>\n<td style=\"border: solid 1px black; border-right: transparent;\"><code>BS_CHECKBOX<code><\/code><\/code><\/td>\n<td style=\"border: solid 1px black; border-left: transparent;\"><code>| WS_TABSTOP<\/code><\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\"><code>AUTOCHECKBOX<\/code><\/td>\n<td style=\"border: solid 1px black;\">Button<\/td>\n<td style=\"border: solid 1px black; border-right: transparent;\"><code>BS_AUTOCHECKBOX<code><\/code><\/code><\/td>\n<td style=\"border: solid 1px black; border-left: transparent;\"><code>| WS_TABSTOP<\/code><\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\"><code>STATE3<\/code><\/td>\n<td style=\"border: solid 1px black;\">Button<\/td>\n<td style=\"border: solid 1px black; border-right: transparent;\"><code>BS_3STATE<code><\/code><\/code><\/td>\n<td style=\"border: solid 1px black; border-left: transparent;\"><code>| WS_TABSTOP<\/code><\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\"><code>AUTO3STATE<\/code><\/td>\n<td style=\"border: solid 1px black;\">Button<\/td>\n<td style=\"border: solid 1px black; border-right: transparent;\"><code>BS_AUTO3STATE<code><\/code><\/code><\/td>\n<td style=\"border: solid 1px black; border-left: transparent;\"><code>| WS_TABSTOP<\/code><\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\"><code>RADIOBUTTON<\/code><\/td>\n<td style=\"border: solid 1px black;\">Button<\/td>\n<td style=\"border: solid 1px black;\" colspan=\"2\"><code>BS_RADIOBUTTON<\/code><\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\"><code>AUTORADIOBUTTON<\/code><\/td>\n<td style=\"border: solid 1px black;\">Button<\/td>\n<td style=\"border: solid 1px black;\" colspan=\"2\"><code>BS_AUTORADIOBUTTON<\/code><\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\"><code>GROUPBOX<\/code><\/td>\n<td style=\"border: solid 1px black;\">Button<\/td>\n<td style=\"border: solid 1px black;\" colspan=\"2\"><code>BS_GROUPBOX<\/code><\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\"><code>PUSHBOX<\/code><\/td>\n<td style=\"border: solid 1px black;\">Button<\/td>\n<td style=\"border: solid 1px black; border-right: transparent;\"><code>BS_PUSHBOX<code><\/code><\/code><\/td>\n<td style=\"border: solid 1px black; border-left: transparent;\"><code>| WS_TABSTOP<\/code><\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\"><code>LTEXT<\/code><\/td>\n<td style=\"border: solid 1px black;\">Static<\/td>\n<td style=\"border: solid 1px black; border-right: transparent;\"><code>SS_LEFT<code><\/code><\/code><\/td>\n<td style=\"border: solid 1px black; border-left: transparent;\"><code>| WS_GROUP<\/code><\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\"><code>RTEXT<\/code><\/td>\n<td style=\"border: solid 1px black;\">Static<\/td>\n<td style=\"border: solid 1px black; border-right: transparent;\"><code>SS_RIGHT<code><\/code><\/code><\/td>\n<td style=\"border: solid 1px black; border-left: transparent;\"><code>| WS_GROUP<\/code><\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\"><code>CTEXT<\/code><\/td>\n<td style=\"border: solid 1px black;\">Static<\/td>\n<td style=\"border: solid 1px black; border-right: transparent;\"><code>SS_CENTER<code><\/code><\/code><\/td>\n<td style=\"border: solid 1px black; border-left: transparent;\"><code>| WS_GROUP<\/code><\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\"><code>ICON<\/code><\/td>\n<td style=\"border: solid 1px black;\">Static<\/td>\n<td style=\"border: solid 1px black;\" colspan=\"2\"><code>SS_ICON<code><\/code><\/code><\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\"><code>EDITTEXT<\/code><\/td>\n<td style=\"border: solid 1px black;\">Edit<\/td>\n<td style=\"border: solid 1px black; border-right: transparent;\"><code>ES_LEFT | WS_BORDER<code><\/code><\/code><\/td>\n<td style=\"border: solid 1px black; border-left: transparent;\"><code>| WS_TABSTOP<\/code><\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\"><code>LISTBOX<\/code><\/td>\n<td style=\"border: solid 1px black;\">ListBox<\/td>\n<td style=\"border: solid 1px black;\" colspan=\"2\"><code>LBS_NOTIFY | WS_BORDER<code><\/code><\/code><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>These initial styles are documented in the Resource Compiler documentation. For each control, look at the section of the documentation that says &#8220;If you do not specify a style, the default style is\u2026&#8221; That default style is the value that your custom style is combined with.<\/p>\n<p>It didn&#8217;t fit in the above table, but dialogs themselves have an initial style of <code>WS_CAPTION<\/code> if you gave the dialog a <code>CAPTION<\/code>.<\/p>\n<p>The &#8220;initial value&#8221; trick makes the <code>NOT<\/code> behavior a little more comprehensible, because it lets you write things like<\/p>\n<pre>    LTEXT \"Hello\", -1, 4, 80, 160, 10, <span style=\"border: solid 1px black;\">NOT WS_GROUP<\/span> | SS_SUNKEN\r\n<\/pre>\n<p>The <code>NOT WS_GROUP<\/code> combines with the initial value to produce a net result of<\/p>\n<pre>SS_LEFT | WS_GROUP | NOT WS_GROUP | SS_SUNKEN\r\n<\/pre>\n<p>Translating this into C, you get<\/p>\n<pre>((SS_LEFT | WS_GROUP) &amp; ~WS_GROUP) | SS_SUNKEN\r\n<\/pre>\n<p>which is <code>SS_LEFT | SS_SUNKEN<\/code>. The <code>NOT WS_GROUP<\/code> removes the <code>WS_GROUP<\/code> style that came by default with the <code>LTEXT<\/code> statement.<\/p>\n<p><b>Bonus chatter<\/b>: Why are there two different expression languages?<\/p>\n<p>The resource language is designed for use in resource statements. The <code>NOT<\/code> syntax, in particular, is handy for removing styles from the initial styles that come with certain controls.<\/p>\n<p>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 <code>#if<\/code> statement in a header file evaluated differently depending on whether you included it from a C\/C++ file or from a resource file.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Oddly different for a different audience.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-107928","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Oddly different for a different audience.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/107928","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=107928"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/107928\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=107928"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=107928"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=107928"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}