In a session titled Diving deep into C++ /CX and WinRT, Marian Luparu talks about exception handling and performance for C++ applications that target Windows 8. The most important thing for developers to understand is how the boundary between WinRT and normal C++ code effect exception handling and performance.
Basic Exception Handling
WinRT does not have the concept of exceptions, it uses HResults internally. Debug information put into classes that inherit from the Platform::Exception don’t travel across the ABI boundary. The message normally associated with exception is only available to the debugger.
C++ and SEH style exceptions will terminate the process if they cross an ABI boundary. This means all C++ exceptions need to be handled, even if that just means wrapping it in a Platform::Exception.
When catching COMException always check the HResult. If you don’t know how to handle a given HResult then rethrow the exception. Similarly, if you call a COM method that returns an error code then it should immediately be turned into an Exception.
Async and Exception Handling
When working with Parallel Patterns Library (PPL), it is important that lambda expressions accept a task instead of a result. For example, use
.then( [](Task<int> result) {…} )
instead of
.then( [](int result) {…} )
If you use the second version and an exception occurs then the “then” block will be skipped entirely.
Async and Threading
Like the async/await syntax in C# and VB, PPL is sticky when it comes to threads. If you begin an operation in the UI thread, then each continuation (the “then” block) will also be scheduled on the UI thread. If you want to use the thread pool instead you need to pass the results of task_continuation_context::use_arbitrary to the “then” method.
C++ Classes vs WinRT Classes
In general developers should not use WinRT style classes (ref class) unless they are forced to because they are interacting with XAML or exposing a WinRT component for other languages to consume. WinRT classes are slower than normal C++ classes are not portable, that is to say they cannot be used in regular C++ applications.
Another way to improve performance is to reduce the conversions between WinRT and C++ types. While chatty communication across WinRT boundaries can be slow, needless allocating memory and copying data can be worse.
Some wrapper classes such as StringReference can help with performance, but care must be used. StringReference is reference counted, so the buffer it wraps must not be altered or deleted until the StringReference is discarded. This problem occurs when WinRT holds onto a copy of the StringReference, which is something that you cannot necessarily predict. Marian Luparu offers another example:
- The StringReference is created to wrap a buffer.
- A WinRT function is called with the StringReference
- The WinRT function invokes a C++ callback
- The callback alters the underlying buffer.
- Control is returned to the WinRT function, which now has a string that was unexpectedly altered.
ArrayReference can likewise be used as a wrapper around C++ arrays. But since WinRT doesn’t expect C++ arrays to be immutable there is less opportunity for disaster.