views:

2394

answers:

4

Without:

  • MFC
  • ATL

How can I use FormatMessage() to get the error text for a HRESULT?

 HRESULT hresult = application.CreateInstance("Excel.Application");

 if (FAILED(hresult))
 {
     // what should i put here to obtain a human-readable
     // description of the error?
     exit (hresult);
 }
+1  A: 

Try this:

void HandleLastError(const char *msg /* = "Error occured" */) {
        DWORD errCode = GetLastError();
        char *err;
        if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
                           NULL,
                           errCode,
                           MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language
                           (LPTSTR) &err,
                           0,
                           NULL))
            return;

        //TRACE("ERROR: %s: %s", msg, err);
        static char buffer[1024];
        _snprintf(buffer, sizeof(buffer), "ERROR: %s: %s\n", msg, err);
        OutputDebugString(buffer);
        LocalFree(err);
}
David Hanak
void HandleLastError(hresult)?
Aaron
Hi David, I'd rather not use MFC: "TRACE()"...
Aaron
Surely you can make these adaptions yourself.
oefe
Sure, OK will do :)
Aaron
@Atklin: If you want to use hresult from a parameter, you obviously don't need the first line (GetLastError()).
David Hanak
GetLastError doesn't return an HResult. It returns a Win32 error code. Might prefer the name PrintLastError since this doesn't actually *handle* anything. And be sure to use FORMAT_MESSAGE_IGNORE_INSERTS.
Rob Kennedy
Thanks for your help guys :) - much appreciated
Aaron
+12  A: 

Here's the proper way to get an error message back from the system for an HRESULT:

LPTSTR errorText = NULL;

FormatMessage(
   // use system message tables to retrieve error text
   FORMAT_MESSAGE_FROM_SYSTEM
   // allocate buffer on local heap for error text
   |FORMAT_MESSAGE_ALLOCATE_BUFFER
   // Important! will fail otherwise, since we're not 
   // (and CANNOT) pass insertion parameters
   |FORMAT_MESSAGE_IGNORE_INSERTS,  
   NULL,    // unused with FORMAT_MESSAGE_FROM_SYSTEM
   hresult,
   MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
   (LPTSTR)&errorText,  // output 
   0, // minimum size for output buffer
   NULL);   // arguments - see note 

if ( NULL != errorText )
{
   // ... do something with the string - log it, display it to the user, etc.

   // release memory allocated by FormatMessage()
   LocalFree(errorText);
   errorText = NULL;
}

The key difference between this and David Hanak's answer is the use of the FORMAT_MESSAGE_IGNORE_INSERTS flag. MSDN is a bit unclear on how insertions should be used, but Raymond Chen notes that you should never use them when retrieving a system message, as you've no way of knowing which insertions the system expects.

FWIW, if you're using Visual C++ you can make your life a bit easier by using the _com_error class:

{
   _com_error error(hresult);
   LPCTSTR errorText = error.ErrorMessage();

   // do something with the error...

   //automatic cleanup when error goes out of scope
}

Not part of MFC or ATL directly as far as i'm aware.

Shog9
I wish I could vote twice!!!
Aaron
You rock! - For someone used to Exception.ToString() -- this is hideous to get the error description from an error code.
Gishu
+2  A: 

Keep in mind that you cannot do the following:

{
   LPCTSTR errorText = _com_error(hresult).ErrorMessage();

   // do something with the error...

   //automatic cleanup when error goes out of scope
}

As the class is created and destroyed on the stack leaving errorText to point to an invalid location. In most cases this location will still contain the error string, but that likelihood falls away fast when writing threaded applications.

So always do it as follows as answered by Shog9 above:

{
   _com_error error(hresult);
   LPCTSTR errorText = error.ErrorMessage();

   // do something with the error...

   //automatic cleanup when error goes out of scope
}
Marius
The `_com_error` object is created on the stack in *both* your examples. The term you're looking for is *temporary*. In the former example, the object is a temporary that is destroyed at the end of the statement.
Rob Kennedy
Yup, meant that. But I'd hope most people would be at least able to figure that out from the code.Technically temporaries are not destroyed at the end of the statement, but at the end of the sequence point. (which is the same thing in this example so this is just splitting hairs.)
Marius
A: 

Man, Thanks David Hanak, the function you gave is extremely useful.

Turki