Divergences from the Windows RC compiler
Table of contents
Intentional divergences from the Windows RC compiler
- In
resinator, using the number6as a resource type is an error and will fail to compile.- The Windows RC compiler allows the number
6(i.e.RT_STRING) to be specified as a resource type. When this happens, the Windows RC compiler will output a.resfile with a resource that has the format of a user-defined resource, but with the typeRT_STRING. The resulting.resfile is basically always invalid/bogus/unreadable, asSTRINGTABLE/RT_STRINGhas a very particular format.
- The Windows RC compiler allows the number
- In
resinator, embeddedNUL(<0x00>) characters are always illegal anywhere in a.rcfile.- The Windows RC compiler behaves very strangely when embedded
NULcharacters are in a.rcfile. For example,1 RCDATA { "a<0x00>" }will give the error “unexpected end of file in string literal”, but1 RCDATA { "<0x00>" }will “successfully” compile and result in an empty.resfile (theRCDATAresource won’t be included at all). Even stranger, whitespace seems to matter in terms of when it will error; if you add a space to the beginning of the1 RCDATA { "a<0x00>" }version then it “successfully” compiles but also results in an empty.res.- TODO: This might be related to the Windows RC compiler’s handling/inferring of UTF-16 encoded files, which
resinatordoesn’t handle yet.
- TODO: This might be related to the Windows RC compiler’s handling/inferring of UTF-16 encoded files, which
- The Windows RC compiler behaves very strangely when embedded
- In
resinator, embedded ‘End of Transmission’ (<0x04>) characters are always illegal outside of string literals.- The Windows RC compiler seemingly treats
<0x04>characters outside of string literals as a ‘skip the next character’ instruction when parsing, i.e.RCDATA<0x04>xgets parsed as if it wereRCDATA. It’s possible to emulate this behavior inresinator, but it seems unlikely that any real.rcfiles are intentionally using this behavior, so by making it a compile error it avoids running into strange behavior if a<0x04>character is ever inserted into a.rcfile accidentally.
- The Windows RC compiler seemingly treats
- In
resinator, embedded ‘Delete’ (<0x7F>) characters are always illegal anywhere in a.rcfile.- The Windows RC compiler seemingly treats
<0x7F>characters as a terminator in some capacity. A few examples:1 RC<0x7F>DATA {}gets parsed as1 RC DATA {}<0x7F>1 RCDATA {}“succeeds” but results in an empty.resfile (no RCDATA resource)1 RCDATA { "<0x7F>" }fails withunexpected end of file in string literal
- It’s possible to emulate this behavior in
resinator, but it seems unlikely that any real.rcfiles are intentionally using this behavior, so by making it a compile error it avoids running into strange behavior if a<0x7F>character is ever inserted into a.rcfile accidentally.
- The Windows RC compiler seemingly treats
- In
resinator, embedded ‘Substitute’ (<0x1A>) characters are always illegal anywhere in a.rcfile. Note: The preprocessor treats it as an ‘end of file’ marker so instead of getting an error it will likely end up as an EOF error. This would change ifresinatoruses a custom preprocessor.- The Windows RC compiler treats
<0x1A>characters as an ‘end of file’ maker but it can also lead to (presumed) infinite loops. For example,1 MENUEX FIXED<0x1A>VERSIONwill cause the Win32 implementation to hang, but1 RCDATA {} <0x1A> 2 RCDATA {}will succeed but only the1 RCDATA {}resource will make it into the.res.
- The Windows RC compiler treats
- In
resinator, the sequence\"within a string literal is an error, noting that""should be used to escape quotes within string literals.- The Windows RC compiler is super permissive in what input it will accept, but in this case it is overly so. For example, if you have
1 RCDATA { "\""BLAH" }with#define BLAH 2elsewhere in the file:- A preprocessor would treat the
\"as an escaped quote and parse the part after the{as:"\"",BLAH," }; it would then replaceBLAHwith2since it thinks it’s outside of a string literal (note: the preprocessor would also normally result in amissing terminating '"' characterwarning since the" }string is never closed; in the Windows RC compiler this warning is either not emitted or not shown to the user). - The RC compiler would then get the resulting
1 RCDATA { "\""2" }and parse the part after the{as:"\""2",}, since in.rcstring literals,""is an escaped quote, not\". In the Windows RC compiler,\"is a weird special case in which it both treats the\as an escape character (in that it doesn’t appear in the parsed string), but doesn’t actually escape the"(note that other invalid escapes like e.g.\kend up with both the\andkin the parsed string).
- A preprocessor would treat the
- The fact that
\"makes it possible for macro expansion to silently happen within what the RC compiler thinks are string literals is enough of a footgun that it makes sense to make it an error instead. Note also that it can lead to really bizarre edge cases/compile errors when, for example, particular control characters follow a\""sequence in a string literal.
- The Windows RC compiler is super permissive in what input it will accept, but in this case it is overly so. For example, if you have
- In
resinator, trying to use a raw data block with resource types that don’t support raw data is an error, noting that if{orBEGINis intended as a filename, it should use a quoted string literal.- The Windows RC compiler will instead try to interpret
{/BEGINas a filename in these cases, which is extremely likely to fail and (if it succeeds) is almost certainly not what the user intended.
- The Windows RC compiler will instead try to interpret
- In
resinator, trying to use a number expression (e.g.(1+1)) as a filename is an error, noting that a quoted string literal should be used instead. Singular number literals are allowed, though (e.g.-1).- The Windows RC compiler will allow any number expression as a filename, but evaluate it such that it is almost certainly not what the user intended–it will take the last number literal in the expression and treat that as the filename. Examples:
(1+1)->1,1+-1->-1.
- The Windows RC compiler will allow any number expression as a filename, but evaluate it such that it is almost certainly not what the user intended–it will take the last number literal in the expression and treat that as the filename. Examples:
- In
resinator, the byte order mark (<U+FEFF>) is always illegal anywhere in a.rcfile except the very start.- For the most part, the Windows RC compiler skips over BOMs everywhere, even within string literals, within names, etc [e.g.
RC<U+FEFF>DATAwill compile as if it wereRCDATA]). However, there are edge cases where a BOM will cause cryptic ‘compiler limit : macro definition too big’ errors (e.g.1<U+FEFF>1as a number literal).- The use-case of BOM outside of the start of the file seems extremely minimal/zero, so emulating the Windows RC behavior doesn’t seem worth the added complexity.
- The potentially unexpected behavior of the BOM bytes missing from the compiled
.resseems worth avoiding (e.g. a string literal with the contents"<U+FEFF>"would be compiled as if it were an empty string).
- For the most part, the Windows RC compiler skips over BOMs everywhere, even within string literals, within names, etc [e.g.
- In
resinator, the private use character<U+E000>is always illegal anywhere in a.rcfile.- This behaves similarly to the byte order mark (it gets skipped/ignored wherever it is), so the same reasoning applies (although
<U+E000>seems to avoid causing errors like the BOM does).
- This behaves similarly to the byte order mark (it gets skipped/ignored wherever it is), so the same reasoning applies (although
- In
resinator, control characters specified as a quoted string with a^in anACCELERATORSresource (e.g."^C") must be in the range ofA-Z(case insensitive).- The Windows RC compiler’s error hints that this is the intended behavior (
control character out of range [^A - ^Z]), but it actually allows for a large number of codepoints >= 0x80 to be used. Of those allowed, it treats them as if they wereA-Zand subtracts0x40from the codepoint to convert it to a ‘control character’, but for arbitrary non-ASCII codepoints that just leads to garbage. The codepoints that are allowed may be based on some sort of Unicode-aware ‘is character’ function or something, but I couldn’t really find a pattern to it. The full list of codepoints that trigger the error can be found here.
- The Windows RC compiler’s error hints that this is the intended behavior (
- In
resinator, the codepointsU+0900,U+0A00,U+0A0D,U+2000,U+FFFE, andU+0D00are illegal outside of string literals, and emit a warning if used inside string literals- The Windows RC compiler will error and/or ignore these codepoints when used outside of string literals, but not always. When used within string literals, the Windows RC compiler will miscompile them (either swap the bytes of the UTF-16 code unit in the
.res, omit it altogether, or some other strange interaction).
- The Windows RC compiler will error and/or ignore these codepoints when used outside of string literals, but not always. When used within string literals, the Windows RC compiler will miscompile them (either swap the bytes of the UTF-16 code unit in the
resinatorwill avoid a miscompilation regarding padding bytes after ‘extra data’ in DIALOG controls, and will emit a warning when it detects that the Windows RC compiler would miscompile- The Windows RC compiler will erroneously add too many padding bytes after the ‘extra data’ section of a DIALOG control if the data ends on an odd offset. This is a miscompilation that results in the subsequent dialog control not to be DWORD aligned, and will likely cause the dialog to be unusable (due to parse errors during dialog initialization at program runtime).
- As far as I can tell, there is no actual use-case for this extra data on controls in a templated DIALOG, as the docs say that “When a dialog is created, and a control in that dialog which has control-specific data is created, a pointer to that data is passed into the control’s window procedure through the lParam of the WM_CREATE message for that control”, but
WM_CREATEis not sent for dialogs (instead onlyWM_INITDIALOGis sent after all of the controls have been created).
- As far as I can tell, there is no actual use-case for this extra data on controls in a templated DIALOG, as the docs say that “When a dialog is created, and a control in that dialog which has control-specific data is created, a pointer to that data is passed into the control’s window procedure through the lParam of the WM_CREATE message for that control”, but
- The Windows RC compiler will erroneously add too many padding bytes after the ‘extra data’ section of a DIALOG control if the data ends on an odd offset. This is a miscompilation that results in the subsequent dialog control not to be DWORD aligned, and will likely cause the dialog to be unusable (due to parse errors during dialog initialization at program runtime).
resinatorwill avoid a miscompilation when a generic CONTROL has its control class specified as a number, and will emit a warning- The Windows RC compiler will incorrectly encode control classes specified as numbers, seemingly using some behavior that might be left over from the 16-bit RC compiler. As far as I can tell, it will always output an unusable dialog template if a CONTROL’s class is specified as a number.
resinatorwill avoid a miscompilation when aVALUEwithin aVERSIONINFOhas the comma between its key and its first value omitted (but only if the value is a quoted string), and will emit a warning- The Windows RC compiler will fail to add padding to get to
DWORD-alignment before the value and sometimes step on the null-terminator of theVALUE’s key string.
- The Windows RC compiler will fail to add padding to get to
resinatorwill avoid a miscompilation when aVALUEwithin aVERSIONINFOmixes numbers and strings, and will emit a warning- The Windows RC compiler will miscompile the byte count of values that have a mix of numbers and strings, as it will use UTF-16 code units as the ‘byte count’ for all of the string values, leading to an incorrect size value being written to the .res.
- Note that there are further miscompilations with a mix of numbers and strings that
resinatoravoids but does not have specific warnings for:VALUE "key", 65535L "a"will compile into0xFFFFand"a\x00"instead of the expected0x0000FFFFand"a\x00, but either (1) adding a comma after65535Lor (2) bumping up the value to be larger than u16 max (e.g.65538L) will cause it to be compiled into au32.VALUE "key", 65538L "a"has a ‘byte count’ of 5 in the compiled.reswhileVALUE "key", 65538L, "a"(added comma after65538L) has a ‘byte count’ of 6 in the compiled.res(note: both of these byte counts are incorrect as per the main miscompilation above; the accurate byte count would be 8 [4 for theu32and 4 for the string–2 for the'a'and 2 for the null terminator])
resinatorsupportsNOTexpressions only inSTYLEstatements,EXSTYLEstatements, andstyle/exstyleparameters, and theNOTmust immediately precede a number literal (as opposed to a number expression–e.g.NOT 1works butNOT (1)does not)- There’s a lot of things about the Windows RC compiler’s implementation of the
NOTexpression that I either don’t understand or don’t think is worth emulating:- The Windows RC compiler allows the use of
NOTexpressions in places where it doesn’t make sense (thex,y, etc parameters ofDIALOGEXfor example) - The parsing rules of
NOTexpressions change depending on the particular parameter they are being used in, e.g.1 | NOT 2is an error if used in thetypeparameter of aMENUEX’sMENUITEM, but perfectly fine if used in thestyleorexstyleparameters of aDIALOG/DIALOGEXcontrol. - The parsing rules of
NOTexpressions are generally bizarre and the Windows RC compiler both allows for seemingly nonsensical expressions and evaluates seemingly normal expressions in nonsensical ways. For example:7 NOT NOT 4 NOT 2 NOT NOT 1,NOT (1 | 2), andNOT () 2are all allowed and all evaluate to2
- The Windows RC compiler allows the use of
- There’s a lot of things about the Windows RC compiler’s implementation of the
resinatorwill error if the resource type (ICON/CURSOR) doesn’t match with the resource type specified in the.ico/.curfile- The Win32 RC compiler will happily compile such a mismatch, but the resulting
.reswill always fail to load theCURSOR/ICONat runtime, since it will compile the data as if it were the type specified in the file, but compile the resource as if it were the type specified in the.rcscript.
- The Win32 RC compiler will happily compile such a mismatch, but the resulting
resinatorwill allowPNGencoded icons withinCURSORresource groups- The Win32 RC compiler will fail to compile such cursors, but there doesn’t seem to be any reason why that is the case as far as I can tell–they can be loaded at runtime just fine in my (albeit limited) testing. Note also that
PNGencoded icons are allowed withinICONresources by the Win32 RC compiler, andCURSORresource groups have the same format.
- The Win32 RC compiler will fail to compile such cursors, but there doesn’t seem to be any reason why that is the case as far as I can tell–they can be loaded at runtime just fine in my (albeit limited) testing. Note also that
- [Not final, somewhat likely to change]
resinatorwill allowRIFFencoded animated icons withinICON(but notCURSOR) resource groups- The Win32 RC compiler will fail to compile such icons, but there seems to be some support for loading them at runtime if they are written to the
.res; however, they don’t work everywhere. This needs to be looked into more, but most likely this will just be made into a compile error since it’s fully unknown territory–there’s nothing anywhere that mentions combiningRIFFanimated icons andRT_GROUP_ICON.
- The Win32 RC compiler will fail to compile such icons, but there seems to be some support for loading them at runtime if they are written to the
resinatorwill error if anICON/CURSORhas an image with a data size that is impossibly large- The Win32 RC compiler takes the image’s reported data size at face value, even if the data size is larger than the size of the entire
.ico/.curfile. When the reported size extends past the EOF, it will still write the full size to the .res file, but everything past the EOF will be zeroes and then that pattern will repeat every 0x4000 bytes (the file contents again, zeroes until the next 0x4000 byte boundary, and so on). This means that a malicious.ico/.curfile could result in essentially unbounded.resfilesize (each image within the resource group can report its data size as up to 4GB, and there can be 65535 images in a resource group).- There is a second bug which makes the Win32 RC compiler infinitely loop until all disk space has been exhausted: the data sizes of images are seemingly interpreted as signed integers, and values that it interprets as negative will (AFAICT) be treated as infinite. When using the
/vflag it will output something likeWriting ICON:1, lang:0x409, size -6000000and then keep writing to the.resfile well beyond the limit of au32.
- There is a second bug which makes the Win32 RC compiler infinitely loop until all disk space has been exhausted: the data sizes of images are seemingly interpreted as signed integers, and values that it interprets as negative will (AFAICT) be treated as infinite. When using the
- The Win32 RC compiler takes the image’s reported data size at face value, even if the data size is larger than the size of the entire
resinatorwill error if anICON/CURSORhas an image with a data size that is impossibly small- If the reported data size of an image is zero, the Win32 RC compiler will miscompile and make up a bogus data size when writing the resource’s
.resdata, but while also not including any actual data bytes (making the.resunusable) - If the reported data size of an image is smaller than the header of a bitmap/PNG, then the Win32 RC compiler will both use the reported size but also read the full header of the image and use it to inform the values of the resource (e.g. it will detect a PNG and set num_planes to 1).
- By always erroring on sizes that can’t possibly hold a real image,
resinatoravoids both of these behaviors that always lead to invalid.resfiles.
- If the reported data size of an image is zero, the Win32 RC compiler will miscompile and make up a bogus data size when writing the resource’s
resinatorwill allow bitmapICON/CURSORimages with versions other thanWindows NT, 3.1x (BITMAPINFOHEADER)(e.g.BITMAPV5HEADER, unknown versions, etc), and will emit a warning- The Win32 RC compiler is strict about the DIB bitmap version it allows, and errors on anything that’s not
Windows NT, 3.1x (BITMAPINFOHEADER). This needs to be tested, but my hunch is that this limitation is artificial, and that if newer bitmap formats were included in a.resthat they would be able to be loaded at runtime just fine.
- The Win32 RC compiler is strict about the DIB bitmap version it allows, and errors on anything that’s not
resinatorwill error if there are any missing bytes in the color table ofBITMAPresources- The Win32 RC compiler will fill missing color palette bytes with zeroes if the size of the color palette is specified to be larger than it actually is in the file, and it will read into the pixel data and use that until it reaches EOF (at which point it will pad with zeroes). Such bitmaps are invalid anyway, so there’s no benefit to this behavior.
- The Win32 RC compiler has no limit of the amount of missing padding bytes it will add, and will either give an out-of-memory error or just unconditionally write huge amounts of zeroes to the color palette (e.g. if the bit depth of the
.bmpis 32 and the number of colors used is set to200,000,000, thennum_colors * 4or800,000,000bytes will be written to the color palette). Such large amounts of colors are extremely likely be indicative of a malformed bitmap, as bit depths 16, 24, and 32 only use color tables for optimization purposes (“The bmiColors color table is used for optimizing colors used on palette-based devices, and must contain the number of entries specified by the biClrUsed member of the BITMAPINFOHEADER.”). - Note: Enforcing this avoids malformed bitmaps potentially inducing really large
.resfiles
resinatorwill error onBITMAPresources that contain more colors than their bit depth allows (2^n, e.g. bit depth 4 can have a maximum of 16 colors, bit depth 8 can have a maximum of 256 colors, etc)- The Win32 RC compiler does not enforce this, but such bitmaps are definitely malformed. Even for bit depths >= 16 where ‘[the] bmiColors color table is used for optimizing colors used on palette-based devices’, it still wouldn’t make sense for the number of colors in the palette to exceed the possible number of colors used by the pixel data.
- TODO: revisit this; bmpsuite says that such bitmaps ‘may be invalid’, see
q/pal8oversizepal.bmp
resinatorwill always error if the code page specified in a#pragma code_pagedirective would overflow au32.- The Win32 RC compiler has different behavior depending on the particular value, mostly erroring with the expected ‘Codepage not valid’, but occasionally something more exotic (
Codepage not integer: ),MultiByteToWideChar failed.,constant too big, etc)
- The Win32 RC compiler has different behavior depending on the particular value, mostly erroring with the expected ‘Codepage not valid’, but occasionally something more exotic (
resinatorwill error if any expression is a single unquoted)character.- The Win32 RC compiler treats a single
)as a ‘valid’ expression that essentially evaluates to something akin to a ‘skip this’ instruction when parsing.- When used as a filename it causes strange behavior where it parses as if it were the filename but then it uses the preceding token when actually doing the filename lookup. For example,
1 RCDATA )will give the errorfile not found: RCDATArather than the expectedfile not found: ). - When used within a raw data block, it will just skip it as if it wasn’t there at all. For example,
1 RCDATA { 1, ), 2 }will be treated as if it were1 RCDATA { 1, 2 }.
- When used as a filename it causes strange behavior where it parses as if it were the filename but then it uses the preceding token when actually doing the filename lookup. For example,
- Note: The Win32 behavior is not emulated because it very likely has many unexplored edge cases that have very bizarre behavior, and it’s very unlikely that (1) this is anything but a bug in the Win32 implementation and (2) there are any valid use-cases of this bug
- The Win32 RC compiler treats a single
resinatorwill error if a resource’s evaluated filename contains aNUL(<0x00>) character.- The Win32 RC compiler will treat the
NULcharacter as a terminator (e.g.1 RCDATA "hello\x00world"will look for a file namedhello), but that behavior seems unlikely to be useful and worth disallowing.
- The Win32 RC compiler will treat the
resinatorwill error whenever a unary + operator is used, and emit a note about unary + not being supported inresinator’s implementation.- The Win32 RC compiler allows
+as a unary operator in some places but not others. In things like raw data blocks, it gives an error likeunexpected value in RCDATA. InDIALOGparameters, it does not always give an error (e.g.+1is allowed but(+1)gives an error).
- The Win32 RC compiler allows
resinatorwill avoid erroneously skipping over tokens after thestyleparameter of aCONTROLwithinDIALOG/DIALOGEXresources if thestyleparameter does not have a comma after it, and will emit a warning.- The Win32 RC compiler allows an extra expression after the
styleparameter ofCONTROLs withinDIALOG/DIALOGEXresources, but only if there is no comma separating them. Example:CONTROL "text", 1, BUTTON, 15 30, 1, 2, 3, 4; the15is the style parameter and the30is completely ignored (i.e. the1, 2, 3, 4arex,y,w,h).- The extra expression can be various things (quoted string,
=, etc) but not anything, e.g. if it’s;then it will error withexpected numerical dialog constant - If the extra expression is a number expression it will no longer ignore it but will behave strangely, e.g. if it is
(30+5)then5will be used as thexparameter
- The extra expression can be various things (quoted string,
- The Win32 RC compiler allows an extra expression after the
resinatorwill allow parameters that are limited to au16to contain numbers withLsuffixes, but will truncate the value to au16and emit a warning about the Win32 behavior.- The Win32 RC compiler will error with something like
version WORDs separated by commas expectedorPRIMARY LANGUAGE ID too largeif any number literal within such a parameter is evaluated to have anl/Lsuffix (since in theory that tells the compiler to use au32for that number). Note, though, that it doesn’t complain about numbers that overflow au16and uses wrapping overflow as is common everywhere else. - Statements that this affects all parameters of:
PRODUCTVERSION,FILEVERSION,LANGUAGE
- The Win32 RC compiler will error with something like
resinatorwill always write the ‘device name’ and ‘face name’ ofFONTDIRENTRYs within theFONTDIRresource as zero-length strings (whenFONTresources are present in the.rcfile)resinatorwill avoid miscompiling theCLASSof aDIALOG/DIALOGEXresource when multipleCLASSstatements are specified for a dialog resource, and will emit a warning.- The Win32 RC compiler will skip every
CLASSstatement except the last, but if any of the precedingCLASSstatements is a number or number expression, then the lastCLASSis treated as a number no matter what. For example, if there are twoCLASSstatements and one isCLASS 5and the last isCLASS "forced ordinal", then the Win32 RC compiler will interpret"forced ordinal"as a number and the value will end up as the ordinal 62790.
- The Win32 RC compiler will skip every
resinatorwill avoid miscompiling theMENUof aDIALOG/DIALOGEXresource when multipleMENUstatements are specified for a dialog resource, and will emit a warning- The Win32 RC compiler will skip every
MENUstatement except the last, but if any of the precedingMENUstatements is treated as a number, then the lastMENUis treated as a number no matter what. For example, if there are twoMENUstatements and one isMENU 5and the last isMENU forcedordinal, then the Win32 RC compiler will interpretforcedordinalas a number and the value will end up as the ordinal 36934.
- The Win32 RC compiler will skip every
resinatorwill avoid miscompiling theMENUof aDIALOG/DIALOGEXresource when the first character is a digit, and will emit a warning- The Win32 RC compiler treats any
MENUid starting with a digit as a number, even though that is not the case when evaluating the ID of aMENU/MENUEXresource which theMENUparameter of aDIALOG/DIALOGEXresource is meant to refer to. This means that if there is aMENUdefined as1ABC MENU { ... }and aDIALOG/DIALOGEXwith aMENUparameter set toMENU 1ABC, theMENUresource will have the name1ABC, but the dialog’sMENUwill be treated as a number and have the value 2899. At runtime, this will cause the dialog to fail to load with the errorThe specified resource name cannot be found in the image file.
- The Win32 RC compiler treats any
resinatorwill error if non-ASCII digit characters are used in a number literal or resource id/type ordinal- The Win32 RC compiler allows non-ASCII digit characters in base 10 number literals and subtracts the value of the ASCII
'0'character from their codepoint value to get their ‘digit’ value, which leads to totally arbitrary results (e.g. the character²has a codepoint of178, and178 - '0' = 130, so a number literal like1²3would end up as the number value1403when evaluated). This is the case for any UTF-16 code unit for which the Windows implementation ofiswdigitreturns true.
- The Win32 RC compiler allows non-ASCII digit characters in base 10 number literals and subtracts the value of the ASCII
Resource data and .res filesize limits
resinatorwill error if a resource’s data length exceeds the max of au32, since the header of the resource needs to be able to specify its data length as au32.- The Win32 RC compiler will
fatal error RW1023: I/O error seeking in fileif the resulting.resfilesize ever exceeds 2GiB (2,147,483,648 bytes). This indirectly limits the size of individual resources; the largest possible resource can be slightly smaller than 2GiB if it’s the only resource in the.res(slightly smaller than 2GiB to allow for the resource headers, etc).
- The Win32 RC compiler will
resinatorwill error if aVERSIONINFOresource contains a node tree that is larger than the max of au16, since the root node needs to be able to specify its byte length (inclusive of all children) as au16.- The Win32 RC compiler will not error and instead the node’s byte length will overflow and wrap back around to 0. This leads to an invalid version node tree.
resinatorwill error if aDIALOG/DIALOGEXresource contains more controls than the max of au16, since the dialog needs to be able to specify its number of controls as au16.- The Win32 RC compiler will not error and instead the number of controls will overflow and wrap back around to 0. This leads to an incorrect dialog resource.
resinatorwill error if a control within aDIALOG/DIALOGEXresource contains more extra data than the max of au16, since the dialog needs to be able to specify the number of extra data bytes of each control as au16.- The Win32 RC compiler will not error and instead the extra data length of the control will overflow and wrap back around to 0. This leads to an incorrect dialog resource.
Unavoidable divergences from the Windows RC compiler
resinatordoes not support UTF-16 encoded.rcfiles.- The Aro preprocessor does not handle UTF-16 encoded files (i.e. it will fail to parse
#includedirectives, etc within UTF-16 encoded files). So,resinatorwould need a preprocessor that handles UTF-16 encoded files for UTF-16 support to be feasible.
- The Aro preprocessor does not handle UTF-16 encoded files (i.e. it will fail to parse
- In
resinator, splices (\at the end of a line) are removed by the preprocessor before checking if any string literals are too long.- The Windows RC compiler includes the splice characters in the string literal length check (even though they don’t show up in the string literal).
- In
resinator, embedded ‘carriage return’ characters (that are not part of aCRLFpair) can lead to files being parsed differently than the Windows RC compiler would parse them.- The Aro preprocessor treats carriage return characters (
'\r') as a line separator when unpaired, and always converts them to new lines ('\n'). The Windows RC tool instead seemingly ignores/skips all\rcharacters. - For example,
RC<\r>DATAwill be compiled by the Windows RC tool as if it wereRCDATA, but Aro’s preprocessor will convert it toRC<\n>DATAwhichresinatorwill parse as separateRCandDATAtokens.
- The Aro preprocessor treats carriage return characters (
.rcfiles that use splices (\at the end of a line) within strings that include whitespace after the splice will be handled differently.- The Win32 RC compiler’s preprocessor seems to collapse whitespace after a splice that’s within a string, while Aro’s preprocessor does not. An example of a file for which this behavior difference can be reproduced can be found here.
Found divergences that haven’t been decided on yet
- The Win32 RC compiler allows unclosed parentheses in certain locations. Examples:
1 DIALOGEX 1( 2, 3, 4 {},1 DIALOGEX 1,(2, 3, 4 {}, and many more.- This only happens with some things, e.g.
1 RCDATA { 1,(2, 3, 4 }will error withmismatched parentheses. - Current thoughts: Seems like a bug in the Win32 implementation to not error with
mismatched parentheses. Don’t really see a reason to be bug-for-bug compatible with this one. - Current
resinatorbehavior:error: expected ')', got ','
- This only happens with some things, e.g.
- The Win32 RC compiler allows the
styleparameter ofCONTROLs withinDIALOG/DIALOGEXresources to be specified as things like a quoted string, the character=, or even control class keywords (EDIT,BUTTON, etc) which are then always evaluated as0. Example:CONTROL "text", 1, BUTTON, "50", 1, 2, 3, 4; the"50"is the style parameter and gets written to the.resas0x00000000.- This does not apply to
x,y,w, orh: Win32 compiler errors withexpected numerical dialog constantif they are specified as a quoted string - This does not apply to
exstyleorhelpid, which seems to make it parse differently and gives errors likeinvalid control type,END expected in dialog. - This does not apply to non-
CONTROLdialog statements, if e.g."50"is specified as thestyleparameter of aCHECKBOXthen it behaves likeexstyleorhelpidin the previous bullet point. - Current thoughts: Seems like a bug in the Win32 implementation to accept quoted strings here. Don’t really see a reason to be bug-for-bug compatible with this one.
- Current
resinatorbehavior:error: expected number or number expression; got '"50"'
- This does not apply to
- The Win32 RC compiler will sometimes error if subtraction is performed with the right-hand-side evaluating to 0. Example:
1 DIALOGEX 1, 2, 3, 4-0 {}will error withBEGIN expected in dialog(note: the lack of whitespace between the two operands seems to matter). Such expressions never seem to be errors within raw data blocks (e.g.1 RCDATA { 4-0 }works fine).- Some more expressions that error:
4-0x0,(4-0) - Some allowed expressions:
4 - 0,100--0,4-(0) - Some insight into the ramifications:
1 DIALOGEX 1, 2, 3, 10-0x0+5 {} hellowill error withfile not found: helloand the verbose output will showWriting {}:+5, meaning it is interpreting the+5 {} helloafter the10-0x0as a new resource with a name of+5and a resource type of{}. - Current thoughts: Ideally,
resinatorshould successfully parse/compile such expressions, but emit a warning that the Win32 RC compiler would fail to compile them. This is easier said than done, though, since the conditions for when the error would occur are tough to pin down or understand. - Current
resinatorbehavior: Successful compilation, e.g.1 DIALOGEX 1, 2, 3, 4-0 {}compiles as if it were1 DIALOGEX 1, 2, 3, 4 {}
- Some more expressions that error:
- The Win32 RC compiler will error when preprocessor errors occur within areas that the preprocessor thinks is outside of a string literal but the RC parser would think is inside of a string literal
- For example, the character
@used outside of a string literal will cause a Win32 RC preprocessor error. If a string literal spans multiple lines without the use of splices (something that the docs say is disallowed but is allowed empirically) and a@(or another character that causes a preprocessor error) appears on a subsequent line, then the Win32 preprocessor will still error even though it would be interpreted as inside a string literal by the Win32 RC parser. - Current thoughts:
resinatoremulates this behavior at lex-time instead of during the preprocessing step. This might make the error message a bit confusing but I think this particular edge case is rare enough that it shouldn’t matter too much. - Current
resinatorbehavior:error: character '@' is not allowed outside of string literals
- For example, the character