420 likes | 1.45k Views
TOOL-845T. Tips and tricks for developing Metro style apps using C++. Tarek Madkour Principal Lead Program Manager Microsoft Corporation. Agenda. Very Brief Recap Windows Runtime C++ for Windows Runtime Common Patterns WinRT Types (esp. String and Exceptions)
E N D
TOOL-845T Tips and tricks for developing Metro style apps using C++ Tarek Madkour Principal Lead Program Manager Microsoft Corporation
Agenda • Very Brief Recap • Windows Runtime • C++ for Windows Runtime • Common Patterns • WinRT Types (esp. String and Exceptions) • Collections and Iteration • Asynchronous Calls • Calling Win32
Why you should care • Get the experience from our own app-building efforts: • Basic fundamentals always come into play • Many learnings from common gotchas • Getting you ready beyond the primer
Quick Recap • WinRT • Visual C++ Component Extensions
Windows 8 APIs Metro style app WinRT Win32 (Desktop Subset) Win32 (Metro style Subset) Windows Core OS Services
Microsoft Confidential C++ for Windows Runtime Set of language extensions and libraries to allow direct consumption and authoring of Windows Runtime types. • delegate void CarouselInitDoneHandler(IUIAnimationVariable^ rotation); • voidCarouselAnimation::Initialize(double angle, CarouselInitDoneHandler^ callback) { • // Create Animation Manager • usingnamespace Engine::Graphics; • UIAnimationManageranimationManager(); • // Create rotation animation variable • IUIAnimationVariable^ rotation = animationManager.CreateAnimationVariable(angle); • // Set the event handler for the story board • rotation->GetCurrentStoryBoard() • ->SetStoryBoardEventHandler( • ref newCarouselStoryBoardHandler(this, &CarouselAnimation::StoryBoard) • ); • // Invoke the callback when done • callback(rotation); • }
String^ • Platform::String^ as a boundary type • Immutable, can have embedded nulls • Copies are not expensive (ref counts) • Inherently more parallel • Can easily interop with std::wstring and wchar_t* • To “modify” the string • Create a new immutable string • Use std::wstring as a string builder
String^ 1. usingnamespace Platform; 2. #include<string> 3. // initializing a String^ 4. String^ str1 = "Test"; // no need for L 5. String^ str2("Test"); 6. String^ str3 = L"Test"; 7. String^ str4(L"Test"); 8. String^ str5 = refnewString(L"Test"); 9. String^ str6(str1); 10. String^ str7 = str2; 11. wchar_tmsg[] = L"Test"; 12. String^ str8 = refnewString(msg); 13. std::wstring wstr1(L"Test"); 14. String^ str9 = refnewString(wstr1.c_str()); 15. String^ str10 = refnewString(wstr1.c_str(), wstr1.length());
String^ 1. // initializing a std::wstring from String^ 2. std::wstring wstr2(str1->Data()); 3. std::wstring wstr3(str1->Data(), str1->Length()); 4. // comparisons 5. if(str1 == str2) { /* ... */ } 6. if(str1 != str2) { /* ... */} 7. if(str1 < str2) { /* ... */} 8. if(str1 < "test") { /* ... */ }
String^ usage considerations • No need for L”” anymore • Compile-time convenience for String^ only • String^ parameter optimizations (Fast Pass) • String parameters on the stack; don’t destroy source string during call • How do you get a mutable string? • Use std::wstring for that • When to use String^ vs. std::wstring? • Judgment: proximity to boundary, how often to you intend to modify the string • Only trust the profiler • String^ works with STL algorithms (e.g. regex)
Exception^ • WinRT APIs throw exceptions deriving from Platform::Exception • … really, it’s HRESULTs under the cover
Exception^ 1. usingnamespace Platform; 2. 3. try 4. { 5. if (filename == "") { throwrefnewInvalidArgumentException(); } 6. autopics = Windows::Storage::KnownFolders::PicturesLibrary; 7. auto res = pics->GetFilesAsync(); 8. } 9. catch (InvalidArgumentException^ e) { /* same as E_INVALIDARG */ } 10. catch (NullReferenceException^ e) { /* same as E_POINTER */ } 11. catch(AccessDeniedException^ e) { /* same as E_ACCESSDENIED */ } 12. catch(FailureException^ e) { /* same as E_FAIL */ }
Exception^ • At the WinRT boundary, every exception will be translated into an HRESULT module_b.dll module_a.exe voidApple() { throwrefnew Platform::FailureException(); } void Orange() { try { ModuleA::Pear(); } catch (Platform::OutOfBoundsException^) { } } void main() { try { ModuleB::Apple(); } catch (Platform::FailureException^) { ModuleB::Orange(); } } void Pear() { // perform out-of-bounds operation } E_FAIL E_BOUNDS
Exception^ usage considerations • You can create “custom” exceptions; but it is not recommended • Use ComException; do not inherit from Platform::Exception • Exception strings are almost the same but not quite • ToString() for debugging; Message for logging • ComException(E_FAIL) and FailureException are not identical • Do not throw ComException(hr) blindly; throw most-derived exception • __cli_WinRTThrowError(hr) has the big switch statement • C++ exceptions turn into a FailureException • Travel as E_FAIL across the boundary
WinRT collections family Vector Map VectorView MapView IVector IMap IVectorView IMapView IIterable IObservableVector IObservableMap IIterator • STL containers are the backing store see <collection.h>
Windows::Foundation::Collections 1. #include<vector> 2. #include<algorithm> 3. #include<utility> • 4. #include<collection.h> 5. namespace WFC = Windows::Foundation::Collections; 6. 7. WFC::IVectorView<int>^ Producer(){ 8. std::vector<int> v; 9. v.push_back(1); 10. v.push_back(2); 11. 12. returnrefnew Platform::VectorView<int>(std::move(v)); 13. } 14. 15.intsum = 0; 16. WFC::IVectorView<int>^ v = Producer(); 17. std::for_each(begin(v), end(v), [&sum](int i) { sum += i; });
Collections usage considerations • Mix STL containers with WinRT collections • STL containers are faster but don’t have observable behavior • Use STL containers internally; use WinRT collections at the boundary • Fire up a profiler where it matters! • Use STL algorithms on WinRT collections • No need to copy the WinRT collection into an STL container • You can have STL containers of ^ types • Use Vector not IVector most of the time • Only use IVector the public API surface
Collections usage considerations (cont.) • VectorView, MapView and Iterator get invalidated • Copy if you plan to change the underlying Vector or Map • Each iteration over collections is a virtual call • Use bulk iteration (GetMany) when it makes sense • Implementing IVector is tricky • Use an underlying Vector
Why Async? • Important for creating smooth experiences • Very common pattern among key WinRT APIs • Favors non-blocking scenarios • Provides you the control over when to block if needed
Basic WinRT Pattern 1. voidmain() 2. { 3. // create the async operation, but don’t start it 4. SomeOperation^ someOp = Operation(); 5. 6. // define what happens when the operation completes 7. someOp->Completed = refnewAsyncOperationCompletedHandler<SomeType^> 8. ([](IAsyncOperation<SomeType^>^ asyncOp) { 9. // do whatever you want with the result after the operation has completed 10. auto result = asyncOp->GetResults(); 11. } 12. 13. // invoke the async operation 14.someOp->Start(); 15. }
Async Example 1. using namespace Windows::Storage; • 2. using namespace Windows::Foundation; • 3. • 4. void main () { • 5. autoop = KnownFolders::PicturesLibrary->GetItemsAsync(); • 6. 7. op->Completed = refnewAsyncOperationCompletedHandler<IVectorView<IStorageItem^>^>( 8. [](IAsyncOperation<IVectorView<IStorageItem^>^>^ op) 9. { 10. autoitems = op->GetResults(); 11. }); 12. 13. op->Start(); 14. }
Async Example (cont.) voidmain() { StorageFolder^ item = Windows::Storage::KnownFolders::PicturesLibrary; CreateStorageFileOperation^ createStorageFileOp = item->CreateFileAsync("bar.txt"); createStorageFileOp->Completed = ref newAsyncOperationCompletedHandler<StorageFile^>( [](IAsyncOperation<StorageFile^>^ asyncOp) { StreamRetrievalOperation^ streamRetrievalOp = asyncOp->GetResults()->OpenAsync(FileAccessMode::ReadWrite); streamRetrievalOp->Completed = ref newAsyncOperationCompletedHandler<IRandomAccessStream^>( [](IAsyncOperation<IRandomAccessStream^>^ asyncOp2) { IOutputStream^ stream = asyncOp2->GetResults()->GetOutputStreamAt(0); BasicBinaryReaderWriter^ bbrw = ref newBasicBinaryReaderWriter(); WriteBinaryStringOperation^ writeBinaryStringOp = bbrw->WriteBinaryStringAsync(stream, "this string gets written into the file"); writeBinaryStringOp->Completed = ref newAsyncOperationCompletedHandler<unsignedint>( [=](IAsyncOperation<unsignedint>^ asyncOp3) { int bytes = asyncOp3->GetResults(); IStreamFlushOperation^ streamFlushOp = stream->FlushAsync(); streamFlushOp->Completed = refnewStreamFlushCompletedEventHandler( [](IStreamFlushOperation^ asyncOp4) { bool result = asyncOp4->GetResults(); }); streamFlushOp->Start(); }); writeBinaryStringOp->Start(); }); streamRetrievalOp->Start(); }); createStorageFileOp->Start(); }
Async simplified • Build on Parallel Patterns Library (PPL) tasks to implement async pattern • Sample available here: http://go.microsoft.com/fwlink/?LinkId=228286 • Using this technique, async code is cleaner and more maintainable
Async Using PPL Sample • 1. voidSomeClass::InvokeOperation() • 2. { • 3. // create the async operation, but don’t start it • 4. autosomeOp= OperationAsync(); • 5.task<SomeType^> t(someOp); • 6. • 6. // define what happens when the operation completes • 7. t.then ([](SomeType^ opResult) { • 9. // do whatever you want with the result after the operation has completed • 10. }); • 11. • 12. // no need to manually “start” the async operation • 13. }
Async Using PPL Sample (cont.) • 1. voidSomeClass::InvokeOperation() • 2. { • 3. StorageFolder^ item = Windows::Storage::KnownFolders::PicturesLibrary; • 4. task<StorageFile^> t(item->CreateFileAsync("bar.txt")); • 5. shared_ptr<IOutputStream^> ostream = make_shared<IOutputStream^>(nullptr); • 6.// use a shared pointer to ensure the stream is valid after function exits • 7. t.then([](StorageFile^ storageFile) { • 8. returnstorageFile->OpenAsync(FileAccessMode::ReadWrite); • 9. }).then([=](IRandomAccessStream^ rastream) -> StringBinaryAsyncOperation { • 10. *ostream= rastream->GetOutputStreamAt(0); • 11. BasicBinaryReaderWriter^ bbrw = refnewBasicBinaryReaderWriter(); • 12. returnbbrw->WriteBinaryStringAsync(ostream, "this string gets written into the file"); • 13. }).then([=](unsignedintbytesWritten) { • 14. return(*ostream)->FlushAsync(); • 15. }).then([](boolflushed) { • 16. // all done! • 17. }); • 18. }
Windows SDK for Metro style apps • Windows 8 introduces the concept of “app container” sandbox • Access to a number of resources (files, devices, etc.) is controlled by the sandbox • A large number for Win32 APIs are replaced by the WinRT library • A good number of Win32 will not be available to Metro style apps • Some Win32 APIs are still available directly to Metro style apps • COM Core • Kernel • Accessibility • Audio • Direct2D • Direct3D • DirectComposition • DirectManipulation • DirectWrite • File Systems • Globalization • Media Foundation • Windows and Messages
Win32 • 1. voidLoadFromImageFile (String^ wFileName) { • 2. HRESULT hr = S_OK; • 3. ComPtr decoder, bitmapSource, wicBitmap, wicFactory, scaler, FlipRotator; • 4. • 5. hr= CoCreateInstance (CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, • 6. IID_IWICImagingFactory, &wicFactory); • 7. if(SUCCEEDED(hr)) • 8. hr= wicFactory->CreateDecoderFromFilename • 9. (wFileName->Data(),nullptr,GENERIC_READ,WICDecodeMetadataCacheOnLoad,&decoder); • 10. if(SUCCEEDED(hr)) • 11. hr= decoder->GetFrame (0, &bitmapSource); • 12. if(SUCCEEDED(hr)) • 13. hr= bitmapSource.As (&wicBitmap); • 14. if(SUCCEEDED(hr)) • 15. GetImageSize(); • 16. __cli_WinRTThrowError(hr); // if fail, throw the most specific Exception^ type • 17. }
Win32 usage considerations • DirectX is one of the primary reasons to drop to Win32 • Use Win32 APIs when there is no WinRT equivalent • Use C++ runtime APIs in platform-agnostic code
Summary • You have access to the Windows 8 Metro style API surface area • All WinRT APIs • The Metro style subset of Win32 and COM • Use WinRT types when calling or exposing WinRT APIs • Use standard C++ types freely within your Metro style app; mix with WinRT types as well • Use async APIs to create responsive Metro style apps
Related sessions [TOOL-479T] A lap around Visual Studio 11 Express for Metro style apps using C++ [TOOL-532T] Using the Windows Runtime from C++ [TOOL-690C] Under the covers with C++ for Metro style apps [TOOL-789C] Bringing existing C++ code into Metro style apps [TOOL-802T] Taming GPU compute with C++ AMP [TOOL-835T] Writing modern C++ code: how C++ has evolved over the years
Further reading and documentation • MSDN Documentation: Building your first Windows Metro style app with C++, C#, or Visual Basic http://go.microsoft.com/fwlink/?LinkID=227459 • Async Sample using the Parallel Patterns Library (PPL) http://go.microsoft.com/fwlink/?LinkId=228286 • Contact tarekm@microsoft.com
thank you Feedback and questions http://forums.dev.windows.com Session feedbackhttp://bldw.in/SessionFeedback
© 2011 Microsoft Corporation. All rights reserved. Microsoft, Windows, Windows Vista and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries. The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.
Windows 8 Metro style Apps Desktop Apps HTML JavaScript HTML / CSS XAML View JavaScript (Chakra) C C++ C# VB Model Controller C C++ C# VB WinRT APIs Devices & Printing Communication & Data Graphics & Media System Services .NET / SL Internet Explorer Win32 Application Model Windows Core OS Services Core