Path: blob/master/samples/winrt/ImageManipulations/common/suspensionmanager.cpp
16339 views
//*********************************************************1//2// Copyright (c) Microsoft. All rights reserved.3// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF4// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY5// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR6// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.7//8//*********************************************************910//11// SuspensionManager.cpp12// Implementation of the SuspensionManager class13//1415#include "pch.h"16#include "SuspensionManager.h"1718#include <collection.h>19#include <algorithm>2021using namespace SDKSample::Common;2223using namespace Concurrency;24using namespace Platform;25using namespace Platform::Collections;26using namespace Windows::Foundation;27using namespace Windows::Foundation::Collections;28using namespace Windows::Storage;29using namespace Windows::Storage::FileProperties;30using namespace Windows::Storage::Streams;31using namespace Windows::UI::Xaml;32using namespace Windows::UI::Xaml::Controls;33using namespace Windows::UI::Xaml::Interop;3435namespace36{37Map<String^, Object^>^ _sessionState = ref new Map<String^, Object^>();38String^ sessionStateFilename = "_sessionState.dat";3940// Forward declarations for object object read / write support41void WriteObject(Windows::Storage::Streams::DataWriter^ writer, Platform::Object^ object);42Platform::Object^ ReadObject(Windows::Storage::Streams::DataReader^ reader);43}4445/// <summary>46/// Provides access to global session state for the current session. This state is serialized by47/// <see cref="SaveAsync"/> and restored by <see cref="RestoreAsync"/> which require values to be48/// one of the following: boxed values including integers, floating-point singles and doubles,49/// wide characters, boolean, Strings and Guids, or Map<String^, Object^> where map values are50/// subject to the same constraints. Session state should be as compact as possible.51/// </summary>52IMap<String^, Object^>^ SuspensionManager::SessionState::get(void)53{54return _sessionState;55}5657/// <summary>58/// Wrap a WeakReference as a reference object for use in a collection.59/// </summary>60private ref class WeakFrame sealed61{62private:63WeakReference _frameReference;6465internal:66WeakFrame(Frame^ frame) { _frameReference = frame; }67property Frame^ ResolvedFrame68{69Frame^ get(void) { return _frameReference.Resolve<Frame>(); }70};71};7273namespace74{75std::vector<WeakFrame^> _registeredFrames;76DependencyProperty^ FrameSessionStateKeyProperty =77DependencyProperty::RegisterAttached("_FrameSessionStateKeyProperty",78TypeName(String::typeid), TypeName(SuspensionManager::typeid), nullptr);79DependencyProperty^ FrameSessionStateProperty =80DependencyProperty::RegisterAttached("_FrameSessionStateProperty",81TypeName(IMap<String^, Object^>::typeid), TypeName(SuspensionManager::typeid), nullptr);82}8384/// <summary>85/// Registers a <see cref="Frame"/> instance to allow its navigation history to be saved to86/// and restored from <see cref="SessionState"/>. Frames should be registered once87/// immediately after creation if they will participate in session state management. Upon88/// registration if state has already been restored for the specified key89/// the navigation history will immediately be restored. Subsequent invocations of90/// <see cref="RestoreAsync(String)"/> will also restore navigation history.91/// </summary>92/// <param name="frame">An instance whose navigation history should be managed by93/// <see cref="SuspensionManager"/></param>94/// <param name="sessionStateKey">A unique key into <see cref="SessionState"/> used to95/// store navigation-related information.</param>96void SuspensionManager::RegisterFrame(Frame^ frame, String^ sessionStateKey)97{98if (frame->GetValue(FrameSessionStateKeyProperty) != nullptr)99{100throw ref new FailureException("Frames can only be registered to one session state key");101}102103if (frame->GetValue(FrameSessionStateProperty) != nullptr)104{105throw ref new FailureException("Frames must be either be registered before accessing frame session state, or not registered at all");106}107108// Use a dependency property to associate the session key with a frame, and keep a list of frames whose109// navigation state should be managed110frame->SetValue(FrameSessionStateKeyProperty, sessionStateKey);111_registeredFrames.insert(_registeredFrames.begin(), ref new WeakFrame(frame));112113// Check to see if navigation state can be restored114RestoreFrameNavigationState(frame);115}116117/// <summary>118/// Disassociates a <see cref="Frame"/> previously registered by <see cref="RegisterFrame"/>119/// from <see cref="SessionState"/>. Any navigation state previously captured will be120/// removed.121/// </summary>122/// <param name="frame">An instance whose navigation history should no longer be123/// managed.</param>124void SuspensionManager::UnregisterFrame(Frame^ frame)125{126// Remove session state and remove the frame from the list of frames whose navigation127// state will be saved (along with any weak references that are no longer reachable)128auto key = safe_cast<String^>(frame->GetValue(FrameSessionStateKeyProperty));129if (SessionState->HasKey(key)) SessionState->Remove(key);130_registeredFrames.erase(131std::remove_if(_registeredFrames.begin(), _registeredFrames.end(), [=](WeakFrame^& e)132{133auto testFrame = e->ResolvedFrame;134return testFrame == nullptr || testFrame == frame;135}),136_registeredFrames.end()137);138}139140/// <summary>141/// Provides storage for session state associated with the specified <see cref="Frame"/>.142/// Frames that have been previously registered with <see cref="RegisterFrame"/> have143/// their session state saved and restored automatically as a part of the global144/// <see cref="SessionState"/>. Frames that are not registered have transient state145/// that can still be useful when restoring pages that have been discarded from the146/// navigation cache.147/// </summary>148/// <remarks>Apps may choose to rely on <see cref="LayoutAwarePage"/> to manage149/// page-specific state instead of working with frame session state directly.</remarks>150/// <param name="frame">The instance for which session state is desired.</param>151/// <returns>A collection of state subject to the same serialization mechanism as152/// <see cref="SessionState"/>.</returns>153IMap<String^, Object^>^ SuspensionManager::SessionStateForFrame(Frame^ frame)154{155auto frameState = safe_cast<IMap<String^, Object^>^>(frame->GetValue(FrameSessionStateProperty));156157if (frameState == nullptr)158{159auto frameSessionKey = safe_cast<String^>(frame->GetValue(FrameSessionStateKeyProperty));160if (frameSessionKey != nullptr)161{162// Registered frames reflect the corresponding session state163if (!_sessionState->HasKey(frameSessionKey))164{165_sessionState->Insert(frameSessionKey, ref new Map<String^, Object^>());166}167frameState = safe_cast<IMap<String^, Object^>^>(_sessionState->Lookup(frameSessionKey));168}169else170{171// Frames that aren't registered have transient state172frameState = ref new Map<String^, Object^>();173}174frame->SetValue(FrameSessionStateProperty, frameState);175}176return frameState;177}178179void SuspensionManager::RestoreFrameNavigationState(Frame^ frame)180{181auto frameState = SessionStateForFrame(frame);182if (frameState->HasKey("Navigation"))183{184frame->SetNavigationState(safe_cast<String^>(frameState->Lookup("Navigation")));185}186}187188void SuspensionManager::SaveFrameNavigationState(Frame^ frame)189{190auto frameState = SessionStateForFrame(frame);191frameState->Insert("Navigation", frame->GetNavigationState());192}193194/// <summary>195/// Save the current <see cref="SessionState"/>. Any <see cref="Frame"/> instances196/// registered with <see cref="RegisterFrame"/> will also preserve their current197/// navigation stack, which in turn gives their active <see cref="Page"/> an opportunity198/// to save its state.199/// </summary>200/// <returns>An asynchronous task that reflects when session state has been saved.</returns>201task<void> SuspensionManager::SaveAsync(void)202{203// Save the navigation state for all registered frames204for (auto&& weakFrame : _registeredFrames)205{206auto frame = weakFrame->ResolvedFrame;207if (frame != nullptr) SaveFrameNavigationState(frame);208}209210// Serialize the session state synchronously to avoid asynchronous access to shared211// state212auto sessionData = ref new InMemoryRandomAccessStream();213auto sessionDataWriter = ref new DataWriter(sessionData->GetOutputStreamAt(0));214WriteObject(sessionDataWriter, _sessionState);215216// Once session state has been captured synchronously, begin the asynchronous process217// of writing the result to disk218return task<unsigned int>(sessionDataWriter->StoreAsync()).then([=](unsigned int)219{220return sessionDataWriter->FlushAsync();221}).then([=](bool flushSucceeded)222{223(void)flushSucceeded; // Unused parameter224return ApplicationData::Current->LocalFolder->CreateFileAsync(sessionStateFilename,225CreationCollisionOption::ReplaceExisting);226}).then([=](StorageFile^ createdFile)227{228return createdFile->OpenAsync(FileAccessMode::ReadWrite);229}).then([=](IRandomAccessStream^ newStream)230{231return RandomAccessStream::CopyAndCloseAsync(232sessionData->GetInputStreamAt(0), newStream->GetOutputStreamAt(0));233}).then([=](UINT64 copiedBytes)234{235(void)copiedBytes; // Unused parameter236return;237});238}239240/// <summary>241/// Restores previously saved <see cref="SessionState"/>. Any <see cref="Frame"/> instances242/// registered with <see cref="RegisterFrame"/> will also restore their prior navigation243/// state, which in turn gives their active <see cref="Page"/> an opportunity restore its244/// state.245/// </summary>246/// <param name="version">A version identifier compared to the session state to prevent247/// incompatible versions of session state from reaching app code. Saved state with a248/// different version will be ignored, resulting in an empty <see cref="SessionState"/>249/// dictionary.</param>250/// <returns>An asynchronous task that reflects when session state has been read. The251/// content of <see cref="SessionState"/> should not be relied upon until this task252/// completes.</returns>253task<void> SuspensionManager::RestoreAsync(void)254{255_sessionState->Clear();256257task<StorageFile^> getFileTask(ApplicationData::Current->LocalFolder->GetFileAsync(sessionStateFilename));258return getFileTask.then([=](StorageFile^ stateFile)259{260task<BasicProperties^> getBasicPropertiesTask(stateFile->GetBasicPropertiesAsync());261return getBasicPropertiesTask.then([=](BasicProperties^ stateFileProperties)262{263auto size = unsigned int(stateFileProperties->Size);264if (size != stateFileProperties->Size) throw ref new FailureException("Session state larger than 4GB");265task<IRandomAccessStreamWithContentType^> openReadTask(stateFile->OpenReadAsync());266return openReadTask.then([=](IRandomAccessStreamWithContentType^ stateFileStream)267{268auto stateReader = ref new DataReader(stateFileStream);269return task<unsigned int>(stateReader->LoadAsync(size)).then([=](unsigned int bytesRead)270{271(void)bytesRead; // Unused parameter272// Deserialize the Session State273Object^ content = ReadObject(stateReader);274_sessionState = (Map<String^, Object^>^)content;275276// Restore any registered frames to their saved state277for (auto&& weakFrame : _registeredFrames)278{279auto frame = weakFrame->ResolvedFrame;280if (frame != nullptr)281{282frame->ClearValue(FrameSessionStateProperty);283RestoreFrameNavigationState(frame);284}285}286}, task_continuation_context::use_current());287});288});289});290}291292#pragma region Object serialization for a known set of types293294namespace295{296// Codes used for identifying serialized types297enum StreamTypes {298NullPtrType = 0,299300// Supported IPropertyValue types301UInt8Type, UInt16Type, UInt32Type, UInt64Type, Int16Type, Int32Type, Int64Type,302SingleType, DoubleType, BooleanType, Char16Type, GuidType, StringType,303304// Additional supported types305StringToObjectMapType,306307// Marker values used to ensure stream integrity308MapEndMarker309};310311void WriteString(DataWriter^ writer, String^ string)312{313writer->WriteByte(StringType);314writer->WriteUInt32(writer->MeasureString(string));315writer->WriteString(string);316}317318void WriteProperty(DataWriter^ writer, IPropertyValue^ propertyValue)319{320switch (propertyValue->Type)321{322case PropertyType::UInt8:323writer->WriteByte(UInt8Type);324writer->WriteByte(propertyValue->GetUInt8());325return;326case PropertyType::UInt16:327writer->WriteByte(UInt16Type);328writer->WriteUInt16(propertyValue->GetUInt16());329return;330case PropertyType::UInt32:331writer->WriteByte(UInt32Type);332writer->WriteUInt32(propertyValue->GetUInt32());333return;334case PropertyType::UInt64:335writer->WriteByte(UInt64Type);336writer->WriteUInt64(propertyValue->GetUInt64());337return;338case PropertyType::Int16:339writer->WriteByte(Int16Type);340writer->WriteUInt16(propertyValue->GetInt16());341return;342case PropertyType::Int32:343writer->WriteByte(Int32Type);344writer->WriteUInt32(propertyValue->GetInt32());345return;346case PropertyType::Int64:347writer->WriteByte(Int64Type);348writer->WriteUInt64(propertyValue->GetInt64());349return;350case PropertyType::Single:351writer->WriteByte(SingleType);352writer->WriteSingle(propertyValue->GetSingle());353return;354case PropertyType::Double:355writer->WriteByte(DoubleType);356writer->WriteDouble(propertyValue->GetDouble());357return;358case PropertyType::Boolean:359writer->WriteByte(BooleanType);360writer->WriteBoolean(propertyValue->GetBoolean());361return;362case PropertyType::Char16:363writer->WriteByte(Char16Type);364writer->WriteUInt16(propertyValue->GetChar16());365return;366case PropertyType::Guid:367writer->WriteByte(GuidType);368writer->WriteGuid(propertyValue->GetGuid());369return;370case PropertyType::String:371WriteString(writer, propertyValue->GetString());372return;373default:374throw ref new InvalidArgumentException("Unsupported property type");375}376}377378void WriteStringToObjectMap(DataWriter^ writer, IMap<String^, Object^>^ map)379{380writer->WriteByte(StringToObjectMapType);381writer->WriteUInt32(map->Size);382for (auto&& pair : map)383{384WriteObject(writer, pair->Key);385WriteObject(writer, pair->Value);386}387writer->WriteByte(MapEndMarker);388}389390void WriteObject(DataWriter^ writer, Object^ object)391{392if (object == nullptr)393{394writer->WriteByte(NullPtrType);395return;396}397398auto propertyObject = dynamic_cast<IPropertyValue^>(object);399if (propertyObject != nullptr)400{401WriteProperty(writer, propertyObject);402return;403}404405auto mapObject = dynamic_cast<IMap<String^, Object^>^>(object);406if (mapObject != nullptr)407{408WriteStringToObjectMap(writer, mapObject);409return;410}411412throw ref new InvalidArgumentException("Unsupported data type");413}414415String^ ReadString(DataReader^ reader)416{417int length = reader->ReadUInt32();418String^ string = reader->ReadString(length);419return string;420}421422IMap<String^, Object^>^ ReadStringToObjectMap(DataReader^ reader)423{424auto map = ref new Map<String^, Object^>();425auto size = reader->ReadUInt32();426for (unsigned int index = 0; index < size; index++)427{428auto key = safe_cast<String^>(ReadObject(reader));429auto value = ReadObject(reader);430map->Insert(key, value);431}432if (reader->ReadByte() != MapEndMarker)433{434throw ref new InvalidArgumentException("Invalid stream");435}436return map;437}438439Object^ ReadObject(DataReader^ reader)440{441auto type = reader->ReadByte();442switch (type)443{444case NullPtrType:445return nullptr;446case UInt8Type:447return reader->ReadByte();448case UInt16Type:449return reader->ReadUInt16();450case UInt32Type:451return reader->ReadUInt32();452case UInt64Type:453return reader->ReadUInt64();454case Int16Type:455return reader->ReadInt16();456case Int32Type:457return reader->ReadInt32();458case Int64Type:459return reader->ReadInt64();460case SingleType:461return reader->ReadSingle();462case DoubleType:463return reader->ReadDouble();464case BooleanType:465return reader->ReadBoolean();466case Char16Type:467return (char16_t)reader->ReadUInt16();468case GuidType:469return reader->ReadGuid();470case StringType:471return ReadString(reader);472case StringToObjectMapType:473return ReadStringToObjectMap(reader);474default:475throw ref new InvalidArgumentException("Unsupported property type");476}477}478}479480#pragma endregion481482483