Опубликован: 15.05.2013 | Доступ: свободный | Студентов: 265 / 9 | Длительность: 24:25:00
Специальности: Системный архитектор
Лекция 14:

Ключевые концепции компонентов WinRT

< Лекция 13 || Лекция 14: 12345 || Лекция 15 >

Сценарии для WinRT-компонентов

Ранее, в разделе "Выбор подхода с использованием смешанных языков" я кратко описал ряд сценариев, в которых WinRT-компоненты могут быть весьма ценной часть вашего приложения. В этом разделе мы рассмотрим эти сценарии глубже и я покажу вам эти сценарии в примерах, где имеется их реализация.

Повышение производительности

Увеличение производительности приложений для Магазина Windows, написанных на HTML, CSS и JavaScript, это один из основных сценариев передачи некоторой части работы на WinRT-компонентам.

Оценивая производительность приложения, обратите особое внимание на те области, где задействованы интенсивные вычисления или передача больших объемов данных. Например, если вы решаете реализовать расширенный экран-заставку, возможно, вы сможете уменьшить время, которое должен ждать пользователь (особенно – при первом запуске) до тех пор, пока приложение не активируется. Любая другая ситуация, где пользователю может понадобиться ждать – там, где у него есть возможность делать что-то более полезное, чем наблюдение за индикатором прогресса – это хорошее место для использования, если это возможно, высокопроизводительных компонентов. Очевидно, есть сценарии, где сама по себе производительность приложения не так влияет на скорость работы с ним, как сетевые задержки, но как только вы получили данные от некоего сервиса, вы быстрее можете произвести их предварительную обработку в компоненте, а не в JavaScript.

Другое хорошее место для использования высокопроизводительных компонентов – это действия, выполняемые при запуске приложения, особенно если при первом запуске требуется много работы. Например, пакет приложения может включать в себя большой объем сжатых данных для уменьшения размера данных, загружаемых из Магазина Windows, но эти данные нужно распаковать при первом запуске. WinRT-компонент может значительно сократить время инициализации. Если компонент использует API WinRT для записи данных в папки приложения, все эти данные так же доступны и из JavaScript посредством тех же самых API.

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

Одно из мест, где очень важна производительность, это фоновые задачи. Как разьяснено в Главе 2, фоновые задачи ограничены несколькими секундами процессорного времени в каждые 15 минут. Поэтому вы сможете сделать гораздо больше в фоновой задаче, написанной на высокопроизводительном языке, нежели в задаче, написанной на JavaScript.

Структура компонента с подобной задачей не отличается от любого другого компонента, как показано в компоненте " C# Tasks" в примере "Фоновые задачи" (http://code.msdn.microsoft.com/windowsapps/Background-Task-Sample-9209ade9). Каждый из классов в пространстве имен Tasks маркирован как public и sealed, и так как компонент виден в JavaScript-проекте посредством ссылки, имена классов (и их открытые методы и свойствва), так же находятся в пространстве имен JavaScript. В результате их имена без проблем могут быть заданы свойству BackgroundTaskBuild.taskEntryPoint.

Другой пример использования той же самой техники доступен в примере "Фоновое определение состояния сети" (http://code.msdn.microsoft.com/windowsapps/Network-status-background-957eb3eb).

Кое что, о чем мы не говорили в Главе 2, но о чем самое время поговорить теперь, это то, что при создании WinRT-компонента для данной задачи, класс, который реализует фоновую задачу, должен быть унаследован от Windows.ApplicationModel.Background.IBackgroundTask (http://msdn.microsoft.com/library/windows/apps/windows.applicationmodel.background.ibackgroundtask.aspx) и реализовывать его метод Run. Этот метод вызывается при активации фоновой задачи. Это можно увидеть в примере "Фоновое определение состояния сети", где вся C#-реализация компонента заключена в нескольких десятках строк кода (проект NetworkStatusTask > BackgroundTask.cs; некоторые комментарии и команды тестового вывода опущены):

namespace NetworkStatusTask
{
public sealed class NetworkStatusBackgroundTask : IBackgroundTask
{
ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings;

// Метод Run – это точка входа для фоновой задачи.
public void Run(IBackgroundTaskInstance taskInstance)
{
// Связывание обработчика отмены операции с фоновой задачей.
taskInstance.Canceled += new BackgroundTaskCanceledEventHandler(OnCanceled);
try
{
ConnectionProfile profile =
NetworkInformation.GetInternetConnectionProfile();
if (profile == null)
{
localSettings.Values["InternetProfile"] = "Not connected to Internet";
localSettings.Values["NetworkAdapterId"] = "Not connected to Internet";
}
else
{
localSettings.Values["InternetProfile"] = profile.ProfileName;

var networkAdapterInfo = profile.NetworkAdapter;
if (networkAdapterInfo == null)
{
localSettings.Values["NetworkAdapterId"] = "Not connected to Internet";
}
else
{
localSettings.Values["NetworkAdapterId"] =
networkAdapterInfo.NetworkAdapterId.ToString();
}
}
}
catch (Exception e)
{
// Тестовый вывод опущен
}
}

// Обработка отмены фоновой задачи.
private void OnCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
{
// Тестовый вывод опущен
}
}
}

Вы можете видеть, что метод Run принимает аргумент, с помощью которого он может зарегистрировать обработчик для отмены задачи. Вышеприведенный код ничего значимого не делает, так как задача исполняет лишь небольшое количество кода. Задачи C# в примере "Фоновые задачи", с другой стороны, имитируют длительные операции, в подобном случае используется обработчик для установки флага, который может остановить эти операции.

Доступ к дополнительным API

Среди API DOM, WinJS, библиотек сторонних разработчиков и внутренних возможностей JavaScript, у JavaScript-разработчиков нет недостатка в API для использования в своих приложениях. В то же время, есть огромное количество API .NET (http://msdn.microsoft.com/library/windows/apps/br230232.aspx) и Win32/COM (http://msdn.microsoft.com/library/windows/apps/br205757.aspx), которые доступны в приложениях на C#, VB, и C++ и не доступны напрямую в JavaScript, в том числе API DirectX b Media Foundation.

За исключением API, которые воздействуют на пользовательский интерфейс или поверхности рисования (а именно, Direct2D и Direct3D), WinRT-компоненты могут делать подобные функции, или, скорее, высокоуровневые операции, построенные на их основе, доступными для приложений, написанных на JavaScript.

В материале "Создание собственных компонентов среды выполнения Windows для разработки эффективных приложений" (http://blogs.msdn.com/b/windowsappdev_ru/archive/2012/08/13/windows-metro.aspx) блога для разрабочтиков приложений для Windows 8, даны некоторые советы по этому поводу. Здесь показано, как использовать API System.IO.Compression в .NET для работы с ZIP-файлами, и API XAudio (часть DirectX) для обхода элемента HTML audio и низкоуровневого проигрывания аудиофайлов. В Главе 4 курса "Пользовательский интерфейс приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript" мы касались этого вопроса, когда, как бы мы ни старались, между воспроизведением записей в элементе audio, всегда между ними была небольшая пауза. Это происходит потому, что некоторое время занимает перевод всех операций элемента в вызовы системного API XAudio. Работая напрямую с тем же самым API, вы можете полностью решить эту проблему (Тем не менее, Microsoft знает о подобном поведении элемента audio и, весьма вероятно, улучшит его производительность в будущем.)

Подобный подход так же можно использовать для взаимодйствия с внешними устройствами, которые не представлены в API WinRT, но представлены в Win32/COM. Мы видели подобное с применением XInput в примере "Рисование с использованием JavaScript и XInput" (http://code.msdn.microsoft.com/windowsapps/XInput-and-JavaScript-c72fe535), в Главе 4 (речь об этом идет и в вышеупомянутом материале блога).

Другой очень простой пример – это создание компонента для ответа на частый вопрос: "Как создать GUID в JavaScript?". Хотя вы можете реализовать процедуру для создания GUID-строки из случайных чисел, это неподходящий GUID, так как нет гарантии его уникальности (в конце концов, GUID – это Глобальный уникальный идентификатор (Globally Unique Identifier). Для того, чтобы сделать все как нужно, используйте API Win32 CoGreatGuid (http://msdn.microsoft.com/library/windows/apps/ms688568.aspx), для чего вы можете создать очень простую оболочку на C++.

"Из пушки по воробьям?" Некоторые разработчики говорят, что разбираться со всеми тонкостями создания WinRT-компонентов только для того, чтобы вызывать один метод наподобие CoCreateGuid – это слишком тяжеловесное решение. Однако, учитывая простоту реализации базового WinRT-компонента, которую мы видели в данной лекции, все, что вы действительно делаете с компонентом, это создаете многоязычную структуру, посредством которой вы можете использовать все возможности каждого из языков. Накладные расходы, на самом деле, невелики. Например, при Release-построении C++-компонента из раздела "Быстрый старт №2" получилась DLL объемом 39 Кб и 3-килобайтный .winmd-файл.

Использование WinRT-компонентов, таким образом, применимо и к использованию COM DLL, которые содержат API сторонних разработчиков, наподобие библиотек кода. Вы можете использовать их в приложениях для Магазина Windows при условии, что они отвечают трем требованиям:

  • DLL находится в пакете приложения.
  • DLL использует только те API Win32/COM (http://msdn.microsoft.com/library/windows/apps/br205757.aspx), которые разрешено использовать с приложениями для Магазина Windows. В противном случае приложение не пройдет сертификацию в Магазине Windows.
  • DLL должна реализовывать то, что называется Regfree COM, то есть, для ее работы не должны требоваться какие-либо записи в системном реестре . (Приложения для Магазина Windows не имеют доступа к реестру, и, таким образом, не могут регистрировать там COM-библиотеки). Лучший материал на эту тему, который я нашел, это "Упрощение развертывания приложения с ClickOnce и Registration-Free COM" (http://msdn.microsoft.com/en-us/magazine/cc188708.aspx) в MSDN Magazine.

Если все эти требования соблюдены, приложение может использовать функцию CoCreateInstanceFromApp (http://msdn.microsoft.com/library/windows/desktop/hh404137%28v=VS.85%29.aspx) из компонента для создания экземпляров объектов из нужной ему DLL.

Скрытие кода и защита интеллектуальной собственности.

В Главе 1 курса "Введение в разработку приложений для Windows 8 с использованием HTML, CSS и JavaScript" мы видели, как загружаются и исполняются приложения. Сейчас, возможно, вы уже понимаете, что как старательно Windows ни пытается закрыть пакеты приложений от случайного доступа, весь код приложений находится на устройстве и пользователь может получить к нему доступ. Другими словами, знайте, что ваш код так же видим в пакете приложения, как если бы это был код страницы в веб-браузере, когда в нем выбирают команду Показать код

Это, конечно, возможно, перенести часть функциональности на веб-сервер, что позволит, кроме того, получить преимущества от обработки данных на строне сервера. Так нередко и делается. Однако, всегда остаются части приложения, которые должны существовать на клиентском компьютере и исполняться на нем, поэтому вы всегда рискуете, что кто-то воспользуется вашим кодом!

Как только разработчики начали экспериментировать с приложениями для Магазина Windows , написанными на HTML, CSS и JavaScript, они задавали вопросы о том, как им защитить свой код. На самом деле, разработчики, использующие C# и Visual Basic задают похожие вопросы, хотя эти языки компилируются в IL (промежуточный язык, intermediate language), существует множество декомпиляторов, которые производят исходный код из IL, есть и программы, которые позволяют раскрывать минимизированный JavaScript-код. Ни JavaScript, ни .NET-языки не умеют достаточно хорошо скрывать подробности реализации приложений.

Код, написанный на C++ и скомпилированный в машинный код, гораздо хуже поддается методам обратной инженерии, хотя и это задача вполне выполнимая для некоторых (в таком случае, вы можете спросить у них, почему бы им не писать собственный код!). Тем не менее, это лучшая защита для кода, который существует на клиентском компьютере. А единственный реальный способ защитить алгоритмы – это хранить их на удаленном сервере.

Если остальное приложение написано на языке, отличном от C++, в особенности – на JavaScript, знайте, что довольно легко выяснить и особенности реализации интерфейса компонента. Вопрос здесь в том, может ли злоумышленник может использовать знание интерфейса компонента для использования этого компонента в его собственных приложениях. Короткий ответ: "Да, может", - так как ваш код покажет ему, как это сделать. В подобных случаях более гибкий и почти непробиваемый подход заключается в том, чтобы поставщик компонента в индивидуальном порядке управлял лицензиями разработчиков. У компонента может быть предусмотрена процедура инициализации, необходимая для включения остальной его функциональности. В этом вызове он может сравнить сведения о пакете приложения, полученные посредством класса Windows.ApplicationModel.PackageId с теми, которые известны ему, с учетом того, что уникальность параметров, идентифицирующих приложение, поддерживается Магазином Windows. Вот нескольуко вариантов проверки:

  • Провести проверку с помощью сетевого сервиса. Это может потребовать наличия сетевого соединения, что, в большинстве сценариев, не проблема. Помните лишь о шифровании данных, которые вы отправляете по сети для того, чтобы избежать их перехвата с помощью инструментов наподобие Fiddler!
  • Сверить данные с зашифрованной информацией, которая находится в самом компоненте. Таким образом, компонент компилируется отдельно для каждой лицензии. Подобный подход сложнее всего для взлома.
  • Произвести сверку с зашифрованным файлом лицензии, распространяющимся вместе с компонентом и уникальным для лицензиата (содержащим, например, имя приложения и издателя). Возможно, это самое простое решение, так как даже если файл лицензии скопирован из пакета лицензированного приложения, содержащаяся в нем информация не будет совпадать с информацией о пакете другого приложения во время его выполнения. Алгоритм шифрования может содержаться внутри скомпилированного компонента, поэтому их будет сложно извлечь для того, чтобы взломать файл с лицензией – не невозможно, но очень сложно. Другое приложение может использовать такой компонент, только если оно использует ту же самую информацию о пакете приложения, что невозможно для приложений, загруженных из Магазина Windows, но возможно для разработчиков, при самостоятельной работе с приложениями, или для недобросовестных организаций.

В итоге, таким образом, учитывайте, что Windows сама по себе не может гарантировать безопасность кода приложений на клиентском устройстве. Дальнейшая защита должна быть реализована приложением самостоятельно.

Библиотеки компонентов

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

Хороший пример библиотеки – это Notifications Extensions Library, которую мы видели в нескольких примерах, в Главе 2: "Плитки и индикаторы событий приложения" (http://code.msdn.microsoft.com/windowsapps/App-tiles-and-badges-sample-5fc49148), "Приложения экрана блокировки" (http://code.msdn.microsoft.com/windowsapps/Lock-screen-apps-sample-9843dc3a), "Запланированные обновления" (http://code.msdn.microsoft.com/windowsapps/Scheduled-notifications-da477093), "Дополнительные плитки" (http://code.msdn.microsoft.com/windowsapps/Secondary-Tiles-Sample-edf2a178) и "Всплывающие уведомления" (http://code.msdn.microsoft.com/windowsapps/toast-notifications-sample-52eeba29). Эта библиотека содержит множество классов с соответствующими методами и свойствами, и так как все эти методы маленькие и быстрые, они реализованы в синхронном виде.

В случае с примерами, библиотека Notifications Extensions Library включена в каждый проект, который ее использует, в виде исходного кода. Более вероятно, однако, в особенности если вы создаете коммерческий проект и следуете руководству "Практическое руководство. Создание пакета средств разработки программного обеспечения" (http://msdn.microsoft.com/library/windows/apps/hh768146.aspx), вы будете передавать лишь скомпилированные DLL, и/или WinMD-файлы вашим клиентам. Клиенты будут добавлять эти библиотеки в свои проекты, в итоге, они будут упакованы вместе с приложениями.

В подобном случае не забудьте предоставить отдельные компоненты, скомпилированные для целевых платформ x86, x64, и ARM для компонентов, написанных на C++.

Параллелизм

Мы уже видели, что рабочие веб-процессы и WinRT-компоненты с асинхронными методами могут работать бок о бок для передачи задач в различные потоки. Если вам действительно нужно подобное, вы можете спроектировать все приложение на основе параллельного выполнения, используя эти механизмы, запуская одновременно несколько асинхронных операций, возможно, с помощью нескольких рабочих процессов. Компоненты WinRT так же могут использовать множественные вызовы Task.Run, а не только отдельные, как мы видели.

Если посмотреть глубже, то WinRT-компоненты могут использовать API в Windows.System.Threading (http://msdn.microsoft.com/library/windows/apps/windows.system.threading.aspx) для получения доступа к пулу потоков, а так же те API в Windows.System.Threading.Core (http://msdn.microsoft.com/library/windows/apps/windows.system.threading.core.aspx), которые работают с семафорами и с другими событиями, связанными с многопоточностью. Подробности подобной работы лежат за пределами данного курса, но я хочу упомянуть их здесь, так как многие встроенные API WinRT их использует, и ваши компоненты могут делать то же самое. И, хотя здесь не идет речь о компонентах, пример "Пул потоков" (http://code.msdn.microsoft.com/windowsapps/Pool-Sample-5aa60454) предоставляет ценную информацию для того, чтобы начать разбираться с этим вопросом.

Что мы только что изучили

  • Приложения для Магазина Windows не обязательно писать на каком-то одном языке. С помощью WinRT-компонентов приложения могут эффективно использовать язык, который наилучшим образом подходит для решения конкретной задачи. Компоненты, однако, не могут работать с пользовательским интерфейсом от имени приложения, написанного на HTML, CSS и JavaScript.
  • Причины для применения нескольких языков в приложении включают в себя улучшение производительности, получение доступа к дополнительным API (включая библиотеки сторонних разработчиков), которые при обычном режиме не доступны JavaScript, скрытие кода для защиты интеллектуальной собственности, создание модульных библиотек компонентов, которые могут использовать приложения, написанные на других языках и эффективное распараллеливание задач.
  • Для решения задач, требующих интенсивных вычислений, компоненты, написанные на C#/VB позволяют достичь повышения производительности примерно на 15% в сравнении с JavaScript-кодом, а компоненты, написанные на C++, примерно на 25%. При тестировании производительности не забудьте выполнить Release-построение приложения и запустить приложение вне отладчика, в противном случае вы увидите другие результаты для разных языков.
  • Приложения для Магазина Windows могут использовать рабочие веб-процессы для создания асинхронных процедур, которые выполняются отдельно от потока пользовательского интерфейса, вы можете заключать эти рабочие процессы в оболочку promise-объектов WinJS для того, чтобы работать с ними так же, как с асинхронными методами WinRT.
  • Асинхронные методы так же могут быть реализованы в WinRT-компонентах с использованием задач, параллелизма и API пула потоков. В сравнении с рабочими веб-процессами, подобные асинхронные методы отличаются более высокой скоростью отзыва, так как их внутреннее устройство соответствует устройству методов.
  • Вне зависимости от языка, на котором написан компонент, уровень проекции JavaScript переводит некоторые из его структур в вид, естественный для JavaScript, включая изменение регистра имен и конверсию типов данных.
  • В связи с сущностью обработчиков событий, которые пересекают границу между JavaScript и WinRT-компонентами, приложения должны предупреждать утечку памяти, вызывая removeEventListener для событий, исходящих из API WinRT или WinRT-компонентов, когда прослушивают их лишь временно.
< Лекция 13 || Лекция 14: 12345 || Лекция 15 >