Создание событий в компонентах среды выполнения Windows
Если компонент среды выполнения Windows создает событие определяемого пользователем типа делегата в фоновом потоке (рабочем потоке) и код JavaScript должен иметь возможность получать это событие, такой механизм можно реализовать одним из следующих способов:
(вариант 1) вызов события с помощью CoreDispatcher, чтобы маршалировать событие в контекст потока JavaScript. Хотя в общем случае этот вариант является наилучшим, в некоторых ситуациях он может не обеспечивать максимальную производительность;
(вариант 2) использование Windows.Foundation.EventHandler<Object> (но информация о типе события будет утеряна). Если вариант 1 нереализуем или его производительность недостаточна, это хороший резервный вариант в том случае, когда потеря информации о типе допустима;
(вариант 3) создание собственных COM-объектов прокси и заглушки для компонента. Этот вариант сложнее всего реализовать, но он сохраняет информацию о типе и может обеспечить более высокую производительность по сравнению с вариантом 1 в сценариях, где это требуется.
Если просто вызвать событие в фоновом потоке без использования одного из этих вариантов, клиент JavaScript не сможет получить событие.
Теоретические основы
Все компоненты и приложения среды выполнения Windows фактически являются COM-объектами независимо от того, на каком языке они написаны. В API Windows большинство компонентов являются гибкими COM-объектами, которые могут одинаково хорошо взаимодействовать с объектами как в фоновом потоке, так и в потоке пользовательского интерфейса. Если COM-объект нельзя сделать гибким, для взаимодействия с другими COM-объектами через границы фонового потока и потока пользовательского интерфейса требуются вспомогательные объекты, называемые заглушками и прокси. (В терминах модели COM это называется взаимодействием между подразделениями потоков).
Большинство объектов в API Windows либо являются гибкими, либо имеют встроенные прокси и заглушки. Однако прокси и заглушки нельзя создавать для универсальных типов, таких как Windows.Foundation.TypedEventHandler<TSender, TResult>, поскольку они не являются полными типами до тех пор, пока им не будет предоставлен аргумент типа. Отсутствие заглушек и прокси представляет проблему только в случае клиентов JavaScript, но если компонент должен быть одновременно доступен из кода на языках JavaScript, C++ и .NET, следует использовать один из следующих трех вариантов.
(Вариант 1) Вызов события с помощью CoreDispatcher
С помощью Windows.UI.Core.CoreDispatcher можно отправлять события любого определяемого пользователем типа делегата, и код JavaScript сможет получать их. Если вы не знаете, какой вариант выбрать, начните с этого. Если задержка между событием и его обработкой окажется слишком большой, попробуйте один из оставшихся вариантов.
В следующем примере показано, как использовать CoreDispatcher для вызова строго типизированного события. Обратите внимание, что используется аргумент типа Toast, а не Object.
public event EventHandler<Toast> ToastCompletedEvent;
private void OnToastCompleted(Toast args)
{
var completedEvent = ToastCompletedEvent;
if (completedEvent != null)
{
completedEvent(this, args);
}
}
public void MakeToastWithDispatcher(string message)
{
Toast toast = new Toast(message);
// Assume you have a CoreDispatcher at class scope.
// Initialize it here, then use it from the background thread.
var window = Windows.UI.Core.CoreWindow.GetForCurrentThread();
m_dispatcher = window.Dispatcher;
Task.Run( () =>
{
if (ToastCompletedEvent != null)
{
m_dispatcher.RunAsync(CoreDispatcherPriority.Normal,
new DispatchedHandler(() =>
{
this.OnToastCompleted(toast);
})); // end m_dispatcher.RunAsync
}
}); // end Task.Run
}
(Вариант 2) Использование EventHandlerObject<Object> с потерей информации о типе
Другой способ отправить событие из фонового потока — использовать Windows.Foundation.EventHandler<Object> в качестве типа события. Windows предоставляет конкретный экземпляр этого универсального типа, а также прокси и заглушку для него. Недостатком такого подхода является потеря информации о типе аргументов события и отправителя. При получении события клиентами C++ и .NET тип должен быть известен (из документации) для правильного обратного приведения. Клиентам JavaScript информация об исходном типе не требуется. Они находят свойства аргументов по их именам в метаданных.
В этом примере показано, как использовать Windows.Foundation.EventHandler<Object> в коде C#:
public sealed Class1
{
// Declare the event
public event EventHandler<Object> ToastCompletedEvent;
// Raise the event
public async void MakeToast(string message)
{
Toast toast = new Toast(message);
// Fire the event from a background thread to allow this thread to continue
Task.Run(() =>
{
if (ToastCompletedEvent != null)
{
OnToastCompleted(toast);
}
});
}
private void OnToastCompleted(Toast args)
{
var completedEvent = ToastCompletedEvent;
if (completedEvent != null)
{
completedEvent(this, args);
}
}
}
Это событие используется на стороне JavaScript следующим образом:
toastCompletedEventHandler: function (event) {
var toastType = event.toast.toastType;
document.getElementById("toasterOutput").innerHTML = "<p>Made " + toastType + " toast</p>";
},
(Вариант 3) Создание собственных прокси и заглушки
Чтобы получить выигрыш в производительности для определенных пользователем типов событий, которые полностью сохраняют информацию о типе, необходимо создать собственные объекты прокси и заглушки и внедрить их пакет приложения. Как правило, этот вариант следует использовать только в тех редких случаях, когда ни одно из вышеописанных решений не подходит. Кроме того, нет никакой гарантии, что этот вариант обеспечит лучшую производительность, чем другие два варианта. Фактическая производительность зависит от множества факторов. С помощью профилировщика Visual Studio или других средств профилирования измерьте фактическую производительность приложения и определите, действительно ли данное событие является узким местом.
В оставшейся части этой статьи показано, как на языке C# создать базовый компонент среды выполнения Windows, а затем на языке C++ создать библиотеку DLL для прокси и заглушки, которые позволят коду JavaScript использовать событие Windows.Foundation.TypedEventHandler<TSender, TResult>, вызываемое компонентом в асинхронной операции. (Для создания компонента также можно использовать C++ или Visual Basic. Действия по созданию прокси и заглушек будут такими же.) Данное пошаговое руководство основано на статье Пример создания внутрипроцессного компонента среды выполнения Windows (C++/CX) и поясняет ее цель.
Это пошаговое руководство содержит следующие части.
Создание компонента среды выполнения Windows. На этом этапе вы создадите два базовых класса среды выполнения Windows. Один класс предоставляет событие типа Windows.Foundation.TypedEventHandler<TSender, TResult>, а другой класс является типом, возвращаемым коду JavaScript в качестве аргумента для TValue. Эти классы не могут взаимодействовать с JavaScript до тех пор, пока не будут выполнены остальные шаги.
Создание приложения JavaScript. Это приложение активирует объект основного класса, вызывает метод и обрабатывает событие, произошедшее в компоненте среды выполнения Windows.
Создание GUID для интерфейсов компонента. Это требование инструментов, создающих классы прокси и загрузки.
Создание IDL-файла для компонента. С помощью этого IDL-файла вы впоследствии создадите исходный код C для прокси и заглушки.
Компиляция кода прокси и заглушки в библиотеку DLL
Регистрация и использование библиотеки DLL прокси и заглушки. Объекты прокси и заглушки регистрируются, чтобы среда выполнения COM могла найти их, а затем ссылаться на DLL прокси и заглушки в проекте приложения.
Создание компонента среды выполнения Windows
В меню Visual Studio выберите пункты Файл > Новый проект. В диалоговом окне Создать проект разверните узел JavaScript > Магазин Windows, а затем выберите Пустое приложение. Присвойте проекту имя ToasterApplication, после чего нажмите кнопку ОК.
Добавьте в решение компонент среды выполнения Windows на языке C#: в обозревателе решений откройте контекстное меню для решения и выберите Добавить > Новый проект. Разверните узел Visual C# > Магазин Windows, а затем выберите Компонент среды выполнения Windows. Присвойте проекту имя ToasterComponent, после чего нажмите кнопку ОК. ToasterComponent будет корневым пространством имен для компонентов, создаваемых на последующих этапах.
В обозревателе решений откройте контекстное меню для решения и выберите пункт Свойства. В диалоговом окне Страницы свойств выберите на левой панели Свойства конфигурации, а затем в верхней части диалогового окна, задайте для свойства Конфигурация значение Отладка, а для свойства Платформа — значение x86, x64 или ARM. Нажмите кнопку ОК.
Важно!
Значение Платформа = Любой ЦП не будет работать, поскольку оно является недопустимым для DLL-библиотеки Win32 с машинным кодом, которую вы впоследствии добавите в решение.
В обозревателе решений переименуйте файл class1.cs в ToasterComponent.cs, чтобы он соответствовал имени проекта. Visual Studio автоматически переименует класс в файле в соответствии с новым именем файла.
В CS-файле добавьте директиву using для пространства имен Windows.Foundation, чтобы получить доступ к TypedEventHandler.
Когда потребуется обращаться к прокси и заглушкам, компонент должен будет использовать интерфейсы для предоставления доступа к открытым членам. В файле ToasterComponent.cs определите интерфейс для тостера и еще один интерфейс для тоста (Toast), создаваемого тостером.
Примечание
В коде C# этот шаг можно пропустить.Вместо этого необходимо сначала создать класс, а затем открыть его контекстное меню и выбрать команды Рефакторинг > Извлечь интерфейс.В получившемся коде вручную установите для интерфейсов режим доступа public.
public interface IToaster { void MakeToast(String message); event TypedEventHandler<Toaster, Toast> ToastCompletedEvent; } public interface IToast { String ToastType { get; } }
Интерфейс IToast имеет строку, которую можно получать для описания типа тоста. Интерфейс IToaster имеет метод для приготовления тоста и событие, указывающее, что тост готов. Поскольку это событие возвращает определенный элемент (а именно тип) тоста, такое событие называется типизированным.
Теперь нам нужны классы, которые реализуют эти интерфейсы и являются открытыми и запечатанными, чтобы они были доступны из приложения JavaScript, которое будет написано позже.
public sealed class Toast : IToast { private string _toastType; public string ToastType { get { return _toastType; } } internal Toast(String toastType) { _toastType = toastType; } } public sealed class Toaster : IToaster { public event TypedEventHandler<Toaster, Toast> ToastCompletedEvent; private void OnToastCompleted(Toast args) { var completedEvent = ToastCompletedEvent; if (completedEvent != null) { completedEvent(this, args); } } public void MakeToast(string message) { Toast toast = new Toast(message); // Fire the event from a thread-pool thread to enable this thread to continue Windows.System.Threading.ThreadPool.RunAsync( (IAsyncAction action) => { if (ToastCompletedEvent != null) { OnToastCompleted(toast); } }); } }
В предыдущем примере кода мы создали тост, а затем запустили рабочий элемент пула потоков, чтобы создать уведомление. Хотя интегрированная среда разработки может предложить применить ключевое слово await к асинхронному вызову, в данном случае это необязательно, поскольку метод не выполняет никаких действий, зависящих от результатов операции.
Важно!
Асинхронный вызов в предыдущем коде использует ThreadPool.RunAsync исключительно для демонстрации простого способа создания события в фоновом потоке. Можно написать данный конкретный метод, как показано в следующем примере, и он будет исправно работать, поскольку планировщик заданий .NET автоматически маршалирует вызовы async/await обратно в поток пользовательского интерфейса.
public async void MakeToast(string message) { Toast toast = new Toast(message) await Task.Delay(new Random().Next(1000)); OnToastCompleted(toast); }
Если выполнить сборку проекта прямо сейчас, она будет выполнена успешно.
Написание приложения JavaScript
Теперь мы можем добавить кнопку в приложение JavaScript, чтобы оно приготовило тост с помощью класса, который мы только что определили. Перед этим необходимо добавить ссылку на только что созданный проект ToasterComponent. В Обозревателе решений откройте контекстное меню для проекта ToasterApplication, выберите Добавить > Ссылки, а затем нажмите кнопку Добавить новую ссылку. В диалоговом окне Добавление ссылки в левой области в разделе Решение выберите проект компонента, а затем в средней области выберите ToasterComponent. Нажмите кнопку ОК.
В обозревателе решений откройте контекстное меню проекта ToasterApplication и выберите команду Назначить автозагружаемым проектом.
В конец файла default.js добавьте пространство имен, которое будет содержать функции для вызова компонента и вызываться им. Пространство имен будет содержать две функции: одну для приготовления тоста и вторую — для обработки события готовности тоста. Реализация makeToast создает объект тостера, регистрирует обработчик событий и готовит тост. До этого момента обработчик событий выполнял не очень много операций, как показано ниже:
WinJS.Namespace.define("ToasterApplication", { makeToast: function () { var toaster = new ToasterComponent.Toaster(); //toaster.addEventListener("ontoastcompletedevent", ToasterApplication.toastCompletedEventHandler); toaster.ontoastcompletedevent = ToasterApplication.toastCompletedEventHandler; toaster.makeToast("Peanut Butter"); }, toastCompletedEventHandler: function (event) { // The sender of the event (the delegate’s first type parameter) // is mapped to event.target. The second argument of the delegate // is contained in event, which means event in this case is a // Toast class, with a toastType string. var toastType = event.toastType; document.getElementById("toasterOutput").innerHTML = "<p>Made " + toastType + " toast</p>"; }, });
Функция makeToast должна вызываться кнопкой. Добавьте в файл default.html кнопку и место для вывода результатов приготовления тоста:
<body> <h1>Click the button to make toast</h1> <button onclick="ToasterApplication.makeToast()">Make Toast!</button> <div id="toasterOutput"> <p>No Toast Yet...</p> </div> </body>
Если бы мы не использовали TypedEventHandler, мы бы уже могли запустить приложение на локальном компьютере и нажать кнопку, чтобы приготовить тост. Однако в нашем приложении ничего не происходит. Чтобы понять, почему это происходит, выполним отладку управляемого кода, вызывающего событие ToastCompletedEvent. Остановите проект, а затем в строке меню выберите команду Отладка > Свойства приложения Toaster. Измените Тип отладчика на Только управляемый код. Снова перейдите в строку меню и выберите Отладка > Исключения, после чего выберите Исключения среды CLR.
Теперь запустите приложение и нажмите кнопку приготовления тоста. Отладчик перехвати исключение недопустимого приведения. Хотя это не очевидно из сообщения, исключение имеет место из-за отсутствия интерфейса прокси.
Первым шагом создания прокси и заглушки компонента является добавление в интерфейсы уникального идентификатора или GUID. Однако формат идентификатора GUID будет зависеть от языка разработки (C#, Visual Basic или другой язык .NET или же C++).
Создание GUID для интерфейсов компонента
Для C#, Visual Basic и других языков .NET
В строке меню выберите Сервис > Создать GUID. В диалоговом окне выберите 5. [Guid("xxxxxxxx-xxxx...xxxx)]. Нажмите кнопку Создать GUID, а затем нажмите кнопку Копировать.
Вернитесь к определению интерфейса и вставьте новый идентификатор GUID непосредственно перед интерфейсом IToaster, как показано в следующем примере. (Не используйте GUID из примера. Каждый уникальный интерфейс должен иметь собственный идентификатор GUID).
[Guid("FC198F74-A808-4E2A-9255-264746965B9F")] public interface IToaster...
Добавьте директиву using для пространства имен System.Runtime.InteropServices.
Повторите эти шаги для интерфейса IToast.
Для языка C++
В строке меню выберите Сервис > Создать GUID. В диалоговом окне выберите 3. static const struct GUID = {…}. Нажмите кнопку Создать GUID, а затем нажмите кнопку Копировать.
Вставьте GUID непосредственно перед определением интерфейса IToaster. После вставки GUID должен быть похож на следующий пример. (Не используйте GUID из примера. Каждый уникальный интерфейс должен иметь собственный идентификатор GUID).
// {F8D30778-9EAF-409C-BCCD-C8B24442B09B} static const GUID <<name>> = { 0xf8d30778, 0x9eaf, 0x409c, { 0xbc, 0xcd, 0xc8, 0xb2, 0x44, 0x42, 0xb0, 0x9b } };
Добавьте директиву using для Windows.Foundation.Metadata, чтобы ввести GuidAttribute в область действия.
Теперь вручную преобразуйте постоянный идентификатор GUID в GuidAttribute, чтобы формат соответствовал следующему примеру. Обратите внимание, что в фигурные скобки заменяются квадратными и круглыми скобками, а конечная точка с запятой удаляется.
// {E976784C-AADE-4EA4-A4C0-B0C2FD1307C3} [GuidAttribute(0xe976784c, 0xaade, 0x4ea4, 0xa4, 0xc0, 0xb0, 0xc2, 0xfd, 0x13, 0x7, 0xc3)] public interface IToaster {...
Повторите эти шаги для интерфейса IToast.
Теперь, когда интерфейсы имеют уникальные идентификаторы, можно создать IDL-файл, передав файл .winmd в программу командной строки winmdidl, а затем создать исходный код C для прокси и заглушки, передав этот IDL-файл в программу командной строки MIDL. Visual Studio сделает это за нас, если мы создадим выполняемые после сборки события, как показано в следующих шагах.
Создание кода прокси и заглушки
Чтобы добавить пользовательское событие, выполняемое после сборки, в обозревателе решений откройте контекстное меню для проекта ToasterComponent и выберите пункт Свойства. В левой области страниц свойств выберите События сборки, а затем нажмите кнопку Изменить событие после сборки. Добавьте следующие команды в командную строку события после сборки. (Сначала должен вызываться пакетный файл, задающий переменные среды для обнаружения средства winmdidl.)
call "$(DevEnvDir)..\..\vc\vcvarsall.bat" $(PlatformName) winmdidl /outdir:output "$(TargetPath)" midl /metadata_dir "%WindowsSdkDir%References\CommonConfiguration\Neutral" /iid "$(ProjectDir)$(TargetName)_i.c" /env win32 /h "$(ProjectDir)$(TargetName).h" /winmd "Output\$(TargetName).winmd" /W1 /char signed /nologo /winrt /dlldata "$(ProjectDir)dlldata.c" /proxy "$(ProjectDir)$(TargetName)_p.c" "Output\$(TargetName).idl"
Важно!
В случае конфигурации проекта ARM или x64 измените значение параметра MIDL /env на x64 или arm32.
Чтобы проверить, что IDL-файл создается каждый раз, когда изменяется файл .winmd, измените поле Выполнить событие после сборки на При обновлении выходных файлов проекта во время сборки.
Страница свойств События сборки должна выглядеть примерно следующим образом:
Пересоберите решение, чтобы создать и откомпилировать IDL-файл.
Чтобы проверить, что решение правильно откомпилировано с помощью MIDL, можно найти файлы ToasterComponent.h, ToasterComponent_i.c, ToasterComponent_p.c и dlldata.c в каталоге проекта ToasterComponent.
Компиляция кода прокси и заглушки в библиотеку DLL
Теперь, когда имеются все необходимых файлы, можно откомпилировать их для создания библиотеки DLL, которая является файлом C++. Чтобы сделать эту процедуру максимально простой, добавьте новый проект, поддерживающий сборку прокси. Откройте контекстное меню решения ToasterApplication и выберите команды Добавить > Новый проект. В левой области диалогового окна Новый проект разверните узлы Visual C++ > Магазин Windows, после чего в средней области выберите пункт DLL (приложения для Магазина Windows). (Обратите внимание, что это НЕ проект компонента среды выполнения Windows.) Присвойте проекту имя Proxies, после чего нажмите кнопку ОК. Эти файлы будут обновляться событиями после сборки, когда что-либо будет изменяться в классе C#.
По умолчанию проект Proxies создает файлы заголовков .h и файлы C++ .cpp. Поскольку библиотека DLL собирается из файлов, полученных из MIDL, файлы .h и .cpp не являются обязательными. В обозревателе решений откройте соответствующие контекстные меню, выберите пункт Удалить и подтвердите операцию удаления.
Теперь, когда проект пуст, можно снова добавить в него файлы, созданные MIDL. Откройте контекстное меню проекта Proxies и выберите команды Добавить > Существующий элемент. В диалоговом окне перейдите в каталог проекта ToasterComponent и выберите следующие файлы: ToasterComponent.h, ToasterComponent_i.c, ToasterComponent_p.c и dlldata.c. Нажмите кнопку Добавить.
В проекте Proxies создайте файл .def для определения экспортов библиотек DLL, описанных в файле dlldata.c. Откройте контекстное меню проекта и выберите команды Добавить > Новый элемент. В левой области диалогового окна выберите Код, а затем в средней области выберите Файл определения модуля. Задайте имя файла proxies.def и нажмите кнопку Добавить. Откройте этот файл .def и добавьте в него конструкции EXPORTS, определенные в файле dlldata.c:
EXPORTS DllCanUnloadNow PRIVATE DllGetClassObject PRIVATE
Если выполнить сборку проекта прямо сейчас, операция завершится сбоем. Чтобы правильно компилировать проект, необходимо изменить способ компиляции и компоновки проекта. В обозревателе решений откройте контекстное меню проекта Proxies и выберите пункт Свойства. Измените страницы свойств следующим образом:
в левой области выберите C/C++ > Препроцессор, а затем в правой области выберите Определения препроцессора, нажмите кнопку со стрелкой вниз и выберите Изменить. Добавьте в поле следующие определения:
WIN32;_WINDOWS
в разделе C/C++ > Предварительно откомпилированные заголовки измените Предварительно откомпилированный заголовок на Не использовать предварительно скомпилированные заголовки, а нажмите кнопку Применить;
в разделе Компоновщик > Общие измените значение параметра Пропустить библиотеку импорта на Да, а затем нажмите кнопку Применить;
в разделе Компоновщик > Ввод выберите Дополнительные зависимости, нажмите кнопку со стрелкой вниз, а затем выберите пункт Изменить. Добавьте в поле следующий текст:
rpcrt4.lib;runtimeobject.lib
Не вставьте эти библиотеки непосредственно в строку списка. С помощью поля ввода убедитесь, что MSBuild в Visual Studio будет поддерживать правильные дополнительные зависимости.
После внесения этих изменений, нажмите кнопку ОК в диалоговом окне страницы свойств.
После этого установите зависимость от проекта ToasterComponent. Это гарантирует, что сборка проекта Toster будет происходить до сборки проекта прокси. Это необходимо, поскольку проект Toaster отвечает за создание файлов для сборки прокси.
Откройте контекстное меню проекта Proxies и выберите команду Зависимости проекта. Установите флажки, чтобы указать, что проект Proxies зависит от проекта ToasterComponent, чтобы сборка в Visual Studio выполнялась в правильном порядке.
Проверьте правильность сборки решений, выбрав в строке меню Visual Studio команды Сборка > Перестроить решение.
Регистрация прокси и заглушки
В проекте приложения ToasterApplication откройте контекстное меню файла package.appxmanifest и выберите команду Открыть с помощью. В диалоговом окне Открыть с помощью выберите Редактор XML и нажмите кнопку ОК. Нам необходимо вставить XML-код, обеспечивающий регистрацию расширения windows.activatableClass.proxyStub и основанный на значениях GUID в прокси. Чтобы найти GUID в файле .appxmanifest, откройте файл ToasterComponent_i.c. Найдите записи, напоминающие значения в следующем примере. Также обратите внимание на определения для IToast, IToaster и третьего интерфейса — типизированного обработчика событий, имеющего два параметра: Toaster и Toast. Это соответствует событию, определяется в классе Toaster. Обратите внимание, что GUID для IToast и IToaster совпадают с GUID, определенными в интерфейсах в файле C#. Поскольку интерфейс обработчика типизированного события создается автоматически, GUID для этого интерфейса также создается автоматически.
MIDL_DEFINE_GUID(IID, IID___FITypedEventHandler_2_ToasterComponent__CToaster_ToasterComponent__CToast,0x1ecafeff,0x1ee1,0x504a,0x9a,0xf5,0xa6,0x8c,0x6f,0xb2,0xb4,0x7d); MIDL_DEFINE_GUID(IID, IID___x_ToasterComponent_CIToast,0xF8D30778,0x9EAF,0x409C,0xBC,0xCD,0xC8,0xB2,0x44,0x42,0xB0,0x9B); MIDL_DEFINE_GUID(IID, IID___x_ToasterComponent_CIToaster,0xE976784C,0xAADE,0x4EA4,0xA4,0xC0,0xB0,0xC2,0xFD,0x13,0x07,0xC3);
Теперь мы копируем GUID, вставляем их в файл package.appxmanifest в узле, которому присвоено имя Extensions, а затем переформатируем их. Запись манифеста выглядит примерно следующим образом (но, опять же, не забывайте использовать собственные GUID). Обратите внимание, что ClassId GUID в XML-файле совпадает с ITypedEventHandler2. Это связано с тем, что GUID является первым значением, указанным в файле ToasterComponent_i.c. Значения GUID задаются с учетом регистра. Вместо того чтобы вручную изменять формат GUID для IToast и IToaster, можно вернуться к определениям интерфейсов и получить значение GuidAttribute, которое имеет правильный формат. В C++ имеется правильно отформатированное значение GUID в комментарии. В любом случае необходимо вручную изменить формат GUID, используемый как для ClassId, так и для обработчика событий.
<Extensions> <!—Use your own GUIDs!!!--> <Extension Category="windows.activatableClass.proxyStub"> <ProxyStub ClassId="1ecafeff-1ee1-504a-9af5-a68c6fb2b47d"> <Path>Proxies.dll</Path> <Interface Name="IToast" InterfaceId="F8D30778-9EAF-409C-BCCD-C8B24442B09B"/> <Interface Name="IToaster" InterfaceId="E976784C-AADE-4EA4-A4C0-B0C2FD1307C3"/> <Interface Name="ITypedEventHandler_2_ToasterComponent__CToaster_ToasterComponent__CToast" InterfaceId="1ecafeff-1ee1-504a-9af5-a68c6fb2b47d"/> </ProxyStub> </Extension> </Extensions>
Вставьте узел XML Extensions в качестве ближайшего дочернего элемента узла Package на одном уровне, например, с узлом Resources.
Прежде чем продолжить, важно проверить следующее:
для ClassId ProxyStub задан первый GUID в файле ToasterComponent_i.c. используйте в качестве classId первый GUID, заданные в этом файле. (Он может совпадать с GUID для ITypedEventHandler2);
Path задает относительный путь к пакету библиотеки прокси. (В данном пошаговом руководстве proxies.dll находится в той же папке, что и ToasterApplication.winmd);
значения GUID имеют правильный формат. (Здесь легко ошибиться);
идентификаторы интерфейсов в манифесте совпадают со значениями IID в файле ToasterComponent_i.c;
имена интерфейсов в манифесте уникальны. Поскольку они не используются системой, разработчик может самостоятельно выбирать их значения. Рекомендуется выбирать имена интерфейсов. которые четко соответствуют интерфейсам, которые вы определили. Имена генерируемых интерфейсов также должны соответствовать их назначению. В качестве помощи при создании имен интерфейсов можно использовать файл ToasterComponent_i.c.
Если попробовать запустить решение на данном этапе, появится сообщение об ошибке. указывающее, что файл proxies.dll не является частью полезной нагрузки. Откройте контекстное меню папки Ссылки в проекте ToasterApplication и выберите команду Добавить ссылку. Установите флажок рядом с проектом Proxies. Кроме того, убедитесь, что установлен флажок ToasterComponent. Нажмите кнопку ОК.
Теперь сборка проекта должна пройти без ошибок. Запустите проект и убедитесь, что с его помощью можно приготовить тост.