Вызов API Windows 10 из настольных приложений

В сегодняшней публикации мы рассмотрим то, как программное обеспечение настольных компьютеров может использовать богатые функциональные возможности Windows 10. Это ценная добавка к публикации «Добавление возможностей UWP в существующее программное обеспечение настольных компьютеров«, в которой мы погружались более детально в тему.

Вот вопрос, который порождает много путаницы. Может ли программное обеспечение для ПК написанное в WPF, WinForms или MFC иметь доступ к  Windows 10 API, используемым универсальной платформой Windows (UWP)?

Ответ: да. Есть некоторые исключения из этого правила (и мы попытаемся их найти), но в целом вы можете получить доступ к API Windows 10. Или, выражаясь по-другому, нет никаких секретных API, которые закрыты от Windows разработчиков.

Как получить доступ к API Windows 10 из WPF приложений
Можно получить доступ к API Windows 10 из ранее существовавшего проекта WPF. Для этого перейдите в окно обозревателя решений и…

1) Щелкните правой кнопкой мыши на References. Выберите «Add Reference …» из контекстного меню. Слева от Reference Manager, выберите Browse и найдите следующий файл: C:\Program Files (x86)\Windows Kits\10\UnionMetadata\winmd. Добавьте его в свой проект в качестве ссылки. Примечание: Вам нужно будет заменить фильтр на «All Files».
2) Щелкните правой кнопкой мыши на References. Выберите «Add Reference …» из контекстного меню. Слева от Reference Manager, перейдите к Browse и найдите каталог «C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETCore\v4.5». Добавьте System.WindowsRuntime.dll в свой проект.

Скажем, я хочу, чтобы моему WPF приложению было известно местоположение, вызвав класс Geolocator в Windows 10 Windows.Devices.Geolocation API. Теперь я могу это сделать, и даже использовать асинхронный шаблон присущий для UWP. Классы и методы, которые мы обычно ассоциируем как UWP код, теперь переплетены с классами и методами из WPF. В примере показано ниже, я беру свою широту и долготу из Geolocator и отображаю их в MessageBox WPF.

  1. private async void Button_Click(object sender, RoutedEventArgs e)
  2. {
  3. var locator = new Windows.Devices.Geolocation.Geolocator();
  4. var location = await locator.GetGeopositionAsync();
  5. var position = location.Coordinate.Point.Position;
  6. var latlong = string.Format(«lat:{0}, long:{1}», position.Latitude, position.Longitude);
  7. var result = MessageBox.Show(latlong);
  8. }

Как узнать, какие API доступны?
Как уже упоминалось выше, есть исключения из этого правила, что Windows 10 API-интерфейсы доступны в настольном программном обеспечении. Первое большое исключение относится XAML UI API. Структура XAML в UWP отличается от той, что в WPF, и вы действительно не захотите микшировать их, так или иначе.
Второй набор API-интерфейсов, которые вы не можете использовать — это те, которые зависят от идентификаторов упаковки приложения. UWP приложения имеют идентификаторы упаковки, в то время как настольное программное обеспечение их не имеет. Идентификационную информацию пакета можно найти в файле манифеста приложения.
Как определить, какие Windows 10 API-интерфейсы требуют идентификаторов пакетов, а какие не требуют? Самый простой способ — это обратиться к этой теме на MSDN.

Разблокирование еще большего объёма API
На самом деле есть способ обеспечить настольное программное обеспечение идентификационными данными пакета. Desktop Bridge позволяет упаковать настольное программное обеспечение для развертывания в Windows Store. В рамках этого процесса, вы создаете файл манифеста для приложения, фактически давая ему идентификаторы пакета.

  1. <?xml version=«1.0» encoding=«utf-8»?>
  2. <Package xmlns=«http://schemas.microsoft.com/appx/manifest/foundation/windows10»
  3.          xmlns:uap=«http://schemas.microsoft.com/appx/manifest/uap/windows10»    
  4.     <Identity Name=«YOUR-APP-GUID-90cd-a7cdf2e0a180»
  5.               Publisher=«CN=YOUR COMPANY»
  6.               Version=«1.x.x.x» />
  7. </Package>

Если вы создаёте пакет настольного программного обеспечения для Windows Store с помощью Desktop Bridge, то большинство API, которые вы ранее не могли использовать, потому что они требуют идентификаторов пакетов, будут доступны для вас. API-интерфейсы, которые зависят от CoreWindow по-прежнему будут проблемой. Однако, как только у вас есть пакет созданный с помощью Desktop Bridge, вы можете добавить UWP компонент (который работает в отдельном процессе контейнера приложения), и вызвать буквально любой UWP API из него.

Более быстрый способ получить эти Windows 10 API
Но допустим, что вы не хотите развертывать приложение в Windows Store на данный момент и просто хотите использовать некоторые из этих Windows 10 API. Как получить доступ к ним из приложения?
Для этого есть специальный NuGet пакет. Он называется UwpDesktop и написан Владимиром Постелом и Лучиана Вищик. Если вы хотите изучить исходный код, он размещён на GitHub.
Для демонстрационных целей, давайте создадим консольное приложение, основываясь на статью 2012 года Скота Хансельмана об использовании WinRT API-интерфейсов (частично, чтобы показать, насколько легче это сделать сегодня). После создания нового настольного консольного приложения, вставьте исходный код 2012 года в метод Main в Program.cs.

  1. static void Main(string[] args)
  2. {
  3. LightSensor light = LightSensor.GetDefault();
  4. if (light != null)
  5. {
  6. uint minReportInterval = light.MinimumReportInterval;
  7. uint reportInterval = minReportInterval > 16 ? minReportInterval : 16;
  8. light.ReportInterval = reportInterval;
  9. light.ReadingChanged += (s, a) =>
  10. {
  11. Console.WriteLine(String.Format(«There was light! {0}», a.Reading.IlluminanceInLux));
  12. };
  13. }
  14. while (Console.ReadLine() != «q») { }
  15. }

Этот код, к сожалению, не будет компилироваться, так как .NET 4.5 не знает о существовании класса LightSensor, как показано ниже.

Вот как мы это исправим. Установим пакет UWPDesktop в свой проект с помощью диспетчера NuGet пакетов.

Вернемся в Program.cs, добавим следующую строку с директивой using в верхней части файла:

  1. using Windows.Devices.Sensors;

Далее … ну, на самом деле это все, что потребовалось. Приложение теперь работает (если у вас есть датчик освещенности, прикрепленный к компьютеру).

И это быстрый способ начать использовать Windows 10 API в управляемом приложении.

Как получить доступ к Windows 10 APIs из C++
Вызов Windows 10 API-интерфейсов возможен не только для управляемого кода. Вы также всегда в состоянии вызывать их из нативных C++ приложений.

Начните с создания нового проекта приложения C ++ Win32. В качестве альтернативы, откройте предварительно существующий проект C++ Windows приложения. Щелкните правой кнопкой мыши на проекте и выберите Properties, чтобы открыть окно настроек. Всего четыре шага необходимо для настройки приложения для осуществления вызовов Windows 10 API.

— Выберите раздел Configuration Properties > C/C++ > General в панели слева. Установите свойство Consume Windows Runtime Extension в состояние Yes.

— В том же окне отредактируйте свойство Additional #using Directories, добавив следующие данные:
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcpackages;
C:\Program Files (x86)\Windows Kits\10\UnionMetadata;
C:\Program Files (x86)\Windows Kits\10\References\Windows.Foundation.UniversalApiContract\1.0.0.0;
C:\Program Files (x86)\Windows Kits\10\References\Windows.Foundation.FoundationContract\1.0.0.0;

— Выберите раздел Configuration Properties > C/C++ > Code Generation в левой панели. Установите свойство Enable Minimal Rebuild в состояние No.

— В завершении всего, выберите раздел Configuration Properties > General и выбирете целевую версию Windows 10. Нажмите кнопку OK, чтобы сохранить параметры конфигурации.

Теперь вы можете начать вызов Windows API. Давайте закончим это приложение чем-то простым — вызовем API Launcher из панели меню C++.

Добавьте следующие директивы using в верхнюю часть главного файла CPP вашего проекта:

  1. using namespace std;
  2. using namespace Platform;
  3. using namespace Windows::Foundation;
  4. using namespace Windows::System;
  5. Include the header ROApi.h in your stdafx.h file.
  6. // C RunTime Header Files
  7. #include <stdlib.h>
  8. #include <malloc.h>
  9. #include <memory.h>
  10. #include <tchar.h>
  11. // TODO: reference additional headers your program requires here
  12. #include «ROApi.h»
  13. Initialize Windows::Foundation in your program’s entry point.
  14. int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
  15.                      _In_opt_ HINSTANCE hPrevInstance,
  16.                      _In_ LPWSTR    lpCmdLine,
  17.                      _In_ int       nCmdShow)
  18. {
  19.     UNREFERENCED_PARAMETER(hPrevInstance);
  20.     UNREFERENCED_PARAMETER(lpCmdLine);
  21.     // TODO: Place code here.
  22.     Windows::Foundation::Initialize();
  23. }

Инициализируйте Windows::Foundation в точке входа вашей программы.

  1. int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
  2.                      _In_opt_ HINSTANCE hPrevInstance,
  3.                      _In_ LPWSTR    lpCmdLine,
  4.                      _In_ int       nCmdShow)
  5. {
  6.     UNREFERENCED_PARAMETER(hPrevInstance);
  7.     UNREFERENCED_PARAMETER(lpCmdLine);
  8.     // TODO: Place code here.
  9.     Windows::Foundation::Initialize();
  10. }

По умолчанию меню Help имеет только одну запись About, так что вам нужно будет добавить новую кнопку. Определите кнопку в вашем файле Resource.h:

  1. #define IDM_SETTINGS  106

Затем отредактируйте файл * .rc, чтобы добавить новую кнопку для настройки, для того, чтобы запустить приложение Windows 10 Параметры.

  1. IDC_HELLOWORLDWIN32TOUWP MENU
  2. BEGIN
  3.     POPUP «&File»
  4.     BEGIN
  5.         MENUITEM «E&xit»,           IDM_EXIT
  6.     END
  7.     POPUP «&Help»
  8.     BEGIN
  9.         // add settings item to help menu around Line 47
  10.         MENUITEM «&Settings …»,   IDM_SETTINGS
  11.         MENUITEM «&About …»,      IDM_ABOUT
  12.     END
  13. END

Наконец переопределите функции вызова для кнопок панели меню, сделав для них вызов Launcher Windows 10 взамен того, что они должны были делать.

  1. LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
  2. {
  3.     switch (message)
  4.     {
  5.     case WM_COMMAND:
  6.     {
  7.         int wmId = LOWORD(wParam);
  8.         // Parse the menu selections:
  9.         switch (wmId)
  10.         {
  11.         case IDM_SETTINGS:
  12.             Launcher::LaunchUriAsync(ref new Uri(L«ms-settings://»));
  13.             break;
  14.         case IDM_ABOUT:
  15.             Launcher::LaunchUriAsync(ref new Uri(L«https://blogs.windows.com/buildingapps//»));
  16.             break;
  17.         case IDM_EXIT:
  18.             DestroyWindow(hWnd);
  19.             break;
  20.         default:
  21.             return DefWindowProc(hWnd, message, wParam, lParam);
  22.         }
  23.     }
  24.     break;
  25.     }
  26. }

При нажатии на Help > Settings появится приложение для Windows 10 Параметры, нажатие на Help> About приведет вас к блогу Windows Developer (одно из моих любимых мест)!

Давайте погрузимся на один уровень глубже. Есть много C ++ приложений и игр, которые используют альтернативные инструменты сборки и альтернативные процессы сборки. Многие из них по-прежнему нацелены на более старые версии Windows, потому что это то, что их клиенты используют. Как эти разработчики приложений могут постепенно переходить к API интерфейсам Windows 10, не отказываясь от своих клиентов Windows 7?

Повторно воспроизведите те же действия, описанные выше для создания исполняемого файла Windows, можно было бы построить DLL для инкапсуляции любых Windows 10 вызовов которые могут понадобиться, изолируя их от остальной части приложения. Назовем эту новую DLL UWPFeatures.dll.

  1. void Game::UpdateTile(int score, int hiScore)
  2. {
  3.     HINSTANCE hinstLib;
  4.     UPDATETILE UpdateTileProc;
  5.     BOOL fFreeResult;
  6.     hinstLib = LoadLibrary(TEXT(«UWPFeatures.dll»));
  7.     if (NULL != hinstLib)
  8.     {
  9.         UpdateTileProc = (UPDATETILE)GetProcAddress(hinstLib, «UpdateTile»);
  10.         if (NULL != UpdateTileProc)
  11.         {
  12.             (UpdateTileProc)(score, hiScore);
  13.         }
  14.         fFreeResult = FreeLibrary(hinstLib);
  15.     }
  16. }

Затем в исходном приложении вызовы метода должны проверить упакован ли UWPFeatures.dll вместе с приложением (что будет справедливо для установки в Windows 10). Если он присутствует, он может динамически загружаться и вызывается. Если он отсутствует, то вместо этого будет выполнен первоначальный вызов. Это обеспечивает быстрый шаблон не только для доступа к API интерфейсу Windows 10 из ранее существовавших C++ игр и приложений, но и делает это таким образом, что не требует тяжелой доработки существующего базового кода.

Подводя итоги
Иногда утверждают, что Windows 10 имеет секретные API, которые доступны только через UWP приложения. В этой статье мы показали, что это не так, и также задействовали некоторые входы и выходы с помощью Windows 10 API, в управляемых и нативных настольных приложениях. Вы найдете ссылки ниже, чтобы получить еще больше справочных материалов по данному вопросу.
UWP API для вызова из настольных приложений
Вызов UWP API из настольного приложения
Этот NuGet пакет позволяет использовать UWP API из ваших настольных приложений
Desktop Bridge
идентификаторы пакетов UWP приложений
Вызов WinRT API из C# настольных приложений (Win 8)

Перевод оригинальной статьи Calling Windows 10 APIs From a Desktop Application
Автор: 
Перевод: Сергей Урусов

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *