How to correctly translate an error code into an error
message
By George Mihaescu
Summary: this is a quick programming tip describing
the correct way to translate the error code of a function call (either Windows
API or user-function) into a message that can be displayed to the user or
logged.
The Problem
You have a Win32 application in which an error code returned
by a function must be translated into a user-friendly text message (e.g. for
display or logging purposes).
The Solution
Use the FormatMessage Win32 API. This function works
with both the Win32 error codes, and error codes from your own functions –
however the parameters passed to the function are slightly different in each
case.
When you need to get the message corresponding to a system
(Win32) error code, use the following:
//get the error code from the last
function call
LONG err = ::GetLastError ();
//translate the error code into a
message
void* p_text;
DWORD count = ::FormatMessage
(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM
|
FORMAT_MESSAGE_MAX_WIDTH_MASK,
NULL,
err,
MAKELANGID
(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)
&p_text,
0,
NULL);
//...display / log the message in p_text
buffer here
//free buffer
::LocalFree (p_text);
When you need to get the message corresponding to one of
your own functions, replace the flag FORMAT_MESSAGE_FROM_SYSTEM in the first parameter with
the flag FORMAT_MESSAGE_FROM_HMODULE
and the second parameter from NULL to an actual handle (HMODULE) of the module
that contains the message table with the error messages. This handle may be
kept NULL if the error messages are contained within the calling process
application file.
Note that the message table with your own error message is
not the same with a string table resource. The message table is a special type
of resource that must be added to the resources of a module using the Microsoft
Message Compiler (mc.exe). Use the following steps to create a MESSAGETABLE resource:
1. Create a text
file (e.g. MyMessages.mc) with the actual messages corresponding to error codes
in your code. For example:
; // MyMessages.mc
; // Header defining the Severities, Facilities and Languages
MessageIdTypedef=LONG
SeverityNames=
(
Success=0x0:STATUS_SEVERITY_SUCCESS
Informational=0x1:STATUS_SEVERITY_INFORMATIONAL
Warning=0x2:STATUS_SEVERITY_WARNING
Error=0x3:STATUS_SEVERITY_ERROR
)
FacilityNames=
(
Application=0x0:FACILITY_APPLICATION
Libraries=0x1:FACILITY_LIBRARIES
Network=0x2:FACILITY_NETWORK
)
LanguageNames=(English=0x409:MSG00409)
; //Add other language definitions here
; //Actual message definitions (add Language=...,
; //then the text on next line, then . on the next line for
; //translations in other languages
MessageId=0x1
Severity=Error
Facility=Application
SymbolicName=GM_APP_MSG_OPERATION_FAILED
Language=English
The operation you have chosen failed.
.
2. Compile this
file through the message compiler:
mc.exe -U MyMessages.mc
If your .mc file is Unicode, also add the –u switch to the mc.exe command line.
The –U switch forces the output to be Unicode (even is the .mc file used as
input is not).
This produces the files: MyMessages.h, MyMessages.rc, MSG00409.bin (there may
be more .bin files, one for each language defined in the header of the .mc text
file).
The header file (MyMessages.h) defines the symbolic constants for the error
codes (so that you can include this header in you code and refer to the error
codes as symbolic constants – unfortunately they are defined as macros so you
may run into collisions, so use symbolic names that reduce that chance in the
.mc file, e.g.: SymbolicName=GM_APP_MSG_OPERATION_FAILED.
The .bin files are binaries containing the compiled text messages in a
particular language (there is one .bin file per each language defined in the
header of the .mc file).
The .rc file is a standard resource file that must be included in the .rc file
of the module you want to contain the error messages. It simply forces the .rc
file to include the .bin file(s) with the compiled messages.
3. Include the .rc
file resulted from above into the module’s .rc file:
#include "MyMessages.rc"
4. Use code similar
to the one below to retrieve your message:
//user-defined error code
LONG err = GM_APP_MSG_OPERATION_FAILED;
void* p_text;
DWORD count = ::FormatMessage
(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_HMODULE
|
FORMAT_MESSAGE_MAX_WIDTH_MASK,
NULL,
err,
MAKELANGID
(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)
&p_text,
0,
NULL);
//...display / log the message in p_text
buffer here
//free buffer
::LocalFree
(p_text);