System. Runtime Пространство имен
Содержит дополнительные типы, поддерживающие различные пространства имен, такие как System , Runtime и Security . Contains advanced types that support diverse namespaces such as the System , Runtime , and the Security namespaces.
Классы
Исключение, возникающее при наличии нескольких несовместимых методов интерфейса, переопределяющих другой метод. The exception that is thrown when there are multiple incompatible interface methods overriding another method.
Задает сведения диапазона исправления для выполнения целевых исправлений .NET. Specifies patch band information for targeted patching of .NET.
Указывает параметры сборки мусора для текущего процесса. Specifies the garbage collection settings for the current process.
Проверяет наличие достаточного количество ресурсов памяти перед выполнением операции. Checks for sufficient memory resources before executing an operation. Этот класс не наследуется. This class cannot be inherited.
Улучшает производительность при запуске доменов приложения в приложениях, требующих JIT-компилятора путем фоновой компиляции методов, выполнение которых ожидается, на основе профилей, созданных во время предыдущих компиляций. Improves the startup performance of application domains in applications that require the just-in-time (JIT) compiler by performing background compilation of methods that are likely to be executed, based on profiles created during previous compilations.
Указывает, что наборы исправлений, скорее всего, не повлияют на метод библиотеки классов .NET, к которому применяется этот атрибут, поэтому его можно встраивать в образы NGen. Indicates that the .NET class library method to which this attribute is applied is unlikely to be affected by servicing releases, and therefore is eligible to be inlined across Native Image Generator (NGen) images.
Перечисления
Указывает, будет ли Куча больших объектов (LOH) сжата во время следующей блокирующей сборки мусора. Indicates whether the next blocking garbage collection compacts the large object heap (LOH).
Настраивает время вмешательства сборщика мусора в работу приложения. Adjusts the time that the garbage collector intrudes in your application.
Комментарии
Типы, найденные в этом пространстве имен, предназначены для использования в расширенной разработке. The types found in this namespace are intended for use in advanced development.
System. Runtime. Interop Services. Windows Runtime Пространство имен
Содержит классы, которые поддерживают взаимодействие между управляемым кодом и средой выполнения Windows и обеспечивают создание типов среды выполнения Windows с помощью управляемого кода. Contains classes that support interoperation between managed code and the Windows Runtime, and that enable the creation of Windows Runtime types with managed code.
Классы
Предоставляет фабричные методы для создания представлений управляемых задач, совместимых с асинхронными действиями и операциями среды выполнения Windows. Provides factory methods to construct representations of managed tasks that are compatible with Windows Runtime asynchronous actions and operations.
Указывает интерфейс по умолчанию для управляемого класса среды выполнения Windows. Specifies the default interface of a managed Windows Runtime class.
Предоставляет данные для события DesignerNamespaceResolve. Provides data for the DesignerNamespaceResolve event.
Хранит сопоставления между делегатами и токенами событий для поддержки реализации события среды выполнения Windows в управляемом коде. Stores mappings between delegates and event tokens, to support the implementation of a Windows Runtime event in managed code.
Указывает версию целевого типа, который первым реализовал указанный интерфейс. Specifies the version of the target type that first implemented the specified interface.
Предоставляет данные для события ReflectionOnlyNamespaceResolve. Provides data for the ReflectionOnlyNamespaceResolve event.
При применении к параметру-массиву в компоненте среды выполнения Windows указывает, что содержимое массива, передаваемого в этом параметре, используется только в качестве входных данных. When applied to an array parameter in a Windows Runtime component, specifies that the contents of the array that is passed to that parameter are used only for input. Вызывающий объект ожидает, что массив не изменится вызовом. The caller expects the array to be unchanged by the call.
Задает имя возвращаемого значения метода в компоненте среды выполнения Windows. Specifies the name of the return value of a method in a Windows Runtime component.
Предоставляет реализацию интерфейса IBuffer среды выполнения Windows (Windows.Storage.Streams.IBuffer), а также все дополнительные необходимые интерфейсы. Provides an implementation of the Windows Runtime IBuffer interface (Windows.Storage.Streams.IBuffer), and all additional required interfaces.
Предоставляет методы расширения для работы с буферами среды выполнения Windows (интерфейс Windows.Storage.Streams.IBuffer). Provides extension methods for operating on Windows Runtime buffers (Windows.Storage.Streams.IBuffer interface).
Предоставляет вспомогательные методы для маршалинга данных между .NET Framework и средой выполнения Windows. Provides helper methods for marshaling data between the .NET Framework and the Windows Runtime.
Предоставляет событие для разрешения запросов типов, предназначенных только для отражений, для типов, предоставляемых файлами метаданных Windows, и методы для выполнения разрешения. Provides an event for resolving reflection-only type requests for types that are provided by Windows Metadata files, and methods for performing the resolution.
При применении к параметру массива в компоненте среды выполнения Windows указывает, что содержимое массива, передаваемое этому параметру, используется только для вывода. When applied to an array parameter in a Windows Runtime component, specifies that the contents of an array that is passed to that parameter are used only for output. Вызывающая сторона не гарантирует, что содержимое инициализируется, и вызванный метод не должен считывать содержимое. The caller does not guarantee that the contents are initialized, and the called method should not read the contents.
Структуры
Токен, который возвращается, когда для события среды выполнения Windows добавляется обработчик событий. A token that is returned when an event handler is added to a Windows Runtime event. Этот маркер используется для последующего удаления обработчика событий из события. The token is used to remove the event handler from the event at a later time.
Интерфейсы
Разрешает активацию одного или нескольких классов средой выполнения Windows. Enables classes to be activated by the Windows Runtime.
Модель приложений Windows Runtime
Наша жизнь наполнена абстракциями. Как разработчики мы часто вынуждены бороться со всяческими проблемами, используя абстракции, о которых мы по большому счету ничего не знаем. Абстракции иногда разваливаются и оказываются не в состоянии полностью скрыть нижележащие сложности. Не поймите меня неправильно, абстракции — великая вещь. Они помогают и пользователям, и разработчикам, но вы здорово облегчите себе жизнь, если вникнете в те абстракции, на которые вы регулярно опираетесь в своей работе, и четко поймете, как именно они действуют. Более того, библиотеки, авторы которых приняли эту реальность, зачастую куда успешнее тех, где этого не сделано, и отчасти это связано с тем, что такие библиотеки позволяют входить в абстракцию при пошаговой отладке, если в том возникает необходимость.
Одна из таких абстракций — 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:
Я также буду использовать макрос ASSERT и функцию HR для обработки ошибок. Об этом я уже говорил, поэтому не буду повторяться. Если вы не уверены в этих этапах, прочитайте мою статью в этой рубрике за май 2013 г. «Introducing Direct2D 1.1» (msdn.microsoft.com/magazine/dn198239).
Наконец, чтобы применять любую из упоминаемых в этой статье 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, но об этом позже.
Следующая часть посложнее. В Windows Runtime отказались от традиционной модели активации COM, в которой применяются идентификаторы классов на основе GUID, в пользу модели, где активация происходит по текстовым идентификаторам классов. Текстовые имена базируются на именах классов с областью видимости в пределах пространств имен, ставших столь популярными благодаря Java и Microsoft .NET Framework, но, прежде чем вы попытаетесь распрощаться с реестром, учтите, что эти новые идентификаторы классов по-прежнему хранятся в реестре. С технической точки зрения, в реестре регистрируются только основные типы (first-party types), тогда как сторонние типы (third-party types) регистрируются лишь в манифесте конкретного приложения. У этого изменения есть свои преимущества и недостатки. Один из недостатков в том, что описывать идентификатор класса при вызове WinRT-функции немного труднее. В Windows Runtime определен новый строковый тип, поддерживающий удаленное взаимодействие (remotable string type), который заменил традиционный строковый тип BSTR, и любые идентификаторы классов нужно предоставлять, используя этот новый «носитель». HSTRING (так он называется) гораздо меньше подвержен ошибкам, чем BSTR, — главным образом потому, что он является неизменяемым (immutable). Простейший способ создать HSTRING — вызвать функцию WindowsCreateString:
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 примерно так:
После активации окна приложение наконец готово к выполнению:
Реализовать это можно разными способами. Здесь я просто получаю интерфейс ICoreDispatcher окна, который представляет цикл прокачки сообщений (message pump) для этого окна. Наконец, существует метод Uninitialize, который мог бы вызываться время от времени, но в остальном бесполезен, и его можно безопасно игнорировать:
Вот и все. Теперь вы можете скомпилировать и запустить приложение. Конечно, в данном случае на экране ничего рисоваться не будет. Можете взять копию dx.h с сайта dx.codeplex.com и начать добавлять Direct2D-код для рендеринга (подробности см. в моей статье «A Modern Library for DirectX Programming» в этой рубрике за июнь 2013 г. по ссылке msdn.microsoft.com/magazine/dn201741) или дождаться следующей статьи, где я покажу, как лучше всего интегрировать Direct2D с базовой моделью приложений WinRT.
Кенни Керр (Kenny Kerr) — высококвалифицированный программист. Живет в Канаде. Автор учебных курсов для Pluralsight, обладатель звания Microsoft MVP. Ведет блог kennykerr.ca Кроме того, читайте его заметки в twitter.com/kennykerr.
Выражаю благодарность за рецензирование статьи эксперту Microsoft Джеймсу Макнеллису (James McNellis).