See the question and my original answer on StackOverflow

If you look at the standard C++ COM server implementation that's generated by all Visual Studio versions (what you get using the wizards like this: ATL Tutoral Step 5: Adding an Event), the C++ code for raising an event (named a "connection point" in COM terms) looks like this.

HRESULT Fire_MyCustomEvent()
{
  ...
  for (int iConnection = 0; iConnection < cConnections; iConnection++)
  {
      CComVariant varResult;
      DISPPARAMS params = { NULL, NULL, 0, 0 };
      hr = pConnection->Invoke(1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, &varResult, NULL, NULL);
  }
  return hr;
}

So, hr does contain an error code but in general, the COM server implementer just does

Fire_MyCustomEvent()

Because in general, there can be more than one caller. What would you do if one caller fail? Fail everyone? In the general case, nothing. See this interesting discussion here on a similar subject: Observers Should Never Throw Exceptions

That said, if you own the COM server, you can catch Invoke error, and in this example, pass EXCEPINFO as the 7th argument to invoke. It will contain the .NET Exception text "This is insane!!!".