Экзамен 70-485 Advanced Windows Store App Development Using C#


    Пока все прогрессивные разработчики предвкушают выход Windows 10 и готовятся разрабатывать приложения под нее, я, после сдачи теста 70-485, хочу поделиться с вами углубленными возможностями разработки приложений под Windows 8 и 8.1. Благо, у разработки по 10 и под 8.1 очень много общего. Эта статья по задумке должна помочь вам сдать экзамен 70-485, но может быть интересна в любом случае, если вы разрабатываете приложения Windows Store. Она содержит в себе некоторые must know сниппеты, описание возможностей и классов WinRT, а также некоторые моменты, по которым могут быть заданы каверзные вопросы.
    Под катом обо всем понемногу. Так как все охватить невозможно, получилась своеобразная памятка.

    Описание экзамена доступно по ссылке: Экзамен 70-485 Advanced Windows Store App Development Using C#
    Советую ознакомиться размещенным там списком тем, по которым будут заданы вопросы. Как и при всех подобных тестированиях, после окончания теста вам будет доступна распечатка с процентом верных ответов по каждой из тем, а значит, для успешной сдачи экзамена необходимо иметь знания по всем темам.

    Сенсоры (пространство Windows.Device.Sensors)
    Accelerometer – Ускорение. Этот сенсор возвращает значения ускорения по осям x, y и z. Проще выражаясь, это движения влево-вправо/к себе и от себя. Также поддерживает событие shaken (при тряске).
    Gyrometer — Угловая скорость/вращение. Возвращает значения относительно осей x, y или z. Другими словами — вращение не сдвигая ось.
    Акселерометры дают верные значения при действиях долгой продолжительности, но выдают много шумов при коротких действиях. Гирометры наоборот хороши при коротких действиях, но дрифтуют при длинных. Желательно использовать оба сенсора для калибровки друг друга.
    Compass – возвращает направление севера и, при наличии возможности, магнитного севера
    Inclinometer — Уклономер pitch, roll, and yaw значения, которые соответствуют углам вращения вокруг осей x, y, и z
    LightSensor – сенсор светочувствительности.
    OrientationSensor – возвращает матрицу поворота и кватернион
    SimpleOrientationSensor – текущее положение устройства, которое в качестве результата выдает положение устройства в виде одного из значений enumeration. Например: Rotated90DegreesCounterclockwise, Faceup, Facedown, NotRotated
    Proximity – сенсор близости объекта — NFC
    Еще есть сенсоры: Barometer, Magnetometer, Altimeter
    По следующей ссылке отличное видео, которое демонстрирует возможности сенсоров: Choosing the right sensor
    Практически все API сенсоров идентичны (немного выделяется SimpleOrientationSensor). Сперва, вы получаете ссылку на сенсор с помощью метода GetDefault() соответствующего класса сенсора, затем вы устанавливаете значение свойству ReportInterval (в миллисекундах), для того, чтобы дать системе знать, как часто проверять изменения значений и как много ресурсов нужно выделить для сенсора. Почти все классы используют довольно похожее событие ReadingChanged для обработки изменений значений сенсора. Акселерометр поддерживает также событие Shaken, а у сенсора SimpleOrientationSensor, в отличие от других, событием изменения ориентации экрана служит событие OrientationChanged.
    При установке ReportInterval следует учитывать, что нельзя установить значение меньшее, чем поддерживается устройством, иначе будет вызвано исключение. Нужно с помощью свойства сенсора MinimumReportInterval узнать минимально поддерживаемый интервал и сравнить его с устанавливаемым интервалом. Если значением ReportInterval установить 0, то устройством будет использован интервал по умолчанию.
    Рассмотрим пример использования акселерометра:
    using Windows.Devices.Sensors;
    
    // в MainPage после this.InitializeComponent()
     Accelerometer _accelerometer = Accelerometer.GetDefault();
          if (_accelerometer != null)
                {
    uint minReportInterval = _accelerometer.MinimumReportInterval;
    uint reportInterval = minReportInterval > 20 ? minReportInterval : 20;
    _accelerometer.ReportInterval = reportInterval;
    
    _accelerometer.ReadingChanged += new TypedEventHandler<Accelerometer, AccelerometerReadingChangedEventArgs>(ReadingChanged);
                }
    // ……… здесь пропущен какой-то код
            private async void ReadingChanged(object sender, AccelerometerReadingChangedEventArgs e)
            {
                await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                {
                    AccelerometerReading reading = e.Reading;
                    txtXAxis.Text = String.Format("{0,5:0.00}", reading.AccelerationX);
                    txtZAxis.Text = String.Format("{0,5:0.00}", reading.AccelerationZ);
                });
            }
            // Dispatcher.RunAsync необходим, чтобы код выполнялся в потоке UI
    

    Работа с камерой
    Есть два стандартных варианта работы с камерой. Первый способ (более простой) это возложить «миссию» на стандартный API и использовать класс CameraCaptureUI. Второй способ – использовать MediaCapture.
    Использование CameraCaptureUI
    Нам понадобится добавить в XAML элемент MediaElement для отображения захватываемого видео:
    <MediaElement x:Name="mediaPreivew" Width="320" />
    

    Теперь можно с помощью подобного кода получить видео с камеры и отобразить его в приложении:
        CameraCaptureUI cameraUI = new CameraCaptureUI();   
        Windows.Storage.StorageFile capturedMedia = await cameraUI.CaptureFileAsync(CameraCaptureUIMode.Video);
        if (capturedMedia != null)
        {
            var stream = await capturedMedia.OpenAsync(FileAccessMode.Read);
            mediaPreivew.SetSource(stream, capturedMedia.ContentType);
            mediaPreivew.Play();
        }
    

    Или второй вариант кода с записью в файл (без предпросмотра):
    Windows.Media.Capture.CameraCaptureUI dialog = new Windows.Media.Capture.CameraCaptureUI();
    Size aspectRatio = new Size(16, 9);
    dialog.PhotoSettings.CroppedAspectRatio = aspectRatio;
    dialog.PhotoSettings.AllowCropping = true;
    Windows.Storage.StorageFile file = await dialog.CaptureFileAsync(Windows.Media.Capture.CameraCaptureUIMode.Photo);
    

    Использование MediaCapture
    В манифесте приложения обязательно необходимо добавить разрешения на использование веб камеры и микрофона.
    Код для захвата:
       _mediaCapture = new Windows.Media.Capture.MediaCapture();
       await _mediaCapture.InitializeAsync();
    // фиксируем ориентацию экрана устройства
    Windows.Graphics.Display.DisplayInformation.AutoRotationPreferences = 
                                                            Windows.Graphics.Display.DisplayOrientations.Landscape; 
    var videoFile = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync("recording.mp4",
                                                             Windows.Storage.CreationCollisionOption.ReplaceExisting);                    
    var profile = Windows.Media.MediaProperties.MediaEncodingProfile.CreateMp4(Windows.Media.MediaProperties.VideoEncodingQuality.HD1080p);  
    await _mediaCapture.StartRecordToStorageFileAsync(profile, videoFile);
    

    При работе с MediaCapture необходимо обязательно освобождать все ресурсы перед закрытием приложения:
    По умолчанию, содержимым OnSuspending является такой вот код:
        private async void OnSuspending(object sender, SuspendingEventArgs e)
            {
                var deferral = e.SuspendingOperation.GetDeferral();
                //TODO: Save application state and stop any background activity
                deferral.Complete();
            }
    

    Здесь между объявлением deferral (англ. – отсрочка) и его завершением должен быть вставлен код, который выполняется при приостановке работы. Зачем же здесь нужен этот deferral? Приостановка работы приложения может быть прервана и тогда выполнение метода будет прервано на середине. Чтобы это не произошло, и система дала возможность завершить процесс, мы получаем deferral и завершаем его в конце операции с помощью deferral.Complete();
    Теперь напишем асинхронную процедуру, в которой остановим захват видео и освободим ресурсы. Любая асинхронная процедура должна быть именно Task. Void желательно использовать только для процедур обработки событий (нажатия на кнопки и т.п.), то есть для тех мест, где Task никак не использовать.
           public async System.Threading.Tasks.Task disp()
            {
                await _mediaCapture.StopRecordAsync();
                await _mediaCapture.StopPreviewAsync();
                capturePreview.Source = null;
                _mediaCapture.Dispose();
            }
    

    Так как у нас _mediaCapture находится в MainPage, а OnSuspending в App.xaml.cs, то нам нужно или объявить _mediaCapture в App.xaml.cs или при вызове ссылаться на объект MainPage. Второй вариант реализовать можно с помощью такого вот небольшого трюка (вряд ли такое будет в экзамене, но это интересное решение):
    В объявления MainPage добавим:
    public static MainPage _mainpage;
    

    И в сам конструктор:
            public MainPage()
            {
                this.InitializeComponent();
                _mainpage = this;
            }
    

    Теперь можем вызвать Task так:
    await MainPage._mainpage.disp();
    

    Как вариант, можно добавить XAML элемент:
    <CaptureElement Name="capturePreview" Height="400" />
    

    И вместо захвата в файл использовать предпросмотр:
         capturePreview.Source = _mediaCapture;
         await _mediaCapture.StartPreviewAsync();
    

    В отличие от Windows.Media.Capture.MediaCapture, — CameraCaptureUI не выбрасывает исключений в случае отсутствия прав, а показывает сообщение. Но помещать код в try/catch обертку нужно и там и там.
    VideoDeviceController – содержит в себе настройки камеры. Такие, как, например: Focus, Zoom, Hue, TorchControl, FlashControl, WhiteBalanceControl, Brightness, Contrast…
    Проверить присутствует ли возможность можно с помощью:
    if (MediaCapture.VideoDeviceController.Brightness.Capabilities.Supported)
    

    или в стиле JavaScript:
    if (videoDev.Brightness.Capabilities.Step != 0)
    

    Небольшой пример использования:
                var videoDev = _mediaCapture.VideoDeviceController;
                if (videoDev.Contrast.Capabilities.Supported)
                {
                    var contrastCapabilities = videoDev.Contrast.Capabilities;
                    var max = contrastCapabilities.Max;
                    videoDev.Contrast.TrySetAuto(false);
                            if (videoDev.Contrast.TrySetValue(max))
                            {
                                // контраст благополучно установлен в максимальное значение.
                            }
                }
    

    Также некоторые настройки можно установить в MediaCaptureInitializationSettings
    Windows.Media.Capture.MediaCaptureInitializationSettings _captureInitSettings = null;
    _captureInitSettings = new Windows.Media.Capture.MediaCaptureInitializationSettings();
    _captureInitSettings.VideoDeviceId = "";
    _captureInitSettings.StreamingCaptureMode=Windows.Media.Capture.StreamingCaptureMode.AudioAndVideo;
    

    И передать их в качестве параметра при инициализации
    await _mediaCapture.InitializeAsync(_captureInitSettings);
    

    Параметры, задаваемые MediaCaptureInitializationSettings потом можно получить для чтения из класса MediaCaptureSettings.

    Еще раз о dependency property
    Dependency property должно быть объявлено с модификаторами public static readonly. Пример:
    // IsSpinningProperty это идентификатор dependency property 
    // нам не нужно передавать никакого значения в параметр PropertyMetadata, поэтому указали null
            public static readonly DependencyProperty IsSpinningProperty =
        DependencyProperty.Register(
            "IsSpinning", typeof(Boolean),
            typeof(MainPage), null
        );
    
    // Далее оболочка property/свойства, так что можно вызывать это свойство, указав после названия класса через точку. 
    // Например так:  bool spin = MainPage.Current.IsSpinning;
            public bool IsSpinning
            {
                get { return (bool)GetValue(IsSpinningProperty); }
                set { SetValue(IsSpinningProperty, value); }
            }
    

    Но это все вам должно быть известно и понятно. А вот следующее может быть в новинку.
    Attached DependencyProperty – используется как эдакое глобальное property, которое может назначаться любому объекту. Одно из назначений Attached DependencyProperty это разрешить различным дочерним элементам устанавливать значения для property, которое установлено в родительском элементе. Например, DockPanel.Dock может быть установлено дочерним элементам стандартного XAML контрола Dock. Далее пример:
    public static readonly DependencyProperty IsMovableProperty = 
            DependencyProperty.RegisterAttached("IsMovable", typeof(Boolean), typeof(MainPage), new PropertyMetadata(false));
            public static void SetIsMovable(UIElement element, Boolean value)
            {
                element.SetValue(IsMovableProperty, value);
            }
            public static Boolean GetIsMovable(UIElement element)
            {
                return (Boolean)element.GetValue(IsMovableProperty);
            }
    

    Здесь объявлен объект DependencyObject который содержит в себе значение IsMovable. Объект привязан к другому объекту MainPage, так что вы можете передать значение в MainPage из любого дочернего элемента окна.
    Таким образом из кода любой контрол может передать значение объекту MainPage
      <TextBlock local:MainPage.IsMovable="True" x:Name="txtOut"></TextBlock>
    

    Изменить значение Attached DependencyProperty можно и из кода с помощью строки похожей на эту:
     MainPage.SetIsMovable(this, !(bool)this.GetValue(IsMovableProperty));
    


    Файлы, права на которые сохраняются после перезапуска приложения
           Windows.Storage.Pickers.FileOpenPicker openPicker=new Windows.Storage.Pickers.FileOpenPicker();
           Windows.Storage.StorageFile file = await openPicker.PickSingleFileAsync();
           if (file != null)   // если файл был выбран
           Windows.Storage.AccessCache.StorageApplicationPermissions.MostRecentlyUsedList.Add(file, "20150706");
    // Здесь мы сохранили файл в списке MRU - последних использованных. В качестве метаинформации мы записали дату. 
    // Можно записать какую-любую иную информацию.
    
    // Получить файл и данные можно так:
           Windows.Storage.AccessCache.AccessListEntry item =
                                      Windows.Storage.AccessCache.StorageApplicationPermissions.MostRecentlyUsedList.Entries[0];
           string metadata = item.Metadata;
    

    Для получения списка файлов
    IReadOnlyList<Windows.Storage.StorageFile> fileList = 
                                      await Windows.Storage.ApplicationData.Current.LocalFolder.GetFilesAsync();
    

    Также можно использовать GetFoldersAsync() для получения списка папок или GetItemsAsync() для того, чтобы получить сразу и файлы и папки.

    Запись в файл
           var folder = Windows.Storage.KnownFolders.PicturesLibrary;
           var file = await folder.CreateFileAsync("hello.txt", Windows.Storage.CreationCollisionOption.ReplaceExisting);
           var stream = await file.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite);
           using (var outputStream = stream.GetOutputStreamAt(0))
           {
               Windows.Storage.Streams.DataWriter dataWriter = new Windows.Storage.Streams.DataWriter(outputStream);
               dataWriter.WriteString("Some text");
               await dataWriter.StoreAsync();
               await outputStream.FlushAsync();
           }
    

    Или еще проще:
           var folder = Windows.Storage.KnownFolders.PicturesLibrary;
           var file = await folder.CreateFileAsync("hello2.txt", Windows.Storage.CreationCollisionOption.ReplaceExisting);
           await Windows.Storage.FileIO.WriteTextAsync(file, "Swift as a shadow");
    

    И в первом и во втором примере, в манифесте приложения нужно добавлять в Capabilities разрешения на доступ к Pictures Library.

    Тестирование
    В Windows Store приложениях содержится пространство имен Microsoft.VisualStudio.TestPlatform.UnitTestFramework, которое отлично тестового фреймворка .NET приложений. Атрибут ExpectedException больше не поддерживается. Вместо него используйте Assert.ThrowsExceptionМетоды, помеченные как TestInitialize/TestCleanup будут выполнены перед, а также после выполнения каждого теста. Знаете ли это удобно, когда нужно уничтожить какой-нибудь объект или изменить параметры для следующего теста.
    [TestInitialize] 
    public void TestSetup() 
    {     
    }  
     [TestCleanup] 
    public void TestCleanIt() 
    {
    }
    

    Еще более удобна появившаяся возможность поддержки облегченных дата тестов. Это возможность запустить тест с различными параметрами в качестве вводных значений. Следующий пример наглядно демонстрирует нам эту фичу:
     [DataTestMethod] 
    [DataRow("25892e17-80f6-715f-9c65-7395632f0223", "Customer #1")] 
    [DataRow("a53e98e4-0197-3513-be6d-49836e406aaa", "Customer #2")] 
    [DataRow("f2s34824-3153-2524-523d-29386e4s6as1", "Customer #3")] 
    public void GetCustomerName_RightID_ReturnExpected(String id, String customerName) 
    {     
    var obj = new SomeClass();  
    var actualCustomer = obj.CallSomeFunctionGetName(id);  
    Assert.AreEqual(customerName, actualCustomer); 
    }
    

    Этот метод выполняется несколько раз, каждый раз со строкой предоставленных данных. Первое значение в примере означает ID покупателя, второе – возвращаемый результат с именем покупателя.
    Кроме атрибута [DataTestMethod] имеется также атрибут [Microsoft.VisualStudio.TestPlatform.UnitTestFramework.AppContainer.UITestMethod], который позволяет выполнить код в потоке интерфейса. Ранее для этого необходимо было использовать dispatcher.

    Даже если вы не занимаетесь тестированием (я не тестирую свой код, а если и тестирую, то делаю это в продакшн), то вы наверняка замечали в углу счетчик с различными цифрами. Что-то вроде:
    045 025 040965 003 000 001
    Цифры, которые указаны на счетчике обозначают (слева направо):
    Composition thread FPS – частота кадров в секунду для потока композиции (composition thread). Кстати, этот поток используется для построения и анимации элементов интерфейса.
    UI thread FPS – частота кадров для потока интерфейса (UI thread)
    Memory Video – использование памяти для текстур
    Batch – количество поверхностей, которые посланы видеокарте (GPU) для прорисовки
    Composition thread CPU – время в миллисекундах, потраченное CPU на поток композиции
    UI CPU – время в миллисекундах потраченное процессором на потоки UI
    Для того чтобы показать или скрыть счетчик в углу, используем следующий код:
    public App() {     
    this.InitializeComponent();      
    DebugSettings.EnableFrameRateCounter = true; 
    }
    

    Раз уж затронули потоки композиции и потоки UI, то хотелось бы немного объяснить разницу между ними. Существуют анимации, либо иные изменения интерфейса, которые не затрагивают базовый макет и остальные объекты сцены. Такие процессы могут быть вычислены заранее. Они происходят в специально отведенном потоке композиции и разгружают таким образом поток UI. Кстати, если анимация не влияет на другие объекты, то она называется независимой.

    Чтобы определить места где графика более нагружена можно задать карту перегрузки с помощью:
    DebugSettings.IsOverdrawHeatMapEnabled=true;
    

    Места, где есть нагрузка, будут выделены более темным цветом.

    Exceptions
    Вы должны использовать событие UnhandledException только для того, чтобы совершать определенные операции, — такие как логирование ошибок или сохранения временных данных в локальное хранилище перед завершением приложения. Вы не должны перехватывать в этом событии ошибки. arg.Handled=true – это очень плохо в данном случае. Исключения, которые не связаны с XAML фреймворком, этим событием не будет перехвачены.

    Пример перехвата ошибки в асинхронном коде:
    // … какой-то код
    try
    {
    await this.DoSomethingAsync();  // вызов метода, который вызывает исключение
    }catch{}
    // … какой-то код
    private Task DoSomethingAsync(){
    // асинхронная операция
    }
    

    Если забыть await, и возникнет ошибка, то она записывается в anonymous Task и игнорируется.
    Если же забыть await для асинхронного void метода, то вызывающий код не сможет ее отловить и возникает необработанная ошибка. Программа прерывается. Вот почему void используется только для событий таких как button click. В других случаях нужно использовать Task.

    При использовании Task.WhenAll появляется AggregateException которое возвращает коллекцию ошибок, в случае если их было несколько. Для выделения конкретной ошибки можно использовать Flatten. Например:
    catch (AggregateException aex) {     
    foreach (Exception ex in aex.Flatten().InnerExceptions) {
    // какая-то обработка
    }}
    

    Task Parallel Library (TPL) предоставляет нам событие для «ловли» исключений — UnobservedTaskException.
    Это событие может быть зарегистрировано, например, при инициализации App.xaml.cs
    TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; 
    

    e.SetObserved внутри события – предотвращает прерывание выполнения кода. UnobservedTaskException это ошибка, которая несмотря на свое название является ошибкой типа AggregateException, а значит может быть Flatten.

    Для записи в лог информации об ошибках создается класс:
    using System.Diagnostics.Tracing;
    using System.Collections.Generic;
    
            class MyEventSource : EventSource
            {
                public static MyEventSource Log = new MyEventSource();
    
                public void Startup() { WriteEvent(1); }
                public void OpenFileStart(string fileName) { WriteEvent(2, fileName); }
            }
    

    И после в коде можно совершать запись в лог:
                MyEventSource.Log.Startup();
                MyEventSource.Log.OpenFileStart("SomeFile");
    

    Поиск устройств
    Для того, чтобы найти какие устройстве присутствуют в системе, можно воспользоваться классом Windows.Devices.Enumeration.DeviceInformation. В следующем примере найдем все устройства, которые могут захватывать видео:
    using Windows.Devices.Enumeration;
    // …….. какой-то код пропущен
                var interfaces = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(DeviceClass.VideoCapture);
                txtOut.Text = interfaces.Count + " найдено";
                foreach (Windows.Devices.Enumeration.DeviceInformation deviceInterface in interfaces)
                {
                    var id = "Id:" + deviceInterface.Id;
                    var name = deviceInterface.Name;
                    var isEnabled = "IsEnabled:" + deviceInterface.IsEnabled;
                    txtOut.Text = txtOut.Text + Environment.NewLine +id.ToString()+" "+name.ToString() + " " + isEnabled.ToString();
                } 
    

    Это был поиск по типу устройства.
    DeviceClass может быть: All, AudioCapture, AudioRender, PortableStorageDevice, VideoCapture, ImageScanner, Location
    Также можно совершить поиск, указав вместо класса устройства строку, сформированную в соответствии с Advanced Query Syntax (AQS). Таким образом можно найти устройство, скажем, по его GUID.

    Регистрация приложения для запуска при подключении устройства
    Вы можете включить Autoplay для всех приложений Windows Store.
    В манифесте приложения нужно добавить декларацию AutoPlay Device, примерно как это показано на скриншоте:


    В методе OnActivated класса App, можно будет проверить значение параметра IActivatedEventArgs.
    if (args.Kind == ActivationKind.Device)
    

    Необходимо переопределить метод OnLaunched для того, чтобы совершить какие-то действия, которые происходят при инициализации приложения, когда пользователь запускает ваше приложение нормальным образом. Для других вариантов запуска нужно использовать OnActivated.
    В некоторых случаях вместо OnActivated нужно использовать специализированные методы:
    OnFileActivated, OnSearchActivated, OnShareTargetActivated, OnFileOpenPickerActivated, OnFileSavePickerActivated, OnCachedFileUpdaterActivated

    DataProtectionProvider
    Следующие методы помогут вам сохранить информацию или поток асинхронно зашифровав ее и расшифровав:
    using Windows.Security.Cryptography.DataProtection;
    using Windows.Storage.Streams;
    using Windows.Security.Cryptography;
    // ..... какой-то код и после него следует защита текста Hi secret
                String descriptor = "LOCAL=user";
                DataProtectionProvider dpp = new DataProtectionProvider(descriptor);
                IBuffer binaryMessage = 
                 Windows.Security.Cryptography.CryptographicBuffer.ConvertStringToBinary("Hi secret", BinaryStringEncoding.Utf8);
                var _protectedBuffer = await dpp.ProtectAsync(binaryMessage);
                txtEncryptedTextBlock.Text = CryptographicBuffer.EncodeToBase64String(_protectedBuffer);
    
               // Для того, чтобы снять защиту с _protectedBuffer:
                DataProtectionProvider datapp = new DataProtectionProvider();
                IBuffer unprotectedBuffer = await datapp.UnprotectAsync(_protectedBuffer);
                txtUnprotectedTextBlock.Text = 
                 CryptographicBuffer.ConvertBinaryToString(BinaryStringEncoding.Utf8, unprotectedBuffer);
    

    Для хранения паролей пользователей используйте следующий класс
    var vault = new Windows.Security.Credentials.PasswordVault();
    vault.Add(new Windows.Security.Credentials.PasswordCredential("My App", "username", "password"));
    

    Лицензирование
    Если ваше приложение размещено в Store, то вы можете задать ему какой-либо пробный период, во время которого разрешается его бесплатное использование. Для этого используется класс CurrentApp.LicenseInformation. При разработке и тестировании используют класс CurrentAppSimulator. Краткий пример:
    using Windows.ApplicationModel.Store;
    
    var licenseInfo = CurrentAppSimulator.LicenseInformation;
                if (licenseInfo.IsActive)
                {
                    if (licenseInfo.IsTrial) // Помните, что IsTrial возвращает true даже если пробный период уже завершен
                    {
                        await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
                        () =>
                        {
                            txtLicenseRemainingDays.Text = (licenseInfo.ExpirationDate - DateTime.Now).Days.ToString();
                        });
                    }
                    else
                    {
                        txtLicenseRemainingDays.Text = "Полная лицензия";
                    }
                }
                else
                {
                    txtLicenseRemainingDays.Text = "Текущий статус лицензии: license is expired!";
                }
    

    Подписка на событие изменения лицензии (для «ловли» окончания пробного периода или покупки приложения):
    CurrentAppSimulator.LicenseInformation.LicenseChanged += LicenseInformation_LicenseChanged;
    

    Приобрести полную лицензию можно так:
    await CurrentApp.RequestAppPurchaseAsync(false);
    

    Если в качестве параметра передать true, то вам будет возвращен Receipt – xml файл с информацией о покупке.
    Вы можете продавать не полностью приложение, а только его определенные фичи. Примерно, как в модели Free-to-play. Скажем, в приложении, которое предоставляет доступ к кулинарным рецептам, пользователь может приобрести дополнительные секретные рецепты (например, старинный рецепт табуретовки).
    ListingInformation listingInfo = await CurrentAppSimulator.LoadListingInformationAsync();
    var productListing = listingInfo.ProductListings["feature1"];
    txtInAppPurchaseFeaturePrice.Text = String.Format("Купите эту возможность за {0}", productListing.FormattedPrice);
    

    Приобрести дополнительную возможность можно с помощью метода CurrentApp.RequestProductPurchaseAsync(false)

    Когда используется класс CurrentAppSimulator, для симуляции работы с лицензиями создается файл WindowsStoreProxy.xml по следующему пути:
    C:\Users\\AppData\Local\Packages\\LocalState\Microsoft\Windows Store\ApiData\WindowsStoreProxy.xml
    Любые модификации, которые вы будете совершать во время тестирования затрагивают и не изменяют файл WindowsStoreProxy.xml, а происходят только в оперативной памяти.
    Вместо WindowsStoreProxy.xml можно подсунуть свой файл:
    StorageFolder proxyDataFolder = await Package.Current.InstalledLocation.GetFolderAsync("trial-configs");
    StorageFile proxyFile = await proxyDataFolder.GetFileAsync("timed-trial.xml");
    await CurrentAppSimulator.ReloadSimulatorAsync(proxyFile);
    

    WinMD
    WinMD — компонент Windows Runtime, который создан специально и только для Windows Store приложений (в отличие от PCL). Если его код, требующий высокопроизводительных вычислений, написать на C++, то при обращении к WinMD из приложений на C#/JavaScript производительность возрастет по сравнение с тем же кодом на C#. Код компонента не может быть написан на JavaScript.
    В библиотеке WinMD методы могут быть перегружены. Если вам нужно объявить несколько перегрузок с одинаковым количеством параметров, вам нужно использовать атрибут Windows.Foundation.Metadata.DefaultOverloadAttribute() для одной этих из перегрузок.
    Если вам нужно использовать вашу библиотеку только из приложений Windows Store, и она будет содержать в себе контролы Windows Store, то вам нужно использовать WinMD, в других случаях вам лучше подойдет PCL.

    Поля, параметры, возвращаемые значения и члены вашего компонента должны быть типов Windows Runtime. Вот несколько примеров сопоставляющие типы WinRT и .Net
    Windows Runtime — .NET Framework
    IIterable — IEnumerable
    IVector — IList
    IVectorView — IReadOnlyList
    IMap<K, V> — IDictionary<TKey, TValue>
    IBindableIterable — IEnumerable
    IBindableVector — IList

    Жизненный цикл приложения
    Running — Suspended — NotRunning
    После ошибки и прерывания работы приложения состояние будет NotRunning
    Приложение переходит в состояние Suspended когда пользователь переключается на другое приложение или устройство переходит в режим экономии энергии. После того, как приложение перешло в состояние Suspending через 5 секунд оно прекращает свою работу

    HASHING
    Вы, разумеется, знаете, что хеширование часто используется для того, чтобы хранить информацию, которая может быть скомпрометирована (скажем, паролей) или для проверки на наличие ошибок при хранении/передаче данных. Небольшой сниппет в тему:
    using Windows.Security.Cryptography;
    using Windows.Storage.Streams;
    using Windows.Security.Cryptography.Core;
         String hashAlgorithmName = HashAlgorithmNames.Sha512;
                IBuffer binaryMessage = CryptographicBuffer.ConvertStringToBinary("Текст для которого будет создан хеш", BinaryStringEncoding.Utf8);
                HashAlgorithmProvider hashProvider = HashAlgorithmProvider.OpenAlgorithm(hashAlgorithmName);
                IBuffer hashedMessage = hashProvider.HashData(binaryMessage);
                if (hashedMessage.Length != hashProvider.HashLength)
                    txtHash.Text = "При создании hash произошла ошибка";
                txtHash.Text = CryptographicBuffer.EncodeToBase64String(hashedMessage);
    

    С помощью класса CryptographicBuffer вы можете сгенерировать 32-битное число:
    RandomNumberTextBlock.Text = CryptographicBuffer.GenerateRandomNumber().ToString();
    

    Или случайные данные:
    UInt32 length = 32;
    IBuffer rndData = CryptographicBuffer.GenerateRandom(length);
    RandomDataTextBlock.Text = CryptographicBuffer.EncodeToHexString(rndData);
    

    HMAC содержит в себе информацию не только о верности данных, но и информацию подтверждающую, что сообщение пришло от авторизованного отправителя. Проще выражаясь, это хеш с подписью.
    Цифровые подписи очень похожи на HMAC, с тем лишь отличием, что в HMAC используется симметричный ключ, а в цифровых подписях – ассиметричный, причем публичный ключ внедряется в сертификат.

    На этом пока что все. Контракт PlayTo, Printing, Backgroundtasks и геолокация не уместились бы здесь, — они заслуживают отдельных статей. Также нужно очень хорошо разобраться с асинхронностью и многопоточностью, пользовательскими и кастомными контролами, анимациями, а также профайлингом (в Windows Store приложениях есть только sampling с небольшими ограничениями) и всеми другими темами экзамена.
    Для того, чтобы успешно сдать экзамен крайне необходимо хорошо владеть английским языком. В отличие от других тестов, которые мне приходилось сдавать, в этом предлагаются определенные сценарии. Прочитав ТЗ и пожелания необходимо дать свои рекомендации или совершить правки кода. Довольно большие объемы текста, которые необходимо прочитать в относительно сжатые сроки. При сдаче очень часто попадаются вопросы, которые подразумевают, что вы владеете не только теорией, но и на практике знаете какие действия необходимо совершить для добавления в приложение того или иного функционала.

    Отрадно, что Россия появилась в списке стран, для которых возможна онлайн сдача экзамена. Пускай, это нововведение пока что и находится на стадии бета-тестирования. Подробнее по ссылке: Онлайн-экзамены

    Комментарии 3

      +3
      Мельком просмотрел статью. Следующее утверждение бросилось в глаза:
      WinMD это библиотека классов Windows Runtime, которая создана специально и только для Windows Store приложений (в отличие от PCL). Если ее код написать на C++, то при обращении к ней из приложений на C#/JavaScript производительность возрастет по сравнение с тем же кодом на C#.
      Хотелось бы немного дополнить.
      Во-первых, WinMD — это файл метаданных для описания API, который предоставляется компонентом, а не библиотека. Наличие WinMD файла ещё не гарантирует корректную загрузку компонента(dll с кодом может вовсе отсутствовать).
      Во-вторых, есть много нюансов работы WinRT компонентов. К примеру, если написать WinRT компонент на C# и вызывать методы из C# кода, то CLR не будет использовать WinRT ABI, а будет вызывать код напрямую. В случае же, если компонент написан на C++, то передача типов будет происходить через ABI, что медленнее прямого вызова C# — C#.

      PS: поздравляю с получением сертификата!!!
        0
        Спасибо, ценные уточнения! Немного подкорректирую.
        Под скоростью выполнения имел в виду то, что если вынести какие-то сложные, требующие высокой производительности вычисления в WinMD, то разница будет довольно заметна. Взято отсюда:
        Splitting Assemblies, WinMD, Diagnostics and Instrumentation

    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

    Самое читаемое