Share via


Criando componentes de tempo de execução do Windows em C++

Este artigo mostra como usar C++ para criar um componente Tempo de Execução do Windows, que uma DLL que pode ser chamada por um aplicativo Windows Store criado usando JavaScript ou C#, Visual Basic ou C++. Aqui estão vários motivos para a criação desse componente:

  • Para obter uma vantagem de desempenho de C++ em operações complexas ou de computação intensa.

  • Para reutilizar o código já gravado e testado.

Quando você cria uma solução que contém um projeto em JavaScript ou .NET e um projeto de componente do Tempo de Execução do Windows, os arquivos do projeto em JavaScript e a DLL compilada são mesclados em um pacote, que você pode depurar localmente, no simulador, ou remotamente em um dispositivo com fio. Você também pode distribuir apenas o projeto do componente como uma extensão SDK. Para obter mais informações, consulte Como: criar um Kit de desenvolvimento de Software.

Em geral, ao codificar seu componente C++, você usa a biblioteca C++ normal e os tipos internos, exceto no limite da interface binária abstrata (ABI) onde você está transmitindo dados para e do código em outro pacote .winmd. Nesse local, use os tipos do Tempo de Execução do Windows e a sintaxe especial suportada pela linguagem Visual C++ para criação e manipulação desses tipos. Além de isso, no seu código Visual C++, use tipos como delegate e event para implementar eventos que podem ser acionados a partir do seu componente e tratados em JavaScript, Visual Basic ou C#. Para obter mais informações sobre a nova sintaxe Visual C++, consulte Referência de linguagem do Visual C++ (C + + / CX).

Regras de uso de maiúsculas e de nomenclatura

JavaScript

O JavaScript diferencia maiúsculas de minúsculas. Portanto, você deve seguir as convenções de uso de maiúsculas:

  • Ao fazer referência a namespaces e classes C++, use a mesma regra de maiúsculas usada no lado do C++.

  • Ao chamar métodos, use a concatenação com maiúsculas e minúsculas mesmo se o nome do método tiver a primeira letra da palavra em maiúscula no lado do C++. Por exemplo, o método GetDate() C++ deve ser chamado a partir de JavaScript como getDate().

  • Um nome de classe ativável e um nome de namespace não podem conter caracteres UNICODE.

.NET

Linguagens .NET seguem as regras normais de uso de maiúsculas.

Criando uma instância do objeto

Somente tipos do Tempo de Execução do Windows podem ser transmitidos pelo limite da ABI. O compilador gerará um erro se o componente tiver um tipo como std::wstring como um tipo de retorno ou parâmetro em um método público. Os tipos internos Extensões de componentes Visual C++ (C++/CX) incluem os escalares normais como int e double, e também seus equivalentes de typedef int32, float64, e assim por diante. Para obter mais informações, consulte Digite System (C + + / CX).

C++

// ref class definition in C++
public ref class SampleRefClass sealed
{
    // Class members...
    // #include <valarray>
public:
    double LogCalc(double input)
    {
        // Use C++ standard library as usual.
        return std::log(input); 
    }
};

JavaScript

//Instantiation in JavaScript (requires "Add reference > Project reference")
var nativeObject = new CppComponent.SampleRefClass();

.NET

//Call a method and display result in a XAML TextBlock
var num = nativeObject.LogCalc(21.5);
ResultText.Text = num.ToString();

Tipos C++ internos, tipos de biblioteca e tipos do Tempo de Execução do Windows

Classe ativável (também conhecida como classe de referência) é uma classe que pode ser instanciada a partir de outra linguagem como JavaScript, C# ou Visual Basic. Para ser consumível a partir de outra linguagem, um componente deve conter, pelo menos, uma classe ativável.

Um componente do Tempo de Execução do Windows pode conter várias classes ativáveis públicas, bem como classes adicionais conhecidas do componente apenas internamente. Todas as classes públicas devem residir no mesmo namespace raiz que tenha o mesmo nome que o arquivo de metadados do componente. Aplique o atributo [WebHostHidden] aos tipos C++ que não devem ser visíveis ao JavaScript.

O código do cliente cria uma instância do componente usando a palavra-chave new (New em Visual Basic) como para qualquer classe.

uma classe ativável deve ser declarada como public ref class sealed. A palavra-chave da classe ref diz para o compilador criar a classe como tipo compatível com Tempo de Execução do Windows, e a palavra-chave sealed especifica que a classe não pode ser herdada. O Tempo de Execução do Windows não tem suporte atualmente para um modelo de herança generalizado; um modelo de herança limitado oferece suporte à criação de controles XAML personalizados. Para obter mais informações, consulte Classes de referência e estruturas (C++/CX).

Para C++, todos os primitivos numéricos são definidos no namespace padrão. O Platform Namespace contém classes C++ específicas do tipo de sistema Tempo de Execução do Windows. Eles incluem Classe Platform::String e Classe Platform::Object. Os tipos de coleção concretos, como Classe Platform::Collections::Map e Classe Platform::Collections::Vector, são definidos no Namespace Platform::Collections. As interfaces públicas que esses tipos implementam são definidas em Namespace Windows::Foundation::Collections (C + + / CX). São esses tipos de interface que são consumidos por JavaScript, C# e Visual Basic. Para obter mais informações, consulte Digite System (C + + / CX).

Hh441569.collapse_all(pt-br,VS.110).gifMétodo que retorna um valor de tipo interno

C++

    // #include <valarray>
public:
    double LogCalc(double input)
    {
        // Use C++ standard library as usual.
        return std::log(input); 
    }

JavaScript

//Call a method
var nativeObject = new CppComponent.SampleRefClass;
var num = nativeObject.logCalc(21.5);
document.getElementById('P2').innerHTML = num;

.NET

Hh441569.collapse_all(pt-br,VS.110).gifMétodo que retorna uma estrutura de valor personalizada

value struct é um objeto de dados antigo simples que pode conter campos que são públicos por padrão. value struct é transmitida por valor.

C++

namespace CppComponent
{
    // Custom struct
    public value struct PlayerData
    {
        Platform::String^ Name;
        int Number;
        double ScoringAverage;
    };

    public ref class Player sealed
    {
    private:
        PlayerData m_player;
    public:
        property PlayerData PlayerStats 
        {
            PlayerData get(){ return m_player; }
            void set(PlayerData data) {m_player = data;}
        }
    };
}

JavaScript

Para passar estruturas de valor definidas pelo usuário por meio da ABI, defina um objeto JavaScript que tenha os mesmos membros da estrutura de valor definida em C++. Em seguida, você poderá transmitir esse objeto como um argumento para o método de C++ de forma que o objeto seja convertido implicitamente no tipo C++.

// Get and set the value struct
function GetAndSetPlayerData() {
    // Create an object to pass to C++
    var myData =
        { name: "Bob Homer", number: 12, scoringAverage: .357 };
    var nativeObject = new CppComponent.Player();
    nativeObject.playerStats = myData;

    // Retrieve C++ value struct into new JavaScript object
    var myData2 = nativeObject.playerStats;
    document.getElementById('P3').innerHTML = myData.name + " , " + myData.number + " , " + myData.scoringAverage.toPrecision(3);
}

Outra abordagem é definir uma classe que implemente IPropertySet (não mostrado).

C#

Em linguagens .NET, você cria apenas uma variável do tipo que é definido no componente C++.

private void GetAndSetPlayerData()
{
    // Create a ref class
    var player = new CppComponent.Player();

    // Create a variable of a value struct
    // type that is defined in C++
    CppComponent.PlayerData myPlayer;
    myPlayer.Name = "Babe Ruth";
    myPlayer.Number = 12;
    myPlayer.ScoringAverage = .398;

    // Set the property
    player.PlayerStats = myPlayer;

    // Get the property and store it in a new variable
    CppComponent.PlayerData myPlayer2 = player.PlayerStats;
    ResultText.Text += myPlayer.Name + " , " + myPlayer.Number.ToString() +
        " , " + myPlayer.ScoringAverage.ToString();
}

Hh441569.collapse_all(pt-br,VS.110).gifMétodos sobrecarregados

Uma classe de referência pública C++ pode conter métodos sobrecarregados, mas o JavaScript limitou a capacidade de diferenciar métodos sobrecarregados. Por exemplo, ela pode determinar a diferença entre estas assinaturas:

public ref class NumberClass sealed 
{
public:
    int GetNumber(int i);
    int GetNumber(int i, Platform::String^ str);
    double GetNumber(int i, MyData^ d);
};

Mas não pode determinar a diferença entre estas assinaturas:

int GetNumber(int i);
double GetNumber(double d);

nem destas:

Em casos ambíguos, você pode garantir que o JavaScript sempre chame uma sobrecarga específica aplicando o atributo Windows::Foundation::Metadata::DefaultOverload à assinatura do método no arquivo de cabeçalho.

Esse JavaScript sempre chama a sobrecarga atribuída:

var nativeObject = new CppComponent.NumberClass();
var num = nativeObject.getNumber(9);
document.getElementById('P4').innerHTML = num;

.NET

Linguagens .NET reconhecem sobrecargas em uma classe de referência C++ assim como em qualquer classe do .NET Framework.

DateTime

No Tempo de Execução do Windows, um objeto Windows::Foundation::DateTime é apenas um inteiro de 64 bits com sinal que representa o número de 100 intervalos de nanossegundo antes ou depois de 1º de janeiro de 1601. Não há nenhum método em um objeto Windows:Foundation::DateTime. Em vez de isso, cada linguagem projeta o DateTime da maneira que é nativa ao idioma: o objeto Date em JavaScript e os tipos System.DateTime e System.DateTimeOffset no .NET Framework.

C++

public  ref class MyDateClass sealed
{
public:
    property Windows::Foundation::DateTime TimeStamp;
    void SetTime(Windows::Foundation::DateTime dt)
    {
        auto cal = ref new Windows::Globalization::Calendar();
        cal->SetDateTime(dt);
        TimeStamp = cal->GetDateTime(); // or TimeStamp = dt;
    }
};

JavaScript

Quando você transmite um valor DateTime de C++ para JavaScript, o JavaScript o aceita como objeto Date e o exibe por padrão como uma cadeia de caracteres de data em formato longo.

function SetAndGetDate() {
    var nativeObject = new CppComponent.MyDateClass();

    var myDate = new Date(1956, 4, 21);
    nativeObject.setTime(myDate);

    var myDate2 = nativeObject.timeStamp;

    //prints long form date string
    document.getElementById('P5').innerHTML = myDate2;

}

.NET

Quando uma linguagem .NET transmite um System.DateTime passa para um componente C++, o método o aceita como um Windows::Foundation::DateTime. Quando o componente transmite um Windows::Foundation::DateTime para um método do .NET Framework, o método do Framework o aceita como DateTimeOffset.

private void DateTimeExample()
{
    // Pass a System.DateTime to a C++ method
    // that takes a Windows::Foundation::DateTime
    DateTime dt = DateTime.Now;
    var nativeObject = new CppComponent.MyDateClass();
    nativeObject.SetTime(dt);

    // Retrieve a Windows::Foundation::DateTime as a
    // System.DateTimeOffset
    DateTimeOffset myDate = nativeObject.TimeStamp;

    // Print the long-form date string
    ResultText.Text += myDate.ToString();
}

Coleções e matrizes

Coleções são sempre transmitidas pelo limite da ABI como identificadores de tipos do Tempo de Execução do Windows, como Windows::Foundation::Collections::IVector^ e Windows::Foundation::Collections::IMap^. Por exemplo, se você retornar um identificador para Platform::Collections::Map, ele será convertido, de forma implícita, em Windows::Foundation::Collections::IMap^. As interfaces de coleção são definidas em um namespace separado das classes C++ que fornecem as implementações concretas. JavaScript e linguagens .NET consomem interfaces. Para obter mais informações, consulte Coleções (C + + / CX) e Matriz e WriteOnlyArray (C + + / CX).

Hh441569.collapse_all(pt-br,VS.110).gifTransmitindo o IVector

C++

// Windows::Foundation::Collections::IVector across the ABI.
//#include <algorithm>
//#include <collection.h>
Windows::Foundation::Collections::IVector<int>^ SortVector(Windows::Foundation::Collections::IVector<int>^ vec)
{
    std::sort(begin(vec), end(vec));
    return vec;
}

JavaScript


var nativeObject = new CppComponent.CollectionExample();
// Call the method to sort an integer array
var inVector = [14, 12, 45, 89, 23];
var outVector = nativeObject.sortVector(inVector);
var result = "Sorted vector to array:";
for (var i = 0; i < outVector.length; i++)
{
    outVector[i];
    result += outVector[i].toString() + ",";
}
document.getElementById('P6').innerHTML = result;

.NET

As linguagens .NET veem o IVector<T> como IList<T>.

        private void SortListItems()
        {
            IList<int> myList = new List<int>();
            myList.Add(5);
            myList.Add(9);
            myList.Add(17);
            myList.Add(2);

            var nativeObject = new CppComponent.CollectionExample();
            IList<int> mySortedList = nativeObject.SortVector(myList);

            foreach (var item in mySortedList)
            {
                ResultText.Text += " " + item.ToString();
            }
        }

Hh441569.collapse_all(pt-br,VS.110).gifTransmitindo o IMap

C++

// #include <map>
//#include <collection.h>
Windows::Foundation::Collections::IMap<int, Platform::String^> ^GetMap(void)
{    
    Windows::Foundation::Collections::IMap<int, Platform::String^> ^ret = 
        ref new Platform::Collections::Map<int, Platform::String^>;
    ret->Insert(1, "One ");
    ret->Insert(2, "Two ");
    ret->Insert(3, "Three ");
    ret->Insert(4, "Four ");
    ret->Insert(5, "Five ");
    return ret;
}

JavaScript

// Call the method to get the map
var outputMap = nativeObject.getMap();
var mStr = "Map result:" + outputMap.lookup(1) + outputMap.lookup(2)
    + outputMap.lookup(3) + outputMap.lookup(4) + outputMap.lookup(5);
document.getElementById('P7').innerHTML = mStr;

.NET

As linguagens .NET veem o IMap como IDictionary<K,V>.

private void GetDictionary()
{
    var nativeObject = new CppComponent.CollectionExample();
    IDictionary<int, string> d = nativeObject.GetMap();
    ResultText.Text += d[2].ToString();
}

Properties

Uma classe de referência pública no Extensões de componentes Visual C++ expõe membros de dados públicos como propriedades, usando a palavra-chave property. O conceito é idêntico às propriedades do .NET Framework. Uma propriedade trivial é semelhante a um membro de dados porque os seus recursos são implícitos. Uma propriedade não trivial tem acessadores get e set e uma variável privada nomeada que é o “armazenamento de backup” do valor. Neste exemplo, o membro privado variable _propertyAValue é o armazenamento de backup de PropertyA. Uma propriedade pode acionar um evento quando seu valor é alterado, e um aplicativo cliente pode se registrar para receber esse evento.

C++


    //Properties
    public delegate void PropertyChangedHandler(Platform::Object^ sender, int arg);
    public ref class PropertyExample  sealed
    {
    public:
        PropertyExample(){}

        // Event that is fired when PropetyA changes
        event PropertyChangedHandler^ PropertyChangedEvent;

        // Property that has custom setter/getter
        property int PropertyA
        {
            int get() { return m_propertyAValue; }
            void set(int propertyAValue) 
            {
                if (propertyAValue != m_propertyAValue)
                {
                    m_propertyAValue = propertyAValue;
                    // Fire event. (See event example below.)
                    PropertyChangedEvent(this, propertyAValue);
                }
            }
        }

        // Trivial get/set property that has a compiler-generated backing store.
        property Platform::String^ PropertyB;

    private:
        // Backing store for propertyA.
        int m_propertyAValue;
    };


JavaScript

var nativeObject = new CppComponent.PropertyExample();
var propValue = nativeObject.propertyA;
document.getElementById('P8').innerHTML = propValue;

//Set the string property
nativeObject.propertyB = "What is the meaning of the universe?";
document.getElementById('P9').innerHTML += nativeObject.propertyB;

.NET

As linguagens .NET acessam as propriedades em um objeto C++ nativo assim como fariam em um objeto do .NET Framework.

private void GetAProperty()
{
    // Get the value of the integer property
    // Instantiate the C++ object
    var obj = new CppComponent.PropertyExample();

    // Get an integer property
    var propValue = obj.PropertyA;
    ResultText.Text += propValue.ToString();

    // Set a string property
    obj.PropertyB = " What is the meaning of the universe?";
    ResultText.Text += obj.PropertyB;

}

Representantes e eventos

delegate é um tipo do Tempo de Execução do Windows que representa um objeto de função. Você pode usar representantes em conjunto com eventos, retornos de chamada e chamadas de método assíncrono a fim de especificar uma ação a ser executada posteriormente. Como um objeto de função, o representante fornece a segurança de tipo ativando o compilador para verificar o tipo de retorno e os tipos de parâmetro da função. A declaração de um representante é semelhante a uma assinatura de função, a implementação é semelhante a uma definição de classe e a invocação é semelhante a uma invocação de função.

Hh441569.collapse_all(pt-br,VS.110).gifAdicionando um ouvinte de eventos

Você pode usar a palavra-chave event para declarar um membro público de um tipo de representante especificado. O código do cliente assina o evento usando os mecanismos padrão fornecidos na linguagem específica.

C++

public:
    event SomeHandler^ someEvent;

Esse exemplo usa o mesmo código C++ que a seção de propriedades anterior.

JavaScript

function Button_Click() {
    var nativeObj = new CppComponent.PropertyExample();
    // Define an event handler method
    var singlecasthandler = function (ev) {
        document.getElementById('P10').innerHTML = "The button was clicked and the value is " + ev;
    };

    // Subscribe to the event
    nativeObj.onpropertychangedevent = singlecasthandler;

    // Set the value of the property and fire the event
    var propValue = 21;
    nativeObj.propertyA = 2 * propValue;

}

.NET

Nas linguagens .NET, assinar um evento em um componente C++ é o mesmo que assinar um evento em uma classe do .NET Framework:

//Subscribe to event and call method that causes it to be fired.
private void TestMethod()
{
    var objWithEvent = new CppComponent.PropertyExample();
    objWithEvent.PropertyChangedEvent += objWithEvent_PropertyChangedEvent;

    objWithEvent.PropertyA = 42;
}

//Event handler method
private void objWithEvent_PropertyChangedEvent(object __param0, int __param1)
{
    ResultText.Text = "the event was fired and the result is " +
         __param1.ToString();
}

Hh441569.collapse_all(pt-br,VS.110).gifAdicionando vários ouvintes de eventos para um evento

O JavaScript tem um método addEventListener que permite que vários identificadores assinem um único evento.

C++

public delegate void SomeHandler(Platform::String^ str);

public ref class LangSample sealed
{
public:
    event SomeHandler^ someEvent;
    property Platform::String^ PropertyA;

    // Method that fires an event
    void FireEvent(Platform::String^ str)
    {
        someEvent(Platform::String::Concat(str, PropertyA->ToString()));
    }
    //...
};

JavaScript

// Add two event handlers
var multicast1 = function (ev) {
    document.getElementById('P11').innerHTML = "Handler 1: " + ev.target;
};
var multicast2 = function (ev) {
    document.getElementById('P12').innerHTML = "Handler 2: " + ev.target;
};

var nativeObject = new CppComponent.LangSample();
//Subscribe to the same event
nativeObject.addEventListener("someevent", multicast1);
nativeObject.addEventListener("someevent", multicast2);

nativeObject.propertyA = "42";

// This method should fire an event
nativeObject.fireEvent("The answer is ");

.NET

Em C#, qualquer número de manipuladores de eventos pode assinar o evento usando o operador += conforme mostrado no exemplo anterior.

Enums

Um enum Tempo de Execução do Windows em C++ é declarado usando public class enum; é similar a um enum de escopo em C++ padrão.

C++

public enum class Direction {North, South, East, West};

public ref class EnumExampleClass sealed
{
public:
    property Direction CurrentDirection
    {
        Direction  get(){return m_direction; }
    }

private:
    Direction m_direction;
};

JavaScript

Os valores enum são transmitidos entre C++ e JavaScript como valores inteiros. É possível declarar um objeto de JavaScript que contém os mesmos valores nomeados que o enum C++ e usá-lo da forma a seguir.

var Direction = { 0: "North", 1: "South", 2: "East", 3: "West" };
//. . .

var nativeObject = new CppComponent.EnumExampleClass();
var curDirection = nativeObject.currentDirection;
document.getElementById('P13').innerHTML =
Direction[curDirection];

.NET

C# e Visual Basic têm suporte a linguagem para enums. Essas linguagens veem uma classe de enum pública C++ como veriam um enum do .NET Framework.

Métodos assíncronos

Para consumir os métodos assíncronos expostos por outros objetos do Tempo de Execução do Windows, use a classe (Runtime de simultaneidade) de tarefas. Para obter mais informações, consulte Asychronous Programming in C++ e Paralelismo de tarefa (tempo de execução de simultaneidade).

Para implementar métodos assíncronos em C++, use a função create_async definida em ppltasks.h. Para obter mais informações, consulte Criando operações assíncronas n C++ para aplicativos da Windows Store. Para obter um exemplo, consulte Passo a passo: Criando um componente básico do Runtime do Windows em C++ e chamá-lo a partir do JavaScript. As linguagens .NET consomem métodos assíncronos C++ assim como qualquer método assíncrono definido no. NET Framework.

Exceções

Você pode lançar qualquer tipo de exceção definido pelo Tempo de Execução do Windows. Não é possível fazer a derivação de tipos personalizados de nenhum tipo de exceção do Tempo de Execução do Windows. No entanto, é possível lançar COMException e fornecer um HRESULT personalizado que pode ser acessado pelo código que captura a exceção. Não é possível especificar uma mensagem personalizada em uma COMException.

Dicas de depuração

Quando você depura uma solução JavaScript que tenha uma DLL de componente, pode definir o depurador de forma que ele percorra o script ou o código nativo no componente, mas não ambos ao mesmo tempo. Para alterar a configuração, selecione o nó de projeto em JavaScript em Gerenciador de Soluções e escolha Propriedades, Depuração, Tipo de Depurador.

Certifique-se de selecionar recursos apropriados no designer de pacote. Por exemplo, se você estiver tentando abrir um arquivo usando as APIs do Tempo de Execução do Windows, certifique-se de marcar a caixa de seleção Acesso à Biblioteca de Documentos no painel Recursos do designer de pacote.

Se o seu código JavaScript não reconhecer as propriedades públicas nem os métodos no componente, certifique-se de que você esteja usando a concatenação com maiúsculas e minúsculas em JavaScript. Por exemplo, o método LogCalc C++ deve ser referenciado como logCalc em JavaScript.

Se você remover um projeto do componente C++ do Tempo de Execução do Windows de uma solução, também deverá remover manualmente a referência ao projeto do projeto em JavaScript. Caso contrário, as operações de depuração e compilação subsequentes não serão executadas. Se necessário, você poderá adicionar uma referência a assembly à DLL.

Consulte também

Conceitos

Passo a passo: Criando um componente básico do Runtime do Windows em C++ e chamá-lo a partir do JavaScript

Outros recursos

Roadmap for Windows Store apps using C++

Desenvolvendo o otimizador de viagem do Mapas Bing, um aplicativo da Windows Store em JavaScript e C++