2

This is how to throw ordinary Win32 errors correctly, automatically retrieving the error description, and it works marvellously well:

if (!SomeWinFunc()) {
  throw std::system_error(GetLastError(),
    std::system_category(),
    "SomeWinFunc crashed badly");
}

However, I'm uncertain on how to deal with COM errors, which are checked idiomatically like this:

HRESULT hr = comObj->SomeFunc();
if (FAILED(hr)) {
  throw std::system_error(hr, // <-- is it correct here?
    std::system_category(),
    "SomeFunc crashed right now");
}

Is it correct to pass an HRESULT to system_error, or there is another way to throw exceptions from a COM function?

rodrigocfd
  • 6,450
  • 6
  • 34
  • 68
  • Did you get anywhere with this? – Michael Gunter Dec 14 '17 at 20:06
  • @MichaelGunter, I still didn't implement a 100% correct solution (possibly starting with your code), but since my needs are simply dealing with system COM objects, the approach I wrote in the question just works, I ran several tests. I plan to go deeper on this as soon as I find time. – rodrigocfd Dec 15 '17 at 23:40

2 Answers2

4

Typically, I handle this using the Microsoft standard _com_error exception type, which can be easily raised by one of the following.

_com_util::CheckError(hr); // throws when FAILED(hr)
_com_issue_error(hr); // throws always

A Win32 error can be converted to an HRESULT like so:

hr = HRESULT_FROM_WIN32(GetLastError());

However, this means that you have to deal with a new exception type, which may be problematic. To do this the pure STL way, I think you need to derive a new error_category and use that instead of system_category. I would look at the <system_error> header and copy the implementation of system_category, updating as necessary for HRESULTs.

Ideally, the different facility codes in the HRESULT would be served by different error_category implementations -- doing so would very effectively communicate the exception and its source. But that is tricky, and you can probably get away with a single com_category that deals with HRESULTs generically.

Michael Gunter
  • 12,528
  • 1
  • 24
  • 58
  • 1
    It's important to note, that you **must not** pass an `HRESULT` when using the `std::system_category` as the context. That category is meant for use with error codes returned by `GetLastError`. For example, if you pass the COM success code `S_FALSE`, it would get interpreted as `ERROR_INVALID_FUNCTION`. Information on how to implement your custom `error_category` can be found at [System error support in C++0x - part 4](http://blog.think-async.com/2010/04/system-error-support-in-c0x-part-4.html). – IInspectable Dec 06 '17 at 22:10
  • 1
    @IInspectable `S_FALSE` is not a good example, because it is not an error code. So there is no reason to throw it. The *error* values which a `HRESULT` can represent do not overlap with the error codes returned by `GetLastError`, because `HRESULT` errors are always negative. So in my opinion a special category for `HRESULT` is not required aslong as you only throw values for which `FAILED(hr)==true`. – zett42 Dec 07 '17 at 07:46
  • @zett42: That depends on your code logic. A successful COM call can yield a condition, from which you cannot recover, and need to throw. Even if that weren't the case, you still need a custom error category to produce error messages. While `FormatMessage` can be used with system-defined `HRESULT` values, things look different when you deal with customer-defined codes. You need additional information to turn those into error messages, namely the module handle. That needs to be encoded in the category, since you cannot pass additional information alongside the error code. – IInspectable Dec 07 '17 at 08:45
  • Found another reason to create a dedicated `error_category` for `HRESULT`: to be able to examine error codes in a generic way. E. g. the following doesn't work: `std::error_code e(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), std::system_category()); if(e == std::errc::no_such_file_or_directory){/*do something*/}`. This could be solved by overriding `std::error_category::default_error_condition()` in a derived class. – zett42 Oct 17 '18 at 10:19
0

This article has a great walkthrough of how to define an std::error_code for COM API errors: https://kb.firedaemon.com/support/solutions/articles/4000121648-fitting-com-into-c-system-error-handling

The gist of it is to just use _com_error to get the message, and the rest is mostly std boilerplate for creating error_codes.

MHebes
  • 2,290
  • 1
  • 16
  • 29