Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/samples/winrt/ImageManipulations/common/suspensionmanager.cpp
16339 views
1
//*********************************************************
2
//
3
// Copyright (c) Microsoft. All rights reserved.
4
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
5
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
6
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
7
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
8
//
9
//*********************************************************
10
11
//
12
// SuspensionManager.cpp
13
// Implementation of the SuspensionManager class
14
//
15
16
#include "pch.h"
17
#include "SuspensionManager.h"
18
19
#include <collection.h>
20
#include <algorithm>
21
22
using namespace SDKSample::Common;
23
24
using namespace Concurrency;
25
using namespace Platform;
26
using namespace Platform::Collections;
27
using namespace Windows::Foundation;
28
using namespace Windows::Foundation::Collections;
29
using namespace Windows::Storage;
30
using namespace Windows::Storage::FileProperties;
31
using namespace Windows::Storage::Streams;
32
using namespace Windows::UI::Xaml;
33
using namespace Windows::UI::Xaml::Controls;
34
using namespace Windows::UI::Xaml::Interop;
35
36
namespace
37
{
38
Map<String^, Object^>^ _sessionState = ref new Map<String^, Object^>();
39
String^ sessionStateFilename = "_sessionState.dat";
40
41
// Forward declarations for object object read / write support
42
void WriteObject(Windows::Storage::Streams::DataWriter^ writer, Platform::Object^ object);
43
Platform::Object^ ReadObject(Windows::Storage::Streams::DataReader^ reader);
44
}
45
46
/// <summary>
47
/// Provides access to global session state for the current session. This state is serialized by
48
/// <see cref="SaveAsync"/> and restored by <see cref="RestoreAsync"/> which require values to be
49
/// one of the following: boxed values including integers, floating-point singles and doubles,
50
/// wide characters, boolean, Strings and Guids, or Map<String^, Object^> where map values are
51
/// subject to the same constraints. Session state should be as compact as possible.
52
/// </summary>
53
IMap<String^, Object^>^ SuspensionManager::SessionState::get(void)
54
{
55
return _sessionState;
56
}
57
58
/// <summary>
59
/// Wrap a WeakReference as a reference object for use in a collection.
60
/// </summary>
61
private ref class WeakFrame sealed
62
{
63
private:
64
WeakReference _frameReference;
65
66
internal:
67
WeakFrame(Frame^ frame) { _frameReference = frame; }
68
property Frame^ ResolvedFrame
69
{
70
Frame^ get(void) { return _frameReference.Resolve<Frame>(); }
71
};
72
};
73
74
namespace
75
{
76
std::vector<WeakFrame^> _registeredFrames;
77
DependencyProperty^ FrameSessionStateKeyProperty =
78
DependencyProperty::RegisterAttached("_FrameSessionStateKeyProperty",
79
TypeName(String::typeid), TypeName(SuspensionManager::typeid), nullptr);
80
DependencyProperty^ FrameSessionStateProperty =
81
DependencyProperty::RegisterAttached("_FrameSessionStateProperty",
82
TypeName(IMap<String^, Object^>::typeid), TypeName(SuspensionManager::typeid), nullptr);
83
}
84
85
/// <summary>
86
/// Registers a <see cref="Frame"/> instance to allow its navigation history to be saved to
87
/// and restored from <see cref="SessionState"/>. Frames should be registered once
88
/// immediately after creation if they will participate in session state management. Upon
89
/// registration if state has already been restored for the specified key
90
/// the navigation history will immediately be restored. Subsequent invocations of
91
/// <see cref="RestoreAsync(String)"/> will also restore navigation history.
92
/// </summary>
93
/// <param name="frame">An instance whose navigation history should be managed by
94
/// <see cref="SuspensionManager"/></param>
95
/// <param name="sessionStateKey">A unique key into <see cref="SessionState"/> used to
96
/// store navigation-related information.</param>
97
void SuspensionManager::RegisterFrame(Frame^ frame, String^ sessionStateKey)
98
{
99
if (frame->GetValue(FrameSessionStateKeyProperty) != nullptr)
100
{
101
throw ref new FailureException("Frames can only be registered to one session state key");
102
}
103
104
if (frame->GetValue(FrameSessionStateProperty) != nullptr)
105
{
106
throw ref new FailureException("Frames must be either be registered before accessing frame session state, or not registered at all");
107
}
108
109
// Use a dependency property to associate the session key with a frame, and keep a list of frames whose
110
// navigation state should be managed
111
frame->SetValue(FrameSessionStateKeyProperty, sessionStateKey);
112
_registeredFrames.insert(_registeredFrames.begin(), ref new WeakFrame(frame));
113
114
// Check to see if navigation state can be restored
115
RestoreFrameNavigationState(frame);
116
}
117
118
/// <summary>
119
/// Disassociates a <see cref="Frame"/> previously registered by <see cref="RegisterFrame"/>
120
/// from <see cref="SessionState"/>. Any navigation state previously captured will be
121
/// removed.
122
/// </summary>
123
/// <param name="frame">An instance whose navigation history should no longer be
124
/// managed.</param>
125
void SuspensionManager::UnregisterFrame(Frame^ frame)
126
{
127
// Remove session state and remove the frame from the list of frames whose navigation
128
// state will be saved (along with any weak references that are no longer reachable)
129
auto key = safe_cast<String^>(frame->GetValue(FrameSessionStateKeyProperty));
130
if (SessionState->HasKey(key)) SessionState->Remove(key);
131
_registeredFrames.erase(
132
std::remove_if(_registeredFrames.begin(), _registeredFrames.end(), [=](WeakFrame^& e)
133
{
134
auto testFrame = e->ResolvedFrame;
135
return testFrame == nullptr || testFrame == frame;
136
}),
137
_registeredFrames.end()
138
);
139
}
140
141
/// <summary>
142
/// Provides storage for session state associated with the specified <see cref="Frame"/>.
143
/// Frames that have been previously registered with <see cref="RegisterFrame"/> have
144
/// their session state saved and restored automatically as a part of the global
145
/// <see cref="SessionState"/>. Frames that are not registered have transient state
146
/// that can still be useful when restoring pages that have been discarded from the
147
/// navigation cache.
148
/// </summary>
149
/// <remarks>Apps may choose to rely on <see cref="LayoutAwarePage"/> to manage
150
/// page-specific state instead of working with frame session state directly.</remarks>
151
/// <param name="frame">The instance for which session state is desired.</param>
152
/// <returns>A collection of state subject to the same serialization mechanism as
153
/// <see cref="SessionState"/>.</returns>
154
IMap<String^, Object^>^ SuspensionManager::SessionStateForFrame(Frame^ frame)
155
{
156
auto frameState = safe_cast<IMap<String^, Object^>^>(frame->GetValue(FrameSessionStateProperty));
157
158
if (frameState == nullptr)
159
{
160
auto frameSessionKey = safe_cast<String^>(frame->GetValue(FrameSessionStateKeyProperty));
161
if (frameSessionKey != nullptr)
162
{
163
// Registered frames reflect the corresponding session state
164
if (!_sessionState->HasKey(frameSessionKey))
165
{
166
_sessionState->Insert(frameSessionKey, ref new Map<String^, Object^>());
167
}
168
frameState = safe_cast<IMap<String^, Object^>^>(_sessionState->Lookup(frameSessionKey));
169
}
170
else
171
{
172
// Frames that aren't registered have transient state
173
frameState = ref new Map<String^, Object^>();
174
}
175
frame->SetValue(FrameSessionStateProperty, frameState);
176
}
177
return frameState;
178
}
179
180
void SuspensionManager::RestoreFrameNavigationState(Frame^ frame)
181
{
182
auto frameState = SessionStateForFrame(frame);
183
if (frameState->HasKey("Navigation"))
184
{
185
frame->SetNavigationState(safe_cast<String^>(frameState->Lookup("Navigation")));
186
}
187
}
188
189
void SuspensionManager::SaveFrameNavigationState(Frame^ frame)
190
{
191
auto frameState = SessionStateForFrame(frame);
192
frameState->Insert("Navigation", frame->GetNavigationState());
193
}
194
195
/// <summary>
196
/// Save the current <see cref="SessionState"/>. Any <see cref="Frame"/> instances
197
/// registered with <see cref="RegisterFrame"/> will also preserve their current
198
/// navigation stack, which in turn gives their active <see cref="Page"/> an opportunity
199
/// to save its state.
200
/// </summary>
201
/// <returns>An asynchronous task that reflects when session state has been saved.</returns>
202
task<void> SuspensionManager::SaveAsync(void)
203
{
204
// Save the navigation state for all registered frames
205
for (auto&& weakFrame : _registeredFrames)
206
{
207
auto frame = weakFrame->ResolvedFrame;
208
if (frame != nullptr) SaveFrameNavigationState(frame);
209
}
210
211
// Serialize the session state synchronously to avoid asynchronous access to shared
212
// state
213
auto sessionData = ref new InMemoryRandomAccessStream();
214
auto sessionDataWriter = ref new DataWriter(sessionData->GetOutputStreamAt(0));
215
WriteObject(sessionDataWriter, _sessionState);
216
217
// Once session state has been captured synchronously, begin the asynchronous process
218
// of writing the result to disk
219
return task<unsigned int>(sessionDataWriter->StoreAsync()).then([=](unsigned int)
220
{
221
return sessionDataWriter->FlushAsync();
222
}).then([=](bool flushSucceeded)
223
{
224
(void)flushSucceeded; // Unused parameter
225
return ApplicationData::Current->LocalFolder->CreateFileAsync(sessionStateFilename,
226
CreationCollisionOption::ReplaceExisting);
227
}).then([=](StorageFile^ createdFile)
228
{
229
return createdFile->OpenAsync(FileAccessMode::ReadWrite);
230
}).then([=](IRandomAccessStream^ newStream)
231
{
232
return RandomAccessStream::CopyAndCloseAsync(
233
sessionData->GetInputStreamAt(0), newStream->GetOutputStreamAt(0));
234
}).then([=](UINT64 copiedBytes)
235
{
236
(void)copiedBytes; // Unused parameter
237
return;
238
});
239
}
240
241
/// <summary>
242
/// Restores previously saved <see cref="SessionState"/>. Any <see cref="Frame"/> instances
243
/// registered with <see cref="RegisterFrame"/> will also restore their prior navigation
244
/// state, which in turn gives their active <see cref="Page"/> an opportunity restore its
245
/// state.
246
/// </summary>
247
/// <param name="version">A version identifier compared to the session state to prevent
248
/// incompatible versions of session state from reaching app code. Saved state with a
249
/// different version will be ignored, resulting in an empty <see cref="SessionState"/>
250
/// dictionary.</param>
251
/// <returns>An asynchronous task that reflects when session state has been read. The
252
/// content of <see cref="SessionState"/> should not be relied upon until this task
253
/// completes.</returns>
254
task<void> SuspensionManager::RestoreAsync(void)
255
{
256
_sessionState->Clear();
257
258
task<StorageFile^> getFileTask(ApplicationData::Current->LocalFolder->GetFileAsync(sessionStateFilename));
259
return getFileTask.then([=](StorageFile^ stateFile)
260
{
261
task<BasicProperties^> getBasicPropertiesTask(stateFile->GetBasicPropertiesAsync());
262
return getBasicPropertiesTask.then([=](BasicProperties^ stateFileProperties)
263
{
264
auto size = unsigned int(stateFileProperties->Size);
265
if (size != stateFileProperties->Size) throw ref new FailureException("Session state larger than 4GB");
266
task<IRandomAccessStreamWithContentType^> openReadTask(stateFile->OpenReadAsync());
267
return openReadTask.then([=](IRandomAccessStreamWithContentType^ stateFileStream)
268
{
269
auto stateReader = ref new DataReader(stateFileStream);
270
return task<unsigned int>(stateReader->LoadAsync(size)).then([=](unsigned int bytesRead)
271
{
272
(void)bytesRead; // Unused parameter
273
// Deserialize the Session State
274
Object^ content = ReadObject(stateReader);
275
_sessionState = (Map<String^, Object^>^)content;
276
277
// Restore any registered frames to their saved state
278
for (auto&& weakFrame : _registeredFrames)
279
{
280
auto frame = weakFrame->ResolvedFrame;
281
if (frame != nullptr)
282
{
283
frame->ClearValue(FrameSessionStateProperty);
284
RestoreFrameNavigationState(frame);
285
}
286
}
287
}, task_continuation_context::use_current());
288
});
289
});
290
});
291
}
292
293
#pragma region Object serialization for a known set of types
294
295
namespace
296
{
297
// Codes used for identifying serialized types
298
enum StreamTypes {
299
NullPtrType = 0,
300
301
// Supported IPropertyValue types
302
UInt8Type, UInt16Type, UInt32Type, UInt64Type, Int16Type, Int32Type, Int64Type,
303
SingleType, DoubleType, BooleanType, Char16Type, GuidType, StringType,
304
305
// Additional supported types
306
StringToObjectMapType,
307
308
// Marker values used to ensure stream integrity
309
MapEndMarker
310
};
311
312
void WriteString(DataWriter^ writer, String^ string)
313
{
314
writer->WriteByte(StringType);
315
writer->WriteUInt32(writer->MeasureString(string));
316
writer->WriteString(string);
317
}
318
319
void WriteProperty(DataWriter^ writer, IPropertyValue^ propertyValue)
320
{
321
switch (propertyValue->Type)
322
{
323
case PropertyType::UInt8:
324
writer->WriteByte(UInt8Type);
325
writer->WriteByte(propertyValue->GetUInt8());
326
return;
327
case PropertyType::UInt16:
328
writer->WriteByte(UInt16Type);
329
writer->WriteUInt16(propertyValue->GetUInt16());
330
return;
331
case PropertyType::UInt32:
332
writer->WriteByte(UInt32Type);
333
writer->WriteUInt32(propertyValue->GetUInt32());
334
return;
335
case PropertyType::UInt64:
336
writer->WriteByte(UInt64Type);
337
writer->WriteUInt64(propertyValue->GetUInt64());
338
return;
339
case PropertyType::Int16:
340
writer->WriteByte(Int16Type);
341
writer->WriteUInt16(propertyValue->GetInt16());
342
return;
343
case PropertyType::Int32:
344
writer->WriteByte(Int32Type);
345
writer->WriteUInt32(propertyValue->GetInt32());
346
return;
347
case PropertyType::Int64:
348
writer->WriteByte(Int64Type);
349
writer->WriteUInt64(propertyValue->GetInt64());
350
return;
351
case PropertyType::Single:
352
writer->WriteByte(SingleType);
353
writer->WriteSingle(propertyValue->GetSingle());
354
return;
355
case PropertyType::Double:
356
writer->WriteByte(DoubleType);
357
writer->WriteDouble(propertyValue->GetDouble());
358
return;
359
case PropertyType::Boolean:
360
writer->WriteByte(BooleanType);
361
writer->WriteBoolean(propertyValue->GetBoolean());
362
return;
363
case PropertyType::Char16:
364
writer->WriteByte(Char16Type);
365
writer->WriteUInt16(propertyValue->GetChar16());
366
return;
367
case PropertyType::Guid:
368
writer->WriteByte(GuidType);
369
writer->WriteGuid(propertyValue->GetGuid());
370
return;
371
case PropertyType::String:
372
WriteString(writer, propertyValue->GetString());
373
return;
374
default:
375
throw ref new InvalidArgumentException("Unsupported property type");
376
}
377
}
378
379
void WriteStringToObjectMap(DataWriter^ writer, IMap<String^, Object^>^ map)
380
{
381
writer->WriteByte(StringToObjectMapType);
382
writer->WriteUInt32(map->Size);
383
for (auto&& pair : map)
384
{
385
WriteObject(writer, pair->Key);
386
WriteObject(writer, pair->Value);
387
}
388
writer->WriteByte(MapEndMarker);
389
}
390
391
void WriteObject(DataWriter^ writer, Object^ object)
392
{
393
if (object == nullptr)
394
{
395
writer->WriteByte(NullPtrType);
396
return;
397
}
398
399
auto propertyObject = dynamic_cast<IPropertyValue^>(object);
400
if (propertyObject != nullptr)
401
{
402
WriteProperty(writer, propertyObject);
403
return;
404
}
405
406
auto mapObject = dynamic_cast<IMap<String^, Object^>^>(object);
407
if (mapObject != nullptr)
408
{
409
WriteStringToObjectMap(writer, mapObject);
410
return;
411
}
412
413
throw ref new InvalidArgumentException("Unsupported data type");
414
}
415
416
String^ ReadString(DataReader^ reader)
417
{
418
int length = reader->ReadUInt32();
419
String^ string = reader->ReadString(length);
420
return string;
421
}
422
423
IMap<String^, Object^>^ ReadStringToObjectMap(DataReader^ reader)
424
{
425
auto map = ref new Map<String^, Object^>();
426
auto size = reader->ReadUInt32();
427
for (unsigned int index = 0; index < size; index++)
428
{
429
auto key = safe_cast<String^>(ReadObject(reader));
430
auto value = ReadObject(reader);
431
map->Insert(key, value);
432
}
433
if (reader->ReadByte() != MapEndMarker)
434
{
435
throw ref new InvalidArgumentException("Invalid stream");
436
}
437
return map;
438
}
439
440
Object^ ReadObject(DataReader^ reader)
441
{
442
auto type = reader->ReadByte();
443
switch (type)
444
{
445
case NullPtrType:
446
return nullptr;
447
case UInt8Type:
448
return reader->ReadByte();
449
case UInt16Type:
450
return reader->ReadUInt16();
451
case UInt32Type:
452
return reader->ReadUInt32();
453
case UInt64Type:
454
return reader->ReadUInt64();
455
case Int16Type:
456
return reader->ReadInt16();
457
case Int32Type:
458
return reader->ReadInt32();
459
case Int64Type:
460
return reader->ReadInt64();
461
case SingleType:
462
return reader->ReadSingle();
463
case DoubleType:
464
return reader->ReadDouble();
465
case BooleanType:
466
return reader->ReadBoolean();
467
case Char16Type:
468
return (char16_t)reader->ReadUInt16();
469
case GuidType:
470
return reader->ReadGuid();
471
case StringType:
472
return ReadString(reader);
473
case StringToObjectMapType:
474
return ReadStringToObjectMap(reader);
475
default:
476
throw ref new InvalidArgumentException("Unsupported property type");
477
}
478
}
479
}
480
481
#pragma endregion
482
483