Создание событий в компонентах среды выполнения Windows

Примечание

Дополнительные сведения о создании событий в компоненте среда выполнения Windows C++/WinRT см. в статье Создание событий в C++/WinRT.

Если компонент среда выполнения Windows вызывает событие пользовательского типа делегата в фоновом потоке (рабочем потоке) и вы хотите, чтобы JavaScript мог получать событие, можно реализовать и (или) вызвать его любым из этих способов.

  • (Вариант 1) Вызовите событие через Windows.UI.Core.CoreDispatcher , чтобы маршалировать событие в контекст потока JavaScript. Хотя обычно это оптимальный вариант, в некоторых случаях он не обеспечивает максимальную производительность.
  • (Вариант 2) Используйте объект>Windows.Foundation.EventHandler< (но потеряйте сведения о типе события). Если вариант 1 неосуществим или если его производительность не является достаточной, то это хороший второй вариант при условии, что потеря сведений о типе является приемлемой. Если вы создаете компонент среда выполнения Windows C#, тип объекта> Windows.Foundation.EventHandler< недоступен. Вместо этого этот тип проецируется на System.EventHandler, поэтому его следует использовать.
  • Вариант 3: создать собственный прокси и заглушку для компонента. Этот вариант сложнее всего реализовать, но он сохраняет сведения о типе и может обеспечивать более высокую, чем вариант 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: использование EventHandler<Object>, но с потерей сведений о типе

Примечание

Если вы создаете компонент среда выполнения Windows C#, тип объекта> Windows.Foundation.EventHandler< недоступен. Вместо этого этот тип проецируется на System.EventHandler, поэтому его следует использовать.

Другой способ отправки события из фонового потока — использовать объект>Windows.Foundation.EventHandler<в качестве типа события. 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.Foundation.TypedEventHandler<TSender, TResult>, а другой класс — это тип, возвращаемый среде JavaScript в качестве аргумента TValue. Эти классы не могут взаимодействовать с JavaScript, если не выполнить последующие действия.
  • Это приложение активирует объект основного класса, вызывает метод и обрабатывает событие, вызываемое компонентом среды выполнения Windows.
  • Это необходимо для средств, создающих классы прокси и заглушки.
  • Затем вы используете файл IDL для генерации исходного кода C для прокси и заглушки.
  • Зарегистрируйте объекты прокси и заглушки, чтобы среда выполнения COM могла их найти, и добавьте ссылку на соответствующую библиотеку DLL в проект приложения.

Создание компонента среды выполнения Windows

В Visual Studio в строке меню выберите Файл > Новый проект. В диалоговом окне Создание проекта разверните узел JavaScript > Универсальное приложение для Windows и выберите Пустое приложение. Назовите проект ToasterApplication и нажмите кнопку ОК .

Добавьте компонент среды выполнения Windows на C# в решение: в обозревателе решений откройте контекстное меню для нужного решения и выберите Добавить > Новый проект. Разверните visual C# > Microsoft Store и выберите компонент среда выполнения Windows. Назовите проект ToasterComponent и нажмите кнопку ОК. ToasterComponent будет корневым пространством имен для компонентов, которые вы создадите позднее.

В обозревателе решений откройте контекстное меню решения и выберите пункт Свойства. В диалоговом окне Страницы свойств выберите элемент Свойства конфигурации в левой области, а затем в верхней части диалогового окна задайте для параметра Конфигурация значение Отладка, а для параметра Платформа выберите "x86", "x64" или "ARM". Нажмите кнопку ОК .

Важно! Значение "Любой ЦП" не подойдет, поскольку оно недопустимо для библиотеки DLL с собственным кодом Win32, которую вы добавите в решение позже.

В обозревателе решений переименуйте файл class1.cs на ToasterComponent.cs, чтобы он соответствовал имени проекта. Visual Studio автоматически переименует класс в файле.

Добавьте в CS-файл директиву using для пространства имен Windows.Foundation, чтобы добавить TypedEventHandler в область действия.

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

Примечание. Для C# этот шаг можно пропустить. Вместо этого сначала создайте класс, а затем откройте его контекстное меню и выберите Рефакторинг > извлечь интерфейс. В созданном коде вручную сделайте интерфейсы общедоступными.

	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);
                    }
                });
           }
        }

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

Примечание. Асинхронный вызов в предыдущем коде использует метод ThreadPool.RunAsync только для демонстрации простого способа активации события в фоновом потоке. Вы можете написать этот метод, как показано в следующем примере, и он будет работать, так как планировщик заданий .NET автоматически маршалирует асинхронные вызовы и вызовы 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 добавьте пространство имен для функций, которые будут вызывать компонент и которые будет вызывать сам компонент. Пространство имен будет содержать две функции: одну для создания всплывающего уведомления и одну для обработки события toast-complete. Реализация makeToast создает объект Toaster, регистрирует обработчик событий и создает всплывающее уведомление. Пока обработчик событий ничего особого не делает, как показано здесь:

	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 in this case event is a
           // Toast class, with a toastType string.
           var toastType = event.toastType;

           document.getElementById('toastOutput').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. Измените значение Тип отладчика на Только управляемый код. В строке меню выберите пункты Отладка > Исключения, а затем — Common Language Runtime Exceptions (Исключения среды CLR).

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

отсутствующие прокси

Первый шаг создания прокси и заглушки компонента — добавление уникального идентификатора или GUID в интерфейсы. Однако используемый формат GUID зависит от применяемого языка.

Создание идентификаторов GUID для интерфейсов компонента (C# и другие языки .NET)

В строке меню выберите "Средства > Создать GUID". В диалоговом окне выберите значение 5. [Guid("xxxxxxx-xxxx... xxxx")]. Нажмите кнопку "Создать GUID", а затем кнопку "Копировать".

средство создания GUID

Вернитесь к определению интерфейса, а затем вставьте новый GUID перед интерфейсом IToaster, как показано в следующем примере. (Не используйте GUID в примере. Каждый уникальный интерфейс должен иметь собственный GUID.)

[Guid("FC198F74-A808-4E2A-9255-264746965B9F")]
        public interface IToaster...

Добавьте директиву using для пространства имен System.Runtime.InteropServices.

Повторите эти действия для интерфейса IToast.

Создание идентификаторов GUID для интерфейсов компонента (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++. Чтобы упростить операцию, добавьте новый проект для поддержки построения прокси-объектов. Откройте контекстное меню для решения ТостерПриложения и выберите Добавить > новый проект. В левой области диалогового окна Новый проект разверните узел Visual C++ > Windows > Univeral Windows, а затем в средней области выберите DLL (приложения UWP). (Обратите внимание, что это НЕ проект компонента среды выполнения Windows на C++.) Назовите проект Proxies и нажмите кнопку ОК. Эти файлы обновляются событиями после построения при изменение класса C#.

По умолчанию проект Proxies создает H-файлы заголовков и CPP-файлы C++. Поскольку библиотека DLL компилируется на основе файлов, созданных MIDL, H- и CPP-файлы не требуются. В обозревателе решений откройте контекстное меню для них, выберите команду Удалить и подтвердите удаление.

Теперь, когда проект пуст, вы можете добавить созданные средством MIDL файлы. Откройте контекстное меню для проекта Прокси-серверы и выберите Добавить > существующий элемент. В диалоговом окне перейдите в каталог проекта ТостерComponent и выберите следующие файлы: Файлы ТостерComponent.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++ >измените значение Предкомпилированные заголовки на Не используются предкомпилированные заголовки, а затем нажмите кнопку Применить.

В разделе Общие компоновщик >изменитезначение Игнорировать библиотеку импорта на Yes и нажмите кнопку Применить.

В разделе Входные данные компоновщика >выберите Дополнительные зависимости, нажмите кнопку со стрелкой вниз и выберите Изменить. Добавьте следующий текст в поле:

rpcrt4.lib;runtimeobject.lib

Не вставляйте эти библиотеки напрямую в строку списка. Используйте окно Изменить, чтобы MSBuild в Visual Studio сохранил необходимые дополнительные зависимости.

После внесения изменений нажмите кнопку ОК в диалоговом окне Страницы свойств.

Затем добавьте зависимость от проекта ToasterComponent. При этом проект Toaster будет построен перед проектом Proxy. Это необходимо, поскольку проект 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). Обратите внимание, что GUID ClassId в 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. Используйте первый идентификатор GUID, указанный в этом файле, для classId. (Он может совпадать с GUID для ITypedEventHandler2.)
  • Path — это относительный путь к пакету для двоичного файла прокси. (В этом руководстве библиотека proxies.dll находится в той же папки, что ToasterApplication.winmd.)
  • Идентификаторы GUID заданы в нужном формате. (Здесь довольно легко запутаться.)
  • Идентификаторы интерфейсов в манифесте совпадают со значениями IID в файле ToasterComponent_i.c.
  • Имена интерфейсов в манифесте уникальны. Так как они не используются системой, значения можете выбирать вы. Рекомендуется выбирать имена интерфейсов, которые соответствуют заданным вами интерфейсам. Для созданных интерфейсов рекомендуется использовать описательные имена. В качестве помощи при создании имен интерфейсов можно использовать файл ToasterComponent_i.c.

Теперь, если вы попытаетесь запустить решение, появится сообщение о том, что файл proxies.dll не входит в полезные данные. Откройте контекстное меню папки Ссылки в проекте ToasterApplication и выберите команду Добавить ссылку. Установите флажок рядом с проектом Proxies. Кроме того, убедитесь, что флажок "ToasterComponent" также установлен. Нажмите кнопку ОК .

Теперь можно выполнить сборку проекта. Запустите проект и убедитесь, что вы можете создать всплывающее уведомление.