Разработка Windows-приложений и использование новых средств Windows из любимого языка программирования не всегда была простой и прямолинейной. Чтобы заказчики получили максимальные преимущества от Windows 8 и чтобы разработчики могли создавать лучшие приложения для Windows, нужно было внести значительные усовершенствования и улучшения в весь процесс программирования. Центральное место в этом занимает Windows Runtime (WinRT).
Windows Runtime — неотъемлемая часть римейка процесса разработки для Windows. Это современный Windows API, используемый для создания новых приложений Windows Store в Windows 8. Windows Runtime спроектирована с нуля для равноценной поддержки нескольких основных языков программирования (C#/Visual Basic, JavaScript и C++). Она позволяет разработчикам использовать существующие навыки и ресурсы, предоставляет продуманные и согласованные API и глубоко интегрируется в цепочку средств разработки.
Windows Runtime позволяет выбирать язык
В настоящее время прикладных разработчиков очень много. Это и те, кто создают веб-приложения с помощью JavaScript/HTML/CSS, и те, кто разрабатывают приложения на основе Microsoft .NET Framework, используя C#/Visual Basic, и те, кто пишут неуправляемые приложения на C++. Windows Runtime позволяет писать приложения Windows Store на любом из этих языков программирования, как показано на рис. 1.
Рис. 1. Windows Runtime позволяет писать новые приложения Windows Store на многих языках
Это выгодно как заказчикам, так и разработчикам. Для разработчиков открывается огромные рыночные возможности зарабатывать деньги на создании приложений Windows Store. А благодаря поддержке всех основных языков Microsoft огромному числу разработчиков доступно создание отличных Windows-приложений, которые заказчики будут с удовольствием покупать и использовать.
Как разработчик вы обладаете необходимыми навыками и опытом в работе со своим любимым языком программирования. У вас также есть множество ресурсов, используемых вами при создании приложений (например, существующий код, инфраструктура сборки и т. д.). Вам не придется изучать совершенно новый язык программирования и инструментарий только для разработки приложений Windows Store в Windows 8.
Windows Runtime естественна и привычна
Прежний опыт разработки для Windows вырабатывался в те времена, когда доминирующим языком был C, когда код на основе исключений был чем-то диковинным и когда привязка к одному языку была вполне приемлема для операционной системы Windows. Современный мир разработки совсем другой. Такие программные средства, как пространства имен, наборы, события, асинхронность и прочее, сегодня не просто распространены — они обязательны.
Давайте посмотрим на использование одного из распространенных сегодня устройств в Windows: на веб-камеру. Вот как раньше объявлялась всего лишь часть API веб-камер в Windows:
HWND VFWAPI capCreateCaptureWindow(
LPCTSTR lpszWindowName,
DWORD dwStyle,
int x,
int y,
nit nWidth,
int nHeight,
HWND hWnd,
int nID
);
Прежние Windows API в основном объявлялись в неуправляемых заголовочных файлах (*.h), поставлявшихся в составе Windows SDK. В этот SDK также включался файл .lib, с которым связывался код, использующий API. Windows API реализовались на C/C++ как неуправляемые C-функции или как неуправляемые COM-компоненты и упаковывались в файл .dll, входящий в состав Windows. Модель программирования была спроектирована с расчетом прежде всего на производительность.
Не были ни пространств имен, ни современных наборов, ни настоящих исключений. Даже классическим разработчикам неуправляемого кода приходилось трудновато, не говоря уже о проблемах с IntelliSense, просмотром кода и др.
Это чисто неуправляемая платформа создавала трудности в использовании и для .NET-разработчиков, пытающихся задействовать Windows API, как показано на рис. 2.
Рис. 2. Предыдущая чисто неуправляемая платформа создавала трудности в использовании
[DllImport("avicap32.dll", EntryPoint ="capCreateCaptureWindow")]
static extern int capCreateCaptureWindow(
string lpszWindowName, int dwStyle,
int X, int Y, int nWidth, int nHeight,
System.IntPtr hwndParent, int nID);
[DllImport("avicap32.dll")]
static extern bool capGetDriverDescription(
int wDriverIndex,
[MarshalAs(UnmanagedType.LPTStr)] ref string lpszName,
int cbName,
[MarshalAs(UnmanagedType.LPTStr)] ref string lpszVer,
int cbVer);
Кому-то приходилось писать уйму кода, что вручную заполнить пробел между вашим кодом и традиционными API-функциями Windows. Если никто этого не делал, вам оставалось лишь самому пытаться писать этот весьма непростой код. Многие ресурсы для разработчиков были нацелены именно на то, чтобы помочь преодолеть эту пропасть, в том числе pinvoke.net, библиотека примеров Vista Bridge (archive.msdn.microsoft.com/VistaBridge) и Windows API Code Pack for Microsoft .NET Framework (archive.msdn.microsoft.com/WindowsAPICodePack).
Теперь в Windows 8 вы можете пользоваться API-функциями Windows в тот же день, как приобретаете эту операционную систему, и ровно тем же способом, как вы сейчас используете другие API в своих приложениях (например из .NET Framework для C#/Visual Basic). Больше не требуется писать никакого кода для заполнения пробела между вашим кодом и традиционными API-функциями Windows. Например, в наши дни пользоваться веб-камерой куда проще:
using Windows.Media.Capture;
MediaCapture captureMgr = new MediaCapture();
await captureMgr.InitializeAsync();
// Начать захват для предварительного просмотра;
// capturePreview - это CaptureElement, определенный в XAML
capturePreview.Source = captureMgr;
await captureMgr.StartPreviewAsync();
Каким же образом использование Windows Runtime получается естественным и привычным для разработчиков? Поскольку многие приложения получают данные из Интернета, изучим этот вопрос в контексте распространенного сетевого сценария: получения данных из RSS-канала. Вот код на C# для асинхронного получения данных из такого канала:
var feedUri =
new Uri("http://www.devhawk.com/rss.xml");
var client = new Windows.Web.Syndication.SyndicationClient();
var feed = await client.RetrieveFeedAsync(feedUri);
var title = feed.Title.Text;
А это эквивалентный код на Visual Basic:
Dim feedUri = New Uri("http://www.devhawk.com/rss.xml");
Dim client = New Windows.Web.Syndication.SyndicationClient();
Dim feed = Await client.RetrieveFeedAsync(feedUri);
Dim title = feed.Title.Text;
Кроме корневого пространства имен (Windows), особых отличий этого кода от того, что вы уже пишете при использовании .NET Framework, нет. Пространства имен есть. Классы и конструкторы есть. Методы и свойства есть. Идентификаторы применяются в .NET Framework, а ключевое слово await предназначено для асинхронного программирования — все, что сегодня ожидает разработчик при написании кода на C# и Visual Basic.
По аналогии с C#/Visual Basic в C++ тоже используются пространства имен, классы, конструкторы, методы и свойства. Вы также можете применять встроенные типы C++, такие как string (в противоположность прежним Win32-типам данных). Стандартные операторы, такие как «::» и «->» и другие, тоже используются так, как и следовало ожидать. А для асинхронного программирования мы предоставляем средства, подобные Concurrency Runtime. Вот код на C++ для асинхронного извлечения данных из RSS-канала:
auto feedUri = ref new
Windows::Foundation::Uri("http://www.devhawk.com/rss.xml");
auto client = ref new Windows::Web::Syndication::SyndicationClient();
task<SyndicationFeed^>
retrieveTask(client->RetrieveFeedAsync(feedUri));
retrieveTask.then([this] (SyndicationFeed^ feed) {
this->title = feed->Title->Text;
});
Наконец, существует JavaScript, в эквивалентном коде которого можно заметить интересные отличия:
var title;
var feedUri =
new Windows.Foundation.Uri("http://www.devhawk.com/rss.xml");
var client = new Windows.Web.Syndication.SyndicationClient();
client.retrieveFeedAsync(feedUri).done(function (feed) {
title = feed.title.text;
});
В коде на JavaScript имена всех методов и свойств используют так называемый горбатый регистр букв (camel case), так как это естественно и привычно для программистов на JavaScript. Мы также применяем при написании асинхронного кода инфраструктуру «обещаний» (promises) с анонимными функциями. И по-прежнему используем встроенные типы этого языка.
Средства разработки также знакомы разработчикам, например IntelliSense и стандартно функционирующий инструментарий вроде ILDASM и .NET Reflector (рис. 3).
Рис. 3. Visual Studio IntelliSense для API-средств WinRT
Windows Runtime обеспечивает эту естественную и привычную среду разработки расширением и объединением фундаментальных концепций из .NET и COM с добавлением современной семантики в WinRT API — классы, конструкторы, статические переменные и события. Эта семантика может предоставляться разными способами в разных языках — в зависимости от того, к чему привыкли программисты на конкретном языке.
Это возможно благодаря тому, что все API-средства WinRT включают метаданные, с помощью которых языки программирования узнают, как API предоставляется через Windows Runtime. Файлы метаданных WinRT используют обновленную версию формата .NET-метаданных. Эти метаданные доступны каждому разработчику, поскольку они устанавливаются на каждый компьютер вместе с Windows 8.
За счет такого римейка не возникает ощущения, что придется переучиваться и использовать совершенно новую платформу. Она не кажется «чужой». Вы используете существующие навыки и ресурсы при создании новых приложений Windows Store.
Богатая функциональность Windows Runtime
В Windows Runtime меньше API, чем в Win32, поэтому изучить их легче, но эта среда, тем не менее, обладает богатой функциональностью. Это богатство Windows Runtime настолько велико, что в статье такого размера его даже не перечислить, но стандартные средства Windows и новые средства Windows 8 охватывают все те области, которые и должны (рис. 4).
Рис. 4. Windows Runtime включает стандартные средства Windows и новые средства Windows 8
User Interface | Пользовательский интерфейс |
HTML5/CSS | HTML5/CSS |
XAML | XAML |
DirectX | DirectX |
Controls | Элементы управления |
Data Binding | Связывание с данными |
SVG | SVG |
Tiles | Тайлы |
Input | Ввод |
Accessibility | Специальные возможности |
Printing | Печать |
Devices | Устройства |
Geolocation | Геопозиционирование |
Portable | Мобильные устройства |
Sensors | Датчики |
NFC | NFC |
Communications & Data | Коммуникации и данные |
Contracts | Контракты |
Local & Cloud Storage | Локальное и облачное хранилища |
Web | Web |
Notifications | Уведомления |
Streams | Потоки данных |
Networking | Сети |
XML | XML |
SMS | SMS |
Background Transfer | Фоновая передача |
Media | Медиа |
Playback | Воспроизведение |
Capture | Захват |
PlayTo | PlayTo |
Fundamentals | Основы |
Application Services | Прикладные сервисы |
Threading/Timers | Потоки/таймеры |
Memory Management | Управление памятью |
Authentication | Аутентификация |
Cryptography | Шифрование |
Globalization | Глобализация |
Более того, в Windows Runtime обеспечена унификация за счет применения единого набора правил проектирования API. Вы изучаете концепции и архитектуру одного API, а потом можете просто применять эти знания при использовании других API.
В Windows Dev Center (dev.windows.com) вы найдете множество примеров, которые охватывают большинство областей, показанных на рис. 4. Ниже дан ряд примеров.
Контракты Вы можете обеспечить пользователям поиск в рамках своего приложения при выборе ими значка1 (charm) Search (Поиск) и отображать предложения в секции Search. Ваше приложение также может обмениваться контентом с другим приложением через значок Share (Общий доступ). Вот некоторые примеры:
Социальные связи Используя классы из пространства имен Windows.ApplicationModel.Contacts, можно запустить Contact Picker и получить контакты. Для приложений, которым нужно предоставлять контакты другим приложениям, можно использовать класс ContactPickerUI и создать набор контактов:
Медиа API-функции Windows, предназначенные для работы с медиа, — от простого воспроизведения и захвата медиа-информации до перекодировки медиа и ее воспроизведения на внешних устройствах через контракт Play To, — находятся в пространстве имен Windows.Media:
Безопасность Вы можете получать удостоверения, а затем передавать их в API, которые того требуют (скажем, для поддержки единого входа). Вы также можете надежно защищать конфиденциальную информацию (счета кредитных карт, банковские счета, программные продукты и др.) на локальном компьютере с применением стойкого алгоритма шифрования на основе паролей. Кроме того, у вас широкий выбор различных алгоритмов шифрования, доступных через пространство имен Windows.Security.Cryptography:
Сети/Web Все, что нужно для работы в сетях (подключение к сети по TCP с помощью класса StreamSocket, подключение по UDP с помощью DatagramSocket, простой запрос о состоянии сети, чтобы определить, можно ли сохранить некие данные приложения в веб-сервисе или это нужно сделать локально), вы найдете в пространстве имен Windows.Networking:
Разумеется, это далеко не вся функциональность Windows Runtime. Вам как разработчику для Windows доступно куда больше функциональности, и вы можете самостоятельно изучить ее в Windows Dev Center по ссылке bit.ly/yyJI2J.
1 В русской версии Windows 8 такое понятие, как charm, переведено как «чудо-кнопка», хотя, по сути, это значок. И именно так этот UI-элемент локализован в любых других новых продуктах Microsoft. Поэтому мы будем использовать термин «значок», а не сказочный вариант «чудо-кнопка». — Прим. ред.
Скорость работы и гибкость Windows Runtime
Windows Runtime на самом низком уровне реализуется как неуправляемые объекты с двоичным контрактом, но предоставляется внешнему миру через метаданные, общую систему типов и общие идиомы проектирования. WinRT API по-прежнему обеспечивает скорость работы предыдущего Windows API, в то же время предоставляя современную среду разработки, как уже было продемонстрировано мной.
Сохранение гибкости и отзывчивости приложений — другая трудная задача. Люди на инстинктивном уровне являются многозадачными существами, если так можно выразиться, а это прямо влияет на наши ожидания того, как приложения будут реагировать на наши действия. Мы ожидаем, что приложения будут оставаться отзывчивыми при любых действиях. Пользуясь своим любимым приложением для чтения новостей, мы хотим добавлять новые каналы, читать новые статьи, сохранять новостные статьи и т. д. И у нас должна быть возможность делать все это, даже когда приложение получает новые статьи из Интернета.
Это становится особенно важно, когда мы взаимодействуем с приложением, используя сенсорный ввод. Мы тут же замечаем, когда это приложение не «склеивается» с нашими пальцами. Даже минимальные проблемы с производительностью могут крайне отрицательно сказаться на впечатлении пользователя от приложения.
Многие современные приложения, подключенные к веб-сайтам социальных сетей, хранят данные в облаке, работают с файлами на жестком диске, взаимодействуют с другими гаджетами и устройствами и т. д. Некоторые из этих источников могут давать непредсказуемые задержки. При неправильном построении приложение будет тратить больше времени на ожидание данных извне и меньше времени на реагирование на потребности пользователя.
Соответствие этому подключенному миру — базовый принцип Windows Runtime. Вы как разработчик должны попасть на волну успеха («The Pit of Success», bit.ly/NjYMXM), создавая приложения, соединяемые со всем миром. С этой целью все API с потенциально интенсивным вводом-выводом, а также длительно выполняемые API являются асинхронными в Windows Runtime. Это были бы наиболее вероятные кандидаты заметного падения производительности, будь они написаны как синхронные (например, они скорее всего потребовали бы более 50 мс на выполнение). Асинхронный подход к API позволяет писать код, который работает быстро и является адаптируемым под различные условия по умолчанию.
Чтобы узнать больше об асинхронной природе Windows Runtime, прочитайте статью в блоге «Keeping apps fast and fluid with asynchrony in the Windows Runtime» по ссылке bit.ly/GBLQLr.
Windows Runtime поддерживает гибридные приложения
Мощь Windows Runtime заключается и в том, что вы не ограничены языком программирования, который был выбран для создания приложения. И вы не ограничены только библиотеками и кодом, доступными этому языку программирования. Вы можете в любой момент использовать другой язык, библиотеку или компонент, который лучше подходит для ваших задач. Библиотека может быть, в том числе, с открытым исходным кодом.
Как быть, если вы пишете игру на JavaScript/HTML/CSS и вам нужна очень быстрая библиотека физики? Вы можете задействовать Box2D в C++. А если вы пишете приложение Windows Store на JavaScript и хотите работать с какими-то архивированными файлами? Вы можете прямо использовать Zip-функциональность из .NET Framework. Или создаете новое музыкальное приложение Windows Store на C#, и вам вдруг понадобилось низкоуровневое программирование аудио? Без проблем, просто используйте Xaudio или WASAPI в C++. Эти возможности иллюстрирует рис. 5.
Рис. 5. Windows Runtime позволяет создавать гибридные приложения
Windows Store App Using JavaScript | Приложение Windows Store на JavaScript |
JavaScript Code | Код на JavaScript |
C#/Visual Basic Code | Код на C#/Visual Basic |
C++ Code | Код на C++ |
Windows Store App Using C#/Visual Basic | Приложение Windows Store на C#/Visual Basic |
Windows Store App Using C++ | Приложение Windows Store на C++ |
Давайте рассмотрим пример программного синтезатора, использующий XAML, написанный на C#. Я хочу добавить некий впечатляющий фильтр в это приложение, поэтому я воспользуюсь Xaudio для прямого управления буферами аудиоданных. С помощью C++/CX я создаю API, которые будут предоставляться проекту на C#.
Сначала я создаю простую WinRT-оболочку для нужной мне функциональности Xaudio (рис. 6).
Рис. 6. XAudioWrapper.h
#pragma once
#include "mmreg.h"
#include <vector>
#include <memory>
namespace XAudioWrapper
{
public ref class XAudio2SoundPlayer sealed
{
public:
XAudio2SoundPlayer(uint32 sampleRate);
virtual ~XAudio2SoundPlayer();
void Initialize();
bool PlaySound(size_t index);
bool StopSound(size_t index);
bool IsSoundPlaying(size_t index);
size_t GetSoundCount();
void Suspend();
void Resume();
private:
interface IXAudio2* m_audioEngine;
interface IXAudio2MasteringVoice* m_masteringVoice;
std::vector<std::shared_ptr<ImplData>> m_soundList;
};
}
Обратите внимание на использование ключевых слов public, ref и sealed в объявлении класса. Именно так вы объявляете открытый WinRT-класс в C++/CX. Это позволит использовать данный класс из других языков вроде JavaScript, C# или Visual Basic. Важно отметить, что это не управляемый C++ или C++/Common Language Infrastructure (CLI). Он не компилируется в промежуточный язык Microsoft intermediate language (MSIL); это целиком и полностью неуправляемый компонент.
Также заметьте, что открытая функциональность (методы, свойства и др.) этого класса ограничены встроенными типами C++ или WinRT-типами. Только этим типам дозволено пересекать языковые границы в WinRT-компонентах. Однако вы можете задействовать существующие библиотеки для C++ (например, Standard Template Library), если они нужны вам в реализации ваших WinRT-компонентов.
Ресурсы
За более подробной информацией обращайтесь на следующие ресурсы.
Часто задаваемые вопросы
В. Является ли Windows Runtime заменой CLR или Microsoft .NET Framework?
О. Нет. Windows Runtime не заменяет ни .NET Framework, ни любую другую инфраструктуру. Когда вы создаете приложение Windows Store на C#, ваш код по-прежнему выполняется в CLR. Более того, при создании приложений Windows Store вам доступны подмножество .NET Framework, а также Win32 и COM.
В. Когда я пишу приложения Windows Store на C++, я использую C++/CLI?
О. Нет. Код, использующий Windows Runtime для приложений Windows Store, пишется на C++/CX. Хотя на первый взгляд он может показаться похожим на C++/CLI, на самом деле он является истинно неуправляемым. Таким образом, вы не введете сбор мусора или другие средства C++/CLI в свое неуправляемое приложение.
Подробнее о создании WinRT-компонентов на C++, читайте раздел «Creating Windows Runtime Components in C++» в Windows Dev Center по ссылке bit.ly/TbgWz7.
Теперь, когда я определил базовый интерфейс для своего класса, кратко обсудим некоторые из его реализованных методов (детали не важны), показанные на рис. 7.
Рис. 7. XAudioWrapper.cpp
XAudio2SoundPlayer::XAudio2SoundPlayer(uint32 sampleRate) :
m_soundList()
{
...
XAudio2Create(&m_audioEngine, flags);
// Создаем эталонный тон (mastering voice)
m_audioEngine->CreateMasteringVoice(
&m_masteringVoice,
XAUDIO2_DEFAULT_CHANNELS,
sampleRate
);
}
bool XAudio2SoundPlayer::PlaySound(size_t index)
{
//
// Буфер настройки
//
XAUDIO2_BUFFER playBuffer = { 0 };
std::shared_ptr<ImplData> soundData = m_soundList[index];
playBuffer.AudioBytes = soundData->playData->Length;
playBuffer.pAudioData = soundData->playData->Data;
playBuffer.Flags = XAUDIO2_END_OF_STREAM;
HRESULT hr = soundData->sourceVoice->Stop();
if (SUCCEEDED(hr))
{
...
}
...
}
Как вы заметите из фрагмента кода на рис. 7, я использую Xaudio2 COM API, доступные в Windows SDK для приложений Windows Store, чтобы подключить звуковой движок. Кроме того, для реализации необходимой функциональности я использую не только WinRT-типы, но и конструкции и типы C++.
Определив и реализовав базовый класс, я могу просто добавить ссылку на свой проект на C++ из проекта на C#. В итоге этот класс, предоставляемый из проекта на C++, становится доступным в проекте на C# (рис. 8).
Рис. 8. MainPage.cs
using XAudioWrapper;
namespace BasicSoundApp
{
public sealed partial class MainPage : Page
{
XAudio2SoundPlayer _audioPlayer = new XAudio2SoundPlayer(48000);
public MainPage()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
_audioPlayer.Initialize();
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
_audioPlayer.PlaySound(0);
}
}}
Как видите, хотя XAudioWrapper был написан на неуправляемом C++, его можно использовать так, будто он является обычным .NET-классом. Я ссылаюсь на его пространство имен, создаю экземпляр компонента и начинаю вызывать предоставляемые им методы — и для вызовов неуправляемого кода никаких выражений DllImport не требуется!
Никаких границ нет. Разработчик на JavaScript не ограничен библиотеками JavaScript, разработчик на C#/Visual Basic — .NET-библиотеками, а разработчик на C++ — библиотеками C/C++. Часто ли вам понадобится создавать свои компоненты? Возможно, нет. Но такой вариант вам доступен.
Подводя итоги, отмечу, что в основе создания приложений Windows Store для Windows 8 лежит Windows Runtime. Это мощная платформа для разработки таких приложений. Windows Runtime обеспечивает такую среду разработки, которая согласуется с привычными нам средами, имеет продуманную архитектуру и богатую функциональность. И на каком бы языке вы ни писали программы, теперь вы можете быть разработчиком, создающим новые приложения Windows Store для Windows 8.