Exceptions (C++/CX)
Error handling in C++/CX is based on exceptions. At the most fundamental level, Windows Runtime components report errors as HRESULT values. In C++/CX, these values are converted to strongly typed exceptions that contain an HRESULT value and a string description that you can access programmatically. Exceptions are implemented as a ref class
that derives from Platform::Exception
. The Platform
namespace defines distinct exception classes for the most common HRESULT values; all other values are reported through the Platform::COMException
class. All exception classes have an Exception::HResult field that you can use to retrieve the original HRESULT. You can also examine call-stack information for user code in the debugger that can help pinpoint the original source of the exception, even if it originated in code that was written in a language other than C++.
Exceptions
In your C++ program, you can throw and catch an exception that comes from a Windows Runtime operation, an exception that's derived from std::exception
, or a user-defined type. You have to throw a Windows Runtime exception only when it will cross the application binary interface (ABI) boundary, for example, when the code that catches your exception is written in JavaScript. When a non-Windows Runtime C++ exception reaches the ABI boundary, the exception is translated into a Platform::FailureException
exception, which represents an E_FAIL HRESULT. For more information about the ABI, see Creating Windows Runtime Components in C++.
You can declare a Platform::Exception by using one of two constructors that take either an HRESULT parameter, or an HRESULT parameter and a Platform::String^ parameter that can be passed across the ABI to any Windows Runtime app that handles it. Or you can declare an exception by using one of two Exception::CreateException method overloads that take either an HRESULT parameter, or an HRESULT parameter and a Platform::String^
parameter.
Standard exceptions
C++/CX supports a set of standard exceptions that represent typical HRESULT errors. Each standard exception derives from Platform::COMException, which in turn derives from Platform::Exception
. When you throw an exception across the ABI boundary, you must throw one of the standard exceptions.
You can't derive your own exception type from Platform::Exception
. To throw a custom exception, use a user-defined HRESULT to construct a COMException
object.
The following table lists the standard exceptions.
Name | Underlying HRESULT | Description |
---|---|---|
COMException | user-defined hresult | Thrown when an unrecognized HRESULT is returned from a COM method call. |
AccessDeniedException | E_ACCESSDENIED | Thrown when access is denied to a resource or feature. |
ChangedStateException | E_CHANGED_STATE | Thrown when methods of a collection iterator or a collection view are called after the parent collection has changed, thereby invalidating the results of the method. |
ClassNotRegisteredException | REGDB_E_CLASSNOTREG | Thrown when a COM class has not been registered. |
DisconnectedException | RPC_E_DISCONNECTED | Thrown when an object is disconnected from its clients. |
FailureException | E_FAIL | Thrown when an operation fails. |
InvalidArgumentException | E_INVALIDARG | Thrown when one of the arguments that are provided to a method is not valid. |
InvalidCastException | E_NOINTERFACE | Thrown when a type can't be cast to another type. |
NotImplementedException | E_NOTIMPL | Thrown if an interface method hasn't been implemented on a class. |
NullReferenceException | E_POINTER | Thrown when there is an attempt to de-reference a null object reference. |
ObjectDisposedException | RO_E_CLOSED | Thrown when an operation is performed on a disposed object. |
OperationCanceledException | E_ABORT | Thrown when an operation is aborted. |
OutOfBoundsException | E_BOUNDS | Thrown when an operation attempts to access data outside the valid range. |
OutOfMemoryException | E_OUTOFMEMORY | Thrown when there's insufficient memory to complete the operation. |
WrongThreadException | RPC_E_WRONG_THREAD | Thrown when a thread calls via an interface pointer which is for a proxy object that does not belong to the thread's apartment. |
HResult and Message properties
All exceptions have an HResult property and a Message property. The Exception::HResult property gets the exception's underlying numeric HRESULT value. The Exception::Message property gets the system-supplied string that describes the exception. In Windows 8, the message is available only in the debugger and is read-only. This means that you cannot change it when you rethrow the exception. In Windows 8.1, you can access the message string programmatically and provide a new message if you rethrow the exception. Better callstack information is also available in the debugger, including callstacks for asynchronous method calls.
Examples
This example shows how to throw a Windows Runtime exception for synchronous operations:
String^ Class1::MyMethod(String^ argument)
{
if (argument->Length() == 0)
{
auto e = ref new Exception(-1, "I'm Zork bringing you this message from across the ABI.");
//throw ref new InvalidArgumentException();
throw e;
}
return MyMethodInternal(argument);
}
The next example shows how to catch the exception.
void Class2::ProcessString(String^ input)
{
String^ result = nullptr;
auto obj = ref new Class1();
try
{
result = obj->MyMethod(input);
}
catch (/*InvalidArgument*/Exception^ e)
{
// Handle the exception in a way that's appropriate
// for your particular scenario. Assume
// here that this string enables graceful
// recover-and-continue. Why not?
result = ref new String(L"forty two");
// You can use Exception data for logging purposes.
Windows::Globalization::Calendar calendar;
LogMyErrors(calendar.GetDateTime(), e->HResult, e->Message);
}
// Execution continues here in both cases.
//#include <string>
std::wstring ws(result->Data());
//...
}
To catch exceptions that are thrown during an asynchronous operation, use the task class and add an error-handling continuation. The error-handling continuation marshals exceptions that are thrown on other threads back to the calling thread so that you can handle all potential exceptions at just one point in your code. For more information, see Asynchronous Programming in C++.
UnhandledErrorDetected event
In Windows 8.1 you can subscribe to the Windows::ApplicationModel::Core::CoreApplication::UnhandledErrorDetected static event, which provides access to unhandled errors that are about to bring down the process. Regardless of where the error originated, it reaches this handler as a Windows::ApplicationModel::Core::UnhandledError object that's passed in with the event args. When you call Propagate
on the object, it creates and throws a Platform::*Exception
of the type that corresponds to the error code. In the catch blocks, you can save user state if necessary and then either allow the process to terminate by calling throw
, or do something to get the program back into a known state. The following example shows the basic pattern:
In app.xaml.h:
void OnUnhandledException(Platform::Object^ sender, Windows::ApplicationModel::Core::UnhandledErrorDetectedEventArgs^ e);
In app.xaml.cpp:
// Subscribe to the event, for example in the app class constructor:
Windows::ApplicationModel::Core::CoreApplication::UnhandledErrorDetected += ref new EventHandler<UnhandledErrorDetectedEventArgs^>(this, &App::OnUnhandledException);
// Event handler implementation:
void App::OnUnhandledException(Platform::Object^ sender, Windows::ApplicationModel::Core::UnhandledErrorDetectedEventArgs^ e)
{
auto err = e->UnhandledError;
if (!err->Handled) //Propagate has not been called on it yet.
{
try
{
err->Propagate();
}
// Catch any specific exception types if you know how to handle them
catch (AccessDeniedException^ ex)
{
// TODO: Log error and either take action to recover
// or else re-throw exception to continue fail-fast
}
}
Remarks
C++/CX does not use the finally
clause.