Microsoft windows desktop runtime что это

Обновлено: 06.07.2024

Поддержка платформ

  • Alpine: 3.10+
  • Debian: 9+
  • Ubuntu: 16.04+
  • Fedora: 29+
  • RHEL: 6+
  • openSUSE: 15+
  • SUSE Enterprise Linux (SLES): 12 SP2+
  • macOS: 10.13+
  • Windows Client: 7, 8.1, 10 (1607+)
  • Windows Server: 2012 R2+
  • x64 на Windows, macOS, и Linux
  • x86 на Windows
  • ARM32 на Windows и Linux
  • ARM64 на Linux (kernel 4.14+)

Удаление элементов управления Windows Forms

  • DataGrid
  • ToolBar
  • ContextMenu
  • Menu
  • MainMenu
  • MenuItem

Рекомендуются следующие замены:

Старый элемент (API) Рекомендуемая замена Другие связанные удаленные API
DataGrid DataGridView DataGridCell, DataGridRow, DataGridTableCollection, DataGridColumnCollection, DataGridTableStyle, DataGridColumnStyle, DataGridLineStyle, DataGridParentRowsLabel, DataGridParentRowsLabelStyle, DataGridBoolColumn, DataGridTextBox, GridColumnStylesCollection, GridTableStylesCollection, HitTestType
ToolBar ToolStrip ToolBarAppearance
ToolBarButton ToolStripButton ToolBarButtonClickEventArgs, ToolBarButtonClickEventHandler, ToolBarButtonStyle, ToolBarTextAlign
ContextMenu ContextMenuStrip
Menu ToolStripDropDown, ToolstripDropDownMenu MenuItemCollection
MainMenu MenuStrip
MenuItem ToolstripMenuItem

Наша цель — продолжать совершенствовать Windows Forms для обеспечения высокого DPI, доступности и надежности, и это позднее изменение было действительно важно.

Этот компонент добавляет пару шаблонов, которые вы можете использовать:

В завершение

Kenny Kerr

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

Одна из таких абстракций — Windows Runtime (WinRT), и сегодня я собираюсь проиллюстрировать это, рассмотрев базовую модель приложений WinRT. Она построена вокруг класса CoreWindow, экземпляр которого находится внутри каждого «современного» приложения Windows Store и Windows Phone. Тем не менее, сравнительно немногие знают о его существовании, и еще меньше, как он работает. Видимо, это можно считать критерием успеха абстракции.

Со времени первого объявления Windows 8 API в 2011 году многое было сказано и написано о различных языковых проекциях, предоставляющих абстракцию над Windows Runtime. Однако лучший способ понять Windows Runtime — отказаться от языковых проекций, включая C++/CX, и использовать стандартный C++ в сочетании с традиционной COM. Только C++ позволяет раздвинуть плотные шторы и увидеть, что происходит на самом деле (с технической точки зрения, то же самое можно сделать и на C, но это было бы излишне болезненно). Возможно, вы все равно предпочтете использовать ту или иную языковую проекцию (скорее всего, C++/CX), но по крайней мере у вас будет гораздо более четкое понимание происходящего.

Для начала откройте Visual Studio 2012 и создайте новый проект Visual C++ для приложения Windows Store или Windows Phone. Не важно, какой шаблон вы используете. Как только он загрузится, перейдите в Solution Explorer и удалите все, что не существенно. Если вы выбрали шаблон на основе XAML, удалите все XAML-файлы. Вы также можете удалить все файлы исходного кода на C++. Вероятно, вы предпочтете сохранить предкомпилированный заголовочный файл, но обязательно удалите все его содержимое. У вас должны остаться лишь ресурсы пакета, необходимые для развертывания приложения, изображения, сертификата и XML-манифеста.

Затем откройте страницы свойств проекта и выберите свойства компилятора — узел C/C++ в дереве слева. Найдите строку для ключа компилятора /ZW — Consume Windows Runtime Extension — и укажите No, чтобы отключить языковые расширения C++/CX. Тем самым вы будете уверены, что ничего загадочного, помимо мистических чудес стандартного компилятора C++, не происходит. Там же установите уровень предупреждений компилятора /W4.

Если вы попробуете скомпилировать проект, то должны увидеть приветствие от компоновщика, который сообщает об ошибке из-за того, что не удалось найти функцию точки входа в проект — WinMain. Добавьте в проект новый файл исходного кода на C++ и первым делом поместите в него недостающую функцию WinMain:

Как видите, это традиционная функция WinMain для Windows-приложения на основе C Runtime Libraries (CRT). Конечно, HINSTANCE и PWSTR не являются фундаментальными типами C++, поэтому вам придется включить заголовочный файл windows.h:

Если вы сохранили предкомпилированный заголовочный файл проекта, то можете включить его здесь. Я также буду использовать ComPtr из Windows Runtime C++ Template Library (WRL), поэтому сейчас самое время включить и его:

Подробнее о WRL я буду рассказывать в следующих статьях из этой рубрики. А пока я буду просто использовать шаблон класса ComPtr для поддержки указателя на COM-интерфейс. На этом этапе достаточно знать, что WRL ComPtr — это просто смарт-указатель на COM-интерфейс. Хотя он предоставляет некоторые средства, уникальные для Windows Runtime, я не стану использовать их в этот раз. С тем же успехом вы могли бы задействовать CComPtr из Active Template Library (ATL) или любой другой смарт-указатель на COM-интерфейс по своему выбору. WRL ComPtr определен в пространстве имен Microsoft::WRL:

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

Первое, что ожидает модель приложений, — MTA (multithreaded apartment). Правильно, в ней находится COM-модель изоляции потоков (COM apartment model). Windows Runtime предоставляет функцию RoInitialize, которая является тонкой оболочкой CoInitializeEx:

Несмотря на тот факт, что обычно достаточно CoInitializeEx, я предлагаю вам использовать RoInitialize. Эта функция позволит поддерживать будущие усовершенствования в Windows Runtime без потенциальной угрозы поломать традиционную COM. Она аналогична OleInitialize, которая тоже вызывает CoInitializeEx и кое-что еще. Смысл в том, чтобы в основном потоке вашего приложения не было ничего загадочного. Единственное, что может слегка удивить, — это не STA (single-threaded apartment). Не волнуйтесь, окно вашего приложения по-прежнему будет выполняться в STA-потоке, но создавать его будет Windows Runtime. Эта STA — на самом деле Application STA (ASTA), и она немного отличается от чистой STA, но об этом позже.

WindowsCreateString выделяет достаточно памяти для хранения копии исходной строки и null-символа, завершающего строку, а затем копирует исходную строку в этот буфер. Чтобы освободить этот вспомогательный буфер, вы должны вызвать WindowsDeleteString, если только владелец строки не возвращается в вызвавшую функцию:

Располагая HSTRING, вы можете получить указатель на вспомогательный буфер этой строки с помощью функции WindowsGetStringRawBuffer:

Необязательный второй параметр возвращает длину строки. Длина хранится вместе со строкой, избавляя вас от необходимости сканировать строку, чтобы определить ее размер. Прежде чем вы начнете строчить класс-оболочку для C++, стоит отметить, что компилятор C++/CX не возится с WindowsCreateString и WindowsDeleteString при генерации кода для строкового литерала или массива const. Вместо них он использует так называемую быстро передаваемую строку (fast-pass string), позволяющую избегать дополнительных операций выделения памяти и копирования, о которых недавно упоминал. Кроме того, это исключает риск утечки памяти. Создается быстро передаваемая строка функцией WindowsCreateStringReference:

Она использует передаваемый вызвавшим кодом HSTRING_HEADER, чтобы избежать выделения памяти из кучи. В этом случае вспомогательный буфер для HSTRING — это сама исходная строка, поэтому вы должны гарантировать, что исходная строка (равно как и заголовок) останется неизменной в течение срока существования HSTRING. Очевидно, что этот подход бесполезен, когда нужно вернуть строку вызвавшей функции, но отличная оптимизация, если требуется передать строку как входной параметр другой функции, чей срок жизни ограничен стеком (т. е. это не относится к асинхронным функциям). WRL также предоставляет оболочки для HSTRING и быстро передаваемых строк.

RoGetActivationFactory — как раз такая функция, и она используется для получения фабрики активации или статического интерфейса для данного класса. Она аналогична COM-функции CoGetClassObject. Учитывая, что эта функция обычно применяется с массивом const, генерируемым компилятором MIDL, имеет смысл написать простой шаблон-функцию, предоставляющий оболочку быстро передаваемой строки. Как он может выглядеть, показано на рис. 1.

Рис. 1. Шаблон функции GetActivationFactory

Шаблон функции GetActivationFactory автоматически распознает длину строки, исключая необходимость передачи аргумента с длиной буфера (а здесь легко ошибиться) или весьма дорогостоящего сканирования строки в период выполнения. Затем перед вызовом функции RoGetActivationFactory подготавливает быстро передаваемую строку. И здесь шаблон функции вновь логически распознает идентификатор интерфейса и безопасно возвращает конечный указатель на COM-интерфейс, обернутый в WRL ComPtr.

Теперь вы можете использовать эту вспомогательную функцию для получения интерфейса ICoreApplication:

Интерфейс ICoreApplication — это то, что приводит в действие ваше приложение. Чтобы использовать этот COM-интерфейс, вам потребуется включить заголовочный файл модели приложений:

Этот заголовочный файл определяет ICoreApplication в пространстве имен ABI::Windows::ApplicationModel::Core, а также текстовый идентификатор класса CoreApplication. Единственный метод интерфейса, над которым вам придется хорошенько подумать, — это Run.

Прежде чем продолжить, было бы полезно оценить, как Windows Runtime выводит ваше приложение в свет, если так можно выразиться. Как уже упоминалось, Windows Runtime считает вас просто гостем в вашем процессе. Тут полная аналогия с тем, как годами работали Windows-службы. В случае Windows-службы Windows Service Control Manager (SCM) запускает ее, используя функцию CreateProcess или одну из ее вариаций. Затем ждет, когда процесс вызовет функцию StartServiceCtrlDispatcher. Эта функция устанавливает обратную связь с SCM, благодаря чему служба и SCM могут взаимодействовать друг с другом. Если, например, службе не удастся своевременно вызвать StartServiceCtrlDispatcher, SCM будет считать, что возникла какая-то ошибка, и уничтожит процесс. Функция StartServiceCtrlDispatcher возвращает управление только по окончании работы службы, поэтому SCM нужно создавать дополнительный поток для службы, чтобы принимать уведомления об обратных вызовах. Служба просто реагирует на события и находится полностью во власти SCM. Как вы увидите, это во многом напоминает модель приложений WinRT.

Windows Runtime ждет от процесса получения интерфейса ICoreApplication и вызова его метода Run. Если процессу не удается своевременно сделать это, Windows Runtime подобно SCM считает, что что-то пошло не так и завершает процесс. К счастью, если к процессу подключен отладчик, Windows Runtime обнаруживает это и отключает тайм-аут — в отличие от SCM. Однако модель та же. Windows Runtime контролирует процесс и, когда происходят какие-то события, вызывает приложение в потоке, созданном исполняющей средой. Конечно, Windows Runtime основана на COM, поэтому вместо функции обратного вызова (как в случае SCM) эта исполняющая среда (которая в этом деле опирается на Process Lifetime Manager [PLM]) ожидает, что приложение предоставит метод Run с COM-интерфейсом, который можно использовать для вызова приложения.

Ваше приложение должно предоставить реализацию IFrameworkViewSource, который также находится в пространстве имен ABI::Windows::ApplicationModel::Core, и тогда CoreApplication вызовет его единственный метод CreateView, как только создаст UI-поток вашего приложения. На самом деле в IFrameworkViewSource содержится не только метод CreateView. Этот интерфейс наследует от IInspectable, базового интерфейса WinRT. В свою очередь IInspectable наследует от IUnknown, базового интерфейса COM.

WRL обеспечивает обширную поддержку реализации COM-классов, но об этом мы поговорим в следующей статье. А пока я хочу подчеркнуть, что корни Windows Runtime уходят в COM и что нет лучшего способа показать это, чем реализовать IUnknown. Для моих целей будет полезным отметить, что срок жизни C++-класса, который реализует IFrameworkViewSource и несколько других интерфейсов, определяется стеком. По сути, функция WinMain приложения сводится к следующему:

Остается лишь написать класс SampleWindow так, чтобы он должным образом реализовал интерфейс IFrameworkViewSource. Хотя CoreApplication безразлично, где реализуются интерфейсы, ваше приложение, как минимум, должно реализовать не только IFrameworkViewSource, но и интерфейсы IFrameworkView и IActivatedEventHandler. В данном случае класс SampleWindow может просто реализовать их всех:

Интерфейс IFrameworkView также определен в пространстве имен ABI::Windows::ApplicationModel::Core, а с IActivatedEventHandler дело обстоит несколько сложнее. Я определил его сам таким образом:

Если у вас есть некоторый опыт работы с COM, вы могли подумать, что это выглядит весьма не ортодоксально, — и были бы правы. Как и следовало бы ожидать, ITypedEventHandler — это просто шаблон класса, и довольно странно так определять COM-интерфейс; самая очевидная проблема в том, что вы, по-видимому, не смогли бы узнать, каким идентификатором интерфейса нужно его пометить. К счастью, все эти интерфейсы генерируются компилятором MIDL, который берет на себя задачу специализации каждого из них, и этим специализациям он назначает GUID, представляющий идентификатор интерфейса. Каким бы сложным ни выглядело предыдущее выражение typedef, оно определяет COM-интерфейс, который наследует непосредственно от IUnknown и предоставляет единственный метод — Invoke.

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

Рис. 2. Метод QueryInterface в SampleWindow

В этой реализации стоит отметить несколько моментов. Прежде всего, метод проверяет, что его аргументы допустимы. Более политкорректная реализация могла бы возвращать E_POINTER, но предполагается, что такие ошибки являются дефектами, которые можно устранить на этапе разработки, поэтому незачем тратить на них лишние процессорные ресурсы в период выполнения. Это обеспечивает наилучшее из возможных поведение, немедленно вызывая ошибку доступа к памяти и создавая аварийный дамп, который довольно легко проанализировать. Если бы вы возвращали E_POINTER, вызвавший дефектный код скорее всего просто проигнорировал бы это. Поэтому лучшая политика — провал на раннем этапе. По сути, такой позиции придерживаются во многих реализациях, включая DirectX и Windows Runtime. Корректная реализация QueryInterface требует уймы усилий. Спецификация COM весьма требовательна к тому, чтобы COM-классы всегда корректно и согласованно обеспечивали определенные гарантии идентификации объектов. Не волнуйтесь, если цепочка выражений if кажется вам устрашающей. В свое время я расскажу и об этом.

И последнее, на что хотелось бы обратить внимание в этой реализации, — она не вызывает AddRef. Обычно перед возвратом управления QueryInterface должен вызывать AddRef применительно к полученному указателю на интерфейс IUnknown. Однако, так как класс SampleWindow находится в стеке, смысла в учете ссылок нет. По той же причине реализация IUnknown-методов AddRef и Release предельно проста:

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

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

Рис. 3. IInspectable-методы в SampleWindow

Далее я должен реализовать IFrameworkViewSource и его метод CreateView. Поскольку класс SampleWindow тоже реализует IFrameworkView, реализация проста. И вновь заметьте, что обычно перед возвратом управления вы должны были бы вызывать AddRef применительно к полученному указателю на интерфейс, производный от IUnknown. Возможно, вы предпочтете на всякий случай вызывать AddRef в теле этой функции:

Интерфейс IFrameworkView — то место, где наконец начинают происходить интересные вещи. После вызова CreateView из приложения для получения указателя на этот интерфейс Windows Runtime вызывает большинство его методов в быстрой последовательности. Важно так же быстро реагировать на эти вызовы, так как все они засчитываются в тот период времени, в течение которого пользователь ожидает запуска вашего приложения. Первым вызывается Initialize, и в нем приложение должно регистрироваться на событие Activated. Это событие уведомляет, что приложение активировано, но вы сами должны активировать его CoreWindow. Метод Initialize довольно прост:

Затем вызывается метод SetWindow, который предоставляет приложению реализацию ICoreWindow, который просто моделирует обычный HWND рабочего стола в Windows Runtime. В отличие от остальных интерфейсов модели приложений ICoreWindow определен в пространстве имен ABI::Windows::UI::Core. В методе SetWindow обязательно делайте копию указателя на интерфейс, так как достаточно скоро он вам понадобится:

Следующий метод — Load; в нем вы должны собрать весь код, отвечающий за подготовку вашего приложения к начальному отображению:

Как минимум, вы должны регистрироваться на события, относящиеся к изменениям размера окна и видимости, а также к изменениям в масштабировании DPI (dots per inch). Кроме того, вы, вероятно, захотите воспользоваться возможностью создать различные объекты фабрики DirectX, загрузить аппаратно-независимые ресурсы и т. д. Это хорошее место для выполнения всех таких операций по той причине, что в этот момент пользователь видит экран-заставку вашего приложения.

Когда метод Load возвращает управление, Windows Runtime считает, что ваше приложение готово к активации, и генерирует событие Activated, которое я буду обрабатывать, реализовав метод Invoke интерфейса IActivatedEventHandler примерно так:

После активации окна приложение наконец готово к выполнению:

Выражаю благодарность за рецензирование статьи эксперту Microsoft Джеймсу Макнеллису (James McNellis).

На внутреннем уровне CLR предоставляет инфраструктуру для управляемого кода, позволяющую работать с файлами WinMD, и переходный уровень между управляемым кодом и Windows Runtime. В этой статье я объясню некоторые детали этого. В итоге вы получите более хорошее представление о том, что происходит «за кулисами», когда ваша управляемая программа вызывает какой-либо WinRT API.

Использование файлов WinMD из управляемого кода

*


Рис. 1. CLR вставляет адаптер метаданных между файлами WinMD и открытым интерфейсом метаданных

ProgramПрограмма
CLR Metadata APIAPI метаданных CLR
.NET Assembly.NET-сборка
Metadata AdapterАдаптер метаданных
.winmdФайл .winmd

Запустив ILDasm, вы сможете лучше понять изменения, которые вносятся адаптером метаданных CLR в файл WinMD. По умолчанию ILDasm показывает содержимое файла WinMD в неструктурированном виде — так, как он кодируется на диске без адаптера. Однако, если вы укажете при запуске ILDasm параметр командной строки /project, он включит адаптер метаданных, и вы увидите метаданные в той форме, в какой их читают CLR и управляемый инструментарий.

Запуск двух копий ILDasm (одной с параметром /project, а другой без него) позволяет легко увидеть изменения, которые вносит адаптер метаданных CLR в файл WinMD.

Табл. 1. WinRT-типы для файла WinMD

КатегорияПримеры
Стандартные WinRT-типыWindows.Foundation.Collections.PropertySet, Windows.Networking.Sockets.DatagramSocket
Элементарные типыByte, Int32, String, Object
Проецируемые типыWindows.Foundation.Uri, Windows.Foundation.DateTime
Проецируемые интерфейсыWindows.Foundation.Collections.IVector<T>, Windows.Foundation.Iclosable
Типы со вспомогательными методами .NET FrameworkWindows.Storage.Streams.IInputStream, Windows.Foundation.IasyncInfo

А вот свойство IAsyncInfo ErrorCode после проецирования CLR:

А вот фрагмент PropertySet после проецирования, выполненного CLR:

Обратите внимание на то, что проецируются три типа: IMap<string,object> в IDictionary<string,object>, IKeyValuePair<string,object> в KeyValuePair<string, object> и IIterable<IKeyValuePair> в IEnumerable<KeyValuePair>. Также заметьте, что метод get_Size из IMap скрывается.

На самом низком уровне Windows Runtime опирается на концепции COM, поэтому не удивительно, что поддержка CLR вызова WinRT API построена на существующей инфраструктуре COM interop.

Эти файлы WinMD предоставляют определения управляемых типов, используемых в период выполнения для доступа к Windows Runtime из управляемого кода. Хотя API, которые CLR считывает из файла WinMD, содержат определение метода в формате, легко применяемом из управляемого кода, нижележащий WinRT API использует другую сигнатуру API, иногда называемую сигнатурой двоичного интерфейса приложения (application binary interface, ABI). Один из примеров различия сигнатур API и ABI в том, что — подобно стандартной COM — WinRT API возвращают HRESULT, а в сигнатуре ABI возвращаемое значение на самом деле является выходным параметром. Я покажу пример того, как сигнатура управляемого метода преобразуется в сигнатуру WinRT ABI, когда буду рассматривать, как CLR вызывает WinRT API.

Оболочки, вызываемые исполняющей средой, и оболочки, вызываемые COM

Соответственно, когда управляемый объект используется из Windows Runtime, он должен быть вызываемым так, будто является WinRT-объектом. В этом случае при передаче в Windows Runtime управляемый объект обертывается в оболочку, вызываемую COM (COM callable wrapper, CCW). Так как Windows Runtime использует ту же инфраструктуру, что и COM, она может взаимодействовать с CCW для доступа к функциональности управляемого объекта (рис. 2).

*

Рис. 2. Применение оболочек для WinRT- и управляемых объектов

CLRCLR
Windows RuntimeWindows Runtime
RCWRCW
CCWCCW
WinRT ObjectWinRT-объект
Managed ObjectУправляемый объект

Интерфейсы маршалинга

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

  1. Преобразование входных управляемых параметров в WinRT-эквиваленты, в том числе создание CCW-оболочек для управляемых объектов.
  2. Поиск интерфейса, который реализует WinRT-метод, вызываемый из RCW, в котором вызывается этот метод.
  3. Вызов WinRT-метода.
  4. Преобразование выходных WinRT-параметров (включая возвращаемые значения) в управляемые эквиваленты.
  5. Преобразование любых HRESULT от WinRT API в управляемое исключение.

Эти операции происходят в интерфейсе маршалинга (marshaling stub), который CLR генерирует в интересах вашей программы. Именно интерфейсы маршалинга в RCW вызываются управляемым кодом перед переходом в WinRT API. Аналогично, Windows Runtime вызывает сгенерированные CLR интерфейсы маршалинга в CCW, когда происходит переход в управляемый код.

Пример вызова

Вообразите WinRT API, который принимает список строк и осуществляет их конкатенацию, вставляя между каждым элементом строку-разделитель. Этот API мог бы иметь такую управляемую сигнатуру:

CLR должна вызвать этот метод так, как он определен в ABI, поэтому ей нужно узнать ABI-сигнатуру метода. К счастью, для получения ABI-сигнатуры по конкретной API-сигнатуре можно применить набор детерминированных преобразований. Первое преобразование — замена проецируемых типов данных их WinRT-эквивалентами, которые возвращают API к форме, в какой она определена в файле WinMD до того, как адаптер метаданных загрузил этот API. В нашем случае IEnumerable<T> на самом деле является проекцией IIterable<T>, поэтому WinMD-представление этой функции выглядит так:

WinRT-строки хранятся в типе данных HSTRING, поэтому для Windows Runtime эта функция на деле выглядит следующим образом:

На уровне ABI, где происходит реальный вызов, WinRT API возвращают HRESULT-значения, и возвращаемое значение от этой сигнатуры является выходным параметром. Кроме того, объекты — это указатели, поэтому ABI-сигнатура для этого метода была бы такой:

Все WinRT-методы должны быть частью интерфейса, реализуемого объектом. Наш метод Join, например, мог бы быть частью интерфейса IConcatenation, поддерживаемого классом StringUtilities. Перед вызовом метода Join исполняющая среда CLR должна получить указатель на интерфейс IConcatenation.

Задача интерфейса маршалинга заключается в преобразовании исходного управляемого вызова RCW в конечный WinRT-вызов WinRT-интерфейса. В данном случае псевдокод для интерфейса маршалинга мог бы выглядеть так, как показано на рис. 3 (вызовы очистки опущены, чтобы не усложнять общую картину).

Рис. 3. Пример интерфейса маршалинга для вызова из CLR в Windows Runtime

В этом примере первый шаг — преобразование управляемых параметров из их управляемого представления в соответствующее WinRT-представление. В данном случае код создает CCW для параметра list и преобразует параметр System.String в HSTRING.

Следующий шаг — поиск WinRT-интерфейса, который предоставляет реализацию Join. Для этого из QueryInterface вызывается WinRT-объект, обернутый в RCW, в котором управляемый код вызывал Join. Самая распространенная причина того, что при вызове какого-либо WinRT-метода генерируется исключение InvalidCastException, заключается в неудачном завершении QueryInterface-вызова. Одна из причин, по которой это может происходить, — WinRT-объект не реализует все интерфейсы, которые ожидает вызвавший код.

Теперь начинаются реальные действия: interop-интерфейс выполняет реальный вызов WinRT-метода Join, предоставляя ему место для сохранения возвращаемого значения HSTRING. Если WinRT-метод завершается неудачей, он сообщает об ошибке через HRESULT, который interop-интерфейс преобразует в объект-исключение и генерирует его. То есть, если ваш управляемый код видит исключение, сгенерированное в результате вызова WinRT-метода, то скорее всего этот WinRT-метод вернул HRESULT с ошибкой и CLR сгенерировала исключение, чтобы передать в ваш код эту ошибку.

Вызовы управляемого кода из Windows Runtime

Вызовы, исходящие из Windows Runtime и адресованные управляемому коду, работают аналогичным образом. CLR реагирует на QueryInterface-вызовы, которые компонент Windows Runtime адресует ей через интерфейс, имеющий таблицу виртуальных функций; эта таблица заполняется с помощью методов interop-интерфейса. Эти методы делают то же самое, о чем я рассказывал ранее, но в обратном направлении.

Рассмотрим еще раз случай с Join API с тем исключением, что на этот раз предположим, что он реализован в управляемом коде и вызывается из компонента Windows Runtime. Псевдокод интерфейса (stub), который обеспечивает такой переход, мог бы выглядеть примерно так, как показано на рис. 4.

Рис. 4. Пример интерфейса маршалинга для вызова CLR из Windows Runtime

Сначала этот код преобразует входные параметры из WinRT-типов данных в управляемые типы. Если исходить из того, что входной список является WinRT-объектом, интерфейс должен получить RCW, представляющий этот объект, чтобы управляемый код мог использовать его. Строковое значение просто преобразуется из HSTRING в System.String.

Далее осуществляется вызов управляемой реализации метода Join в CCW. Если этот метод генерирует исключение, interop-интерфейс захватывает его и преобразует в HRESULT с ошибкой, возвращаемый вызвавшему WinRT-коду. Это объясняет, почему некоторые исключения, генерируемые из управляемого кода, вызываемого компонентами Windows Runtime, не приводят к краху процесса. Если компонент Windows Runtime обрабатывает HRESULT ошибки, то в конечном счете это равносильно захвату и обработке сгенерированного исключения.

Проецируемые интерфейсы

Рис. 5. Концептуальная реализация IDictionary в терминах IMap

Заметьте, что в процессе своей работы этот интерфейс преобразования несколько раз обращается к нижележащей реализации IMap. Например, вы написали следующий фрагмент управляемого кода, чтобы выяснить, содержит ли объект Windows.Foundation.Collections.PropertySet ключ «NYJ»:

Поскольку вызов TryGetValue определяет, содержит ли набор свойств этот ключ, стек вызовов мог бы выглядеть, как показано в табл. 2.

Табл. 2. Стек вызовов для вызова TryGetValue

СтекОписание
PropertySet::HasKeyWinRT-реализация PropertySet
HasKey_StubИнтерфейс маршалинга, преобразующий вызов HasKey интерфейса словаря в WinRT-вызов
TryGetValue_StubИнтерфейс, реализующий IDictionary в терминах IMap
ApplicationУправляемый код приложения, вызывающий PropertySet.TryGetValue

Заключение

Уважаемый читатель, вы уже наверное уже слышали о новых операционных системах Windows 8 и Windows RT и догадываетесь, что между ними должны быть какие-то отличия, хотя бы потому, что называются по-разному.

ОС Windows RT: пользовательский интерфейс Metro - проблемы и недостатки - обзор

Так в чем же разница между Windows 8 и Windows RT?
Самых заметных различий между этими системами (точнее между одной системой и ее версией) несколько. Кстати, Microsoft уже опубликовал целую схему, иллюстрирующую все эти отличия. Копировать ее здесь мы не будем, однако основные моменты отметим:

  • Windows RT будет работать только на устройствах с ARM-процессорами;
  • у Windows RT будет свой Desktop-режим (режим рабочего стола), но исключительно для предустановленных программ, разработанных Microsoft. Т.е. кастомизированных под сенсорный интерфейс версий Microsoft Word, Excel , PowerPoint, OneNote и других приложений из нового пакета Microsoft Office;
  • Windows RT будет поддерживать шифрование данных на тех устройствах, с которыми поставляется;
  • ни старые, ни новые x86/x64-программы работать с Windows RT не будут.
Ξ Когда Zoom тормозит, лагает, виснет и т.п. - как с этим бороться

На каких устройствах мы увидим Windows RT?

Пока производители компьютерной техники демонстрируют крайнюю осторожность во всем, что касается установки этой ОС на своих новых продуктах. Понять их можно: никто не занет, как именно отреагирует потребитель на такую мягко говоря специфическую версию Windows 8. Тем не менее список устройств, которые поставляются с Windows RT в качестве предустановленной операционной системы достаточно широк и включает в частности модели:

Samsung Ativ Tab. Планшет на Windows RT. Обзор. Недостатки

Станет ли Windows RT на более ранние устройства с ARM-процессорами?
Считается, что не станет. Официально Windows RT поставляется только в предустановленном виде с новыми девайсами. Исключений вроде бы не предвидится. Однако грамотных энтузиастов по всему миру не перечесть, и кто знает что и куда они могут установить.

Ξ Просмотр фотографий Windows: как вернуть "старое средство" в Windows 11

Во-вторых, по предварительной информации, OEM-лицензия для производителей компьютеров с Windows RT будет стоить порядка $80 за копию, в связи с чем такие девайсы скорее всего будут обходиться своим владельцам дороже, чем их Android-аналоги (для сравнения: OEM-лицензия Android бесплатная).

Читайте также: