Redigera

Dela via


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.

See also

C++/CX Language Reference
Namespaces Reference