Поделиться через


Создание компонентов среды выполнения Windows с помощью C# и Visual Basic

Управляемый код можно использовать для создания собственных типов среда выполнения Windows и их упаковки в компоненте среда выполнения Windows. Компонент можно использовать в приложениях универсальная платформа Windows (UWP), написанных на C++, JavaScript, Visual Basic или C#. В данном разделе описываются правила создания компонентов и рассматриваются некоторые аспекты поддержки среды выполнения Windows в .NET. Как правило, такая поддержка разрабатывается таким образом, чтобы быть прозрачной для разработчиков для .NET. Однако при создании компонента для использования с JavaScript или C++необходимо учитывать различия в том, как эти языки поддерживают среда выполнения Windows.

Если вы создаете компонент для использования только в приложениях UWP, написанных в Visual Basic или C#, а компонент не содержит элементы управления UWP, то рекомендуется использовать шаблон библиотеки классов вместо шаблона проекта компонента среда выполнения Windows в Microsoft Visual Studio. Существует меньше ограничений для простой библиотеки классов.

Примечание.

Для создания компонента среды выполнения Windows разработчики C#, создающие классические приложения в .NET 6 или более поздней версии, могут использовать C#/WinRT. См. статью Создание компонентов среды выполнения Windows с помощью C#/WinRT.

Объявление типов в компонентах среда выполнения Windows

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

Внешние элементы типов могут предоставлять только среда выполнения Windows типы для их параметров и возвращаемых значений. В следующем списке описываются ограничения типов .NET, предоставляемых компонентом среда выполнения Windows.

  • Поля, параметры и возвращаемые значения всех общедоступных типов и членов компонента должны быть среда выполнения Windows типами. Это ограничение включает среда выполнения Windows типы, которые вы создаете, а также типы, предоставляемые самим среда выполнения Windows. Он также включает ряд типов .NET. Включение этих типов является частью поддержки, которую предоставляет .NET для обеспечения естественного использования среда выполнения Windows в управляемом коде— код, как представляется, использует знакомые типы .NET вместо базовых типов среда выполнения Windows. Например, можно использовать примитивы .NET, такие как Int32 и Double, некоторые основные типы, такие как DateTimeOffset и URI, а также некоторые часто используемые универсальные типы интерфейсов, такие как IEnumerable T> (IEnumerable<(Of T) в Visual Basic) и IDictionary<TKey,TValue>. Обратите внимание, что аргументы типов этих универсальных типов должны быть среда выполнения Windows типами. Это рассматривается в разделах, посвященных передаче типов среда выполнения Windows управляемому коду и передаче управляемых типов в среда выполнения Windows далее в этом разделе.

  • Общедоступные классы и интерфейсы могут содержать методы, свойства и события. Можно объявить делегаты для событий или использовать делегат EventHandler<T> . Общедоступный класс или интерфейс не может:

    • Будьте универсальными.
    • Реализуйте интерфейс, который не является интерфейсом среда выполнения Windows (однако можно создать собственные интерфейсы среда выполнения Windows и реализовать их).
    • Наследуются от типов, которые не находятся в среда выполнения Windows, например System.Exception и System.EventArgs.
  • Все общедоступные типы должны иметь корневое пространство имен, соответствующее имени сборки, и имя сборки не должно начинаться с "Windows".

    Совет. По умолчанию проекты Visual Studio имеют имена пространств имен, соответствующие имени сборки. В Visual Basic инструкция namespace для этого пространства имен по умолчанию не отображается в коде.

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

  • Открытые классы должны быть запечатаны (Неустранимые в Visual Basic). Если для модели программирования требуется полиморфизм, можно создать общедоступный интерфейс и реализовать этот интерфейс на классах, которые должны быть полиморфными.

Отладка компонента

Если приложение UWP и компонент создаются с помощью управляемого кода, их можно отлаживать одновременно.

При тестировании компонента в рамках приложения UWP с помощью C++можно одновременно выполнять отладку управляемого и машинного кода. По умолчанию используется только машинный код.

Отладка собственного кода C++ и управляемого кода

  1. Откройте контекстное меню проекта Visual C++ и выберите пункт "Свойства".
  2. На страницах свойств в разделе "Свойства конфигурации" выберите "Отладка".
  3. Выберите тип отладчика и в раскрывающемся списке измените собственный код только на смешанный (управляемый и собственный). Выберите OK.
  4. Задайте точки останова в машинном и управляемом коде.

При тестировании компонента в составе приложения UWP с помощью JavaScript решение по умолчанию находится в режиме отладки JavaScript. В Visual Studio вы не можете одновременно отлаживать JavaScript и управляемый код.

Отладка управляемого кода вместо JavaScript

  1. Откройте контекстное меню для проекта JavaScript и выберите "Свойства".
  2. На страницах свойств в разделе "Свойства конфигурации" выберите "Отладка".
  3. Выберите тип отладчика и в раскрывающемся списке измените скрипт только на управляемый. Выберите OK.
  4. Задайте точки останова в управляемом коде и отладите как обычно.

Передача типов среда выполнения Windows в управляемый код

Как упоминалось ранее в разделе Объявление типов в компонентах среда выполнения Windows, некоторые типы .NET могут отображаться в сигнатурах членов общедоступных классов. Это часть поддержки, которую предоставляет .NET для обеспечения естественного использования среда выполнения Windows в управляемом коде. Он включает примитивные типы и некоторые классы и интерфейсы. Если компонент используется из JavaScript или из кода C++, важно знать, как типы .NET отображаются вызывающему объекту. В пошаговом руководстве по созданию компонента C# или Visual Basic среда выполнения Windows и вызову его из JavaScript, например с помощью JavaScript. В этом разделе рассматриваются часто используемые типы.

В .NET примитивные типы, такие как структура Int32 , имеют множество полезных свойств и методов, таких как метод TryParse . Напротив, примитивные типы и структуры в среда выполнения Windows имеют только поля. При передаче этих типов в управляемый код они, как представляется, типы .NET, и вы можете использовать свойства и методы типов .NET, как обычно. В следующем списке перечислены подстановки, которые выполняются автоматически в интегрированной среде разработки.

  • Для среда выполнения Windows примитивов Int32, Int64, Single, Double, Boolean, String (неизменяемая коллекция символов Юникода), Enum, UInt32, UInt64 и Guid, используйте тип того же имени в пространстве имен системы.
  • Для UInt8 используйте System.Byte.
  • Для Char16 используйте System.Char.
  • Для интерфейса IInspectable используйте System.Object.

Если C# или Visual Basic предоставляет ключевое слово языка для любого из этих типов, вместо этого можно использовать ключевое слово языка.

Помимо примитивных типов, некоторые основные, часто используемые среда выполнения Windows типы отображаются в управляемом коде в качестве эквивалентов .NET. Например, предположим, что код JavaScript использует класс Windows.Foundation.Uri , и вы хотите передать его в метод C# или Visual Basic. Эквивалентный тип в управляемом коде — это класс .NET System.Uri , и это тип, используемый для параметра метода. Вы можете определить, когда тип среда выполнения Windows отображается как тип .NET, так как IntelliSense в Visual Studio скрывает тип среда выполнения Windows при написании управляемого кода и представляет эквивалентный тип .NET. (Обычно два типа имеют одинаковое имя. Однако обратите внимание, что структура Windows.Foundation.DateTime отображается в управляемом коде как System.DateTimeOffset и не как System.DateTime.)

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

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

Среда выполнения Windows .NET
IIterable<T> IEnumerable<T>
IVector<T> IList<T>
IVectorView<T> IReadOnlyList<T>
IMap<K, V> IDictionary<TKey, TValue>
IMapView<K, V> IReadOnlyDictionary<TKey, TValue>
IKeyValuePair<K, V> KeyValuePair<TKey, TValue>
IBindableIterable IEnumerable
IBindableVector IList
Windows.UI.Xaml.Data.INotifyPropertyChanged System.ComponentModel.INotifyPropertyChanged.
Windows.UI.Xaml.Data.PropertyChangedEventHandler System.ComponentModel.PropertyChangedEventHandler
Windows.UI.Xaml.Data.PropertyChangedEventArgs System.ComponentModel.PropertyChangedEventArgs

Если тип реализует несколько интерфейсов, можно использовать любой из интерфейсов, которые он реализует в качестве типа параметра или возвращаемого типа элемента. Например, можно передать или вернуть int словаря, строку> (Dictionary<(Of Integer, String) в Visual Basic в качестве int iDictionary, string, IReadOnlyDictionary<<int, string>> или IEnumerable<System.Collections.Generic.KeyValuePair<TKey, TValue.>>

Внимание

JavaScript использует интерфейс, который сначала отображается в списке интерфейсов, реализуемых управляемым типом. Например, если вы возвращаете int словаря<, строка> в код JavaScript отображается как IDictionary<int, строка> независимо от того, какой интерфейс указывается в качестве возвращаемого типа. Это означает, что если первый интерфейс не включает элемент, который отображается в последующих интерфейсах, этот элемент не отображается в JavaScript.

В среда выполнения Windows, IMap<K, V> и IMapView<K, V> итерируются с помощью IKeyValuePair. При передаче их в управляемый код они отображаются как IDictionary TKey, TValue> и IReadOnlyDictionary<<TKey, TValue>, поэтому, естественно, вы используете System.Collections.Generic.KeyValuePair<TKey, TValue> для перечисления их.

Представление интерфейсов в управляемом коде влияет на представление типов, реализующих эти интерфейсы. Например, класс PropertySet реализует IMap<K, V>, который отображается в управляемом коде как IDictionary<TKey, TValue>. PropertySet отображается так, как если бы он реализовал IDictionary<TKey, TValue> вместо IMap<K, V>, поэтому в управляемом коде он, как представляется, имеет метод Add, который ведет себя как метод Add в словарях .NET. Как представляется, он не имеет метода Insert . Этот пример можно увидеть в пошаговом руководстве по созданию компонента C# или Visual Basic среда выполнения Windows и вызове из JavaScript.

Передача управляемых типов в среда выполнения Windows

Как описано в предыдущем разделе, некоторые типы среда выполнения Windows могут отображаться как типы .NET в подписях членов компонента или в подписях членов среда выполнения Windows при их использовании в интегрированной среде разработки. Когда вы передаете типы .NET этим членам или используете их в качестве возвращаемых значений членов компонента, они отображаются в коде на другой стороне в качестве соответствующего типа среда выполнения Windows. Примеры эффектов, которые могут возникнуть при вызове компонента из JavaScript, см. в разделе "Возврат управляемых типов из компонента" в пошаговом руководстве по созданию компонента C# или Visual Basic среда выполнения Windows и вызову его из JavaScript.

Перегруженные методы

В среда выполнения Windows методы могут быть перегружены. Однако при объявлении нескольких перегрузок с одинаковым числом параметров необходимо применить атрибут Windows.Foundation.Metadata.DefaultOverloadAttribute только к одному из этих перегрузок. Эта перегрузка является единственной, которую можно вызвать из JavaScript. Например, в следующем коде перегрузка, которая принимает целое число в Visual Basic, является перегрузкой по умолчанию.

public string OverloadExample(string s)
{
    return s;
}

[Windows.Foundation.Metadata.DefaultOverload()]
public int OverloadExample(int x)
{
    return x;
}
Public Function OverloadExample(ByVal s As String) As String
    Return s
End Function

<Windows.Foundation.Metadata.DefaultOverload> _
Public Function OverloadExample(ByVal x As Integer) As Integer
    Return x
End Function

[ВАЖНО] JavaScript позволяет передать любое значение в OverloadExample и принудит значение к типу, который требуется параметру. Можно вызвать OverloadExample с "сорока двумя", "42" или 42.3, но все эти значения передаются перегрузке по умолчанию. Перегрузка по умолчанию в предыдущем примере возвращает значение 0, 42 и 42 соответственно.

Нельзя применить атрибут DefaultOverloadAttribute к конструкторам. Все конструкторы в классе должны иметь разные числа параметров.

Реализация IStringable

Начиная с Windows 8.1, среда выполнения Windows включает интерфейс IStringable, один метод IStringable.ToStringing, обеспечивает базовую поддержку форматирования, сравнимую с тем, что предоставлено Object.ToString. Если вы решили реализовать IStringable в общедоступном управляемом типе, экспортируемом в компоненте среда выполнения Windows, применяются следующие ограничения:

  • Интерфейс IStringable можно определить только в связи "класс реализует", например следующий код в C#:

    public class NewClass : IStringable
    

    Или следующий код Visual Basic:

    Public Class NewClass : Implements IStringable
    
  • Невозможно реализовать IStringable в интерфейсе.

  • Невозможно объявить параметр типа IStringable.

  • IStringable не может быть возвращаемым типом метода, свойства или поля.

  • Невозможно скрыть реализацию IStringable из базовых классов с помощью определения метода, например следующего:

    public class NewClass : IStringable
    {
       public new string ToString()
       {
          return "New ToString in NewClass";
       }
    }
    

    Вместо этого реализация IStringable.ToString должна всегда переопределять реализацию базового класса. Можно скрыть реализацию ToString только путем вызова его в экземпляре строго типизированного класса.

Примечание.

При различных условиях вызовы из машинного кода в управляемый тип, реализующий IStringable или скрывающий реализацию ToString , может привести к неожиданному поведению.

Асинхронные операции

Чтобы реализовать асинхронный метод в компоненте, добавьте "Async" в конец имени метода и верните один из интерфейсов среда выполнения Windows, представляющих асинхронные действия или операции: IAsyncActionWithProgress<TProgress>, IAsyncOperation TResult или IAsyncOperationWithProgress<<TResult>, TProgress>.

Для реализации асинхронного метода можно использовать задачи .NET (класс task и универсальный класс Task<TResult>). Необходимо вернуть задачу, представляющую текущую операцию, например задачу, возвращаемую из асинхронного метода, написанного в C# или Visual Basic, или задачу, возвращаемую из метода Task.Run . При использовании конструктора для создания задачи необходимо вызвать метод Task.Start перед возвратом.

Для метода, использующего (Awaitв Visual Basic), требуется await ключевое async слово (Asyncв Visual Basic). Если вы предоставляете такой метод из компонента среда выполнения Windows, примените async ключевое слово к делегату, который передается методу Run.

Для асинхронных действий и операций, которые не поддерживают отчеты об отмене или ходе выполнения, можно использовать метод расширения WindowsRuntimeSystemExtensions.AsAsyncAction или AsAsyncOperation<TResult> , чтобы упаковать задачу в соответствующий интерфейс. Например, следующий код реализует асинхронный метод с помощью метода Task.Run<TResult> для запуска задачи. Метод расширения AsAsyncOperation<TResult> возвращает задачу в виде среда выполнения Windows асинхронной операции.

public static IAsyncOperation<IList<string>> DownloadAsStringsAsync(string id)
{
    return Task.Run<IList<string>>(async () =>
    {
        var data = await DownloadDataAsync(id);
        return ExtractStrings(data);
    }).AsAsyncOperation();
}
Public Shared Function DownloadAsStringsAsync(ByVal id As String) _
     As IAsyncOperation(Of IList(Of String))

    Return Task.Run(Of IList(Of String))(
        Async Function()
            Dim data = Await DownloadDataAsync(id)
            Return ExtractStrings(data)
        End Function).AsAsyncOperation()
End Function

В следующем коде JavaScript показано, как метод можно вызывать с помощью объекта WinJS.Promise. Функция, передаваемая затем методу, выполняется при завершении асинхронного вызова. Параметр stringList содержит список строк, возвращаемых методом DownloadAsStringAsync , и функция выполняет любую обработку.

function asyncExample(id) {

    var result = SampleComponent.Example.downloadAsStringAsync(id).then(
        function (stringList) {
            // Place code that uses the returned list of strings here.
        });
}

Для асинхронных действий и операций, поддерживающих отчеты об отмене или ходе выполнения, используйте класс AsyncInfo для создания запущенной задачи и для подключения функций отчетов об отмене и ходе выполнения задачи с функциями отчетов об отмене и ходе выполнения соответствующего интерфейса среда выполнения Windows. Пример, поддерживающий отчеты об отмене и ходе выполнения, см. в пошаговом руководстве по созданию компонента C# или Visual Basic среда выполнения Windows и вызову из JavaScript.

Обратите внимание, что можно использовать методы класса AsyncInfo , даже если асинхронный метод не поддерживает отчеты об отмене или ходе выполнения. Если вы используете лямбда-функцию Visual Basic или анонимный метод C#, не предоставляйте параметры для интерфейса токена и IProgress<T> . Если вы используете лямбда-функцию C#, укажите параметр токена, но игнорируйте его. Предыдущий пример, который использовал метод TResult AsAsyncOperation<, выглядит следующим образом при использовании перегрузки метода AsyncInfo.Run<TResult>(Func<CancellationToken, Task<TResult>>).>

public static IAsyncOperation<IList<string>> DownloadAsStringsAsync(string id)
{
    return AsyncInfo.Run<IList<string>>(async (token) =>
    {
        var data = await DownloadDataAsync(id);
        return ExtractStrings(data);
    });
}
Public Shared Function DownloadAsStringsAsync(ByVal id As String) _
    As IAsyncOperation(Of IList(Of String))

    Return AsyncInfo.Run(Of IList(Of String))(
        Async Function()
            Dim data = Await DownloadDataAsync(id)
            Return ExtractStrings(data)
        End Function)
End Function

Если вы создаете асинхронный метод, который при необходимости поддерживает отчеты об отмене или ходе выполнения, попробуйте добавить перегрузки, которые не имеют параметров для маркера отмены или интерфейса IProgress<T> .

Создание исключений

Вы можете вызвать любой тип исключения, включенный в .NET для приложений Windows. Вы не можете объявить собственные общедоступные типы исключений в компоненте среда выполнения Windows, но вы можете объявить и создать недоступные типы.

Если компонент не обрабатывает исключение, соответствующее исключение возникает в коде, который называется компонентом. Способ отображения исключения вызывающей стороне зависит от того, как вызывающий язык поддерживает среда выполнения Windows.

  • В JavaScript исключение отображается как объект, в котором сообщение об исключении заменяется трассировкой стека. При отладке приложения в Visual Studio вы увидите исходный текст сообщения, отображаемый в диалоговом окне исключения отладчика, идентифицируемый как "Сведения о WinRT". Вы не можете получить доступ к исходному тексту сообщения из кода JavaScript.

    Совет. В настоящее время трассировка стека содержит тип управляемого исключения, но мы не рекомендуем анализировать трассировку для определения типа исключения. Вместо этого используйте значение HRESULT, как описано далее в этом разделе.

  • В C++исключение отображается как исключение платформы. Если свойство HResult управляемого исключения можно сопоставить с HRESULT определенного исключения платформы, используется конкретное исключение; в противном случае создается исключение Platform::COMException . Текст сообщения управляемого исключения недоступен для кода C++. Если возникло определенное исключение платформы, появится текст сообщения по умолчанию для этого типа исключения; в противном случае текст сообщения не отображается. См. исключения (C++/CX).

  • В C# или Visual Basic исключение является обычным управляемым исключением.

При возникновении исключения из компонента можно упростить обработку исключения вызывающего объекта JavaScript или C++ путем создания неогласного типа исключения, значение свойства HResult которого зависит от вашего компонента. HrESULT доступен вызывающей объекту JavaScript через свойство номера объекта исключения и вызывающий объект C++ через свойство COMException::HResult.

Примечание.

Используйте отрицательное значение для HRESULT. Положительное значение интерпретируется как успешное, и исключение не возникает в вызывающем объекте JavaScript или C++.

Объявление и создание событий

При объявлении типа для хранения данных для события наследуется от Object вместо EventArgs, так как EventArgs не является типом среда выполнения Windows. Используйте EventHandler<TEventArgs> в качестве типа события и используйте тип аргумента события в качестве аргумента универсального типа. Создайте событие так же, как и в приложении .NET.

Если компонент среда выполнения Windows используется из JavaScript или C++, событие следует шаблону событий среда выполнения Windows, которые ожидают эти языки. При использовании компонента из C# или Visual Basic событие отображается как обычное событие .NET. Пример представлен в пошаговом руководстве по созданию компонента C# или Visual Basic среда выполнения Windows и вызову его из JavaScript.

Если вы реализуете пользовательские методы доступа к событиям (объявите событие с помощью пользовательского ключевого слова в Visual Basic), необходимо следовать шаблону событий среда выполнения Windows в реализации. Сведения о пользовательских событиях и методах доступа к событиям см. в среда выполнения Windows компонентах. Обратите внимание, что при обработке события из кода C# или Visual Basic он по-прежнему представляется обычным событием .NET.

Следующие шаги

После создания компонента среда выполнения Windows для собственного использования вы можете обнаружить, что функциональные возможности, которые он инкапсулирует, полезны для других разработчиков. У вас есть два варианта упаковки компонента для распространения другим разработчикам. См. раздел "Распространение управляемого среда выполнения Windows компонента".

Дополнительные сведения о функциях языка Visual Basic и C# и поддержке .NET для среда выполнения Windows см. в документации по Visual Basic и C#.

Устранение неполадок

Симптом Средство
В приложении C++/WinRT, при работе с компонентом C# среды выполнения Windows, который использует XAML, компилятор создает ошибку в форме "'MyNamespace_XaRuntime componentmlTypeInfo': не является членом 'winrt::MyNamespace'"— где MyNamespace — имя пространства имен компонента среды выполнения Windows. В pch.hфайле используемого приложения C++/WinRT, добавьте #include <winrt/MyNamespace.MyNamespace_XamlTypeInfo.h>, заменив MyNamespace соответствующим образом.