Click here to Skip to main content
15,891,943 members
Articles / Programming Languages / C++

Do Not Call GetLastError() !

Rate me:
Please Sign up or sign in to vote.
4.45/5 (22 votes)
27 Mar 2001CPOL 203.3K   3K   52   29
Use CWin32Error instead !! It encapsulates WIN32 error message strings with the error numbers. Has a smart-copy mechanism for passing objects by value, useful in exception-handling. Is TRACE() compatible!

Motivation

In your last project, how many times did you read the following in the MSDN help files:

If the function fails, the return value is NULL (or FALSE, or 0, or -1, or INVALID_HANDLE_VALUE).
To get extended error information, call GetLastError.

Then, while debugging, how many times did you:

  1. copy the current value of the error code from the variables window
  2. launch the Error Lookup tool
  3. paste the contents
  4. and hit a button on the tool

All of this just to see what the meaning of that error number was?

Or, how many times did you separately look up the FormatMessage documentation on MSDN (took time to swap the CDs too! :) ), just to copy and paste the code for retrieving error messages into your project?

Then, how many times did you wonder if it was worth introducing all those lines calling FormatMessage right in the middle of the main logic of your app?

Or, how many times did you recall the discussions of exceptions vs error codes?

Or, how many times did you have to explain the meaning of your own, home-grown, error strings to your marketing folks?

Why, coming to MFC, you even have a CPoint class, and a CSize class, but no CWin32Error class!!

That was so, until now! :)

CWin32Error Summary

A C++ class with a very small footprint that encapsulates both the WIN32 error-message strings and the error-code numbers returned by GetLastError(). Employs a smart-copy mechanism for minimal overheads while passing or returning objects by value. Provides DWORD and const char* conversions. Objects can also be sent to the TRACE() macro directly!

CWin32Error Sample Usage

Example 1: Exception Handling

void MyFunction( void ) throw CWin32Error
{
    if(! SomeWin32API())
    {
        // ::GetLastError() and ::FormatMessage()
        // automatically get called during construction.
        // Catch by value or by ref--the code stays the same.
        // Smart copying means you can rethrow the object by 
        // value as many times as you like!
        throw CWin32Error(); 
    }
}

void ClientToMyFunction( void )
{
    try
    {
        MyFunction();
    }
    catch( CWin32Error e ) // catching by value is OK (smart copying)
    {
        // Note: Automatic conversion to const TCHAR* type.
        ::OutputDebugTrace( e ); 
    }
}

Example 2: "Vanilla" Usage (No exception-handling)

The following code shows how a function can return a CWin32Error object. Smart copy means you can return objects even during the normal course of execution, without having to take an undue performance hit.

CWin32Error MyFunction( void ) 
{
    if( ! SomeWin32API() )
    {
        // Constructor gets last error and keeps the text 
        // of the error message ready.
        CWin32Error e; 

        // Don't have to call a member function even in 
        // MFC trace macro [i.e. no e.c_str() or (const char*) e]
        TRACE( "%s\n", e );

        return e;
    }
    // In Win32 API, the '0' value corresponds to the 
    // error string: "Operation completed successfully"
    // The error string is automatically taken from the OS 
    // in the returned CWin32Error object.

    return 0; // ctor taking unsigned int called here
}

Example 3: Simplest: As a message formatting class

The code given below shows how you can use CWin32Error purely to make the source-code look pretty!

void MyFunction( void ) 
{
    if( ! SomeWin32API() )
    {
        // If you want to retrieve the error code yourself...
        DWORD dwErr = ::GetLastError();

        // ...perhaps for some check on the code like this...
        if( AlertUser( dwErr ) )
        {
            // This form of CWin32Error ctor does NOT call 
            // ::GetLastError().

            CWin32Error e = dwErr; 
            
            // CWin32Error supplies const char* conversion
            AfxMessageBox( e );
        }
        // Else, forget prompting the user. 
        // Just return from the function...
        return;
    }
    // other code ...
}

CWin32Error Features

  • Is an incredibly small but enourmously useful C++ class.
  • Encapsulates the two basic Win32 APIs:
        GetLastError and
        FormatMessage.
  • Employs smart copy methods to minimize the overheads of memory allocation, retrieval of system error strings, and copying strings.
  • Has all member-functions inline. Just #include the Win32Error.h file.
  • Does not depend on MFC. Use freely with any C++ library.
  • Compiles under both UNICODE and MBCS.
  • Is very convenient and simple to use - in fact, the objects of this class are *best* created on the stack, and can be passed by value without undue performance hit.
  • Provides automatic conversion to const TCHAR* type. Pass objects, as they are, to MessageBox() or AfxMessageBox().
  • Supports the MFC TRACE macro. Pass objects to TRACE as they are - you don't have to call object.c_str() method or so. e.g. TRACE( "%s\n", e ); and not TRACE( "%s\n", e.c_str() );
  • Helps in taking advantage of the exception-handling features of the C++ language.

Documentation

The main source code is in only one file: Win32Error.h.

A console app demo is included in the download zip file.

Technical notes are included in the ReadMe.Win32Error.txt in the zip file.

Comments, suggestions, and bug reports are most welcome.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Other
India India
Ajit R. Jadhav is an engineer-cum-physicist from Pune, India. He completed a PhD on a topic from computational science and engineering in 2009. Earlier, he did a Diploma in Advanced Computing from C-DAC, Pune (1994), an MTech from IIT Madras (1987), and a Distinction Class BE from COEP, Pune (1983).

He has been programming Windows in C++ since 1994. He has also had many years of industrial, research and teaching experience in the past. Currently, he works as a consultant in computational mechanics with a small firm in Pune. History of science and philosophy continue to remain his other interests.

Comments and Discussions

 
PraiseThank you. :) Pin
Andrew Truckle27-Feb-16 2:20
professionalAndrew Truckle27-Feb-16 2:20 
GeneralMy vote of 1 Pin
Oracle2k22-Apr-12 12:19
Oracle2k22-Apr-12 12:19 
GeneralMy vote of 1 Pin
Oracle2k22-Apr-12 12:19
Oracle2k22-Apr-12 12:19 
GeneralFYI: two more articles on the subject Pin
Nick Alexeev25-Dec-10 11:17
professionalNick Alexeev25-Dec-10 11:17 
GeneralMultiple languages Pin
Milind Shingade31-May-06 19:08
Milind Shingade31-May-06 19:08 
GeneralMyOwn Error Handling Pin
Member 135487425-Nov-04 1:10
Member 135487425-Nov-04 1:10 
GeneralAlternative: WFC's wfc_get_error_string Pin
graham_k_200421-May-04 8:30
graham_k_200421-May-04 8:30 
GeneralThings that I don't like Pin
Sardaukar29-Mar-01 1:11
Sardaukar29-Mar-01 1:11 
GeneralRe: Things that I don't like Pin
Ajit Jadhav29-Mar-01 6:13
Ajit Jadhav29-Mar-01 6:13 
GeneralWay Ahead of You Pin
28-Mar-01 12:56
suss28-Mar-01 12:56 
GeneralRe: Way Ahead of You Pin
28-Mar-01 13:20
suss28-Mar-01 13:20 
GeneralRe: Way Ahead of You Pin
Ajit Jadhav28-Mar-01 22:38
Ajit Jadhav28-Mar-01 22:38 
GeneralRe: Way Ahead of You Pin
30-Mar-01 7:20
suss30-Mar-01 7:20 
GeneralRe: Way Ahead of You Pin
30-Mar-01 7:21
suss30-Mar-01 7:21 
GeneralRe: Way Ahead of You Pin
Ajit Jadhav30-Mar-01 7:59
Ajit Jadhav30-Mar-01 7:59 
GeneralRe: Way Ahead of You Pin
Mike Junkin2-Apr-01 3:27
Mike Junkin2-Apr-01 3:27 
GeneralRe: Way Ahead of You Pin
Ajit Jadhav2-Apr-01 6:20
Ajit Jadhav2-Apr-01 6:20 
GeneralRe: Way Ahead of You Pin
Mike Junkin2-Apr-01 7:41
Mike Junkin2-Apr-01 7:41 
GeneralRe: Way Ahead of You Pin
Ajit Jadhav2-Apr-01 14:48
Ajit Jadhav2-Apr-01 14:48 
GeneralRe: Way Ahead of You Pin
Mike Junkin3-Apr-01 5:01
Mike Junkin3-Apr-01 5:01 
GeneralRe: Way Ahead of You Pin
Ajit Jadhav3-Apr-01 9:33
Ajit Jadhav3-Apr-01 9:33 
GeneralRe: Way Ahead of You Pin
Mike Junkin3-Apr-01 12:00
Mike Junkin3-Apr-01 12:00 
GeneralRe: Way Ahead of You Pin
Ajit Jadhav4-Apr-01 4:09
Ajit Jadhav4-Apr-01 4:09 
GeneralRe: Way Ahead of You Pin
Mike Junkin4-Apr-01 6:29
Mike Junkin4-Apr-01 6:29 
Hi Ajit,

-- Where I differ: I think msessages still should be tied in integrally with DWORD as CWin32Error now does --

I'm not trying to say there is some fundemental problem with this or that it's incorrect to do this. In fact there's a lot to be said for using as few classes as possible. Too many small classes not doing enough is no better than one big class doing too much.

-- There exists a 1:1 correspondence between error DWORDs and error messages and that's why, in my view, it need not be modelled by separate classes. Separate classes are called for if the mapping were Many:1 or 1:Many. --

Two points. The first is that multiplicity of the relationship between two application domain concepts is only one component that goes into determining how to model them with classes.

Second there's only a 1:1 correspondence between a status value and an error message for a sub-set of what your trying to model. Throw in COM errors and it's 1 -> 0:1. Throw in user messages and it might very well be N -> 1.

Having said that, there is still nothing that necessarily makes it desirable to split the value from the message as I did in my example. And, again, modeling it with a single class - even if it turns into many-to-one relationship between some user errors and messages - is still a perfectly viable way to do it.

One other reason I liked the idea of splitting the value and the message was that it separates something that is used frequently from something that is used infrequently. The error value classes will likely be used in every module while the error messageing classes would likely be used only in the module resposible for reporting/logging errors.

-- Thus, the space cost of the private message strings themselves will be carried right in the program binary. --

Don't read to much into the example. The point was only that custom messages might require some type of specialized processing.

-- So come to think of it, is heap really a biggy? --

In many situations absolutely not. Especially a desk top app that's likely single threaded. But for some types of applications yes, it's very important. The heap is a serialization point. Even third party heaps like smart heap don't completely elminate the problem. For multi-threaded applications where performance is important how the heap is used is critical.

For me, this is 90 percent of what I work on so I could very well be paying too much attention to it. But that's what led me to split the buffering out of the formatter - once it was separate from the value class.

-- If poor practice shows us the reasons to avoid it, what about the following? --

Your code shows a strcpy into too small of a buffer but I'm not sure what your point is. Do you mean there might be times when the error message is bigger than the supplied buffer?

Obviously you'd never strcpy a const char* of unknown length. So given that you've protected yourself against program error if the message is too big it becomes a question of will this screw up your error reporting. Of course it will if all your messages are longer than your buffer but how likely is that? You have quite a bit of control over any custom error messages and a modest buffer will handle all the system messages.

Oh and you certainly converted your unicode message back to ansii before using strcpy on it eh?

-- // BTW, did you appreciate parentheses around 48?
-- // Nice alignment at DWORD boundaries. s not sz.

You got me here. What do the parens do for you? Why isn't the char sz[64] dword aligned?

Also, what did you mean by the second code snippet?

Btw, the compiler will memset the buffer for you:

char s[MY_MAX_SIZE] = {0};

But it's more efficient to skip initializing it (or using memset) and just zero the last byte. That can be done either before or after strncpy since your passing in sizeof(s)-1 as the max length.

Look forward to the next version of your class..

regards

mike
GeneralRe: Way Ahead of You Pin
Ajit Jadhav4-Apr-01 17:09
Ajit Jadhav4-Apr-01 17:09 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.