Программная логика приложений для Windows 8 и их взаимодействие с системой

Устройства и печать

Разбить на страницы
Показывать лекцию целиком

Введение

Материалы к лекциям 10-12 Вы можете скачать здесь.

Меня иногда поражает количество устройств, подключенных к тому скромному ноутбуку, на котором я это пишу. Помимо док-станции, которая сейчас служит подставкой для монитора и весьма эффективным пылесборником, есть мышь (проводная), клавиатура (беспроводная), два больших монитора, колонки, USB-диск, наушники, игровой контроллер Xbox и иногда – внешний жёсткий диск. Добавьте сюда пару принтеров и медиа-приемников, подключенных к локальной сети. Да, возможно я и близко не подошёл к тому, что у многих моих читателей, вероятно, есть дома и на работе!

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

С некоторыми темами мы уже столкнулись, здесь я хочу их кратко упомянуть:

  • В Главе 2 курса "Пользовательский интерфейс приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript" мы узнали о возможности Съемные носители (Removable Storage) (в манифесте), которая позволяет приложению работать с USB-дисками и другими подключаемыми носителями информации. Когда устройство подключено, его папки становятся доступными с помощью Windows.Storage.KnownFolders.removableDevices (http://msdn.microsoft.com/library/windows/apps/windows.storage.knownfolders.removabledevices.aspx ), что является обычным объектом StorageFolder, подпапки которого подключены к конкретным запоминающим устройствам. Смотрите пример "Съемные носители" (http://code.msdn.microsoft.com/windowsapps/Removable-Storage-52cc49f0 ).
  • В Главе 2 курса "Введение в разработку приложений для Windows 8 с использованием HTML, CSS и JavaScript" и Главе 4 курса "Пользовательский интерфейс приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript" мы рассматривали API Windows.Media.Capture (http://msdn.microsoft.com/library/windows/apps/windows.media.capture.aspx ) для того, чтобы без особых усилий получать аудио, изображения и видео с подключенной к устройству камеры (смотрите пример "Пользовательский интерфейс камеры" ( http://code.msdn.microsoft.com/windowsapps/CameraCaptureUI-Sample-845a53ac)). Это включает возможность выбирать устройство захвата посредством Сценария 2 примера "Захват мультимедиа с использованием устройств для захвата" (http://code.msdn.microsoft.com/windowsapps/Media-Capture-Sample-adf87622 ), для чего используется API в Windows.Devices.Enumeration (http://msdn.microsoft.com/library/windows/apps/windows.devices.enumeration.aspx ).
  • Кроме того, в Главе 4 курса "Пользовательский интерфейс приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript" мы рассматривали API Windows.Media.PlayTo (http://msdn.microsoft.com/library/windows/apps/windows.media.playto.aspx ) для воспроизведения мультимедиа на устройствах, поддерживающих функцию воспроизведения на целевом устройстве, как показано в примере "Проигрывание мультимедиа на целевых устройствах воспроизведения" (http://code.msdn.microsoft.com/windowsapps/Media-PlayTo-Sample-fedcb0f9 ). Помимо этого есть гораздо больше, чем можно поместить в этот курс. Аппаратное обеспечение – это целый мир. Но мы , по крайней мере, будем знать, где находятся те или иные ресурсы и потратим некоторое время на изучение особенно интересных областей. Это следующие:
  • Использование устройств с помощью API, которые не доступны напрямую в JavaScript, таких, как Win32. Приложения так же могут перечислять дополнительные устройства с помощью определеного интерфейса класса (Данная возможность взаимодействия с подобными устройствами в настоящий момент ограничена для приложений для Магазина Windows).
  • Подключение к устройствам, находящимся в непосредственной близости от устройства, на котором исполняется ваше приложение, посредством WiFi Direct, Bluetooth, и технологии Near-Field Сommunications (NFC). В последнем случае, NFC может связывать приложения, работающие на устройствах, или использоваться для получения информации от недорогих RFID-меток.
  • Последнее, но не менее важное – это печать, возможность которой несложно добавить к приложениям для Магазина Windows.
  • Кроме того, позвольте мне упомянуть класс приложений, которые мы в этом курсе не рассматриваем: Приложения Магазина Windows для устройств (Windows Store Device Apps), как их называют. Это приложения, которые могут быть автоматически получены из Магазина Windows, когда соответствующее устройство подключено к компьютеру под управлением Windows. Здесь наблюдается удивительное разнообразие, и мы можем здесь увидеть всё – от самых обычных наушников, мониторов, принтеров, Камер, мышей и клавиатур до новейших интеллектуальных телевизоров, устройств управления, домашних аудиосистем, медицинских сенсоров, научной аппаратуры, игрушек, устройств для оплаты покупок, музыкальных инструментов и так далее. Программы для устройств – это те самые приложения, которые всегда использовались на CD и служили для того, чтобы раскрыть функциональность устройства, теперь это – просто приложения, выполняющую такую функцию. Приложения для Магазина Windows, в общем случае, не могут работать со специализированными устройствами, если только нет некоторых общедоступных API, как мы увидим в первом разделе ниже.

    Написание приложений для устройств выходит за пределы данного курса, но если вам это нужно, вы можете обратиться к видео с Channel 9 "Windows Store Device App Workshop" (http://channel9.msdn.com/Events/Windows-Store-Device-Apps-Workshop/Windows-Store-Device-Apps-Workshop-2012?t=charms ), вместе с материалом "Windows 8 Device Experience: Windows Store Device Apps" (http://msdn.microsoft.com/library/windows/hardware/br259108.aspx ) и "Windows Store device Apps for Specialized Connected Devices" (http://msdn.microsoft.com/library/windows/hardware/jj552525 ). Имеется и несколько примеров, таких, как "Windows 8 device app for camera sample" (http://code.msdn.microsoft.com/windowsapps/Metro-style-device-app-for-4f39b7bf ) и "Device apps for printers sample"( http://code.msdn.microsoft.com/windowsapps/Device-app-for-printers-91f363a9). Гораздо более специфичен пример "Custom driver access sample" (http://code.msdn.microsoft.com/windowsapps/Custom-device-access-sample-43bde679 ), который работает с аппаратным обеспечением FX2 в "OSR USB FX2 Learning Kit" (https://www.osronline.com/custom.cfm?name=index_fullframeset.cfmpgpageURL=https://www.osronline.com/store/index.cfm ) (из Open System Resources). Это аппаратное обеспечение предназначено для людей, изучающих разработку драйверов устройств, которые хотят понять тонкости этого дела.

    Работа с устройствами

    Как уже сказано, приложения для Магазина Windows обычно не имеют доступа к специализированному аппаратному обеспечению и интерфейсам, существующим у драйверов приложения: это особая привилегия приложений для устройств. Однако, если устройство и его драйвер подключили к системе некоторое API, у других приложений есть свойства для того, чтобы работать с ним, как показано на рис 10.1.

    Система API, доступных приложениям для Магазина Windows, разнообразна. Как мы видели ранее, WinRT может получать доступ к камере, целевым устройствам воспроизведения мультимедиа, устройствам хранения (в том числе – к USB-дискам, камерам, медиа-проигрывателям), и к устройствам ввода, в последнем случае подробности работы с аппаратным обеспечением скрыты за уровнем абстракции, наподобие указателей. WinRT так же предоставляет абстракцию, с помощью которой приложения могут работать с любым количеством принтером, как мы увидим в разделе "Простая печать" позже.

    (рис 10.1) Приложения для Магазина Windows в Windows 8 могут работать лишьс устройствами, которые имеют представляющие их API. У приложений Магазина Windows для устройств есть возможность работать с конкретными устройствами напрямую, используя их драйверы

    В некоторых случаях, как мы увидим в нашем первом примере, посвященном работе с контроллером Xbox, это API Win32/COM, которые построены на определенном аппаратном интерфейсе. В случае с контроллерами API XInput (часть of DirectX) делает именно это. XInput не доступен напрямую приложениям, написанным на JavaScript, но компонент WinRT может получить такой доступ от имени приложения. Подобный компонент, как мы подробнее увидим в Главе 5, расширяет API WinRT для приложения и включается в его пакет. API, предоставленные такими компонентами, воспринимаются приложением как API WinRT. Они лишь расположены в пространствах имен, отличных от Windows.

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

    Еще один класс устройств, с которыми может работать приложение, называются Переносными устройствами Windows (Windows Portable Devices), которые включают в себя съемные носители и множество интересных устройств, использующих Bluetooth. Это один из случаев, когда полезна возможность перечислять устройства, так как имеется элемент управления ActiveX, который, в сущности, является COM API, посредством которого вы можете обмениваться информацией с такими устройствами.

    API XInput и игровые контроллеры

    API XInput API, часть DirectX, это API Win32, которое предназначено для работы с игровыми контроллероами и находится в списке API Win32/COM, доступ к которым можно получить из приложений для Магазина Windows. Видимо, наиболее часто используемая его функция, это XInputGetState, которая возвращает структуру XINPUT_STATE, которая описывает позицию джойстиков игрового манипулятора, силу нажатия на переключатели, состояния кнопок. Эти данные обычно считывают с каждым кадром анимации в чем-то вроде игрового приложения. API не вызывает события, когда меняется состояние игрового контроллера.

    Пример "Рисование с использованием JavaScript и XInput" ( http://code.msdn.microsoft.com/windowsapps/XInput-and-JavaScript-c72fe535) в Windows SDK показывает именно это. Так как API XInput недоступно напрямую из JavaScript, необходимо создать для этих целей WinRT-компонент. Проще говоря, вы создаете компонент с общедоступным классом внутри пространства имен, которое совпадает с именем файла компонента и затем добавляете ссылку на этот компонент в проект JavaScript-приложения в VisualStudio. Это импортирует пространство имен и делает его доступным в JavaScript. Подробности об этом будут в Главе 5, но обычный код C++-компонента выглядит так, как показано ниже – сначала – в заголовочном (Controller.h) файле в проекте GameController:

         
    namespace GameController
    {
    public value struct State
    {
    // [Опусщено – содержит те же значения, что и структура Win32 XINPUT_STATE
    };
    public ref class Controller sealed
    {
    ~Controller();
    uint32 m_index;
    bool   m_isControllerConnected; // Подключен ли контроллер?
    XINPUT_CAPABILITIES m_xinputCaps;     // Возможности контроллера
    XINPUT_STATE  m_xinputState;     // Текущее состояние контроллера
    uint64 m_lastEnumTime;    // Время, когда в последний раз было проверено
    // подключение контроллера
    
    public:
    Controller(uint32 index);
    
    void SetState(uint16 leftSpeed, uint16 rightSpeed);
    State GetState();
    };
    }
    
    Реализация GetState в Controller.cpp затем просто вызывает XInputGetState и 
    копирует его свойства в экземпляр общедоступной структуры State:
    State Controller::GetState()
    {
    // по умолчанию возвращает controllerState который показывает, что контроллер не подключен
    State controllerState;
    controllerState.connected = false;
    // Приложению следует избегать вызова функции XInput в каждом кадре, если нет
    // подключенных устройств, так как первичное перечисление устройств может ухудшить
    // производительность приложения.
    uint64 currentTime = ::GetTickCount64();
    if (!m_isControllerConnected pgpg currentTime - m_lastEnumTime<EnumerateTimeout)
    {
    return controllerState;
    }
    
    m_lastEnumTime = currentTime;
    auto stateResult = XInputGetState(m_index, pgm_xinputState);
    
    if (stateResult == ERROR_SUCCESS)
    {
    m_isControllerConnected = true; controllerState.connected = true; controllerState.controllerId = m_index; 
    controllerState.packetNumber = m_xinputState.dwPacketNumber;
    controllerState.LeftTrigger = m_xinputState.Gamepad.bLeftTrigger;
    controllerState.RightTrigger = m_xinputState.Gamepad.bRightTrigger;
    
    // И так далее [копирование других свойств опущено.]
    }
    else
    {
    m_isControllerConnected = false;
    }
    
    return controllerState;
    }
    Конструктор для объекта Controller так же весьма прост
    Controller::Controller(uint32 index)
    {
    m_index = index;
    m_lastEnumTime = ::GetTickCount64() - EnumerateTimeout;
    }

    В приложении JavaScript, когда добавлена ссылка на компонент – пространство имен GameController будет содержать общедоступное API компонента и мы можем использовать его так же, как если бы оно было встроено в Windows. В примере, сначала создаётся экземпляр объекта Controller (с нулевым индексом) и затем начинают выводиться кадры анимации: (program.js):

      app.onactivated = function (eventObj) {
      if (eventObj.detail.kind ===
      Windows.ApplicationModel.Activation.ActivationKind.launch) {
      // [Другие настройки опущены]
    
      // Создаёт экземпляр объекта Controller из компонента WinRT
      controller = new GameController.Controller(0);
    
      // Начало цикла рендеринга
      requestAnimationFrame(renderLoop);
      };
      };
    

    Затем функция renderLoop просто вызывает метод компонента getState и выполняет рисование в элементе canvas перед повторением цикла (так же в program.js, хотя много кода опущено):

      function renderLoop() {
      var state = controller.getState();
    
      if (state.connected) {
      controllerPresent.style.visibility = "hidden";
    
      // Код, добавленный в пример для расширения его функциональности
      if (state.leftTrigger) {
      context.clearRect(0, 0, sketchSurface.width, sketchSurface.height);
      requestAnimationFrame(renderLoop);
      return;
      }
    
      if (state.a) {
      context.strokeStyle = "green";
      } else if (state.b) {
      context.strokeStyle = "red";
      } else if (state.x) {
      context.strokeStyle = "blue";
      } else if (state.y) {
      context.strokeStyle = "orange";
      }
    
      // Обработка состояния и рисование в элементе canvas [код опущен]
      };
    
      // Повторить для следующего кадра
      requestAnimationFrame(renderLoop);
      };
    

    Вывод данных этим примером показан на рис 10.2, что отражает добавленную мной возможность, показанную в вышеприведенном коде, которая делает программу интересней для моего сына: изменение цветов с помощью кнопок A/B/X/Y и очистка экрана с помощью левого переключателя. Как вы можете видеть, то, что я рисую в этой программе не слишком отличается от того, что рисует он.

    В итоге, хотя WinRT не содержит средств для работы с API наподобие XInput, приложение может делать это самостоятельно, благодаря реализации простого компонента. Обратите внимание на то, что различные аспекты интерфейса компонента, наподобие особенностей написания имени, изменятся, когда он будет спроецирован в JavaScript. В лекциях 13-14 мы рассмотрим это подробнее. Сейчас же это показывает нам, что доступ к подобным специализированным устройство – это не такая уж и сложная задача.

    (рис 10.2) Пример "Рисование с использованием JavaScript и XInput" с некоторыми изменениями, которые позволяют менять цвет. Изменение ширины линии регулируется позицией правого переключателя

    Перечисление устройств в классе

    Если вдруг вы знаете GUID интерфейса класса устройств для конкретной группы устройств, вы можете использовать статический метод ) для получения подробной информации об этих устройствах. Подробности об этом можно найти в материале "Перечисление устройств" (http://msdn.microsoft.com/library/windows/apps/hh464977.aspx ), однако, позвольте мне сделать краткий обзор.

    То, что передают в ), с ними мы встречались в Главе 2 курса "Пользовательский интерфейс приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript". Селектор устройства обычно выглядит так:

    System.Devices.InterfaceClassGuid:="{E5323777-F976-4F5B-9B55-B94699C46E44}" 
    AND System.Devices.InterfaceEnabled:=System.StructuredQueryType.Boolean#True

    Здесь показан класс интерфейса GUID для вебкамер. Результат вызова findAllAsync это объект ), который может рассматриваться в JavaScript в виде массива объектов ). В Сценарии 1 примера "Перечисление устройств" (http://code.msdn.microsoft.com/windowsapps/Device-Enumeration-Sample-a6e45169 ) показано, как использовать этот массив для вывода подробной информации о каждом устройстве:

    Windows.Devices.Enumeration.DeviceInformation.findAllAsync(selector, null).done(
    function(devinfoCollection) {
    var numDevices = devinfoCollection.length;
    for (var i = 0; i<numDevices; i++) {	
    displayDeviceInterface(devinfoCollection[i], id("scenario1Output"), i);
    }	
    });	

    Некоторые результаты работы примера показаны на рис 10.3 и рис 10.4.

    (рис 10.3) Вывод данных примером "Перечисление устройств" для вебкамеры – он отлично представляет камеру, прикрепленную к моему монитору (рис 10.4) Вывод данных примером "Перечисление устройств" для принтера, который выглядит точно так же, как тот, что стоит у моего стола

    Сценарий 2 примера выполняет те же действия (с выводом в виде обычного текста) для объектов Plug and Play (PnP), используя ). Это API позволяет перечислять устройства по интерфейсу, классу интерфейса и контейнеру (видимые и локализованные сведения об устройстве, наподобие производителя и названия модели):

    Windows.Devices.Enumeration.Pnp.PnpObject.findAllAsync(deviceContainerType,	
    propertiesToRetrieve).done(function (containerCollection) {	
    var numContainers = containerCollection.length;	
    for (var i = 0; i<numContainers; i++) {	
    displayDeviceContainer(containerCollection[i], id("scenario2Output"));
    }	
    });	

    В вышеприведенном вызове переменная ), которые вам интересны. В примере использованы следующие:

    var propertiesToRetrieve = ["System.ItemNameDisplay", "System.Devices.ModelName", "System.Devices.Connected"];

    Результат перечисления – переменная ), которая содержит экземпляры объекта ). Пример берет информацию из этих объектов и выводит информацию по каждому из них в виде текста.

    Обратите внимание, что существует вариант findAllAsync (http://msdn.microsoft.com/library/windows/apps/br225512.aspx ), который принимает AQS-строку в виде фильтра. Это строка, которую можно получить из API наподобие ), которое упрощает перечисление подобных устройств.

    Переносные устройства Windows и возможности Bluetooth

    В дополнение к перечислению принтеров и вебкамер (так как у них есть стандартные GUID класса интерфейса), Сценарий 1 примера "Перечисление устройств" (http://code.msdn.microsoft.com/windowsapps/Device-Enumeration-Sample-a6e45169 ) так же работает с переностыми устройствами, как и пример "Переносные устройства" ( http://code.msdn.microsoft.com/windowsapps/Portable-Device-f97089b5). Это открывает нам дверь в тему Переносных устройств Windows (Windows Portable Devices или WPD) (http://msdn.microsoft.com/library/windows/hardware/gg463541.aspx ), технологии работы с драйверами, которая поддерживает устройства наподобии телефонов, цифровых камер, портативных медиа-проигрывателей и так далее, а так же – всё увеличивающийся класс Bluetooth-устройств, когда основная задача взаимодействия – передать данные между устройством и системой. WPD поддерживают инфраструктуру для реализации этой цели.

    В WinRT, API Windows.Devices.Portable (http://msdn.microsoft.com/library/windows/apps/windows.devices.portable.aspx ) предоставляет средства для взаимодействия с этими устройства. Здесь вы можете найти классы ), который описан в Главе 2 курса "Пользовательский интерфейс приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript". Он создает список съемных носителей информации, из которого можно выбрать устройства и затем показывает первое изображение, найденное на выбранном устройстве:

    В примере о съемных носителях, в Сценарии 4, так же показано, как использовать автоматический запуск (AutoPlay) для автоматического вызова приложения, когда подключается подходящее устройство. Это включает в себя объявление в манифесте возможности Автозапуск содержимого (AutoPlay Content) (подключение устройства хранения данных) и/или Автозапуск устройства (AutoPlay Device) (подключение устройства). Смотрите материал "Запуск приложения с помощью автозапуска" (http://msdn.microsoft.com/library/windows/apps/hh452731.aspx ) для того, чтобы узнать подробности.

    Что касается Bluetooth-устройств, то ) предоставляет возможность работы с устройствами, имеющими отношение к телефонам. Смотрите материал "Управление вызовами на устройстве связи Bluetooth по умолчанию" (http://msdn.microsoft.com/library/windows/apps/hh452727.aspx ) и пример "Управление вызовами на Bluetooth-устройстве" (http://code.msdn.microsoft.com/windowsapps/Call-Control-b52ad696 ).

    Другая группа Bluetooth-устройств включает в себя те, которые собирают сведения о физическом состоянии, о таком, как пульс, кровяное давление и температура. Смотрите материал "Bluetooth с низким энергопотреблением" в Википедии ( http://en.wikipedia.org/wiki/Bluetooth_low_energy). Работа с ними показана в примере "Устройства Bluetooth с низким энергопотреблением и сведения о физическом состоянии" (http://code.msdn.microsoft.com/windowsapps/Bluetooth-LE-Metro-sample-a2ba1b5b ), что мы кратко рассмотрим ниже, но в настоящее время доступ к таким устройствам ограничен приложениями для устройств.

    Другой пример, "Простое управлени с помощью Bluetooth-устройства" (http://code.msdn.microsoft.com/windowsapps/Bluetooth-Simple-Key-e29ea456 ), кторый работает с контроллером CC2540 Mini Development Kit (http://www.ti.com/tool/cc2540dk-mini ). Это устройство было создано Texas Instruments для того, чтобы помочь разработке устройств на основе их микросхемы CC2540; в случае с примером, кнопки на устройстве управляют кнопками в приложении.

    В последних двух примерах интересно то, как приложения подключаются к конкретному устройству с использованием элемента управления ActiveX, который называется PortableDeviceAutomation.Factory (один из нескольких доступных ActiveX-объектов такого рода). Например, для того, чтобы подключиться к термометру, мы сначала перечисляем устройство с помощью его GUID класса, {00001809-0000-1000-8000-00805f9b34fb} (смотрите js/thermometer.js в примере "Устройства Bluetooth с низким энергопотреблением и сведения о физическом состоянии"):

    Windows.Devices.Enumeration.DeviceInformation.findAllAsync(
    "System.Devices.InterfaceClassGuid:=\"{00001809-0000-1000-8000-00805f9b34fb}\"", null)
          

    Результат этой асинхронной операции доступен в переменной, которая называется devices, а вот, как получают доступ к конкретному устройству и задают прослушиватели для его событий:

    // Используется WPD Automation для инициализации объекта устройства
    var deviceFactory = new ActiveXObject("PortableDeviceAutomation.Factory");
    
    // Для целей данного примера инициализируем первое устройство
    deviceFactory.getDeviceFromIdAsync(devices[0].id, function (device) {
    //	Переменная 'device' содержит объект, соответствующий объекту устройства.
    // Инициализация сервиса измерения температуры и ожидание результатов измерений
    tempService = device.services[0];
    tempService.onTemperatureMeasurement =
    function (timestamp, thermometerMeasurementValue) {
    // ...
    };
    });
          
    Страницы:

    Введение

    Материалы к лекциям 10-12 Вы можете скачать здесь.

    Меня иногда поражает количество устройств, подключенных к тому скромному ноутбуку, на котором я это пишу. Помимо док-станции, которая сейчас служит подставкой для монитора и весьма эффективным пылесборником, есть мышь (проводная), клавиатура (беспроводная), два больших монитора, колонки, USB-диск, наушники, игровой контроллер Xbox и иногда – внешний жёсткий диск. Добавьте сюда пару принтеров и медиа-приемников, подключенных к локальной сети. Да, возможно я и близко не подошёл к тому, что у многих моих читателей, вероятно, есть дома и на работе!

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

    С некоторыми темами мы уже столкнулись, здесь я хочу их кратко упомянуть:

  • В Главе 2 курса "Пользовательский интерфейс приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript" мы узнали о возможности Съемные носители (Removable Storage) (в манифесте), которая позволяет приложению работать с USB-дисками и другими подключаемыми носителями информации. Когда устройство подключено, его папки становятся доступными с помощью Windows.Storage.KnownFolders.removableDevices (http://msdn.microsoft.com/library/windows/apps/windows.storage.knownfolders.removabledevices.aspx ), что является обычным объектом StorageFolder, подпапки которого подключены к конкретным запоминающим устройствам. Смотрите пример "Съемные носители" (http://code.msdn.microsoft.com/windowsapps/Removable-Storage-52cc49f0 ).
  • В Главе 2 курса "Введение в разработку приложений для Windows 8 с использованием HTML, CSS и JavaScript" и Главе 4 курса "Пользовательский интерфейс приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript" мы рассматривали API Windows.Media.Capture (http://msdn.microsoft.com/library/windows/apps/windows.media.capture.aspx ) для того, чтобы без особых усилий получать аудио, изображения и видео с подключенной к устройству камеры (смотрите пример "Пользовательский интерфейс камеры" ( http://code.msdn.microsoft.com/windowsapps/CameraCaptureUI-Sample-845a53ac)). Это включает возможность выбирать устройство захвата посредством Сценария 2 примера "Захват мультимедиа с использованием устройств для захвата" (http://code.msdn.microsoft.com/windowsapps/Media-Capture-Sample-adf87622 ), для чего используется API в Windows.Devices.Enumeration (http://msdn.microsoft.com/library/windows/apps/windows.devices.enumeration.aspx ).
  • Кроме того, в Главе 4 курса "Пользовательский интерфейс приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript" мы рассматривали API Windows.Media.PlayTo (http://msdn.microsoft.com/library/windows/apps/windows.media.playto.aspx ) для воспроизведения мультимедиа на устройствах, поддерживающих функцию воспроизведения на целевом устройстве, как показано в примере "Проигрывание мультимедиа на целевых устройствах воспроизведения" (http://code.msdn.microsoft.com/windowsapps/Media-PlayTo-Sample-fedcb0f9 ). Помимо этого есть гораздо больше, чем можно поместить в этот курс. Аппаратное обеспечение – это целый мир. Но мы , по крайней мере, будем знать, где находятся те или иные ресурсы и потратим некоторое время на изучение особенно интересных областей. Это следующие:
  • Использование устройств с помощью API, которые не доступны напрямую в JavaScript, таких, как Win32. Приложения так же могут перечислять дополнительные устройства с помощью определеного интерфейса класса (Данная возможность взаимодействия с подобными устройствами в настоящий момент ограничена для приложений для Магазина Windows).
  • Подключение к устройствам, находящимся в непосредственной близости от устройства, на котором исполняется ваше приложение, посредством WiFi Direct, Bluetooth, и технологии Near-Field Сommunications (NFC). В последнем случае, NFC может связывать приложения, работающие на устройствах, или использоваться для получения информации от недорогих RFID-меток.
  • Последнее, но не менее важное – это печать, возможность которой несложно добавить к приложениям для Магазина Windows.
  • Кроме того, позвольте мне упомянуть класс приложений, которые мы в этом курсе не рассматриваем: Приложения Магазина Windows для устройств (Windows Store Device Apps), как их называют. Это приложения, которые могут быть автоматически получены из Магазина Windows, когда соответствующее устройство подключено к компьютеру под управлением Windows. Здесь наблюдается удивительное разнообразие, и мы можем здесь увидеть всё – от самых обычных наушников, мониторов, принтеров, Камер, мышей и клавиатур до новейших интеллектуальных телевизоров, устройств управления, домашних аудиосистем, медицинских сенсоров, научной аппаратуры, игрушек, устройств для оплаты покупок, музыкальных инструментов и так далее. Программы для устройств – это те самые приложения, которые всегда использовались на CD и служили для того, чтобы раскрыть функциональность устройства, теперь это – просто приложения, выполняющую такую функцию. Приложения для Магазина Windows, в общем случае, не могут работать со специализированными устройствами, если только нет некоторых общедоступных API, как мы увидим в первом разделе ниже.

    Написание приложений для устройств выходит за пределы данного курса, но если вам это нужно, вы можете обратиться к видео с Channel 9 "Windows Store Device App Workshop" (http://channel9.msdn.com/Events/Windows-Store-Device-Apps-Workshop/Windows-Store-Device-Apps-Workshop-2012?t=charms ), вместе с материалом "Windows 8 Device Experience: Windows Store Device Apps" (http://msdn.microsoft.com/library/windows/hardware/br259108.aspx ) и "Windows Store device Apps for Specialized Connected Devices" (http://msdn.microsoft.com/library/windows/hardware/jj552525 ). Имеется и несколько примеров, таких, как "Windows 8 device app for camera sample" (http://code.msdn.microsoft.com/windowsapps/Metro-style-device-app-for-4f39b7bf ) и "Device apps for printers sample"( http://code.msdn.microsoft.com/windowsapps/Device-app-for-printers-91f363a9). Гораздо более специфичен пример "Custom driver access sample" (http://code.msdn.microsoft.com/windowsapps/Custom-device-access-sample-43bde679 ), который работает с аппаратным обеспечением FX2 в "OSR USB FX2 Learning Kit" (https://www.osronline.com/custom.cfm?name=index_fullframeset.cfmpgpageURL=https://www.osronline.com/store/index.cfm ) (из Open System Resources). Это аппаратное обеспечение предназначено для людей, изучающих разработку драйверов устройств, которые хотят понять тонкости этого дела.

    Работа с устройствами

    Как уже сказано, приложения для Магазина Windows обычно не имеют доступа к специализированному аппаратному обеспечению и интерфейсам, существующим у драйверов приложения: это особая привилегия приложений для устройств. Однако, если устройство и его драйвер подключили к системе некоторое API, у других приложений есть свойства для того, чтобы работать с ним, как показано на рис 10.1.

    Система API, доступных приложениям для Магазина Windows, разнообразна. Как мы видели ранее, WinRT может получать доступ к камере, целевым устройствам воспроизведения мультимедиа, устройствам хранения (в том числе – к USB-дискам, камерам, медиа-проигрывателям), и к устройствам ввода, в последнем случае подробности работы с аппаратным обеспечением скрыты за уровнем абстракции, наподобие указателей. WinRT так же предоставляет абстракцию, с помощью которой приложения могут работать с любым количеством принтером, как мы увидим в разделе "Простая печать" позже.

    (рис 10.1) Приложения для Магазина Windows в Windows 8 могут работать лишьс устройствами, которые имеют представляющие их API. У приложений Магазина Windows для устройств есть возможность работать с конкретными устройствами напрямую, используя их драйверы

    В некоторых случаях, как мы увидим в нашем первом примере, посвященном работе с контроллером Xbox, это API Win32/COM, которые построены на определенном аппаратном интерфейсе. В случае с контроллерами API XInput (часть of DirectX) делает именно это. XInput не доступен напрямую приложениям, написанным на JavaScript, но компонент WinRT может получить такой доступ от имени приложения. Подобный компонент, как мы подробнее увидим в Главе 5, расширяет API WinRT для приложения и включается в его пакет. API, предоставленные такими компонентами, воспринимаются приложением как API WinRT. Они лишь расположены в пространствах имен, отличных от Windows.

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

    Еще один класс устройств, с которыми может работать приложение, называются Переносными устройствами Windows (Windows Portable Devices), которые включают в себя съемные носители и множество интересных устройств, использующих Bluetooth. Это один из случаев, когда полезна возможность перечислять устройства, так как имеется элемент управления ActiveX, который, в сущности, является COM API, посредством которого вы можете обмениваться информацией с такими устройствами.

    API XInput и игровые контроллеры

    API XInput API, часть DirectX, это API Win32, которое предназначено для работы с игровыми контроллероами и находится в списке API Win32/COM, доступ к которым можно получить из приложений для Магазина Windows. Видимо, наиболее часто используемая его функция, это XInputGetState, которая возвращает структуру XINPUT_STATE, которая описывает позицию джойстиков игрового манипулятора, силу нажатия на переключатели, состояния кнопок. Эти данные обычно считывают с каждым кадром анимации в чем-то вроде игрового приложения. API не вызывает события, когда меняется состояние игрового контроллера.

    Пример "Рисование с использованием JavaScript и XInput" ( http://code.msdn.microsoft.com/windowsapps/XInput-and-JavaScript-c72fe535) в Windows SDK показывает именно это. Так как API XInput недоступно напрямую из JavaScript, необходимо создать для этих целей WinRT-компонент. Проще говоря, вы создаете компонент с общедоступным классом внутри пространства имен, которое совпадает с именем файла компонента и затем добавляете ссылку на этот компонент в проект JavaScript-приложения в VisualStudio. Это импортирует пространство имен и делает его доступным в JavaScript. Подробности об этом будут в Главе 5, но обычный код C++-компонента выглядит так, как показано ниже – сначала – в заголовочном (Controller.h) файле в проекте GameController:

         
    namespace GameController
    {
    public value struct State
    {
    // [Опусщено – содержит те же значения, что и структура Win32 XINPUT_STATE
    };
    public ref class Controller sealed
    {
    ~Controller();
    uint32 m_index;
    bool   m_isControllerConnected; // Подключен ли контроллер?
    XINPUT_CAPABILITIES m_xinputCaps;     // Возможности контроллера
    XINPUT_STATE  m_xinputState;     // Текущее состояние контроллера
    uint64 m_lastEnumTime;    // Время, когда в последний раз было проверено
    // подключение контроллера
    
    public:
    Controller(uint32 index);
    
    void SetState(uint16 leftSpeed, uint16 rightSpeed);
    State GetState();
    };
    }
    
    Реализация GetState в Controller.cpp затем просто вызывает XInputGetState и 
    копирует его свойства в экземпляр общедоступной структуры State:
    State Controller::GetState()
    {
    // по умолчанию возвращает controllerState который показывает, что контроллер не подключен
    State controllerState;
    controllerState.connected = false;
    // Приложению следует избегать вызова функции XInput в каждом кадре, если нет
    // подключенных устройств, так как первичное перечисление устройств может ухудшить
    // производительность приложения.
    uint64 currentTime = ::GetTickCount64();
    if (!m_isControllerConnected pgpg currentTime - m_lastEnumTime<EnumerateTimeout)
    {
    return controllerState;
    }
    
    m_lastEnumTime = currentTime;
    auto stateResult = XInputGetState(m_index, pgm_xinputState);
    
    if (stateResult == ERROR_SUCCESS)
    {
    m_isControllerConnected = true; controllerState.connected = true; controllerState.controllerId = m_index; 
    controllerState.packetNumber = m_xinputState.dwPacketNumber;
    controllerState.LeftTrigger = m_xinputState.Gamepad.bLeftTrigger;
    controllerState.RightTrigger = m_xinputState.Gamepad.bRightTrigger;
    
    // И так далее [копирование других свойств опущено.]
    }
    else
    {
    m_isControllerConnected = false;
    }
    
    return controllerState;
    }
    Конструктор для объекта Controller так же весьма прост
    Controller::Controller(uint32 index)
    {
    m_index = index;
    m_lastEnumTime = ::GetTickCount64() - EnumerateTimeout;
    }

    В приложении JavaScript, когда добавлена ссылка на компонент – пространство имен GameController будет содержать общедоступное API компонента и мы можем использовать его так же, как если бы оно было встроено в Windows. В примере, сначала создаётся экземпляр объекта Controller (с нулевым индексом) и затем начинают выводиться кадры анимации: (program.js):

      app.onactivated = function (eventObj) {
      if (eventObj.detail.kind ===
      Windows.ApplicationModel.Activation.ActivationKind.launch) {
      // [Другие настройки опущены]
    
      // Создаёт экземпляр объекта Controller из компонента WinRT
      controller = new GameController.Controller(0);
    
      // Начало цикла рендеринга
      requestAnimationFrame(renderLoop);
      };
      };
    

    Затем функция renderLoop просто вызывает метод компонента getState и выполняет рисование в элементе canvas перед повторением цикла (так же в program.js, хотя много кода опущено):

      function renderLoop() {
      var state = controller.getState();
    
      if (state.connected) {
      controllerPresent.style.visibility = "hidden";
    
      // Код, добавленный в пример для расширения его функциональности
      if (state.leftTrigger) {
      context.clearRect(0, 0, sketchSurface.width, sketchSurface.height);
      requestAnimationFrame(renderLoop);
      return;
      }
    
      if (state.a) {
      context.strokeStyle = "green";
      } else if (state.b) {
      context.strokeStyle = "red";
      } else if (state.x) {
      context.strokeStyle = "blue";
      } else if (state.y) {
      context.strokeStyle = "orange";
      }
    
      // Обработка состояния и рисование в элементе canvas [код опущен]
      };
    
      // Повторить для следующего кадра
      requestAnimationFrame(renderLoop);
      };
    

    Вывод данных этим примером показан на рис 10.2, что отражает добавленную мной возможность, показанную в вышеприведенном коде, которая делает программу интересней для моего сына: изменение цветов с помощью кнопок A/B/X/Y и очистка экрана с помощью левого переключателя. Как вы можете видеть, то, что я рисую в этой программе не слишком отличается от того, что рисует он.

    В итоге, хотя WinRT не содержит средств для работы с API наподобие XInput, приложение может делать это самостоятельно, благодаря реализации простого компонента. Обратите внимание на то, что различные аспекты интерфейса компонента, наподобие особенностей написания имени, изменятся, когда он будет спроецирован в JavaScript. В лекциях 13-14 мы рассмотрим это подробнее. Сейчас же это показывает нам, что доступ к подобным специализированным устройство – это не такая уж и сложная задача.

    (рис 10.2) Пример "Рисование с использованием JavaScript и XInput" с некоторыми изменениями, которые позволяют менять цвет. Изменение ширины линии регулируется позицией правого переключателя

    Перечисление устройств в классе

    Если вдруг вы знаете GUID интерфейса класса устройств для конкретной группы устройств, вы можете использовать статический метод ) для получения подробной информации об этих устройствах. Подробности об этом можно найти в материале "Перечисление устройств" (http://msdn.microsoft.com/library/windows/apps/hh464977.aspx ), однако, позвольте мне сделать краткий обзор.

    То, что передают в ), с ними мы встречались в Главе 2 курса "Пользовательский интерфейс приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript". Селектор устройства обычно выглядит так:

    System.Devices.InterfaceClassGuid:="{E5323777-F976-4F5B-9B55-B94699C46E44}" 
    AND System.Devices.InterfaceEnabled:=System.StructuredQueryType.Boolean#True

    Здесь показан класс интерфейса GUID для вебкамер. Результат вызова findAllAsync это объект ), который может рассматриваться в JavaScript в виде массива объектов ). В Сценарии 1 примера "Перечисление устройств" (http://code.msdn.microsoft.com/windowsapps/Device-Enumeration-Sample-a6e45169 ) показано, как использовать этот массив для вывода подробной информации о каждом устройстве:

    Windows.Devices.Enumeration.DeviceInformation.findAllAsync(selector, null).done(
    function(devinfoCollection) {
    var numDevices = devinfoCollection.length;
    for (var i = 0; i<numDevices; i++) {	
    displayDeviceInterface(devinfoCollection[i], id("scenario1Output"), i);
    }	
    });	

    Некоторые результаты работы примера показаны на рис 10.3 и рис 10.4.

    (рис 10.3) Вывод данных примером "Перечисление устройств" для вебкамеры – он отлично представляет камеру, прикрепленную к моему монитору (рис 10.4) Вывод данных примером "Перечисление устройств" для принтера, который выглядит точно так же, как тот, что стоит у моего стола

    Сценарий 2 примера выполняет те же действия (с выводом в виде обычного текста) для объектов Plug and Play (PnP), используя ). Это API позволяет перечислять устройства по интерфейсу, классу интерфейса и контейнеру (видимые и локализованные сведения об устройстве, наподобие производителя и названия модели):

    Windows.Devices.Enumeration.Pnp.PnpObject.findAllAsync(deviceContainerType,	
    propertiesToRetrieve).done(function (containerCollection) {	
    var numContainers = containerCollection.length;	
    for (var i = 0; i<numContainers; i++) {	
    displayDeviceContainer(containerCollection[i], id("scenario2Output"));
    }	
    });	

    В вышеприведенном вызове переменная ), которые вам интересны. В примере использованы следующие:

    var propertiesToRetrieve = ["System.ItemNameDisplay", "System.Devices.ModelName", "System.Devices.Connected"];

    Результат перечисления – переменная ), которая содержит экземпляры объекта ). Пример берет информацию из этих объектов и выводит информацию по каждому из них в виде текста.

    Обратите внимание, что существует вариант findAllAsync (http://msdn.microsoft.com/library/windows/apps/br225512.aspx ), который принимает AQS-строку в виде фильтра. Это строка, которую можно получить из API наподобие ), которое упрощает перечисление подобных устройств.

    Переносные устройства Windows и возможности Bluetooth

    В дополнение к перечислению принтеров и вебкамер (так как у них есть стандартные GUID класса интерфейса), Сценарий 1 примера "Перечисление устройств" (http://code.msdn.microsoft.com/windowsapps/Device-Enumeration-Sample-a6e45169 ) так же работает с переностыми устройствами, как и пример "Переносные устройства" ( http://code.msdn.microsoft.com/windowsapps/Portable-Device-f97089b5). Это открывает нам дверь в тему Переносных устройств Windows (Windows Portable Devices или WPD) (http://msdn.microsoft.com/library/windows/hardware/gg463541.aspx ), технологии работы с драйверами, которая поддерживает устройства наподобии телефонов, цифровых камер, портативных медиа-проигрывателей и так далее, а так же – всё увеличивающийся класс Bluetooth-устройств, когда основная задача взаимодействия – передать данные между устройством и системой. WPD поддерживают инфраструктуру для реализации этой цели.

    В WinRT, API Windows.Devices.Portable (http://msdn.microsoft.com/library/windows/apps/windows.devices.portable.aspx ) предоставляет средства для взаимодействия с этими устройства. Здесь вы можете найти классы ), который описан в Главе 2 курса "Пользовательский интерфейс приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript". Он создает список съемных носителей информации, из которого можно выбрать устройства и затем показывает первое изображение, найденное на выбранном устройстве:

    В примере о съемных носителях, в Сценарии 4, так же показано, как использовать автоматический запуск (AutoPlay) для автоматического вызова приложения, когда подключается подходящее устройство. Это включает в себя объявление в манифесте возможности Автозапуск содержимого (AutoPlay Content) (подключение устройства хранения данных) и/или Автозапуск устройства (AutoPlay Device) (подключение устройства). Смотрите материал "Запуск приложения с помощью автозапуска" (http://msdn.microsoft.com/library/windows/apps/hh452731.aspx ) для того, чтобы узнать подробности.

    Что касается Bluetooth-устройств, то ) предоставляет возможность работы с устройствами, имеющими отношение к телефонам. Смотрите материал "Управление вызовами на устройстве связи Bluetooth по умолчанию" (http://msdn.microsoft.com/library/windows/apps/hh452727.aspx ) и пример "Управление вызовами на Bluetooth-устройстве" (http://code.msdn.microsoft.com/windowsapps/Call-Control-b52ad696 ).

    Другая группа Bluetooth-устройств включает в себя те, которые собирают сведения о физическом состоянии, о таком, как пульс, кровяное давление и температура. Смотрите материал "Bluetooth с низким энергопотреблением" в Википедии ( http://en.wikipedia.org/wiki/Bluetooth_low_energy). Работа с ними показана в примере "Устройства Bluetooth с низким энергопотреблением и сведения о физическом состоянии" (http://code.msdn.microsoft.com/windowsapps/Bluetooth-LE-Metro-sample-a2ba1b5b ), что мы кратко рассмотрим ниже, но в настоящее время доступ к таким устройствам ограничен приложениями для устройств.

    Другой пример, "Простое управлени с помощью Bluetooth-устройства" (http://code.msdn.microsoft.com/windowsapps/Bluetooth-Simple-Key-e29ea456 ), кторый работает с контроллером CC2540 Mini Development Kit (http://www.ti.com/tool/cc2540dk-mini ). Это устройство было создано Texas Instruments для того, чтобы помочь разработке устройств на основе их микросхемы CC2540; в случае с примером, кнопки на устройстве управляют кнопками в приложении.

    В последних двух примерах интересно то, как приложения подключаются к конкретному устройству с использованием элемента управления ActiveX, который называется PortableDeviceAutomation.Factory (один из нескольких доступных ActiveX-объектов такого рода). Например, для того, чтобы подключиться к термометру, мы сначала перечисляем устройство с помощью его GUID класса, {00001809-0000-1000-8000-00805f9b34fb} (смотрите js/thermometer.js в примере "Устройства Bluetooth с низким энергопотреблением и сведения о физическом состоянии"):

    Windows.Devices.Enumeration.DeviceInformation.findAllAsync(
    "System.Devices.InterfaceClassGuid:=\"{00001809-0000-1000-8000-00805f9b34fb}\"", null)
          

    Результат этой асинхронной операции доступен в переменной, которая называется devices, а вот, как получают доступ к конкретному устройству и задают прослушиватели для его событий:

    // Используется WPD Automation для инициализации объекта устройства
    var deviceFactory = new ActiveXObject("PortableDeviceAutomation.Factory");
    
    // Для целей данного примера инициализируем первое устройство
    deviceFactory.getDeviceFromIdAsync(devices[0].id, function (device) {
    //	Переменная 'device' содержит объект, соответствующий объекту устройства.
    // Инициализация сервиса измерения температуры и ожидание результатов измерений
    tempService = device.services[0];
    tempService.onTemperatureMeasurement =
    function (timestamp, thermometerMeasurementValue) {
    // ...
    };
    });
          
    Вернуться к учебному плану