This source file includes following definitions.
- get
- WeakFrame
- RegisterFrame
- UnregisterFrame
- SessionStateForFrame
- RestoreFrameNavigationState
- SaveFrameNavigationState
- SaveAsync
- RestoreAsync
- WriteString
- WriteProperty
- WriteStringToObjectMap
- WriteObject
- ReadString
- ReadStringToObjectMap
- ReadObject
#include "pch.h"
#include "SuspensionManager.h"
#include <collection.h>
#include <algorithm>
using namespace SDKSample::Common;
using namespace Concurrency;
using namespace Platform;
using namespace Platform::Collections;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::Storage;
using namespace Windows::Storage::FileProperties;
using namespace Windows::Storage::Streams;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Interop;
namespace
{
Map<String^, Object^>^ _sessionState = ref new Map<String^, Object^>();
String^ sessionStateFilename = "_sessionState.dat";
void WriteObject(Windows::Storage::Streams::DataWriter^ writer, Platform::Object^ object);
Platform::Object^ ReadObject(Windows::Storage::Streams::DataReader^ reader);
}
IMap<String^, Object^>^ SuspensionManager::SessionState::get(void)
{
return _sessionState;
}
private ref class WeakFrame sealed
{
private:
WeakReference _frameReference;
internal:
WeakFrame(Frame^ frame) { _frameReference = frame; }
property Frame^ ResolvedFrame
{
Frame^ get(void) { return _frameReference.Resolve<Frame>(); }
};
};
namespace
{
std::vector<WeakFrame^> _registeredFrames;
DependencyProperty^ FrameSessionStateKeyProperty =
DependencyProperty::RegisterAttached("_FrameSessionStateKeyProperty",
TypeName(String::typeid), TypeName(SuspensionManager::typeid), nullptr);
DependencyProperty^ FrameSessionStateProperty =
DependencyProperty::RegisterAttached("_FrameSessionStateProperty",
TypeName(IMap<String^, Object^>::typeid), TypeName(SuspensionManager::typeid), nullptr);
}
void SuspensionManager::RegisterFrame(Frame^ frame, String^ sessionStateKey)
{
if (frame->GetValue(FrameSessionStateKeyProperty) != nullptr)
{
throw ref new FailureException("Frames can only be registered to one session state key");
}
if (frame->GetValue(FrameSessionStateProperty) != nullptr)
{
throw ref new FailureException("Frames must be either be registered before accessing frame session state, or not registered at all");
}
frame->SetValue(FrameSessionStateKeyProperty, sessionStateKey);
_registeredFrames.insert(_registeredFrames.begin(), ref new WeakFrame(frame));
RestoreFrameNavigationState(frame);
}
void SuspensionManager::UnregisterFrame(Frame^ frame)
{
auto key = safe_cast<String^>(frame->GetValue(FrameSessionStateKeyProperty));
if (SessionState->HasKey(key)) SessionState->Remove(key);
_registeredFrames.erase(
std::remove_if(_registeredFrames.begin(), _registeredFrames.end(), [=](WeakFrame^& e)
{
auto testFrame = e->ResolvedFrame;
return testFrame == nullptr || testFrame == frame;
}),
_registeredFrames.end()
);
}
IMap<String^, Object^>^ SuspensionManager::SessionStateForFrame(Frame^ frame)
{
auto frameState = safe_cast<IMap<String^, Object^>^>(frame->GetValue(FrameSessionStateProperty));
if (frameState == nullptr)
{
auto frameSessionKey = safe_cast<String^>(frame->GetValue(FrameSessionStateKeyProperty));
if (frameSessionKey != nullptr)
{
if (!_sessionState->HasKey(frameSessionKey))
{
_sessionState->Insert(frameSessionKey, ref new Map<String^, Object^>());
}
frameState = safe_cast<IMap<String^, Object^>^>(_sessionState->Lookup(frameSessionKey));
}
else
{
frameState = ref new Map<String^, Object^>();
}
frame->SetValue(FrameSessionStateProperty, frameState);
}
return frameState;
}
void SuspensionManager::RestoreFrameNavigationState(Frame^ frame)
{
auto frameState = SessionStateForFrame(frame);
if (frameState->HasKey("Navigation"))
{
frame->SetNavigationState(safe_cast<String^>(frameState->Lookup("Navigation")));
}
}
void SuspensionManager::SaveFrameNavigationState(Frame^ frame)
{
auto frameState = SessionStateForFrame(frame);
frameState->Insert("Navigation", frame->GetNavigationState());
}
task<void> SuspensionManager::SaveAsync(void)
{
for (auto&& weakFrame : _registeredFrames)
{
auto frame = weakFrame->ResolvedFrame;
if (frame != nullptr) SaveFrameNavigationState(frame);
}
auto sessionData = ref new InMemoryRandomAccessStream();
auto sessionDataWriter = ref new DataWriter(sessionData->GetOutputStreamAt(0));
WriteObject(sessionDataWriter, _sessionState);
return task<unsigned int>(sessionDataWriter->StoreAsync()).then([=](unsigned int)
{
return sessionDataWriter->FlushAsync();
}).then([=](bool flushSucceeded)
{
(void)flushSucceeded;
return ApplicationData::Current->LocalFolder->CreateFileAsync(sessionStateFilename,
CreationCollisionOption::ReplaceExisting);
}).then([=](StorageFile^ createdFile)
{
return createdFile->OpenAsync(FileAccessMode::ReadWrite);
}).then([=](IRandomAccessStream^ newStream)
{
return RandomAccessStream::CopyAndCloseAsync(
sessionData->GetInputStreamAt(0), newStream->GetOutputStreamAt(0));
}).then([=](UINT64 copiedBytes)
{
(void)copiedBytes;
return;
});
}
task<void> SuspensionManager::RestoreAsync(void)
{
_sessionState->Clear();
task<StorageFile^> getFileTask(ApplicationData::Current->LocalFolder->GetFileAsync(sessionStateFilename));
return getFileTask.then([=](StorageFile^ stateFile)
{
task<BasicProperties^> getBasicPropertiesTask(stateFile->GetBasicPropertiesAsync());
return getBasicPropertiesTask.then([=](BasicProperties^ stateFileProperties)
{
auto size = unsigned int(stateFileProperties->Size);
if (size != stateFileProperties->Size) throw ref new FailureException("Session state larger than 4GB");
task<IRandomAccessStreamWithContentType^> openReadTask(stateFile->OpenReadAsync());
return openReadTask.then([=](IRandomAccessStreamWithContentType^ stateFileStream)
{
auto stateReader = ref new DataReader(stateFileStream);
return task<unsigned int>(stateReader->LoadAsync(size)).then([=](unsigned int bytesRead)
{
(void)bytesRead;
Object^ content = ReadObject(stateReader);
_sessionState = (Map<String^, Object^>^)content;
for (auto&& weakFrame : _registeredFrames)
{
auto frame = weakFrame->ResolvedFrame;
if (frame != nullptr)
{
frame->ClearValue(FrameSessionStateProperty);
RestoreFrameNavigationState(frame);
}
}
}, task_continuation_context::use_current());
});
});
});
}
#pragma region Object serialization for a known set of types
namespace
{
enum StreamTypes {
NullPtrType = 0,
UInt8Type, UInt16Type, UInt32Type, UInt64Type, Int16Type, Int32Type, Int64Type,
SingleType, DoubleType, BooleanType, Char16Type, GuidType, StringType,
StringToObjectMapType,
MapEndMarker
};
void WriteString(DataWriter^ writer, String^ string)
{
writer->WriteByte(StringType);
writer->WriteUInt32(writer->MeasureString(string));
writer->WriteString(string);
}
void WriteProperty(DataWriter^ writer, IPropertyValue^ propertyValue)
{
switch (propertyValue->Type)
{
case PropertyType::UInt8:
writer->WriteByte(UInt8Type);
writer->WriteByte(propertyValue->GetUInt8());
return;
case PropertyType::UInt16:
writer->WriteByte(UInt16Type);
writer->WriteUInt16(propertyValue->GetUInt16());
return;
case PropertyType::UInt32:
writer->WriteByte(UInt32Type);
writer->WriteUInt32(propertyValue->GetUInt32());
return;
case PropertyType::UInt64:
writer->WriteByte(UInt64Type);
writer->WriteUInt64(propertyValue->GetUInt64());
return;
case PropertyType::Int16:
writer->WriteByte(Int16Type);
writer->WriteUInt16(propertyValue->GetInt16());
return;
case PropertyType::Int32:
writer->WriteByte(Int32Type);
writer->WriteUInt32(propertyValue->GetInt32());
return;
case PropertyType::Int64:
writer->WriteByte(Int64Type);
writer->WriteUInt64(propertyValue->GetInt64());
return;
case PropertyType::Single:
writer->WriteByte(SingleType);
writer->WriteSingle(propertyValue->GetSingle());
return;
case PropertyType::Double:
writer->WriteByte(DoubleType);
writer->WriteDouble(propertyValue->GetDouble());
return;
case PropertyType::Boolean:
writer->WriteByte(BooleanType);
writer->WriteBoolean(propertyValue->GetBoolean());
return;
case PropertyType::Char16:
writer->WriteByte(Char16Type);
writer->WriteUInt16(propertyValue->GetChar16());
return;
case PropertyType::Guid:
writer->WriteByte(GuidType);
writer->WriteGuid(propertyValue->GetGuid());
return;
case PropertyType::String:
WriteString(writer, propertyValue->GetString());
return;
default:
throw ref new InvalidArgumentException("Unsupported property type");
}
}
void WriteStringToObjectMap(DataWriter^ writer, IMap<String^, Object^>^ map)
{
writer->WriteByte(StringToObjectMapType);
writer->WriteUInt32(map->Size);
for (auto&& pair : map)
{
WriteObject(writer, pair->Key);
WriteObject(writer, pair->Value);
}
writer->WriteByte(MapEndMarker);
}
void WriteObject(DataWriter^ writer, Object^ object)
{
if (object == nullptr)
{
writer->WriteByte(NullPtrType);
return;
}
auto propertyObject = dynamic_cast<IPropertyValue^>(object);
if (propertyObject != nullptr)
{
WriteProperty(writer, propertyObject);
return;
}
auto mapObject = dynamic_cast<IMap<String^, Object^>^>(object);
if (mapObject != nullptr)
{
WriteStringToObjectMap(writer, mapObject);
return;
}
throw ref new InvalidArgumentException("Unsupported data type");
}
String^ ReadString(DataReader^ reader)
{
int length = reader->ReadUInt32();
String^ string = reader->ReadString(length);
return string;
}
IMap<String^, Object^>^ ReadStringToObjectMap(DataReader^ reader)
{
auto map = ref new Map<String^, Object^>();
auto size = reader->ReadUInt32();
for (unsigned int index = 0; index < size; index++)
{
auto key = safe_cast<String^>(ReadObject(reader));
auto value = ReadObject(reader);
map->Insert(key, value);
}
if (reader->ReadByte() != MapEndMarker)
{
throw ref new InvalidArgumentException("Invalid stream");
}
return map;
}
Object^ ReadObject(DataReader^ reader)
{
auto type = reader->ReadByte();
switch (type)
{
case NullPtrType:
return nullptr;
case UInt8Type:
return reader->ReadByte();
case UInt16Type:
return reader->ReadUInt16();
case UInt32Type:
return reader->ReadUInt32();
case UInt64Type:
return reader->ReadUInt64();
case Int16Type:
return reader->ReadInt16();
case Int32Type:
return reader->ReadInt32();
case Int64Type:
return reader->ReadInt64();
case SingleType:
return reader->ReadSingle();
case DoubleType:
return reader->ReadDouble();
case BooleanType:
return reader->ReadBoolean();
case Char16Type:
return (char16_t)reader->ReadUInt16();
case GuidType:
return reader->ReadGuid();
case StringType:
return ReadString(reader);
case StringToObjectMapType:
return ReadStringToObjectMap(reader);
default:
throw ref new InvalidArgumentException("Unsupported property type");
}
}
}
#pragma endregion