October 4th, 2017

The Resource Compiler’s preprocessor is not the same as the C preprocessor

A customer had a project written in Visual C++, and the Build step failed with an error from the Resource Compiler:

Fatal error RC1015: cannot open include file 'vcruntime.h'

For reasons known (or perhaps not) only to the original developer of the project, this particular RC file had an enormous number of #included header files, with many levels of nesting. Most of the header files employed the #pragma once directive to avoid multiple inclusion. But upon reading of the documentation for the Resource Compiler, it seems that the RC.EXE compiler doesn’t recognize #pragma once.

The customer was able to replicate the problem by creating a pair of mutually-including header files:

// file1.h
#include "file2.h"

// file2.h
#include "file1.h"

which produced the same “cannot open include file” error message.

From this investigation, we are fairly confident that this is the root cause of the original error message. If this had been a problem with include files arise in C++ code, we could have used the /showincludes command line switch to troubleshoot the problem, but the RC.EXE compiler does not provide any switches for diagnosing problems with include files.

The customer wanted us to confirm their conclusions, and also to indicate whether the behavior with RC.EXE is by design, or whether they should file a defect report.

The Resource Compiler’s preprocessor is not the same as the C preprocessor, even though they superficially resemble each other. In particular, #pragma is conspiciously missing from the table of supported preprocessor directives.

In order to get the effect of #pragma once in the Resource Compiler, you need to use the old-fashioned include guard technique.

Or better would be to avoid including so much junk. Many header files use #ifdef RC_INVOKED to detect whether they are being included by the Resource Compiler. When included by the Resource Compiler, they define only the identifiers needed for resource files and skip over all the other junk.

Bonus chatter: The Resource Compiler differs from the C preprocessor in another significant way: As noted on the same page that has the list of preprocessor directives, if a file has the extension .c or .h, then the Resource Compiler ignores all lines that aren’t preprocessor directives. If you want to include another file that has resource content, you need to give it another extension. Typically, that extensions is .rc, although you may find the extension .dlg in older code.

Bonus bonus chatter: The Resource Compiler has two expression evaluators. The one used by the preprocessor follows the C language rules. But the one used by the resource parser follows its own weird rules. For example, in resource files, you can say 3 | NOT 2, which is equivalent to 3 & ~2. It means that in resource files, the | operator is not symmetric!

Topics
Code

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

0 comments

Discussion are closed.