Build Windows message resources with windmc
You will turn a small Windows message compiler definition into a C header, an .rc resource script, a language-specific .bin file and, when needed, a debug include file. This guide uses the installed x86_64-w64-mingw32-windmc from Debian's binutils-mingw-w64-x86-64 package, version 2.41.90.20240122.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about 15 minutes if you already have an .mc file, or 30 minutes to adapt the example. No elevated privileges are needed. Work in a scratch or build directory: windmc creates and can replace generated files there.
1. Check the compiler and make a workspace
Confirm which executable is being used and record its version. The UCRT alias has the same installed manpage content and output contract, but the commands below use the canonical executable.
command -v x86_64-w64-mingw32-windmc
x86_64-w64-mingw32-windmc --version
mkdir -p build/messages/{headers,resources,debug}
cd build/messages
Expected version output begins like this:
GNU windmc (GNU Binutils) 2.41.90.20240122
Checkpoint
You are in a disposable build directory, and the command name resolves to the cross-toolchain you intend to use.
2. Create a minimal message definition
The input is an .mc file. Its declarations assign names and numeric values to severity and facility fields, map a language name to a language identifier, then define one or more messages. Save this as messages.mc:
MessageIdTypedef=DWORD
SeverityNames=(Success=0x0:STATUS_SEVERITY_SUCCESS)
FacilityNames=(System=0x0:FACILITY_SYSTEM)
LanguageNames=(English=0x409:MSG00409)
MessageId=0x0001
Severity=Success
Facility=System
SymbolicName=MSG_HELLO
Language=English
Hello from windmc.
.
This deliberately small definition is useful for checking the toolchain. In a real project, keep the message identifiers stable: compiled programs and logs may depend on them.
3. Generate the normal build inputs
Run windmc with separate destinations for headers and resources. The resource directory receives the generated .rc script and the language binary that it references.
x86_64-w64-mingw32-windmc \
--headerdir=headers \
--rcdir=resources \
messages.mc
Check the result before handing it to a compiler:
find headers resources -maxdepth 1 -type f -printf '%p\n' | sort
For the example, the list includes headers/messages.h, resources/messages.rc and resources/MSG00409.bin. The header contains a MSG_HELLO definition. The resource script names the language binary, so keep those generated files together.
Checkpoint
The header is for C or C++ code, the .rc file is for windres, and the .bin file carries the message text for the declared language.
4. Add a symbolic debug map when diagnostics need it
windmc does not create a .dbg file unless you request one. The --xdbg argument names its output directory in this installed build.
x86_64-w64-mingw32-windmc \
--headerdir=headers \
--rcdir=resources \
--xdbg=debug \
messages.mc
ls -l debug/messages.dbg
The debug include maps message identifiers to symbolic names. It is an optional build artefact, not a replacement for the header or resource script. If your build does not consume it, omit --xdbg.
5. Choose encoding deliberately
The default input is ASCII. The default input and output codepage is 1252, and binary messages default to UTF-16. Those defaults are easy to miss when a file contains non-ASCII text.
- Use
--unicode_inwhen the.mcinput is UTF-16. - Use
--codepage_in=CODEPAGEfor an input codepage that is not the default. - Use
--codepage_out=CODEPAGEfor generated text files that need a specific codepage. - Use
--unicode_outfor UTF-16 binary messages, or--ascii_outfor ASCII binary messages.
Do not select an encoding option merely because it sounds more portable. Make it match the bytes actually stored in the input and the consumer's documented expectation.
6. Control identifiers and binary details
Headers use hexadecimal constants by default. Add --decimal_values if generated headers must use decimal values. Add --customflag only when your message identifiers require the customer bit.
Binary message text is terminated by CR/LF by default. Use --nullterminate when the reader requires a zero terminator. --maxlength=CHARACTERS asks windmc to warn when a message exceeds the chosen length; it does not shorten the message.
When several inputs would otherwise produce colliding binary names, --binprefix prefixes the binary filename with the source basename. For a different header suffix, use --extension=EXTENSION.
7. Compile and diagnose without guessing
Pass the generated resource script to the matching resource compiler in your normal cross-build. First inspect its referenced binary path:
sed -n '1,80p' resources/messages.rc
x86_64-w64-mingw32-windmc --help | sed -n '1,80p'
The first command should show a MESSAGETABLE entry naming the generated language binary. If a resource compiler cannot find it, fix the build's working directory or copy the complete generated resource set; do not edit the generated script by hand.
For an input or option failure, rerun with --verbose. It reports the selected target and codepages, which often exposes an unexpected encoding or target. --target selects the BFD format for binary output; use a target listed by --help rather than inventing one.
Recovery
Generated files are build artefacts. If an experiment produces the wrong output, remove only the contents of this scratch build directory and rerun from the unchanged .mc source. Do not delete a shared source or release directory, and do not overwrite checked-in generated files until the diff has been reviewed.
Done means
--versionreports the expected windmc installation.- The command completes successfully from the intended build directory.
- The header, resource script and language binary exist in their planned destinations.
- The resource script references a binary that is present beside it.
- Encoding and termination options match the consumer, rather than relying on an accidental default.