Compartilhar via


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

Essa explicação passo a passo mostra como criar uma DLL básica de componente do Tempo de Execução do Windows acessível de JavaScript, C# ou Visual Basic. Antes de iniciar esta explicação passo a passo, verifique se você entende conceitos como a interface binária abstrata (ABI) classes de referência e as extensões de componentes Visual C++ que facilitam o trabalho com classes de referência. Para obter mais informações, consulte Criando componentes de tempo de execução do Windows em C++ e Referência de linguagem do Visual C++ (C + + / CX).

Criando o projeto de componente C++

Neste exemplo, criaremos o projeto do componente primeiro, mas você pode criar o projeto em JavaScript primeiro. A ordem não importa.

Observe que a classe principal do componente contém exemplos de definições de propriedade e de método e uma declaração de evento. Eles são fornecidos apenas para mostrar como é feito. Eles não são necessários e, neste exemplo, substituiremos todo o código gerado pelo nosso próprio código.

Para criar o projeto de componente C++

  1. Na barra de menus do Visual Studio, escolha Arquivo, Novo, Projeto.

  2. Na caixa de diálogo Novo Projeto, no painel esquerdo, expanda Visual C++ e selecione o nó de aplicativos Windows Store.

  3. No painel central, selecione Componente do Tempo de Execução do Windows e, em seguida, forneça o nome CppLib ao projeto.

  4. Escolha o botão OK.

Adicionando uma classe ativável ao componente

Uma classe ativável é uma classe que o JavaScript pode criar usando uma expressão new. No seu componente, declare-o como public ref class sealed. Na verdade, os arquivos Class1.h e .cpp já têm uma classe de referência. Você pode alterar o nome, mas nesse exemplo, usaremos o nome padrão Class1. Você pode definir classes de referência adicionais no seu componente, se forem necessárias. Para obter mais informações sobre as classes ref, consulte Digite System (C + + / CX).

Adicionando o #include necessário e usando instruções

Para adicionar o #include necessário e usar instruções

  1. Adicione estas políticas de #include à Class1.h:

    #include <collection.h>
    #include <amp.h>
    #include <amp_math.h>
    
  2. Adicione estas políticas de #include à Class1.cpp:

    #include <ppltasks.h>
    #include <concurrent_vector.h>
    
  3. Adicione estas instruções de uso à Class1.cpp:

    using namespace concurrency;
    using namespace Platform::Collections;
    using namespace Windows::Foundation::Collections;
    using namespace Windows::Foundation;
    using namespace Windows::UI::Core;
    

Adicionando um método público síncrono à classe

Métodos que são executados rapidamente podem ser implementados como métodos síncronos. Eles são executados no mesmo segmento que o chamador JavaScript. Todos os tipos de parâmetro e valores de retorno em métodos públicos devem ser tipos compatíveis com o Tempo de Execução do Windows.

Para adicionar um método público síncrono à classe

  1. Adicione estas declarações à classe em Class1.h:

    public:
    
        Windows::Foundation::Collections::IVector<double>^  ComputeResult(double input);
        Windows::Foundation::IAsyncOperationWithProgress<Windows::Foundation::Collections::IVector<int>^, double>^
            Class1::GetPrimesOrdered(int first, int last);
        Windows::Foundation::IAsyncActionWithProgress<double>^ Class1::GetPrimesUnordered(int first, int last);
        event PrimeFoundHandler^ primeFoundEvent;
    private:
        bool is_prime(int n);
        Windows::UI::Core::CoreDispatcher^ m_dispatcher;
    private:
        void ComputeResultImpl(concurrency::array_view<float, 1>&);
    
  2. Adicione estas implementações à Class1.cpp:

    void Class1::ComputeResultImpl(array_view<float, 1>& logs) 
    {
        parallel_for_each(
            logs.extent,
             [=] (index<1> idx) restrict(amp) {
                logs[idx] = concurrency::fast_math::log10(logs[idx]);
            }
        );
    }
    //Public API
    IVector<double>^ Class1::ComputeResult(double input)
    {
       // Implement your function in ISO C++ or
       // call into your C++ lib or DLL here. This example uses AMP.
       float numbers[] = { 1.0, 10.0, 60.0, 100.0, 600.0, 10000.0 };
        array_view<float, 1> logs(6, numbers);
       ComputeResultImpl(logs);
    
       // Return a Windows Runtime-compatible type across the ABI
       auto res = ref new Vector<double>();
       int len = safe_cast<int>(logs.extent.size());
       for(int i = 0; i < len; i++)
       {      
          res->Append(logs[i]);
       }
       return res;
    }
    

Adicionando métodos públicos assíncrono à classe

Implemente métodos que podem demorar algum tempo para serem executados como métodos assíncronos. Um método assíncrono é executado em um segmento de segundo plano e não bloqueia o segmento JavaScript. Crie o método público async usando o método create_async internamente. O JavaScript chama o método como qualquer promessa. Como com um método síncrono, todos os tipos de parâmetro devem ser tipos compatíveis com o Tempo de Execução do Windows. Em um método assíncrono, o tipo de retorno deve ser um destes:

  • IAsyncAction para os métodos assíncronos que não retornam nenhum valor e não fornecem informações sobre o andamento.

  • IAsyncActionWithProgress para os métodos assíncronos que não retornam nenhum valor, mas fornecem informações sobre o andamento.

  • IAsyncOperation<T> para os métodos assíncronos que retornam um valor T, mas não fornecem informações sobre o andamento.

  • IAsyncOperationWithProgress<T> para os métodos assíncronos que retornam um valor T e também fornecem informações sobre o andamento.

Para obter mais informações, consulte Criando operações assíncronas n C++ para aplicativos da Windows Store.

Para adicionar um método assíncrono que retorna um resultado e fornece informações sobre o andamento

  1. Adicione estas declarações à classe em Class1.h:

        Windows::Foundation::IAsyncOperationWithProgress<Windows::Foundation::Collections::IVector<int>^, double>^
            Class1::GetPrimesOrdered(int first, int last);
        Windows::Foundation::IAsyncActionWithProgress<double>^ Class1::GetPrimesUnordered(int first, int last);
        event PrimeFoundHandler^ primeFoundEvent;
    private:
        bool is_prime(int n);
    
  2. Adicione estas implementações à Class1.cpp:

    // Determines whether the input value is prime.
    bool Class1::is_prime(int n)
    {
       if (n < 2)
          return false;
       for (int i = 2; i < n; ++i)
       {
          if ((n % i) == 0)
             return false;
       }
       return true;
    }
    
    // This method computes all primes, orders them, then returns the ordered results.
    IAsyncOperationWithProgress<IVector<int>^, double>^ Class1::GetPrimesOrdered(int first, int last)
    {
        return create_async([this, first, last]
        (progress_reporter<double> reporter) -> IVector<int>^ {
            // Ensure that the input values are in range.
            if (first < 0 || last < 0) {
                throw ref new InvalidArgumentException();
            }
            // Perform the computation in parallel.
            concurrent_vector<int> primes;
            long operation = 0;
            long range = last - first + 1;
            double lastPercent = 0.0;
    
            parallel_for(first, last + 1, [this, &primes, &operation, 
                range, &lastPercent, reporter](int n) {
                    // Report progress message.
                    double progress = 100.0 * InterlockedIncrement(&operation) / range;
                    if (progress >= lastPercent)
                    {
                        reporter.report(progress);
                        lastPercent += 1.0;
                    }
    
                    // If the value is prime, add it to the local vector.
                    if (is_prime(n)) {
                        primes.push_back(n);
                    }
            });
    
    
            // Sort the results.
            std::sort(begin(primes), end(primes), std::less<int>());
    
            // Copy the results to an IVector object. The IVector
            // interface makes collections of data available to other
            // Windows Runtime components.
            IVector<int>^ results = ref new Vector<int>();
            std::for_each(std::begin(primes), std::end(primes), [&results](int prime) {
                results->Append(prime);
            });
            reporter.report(100.0);
            return results;
        });
    }
    

Para adicionar um método assíncrono que dispara eventos e fornece informações sobre o andamento

  1. Adicione estas declarações públicas à classe em Class1.h:

    Windows::Foundation::IAsyncActionWithProgress<double>^ Class1::GetPrimesUnordered(int first, int last);
    event PrimeFoundHandler^ primeFoundEvent;
    

    Adicione esta declaração privada:

    Windows::UI::Core::CoreDispatcher^ m_dispatcher;
    

    Adicione o seguinte representante no escopo de namespace à Class1.h:

    public delegate void PrimeFoundHandler(int i);
    

    Usamos este objeto para prepara os eventos da função paralela de volta para o segmento da interface do usuário.

  2. Adicione estas implementações à Class1.cpp:

    // This method returns no value. Instead, it fires an event each time a prime is found, and transfers the prime through the event.
    IAsyncActionWithProgress<double>^ Class1::GetPrimesUnordered(int first, int last)
    {
    
       auto window = Windows::UI::Core::CoreWindow::GetForCurrentThread();
       m_dispatcher = window->Dispatcher;
    
        return create_async([this, first, last](progress_reporter<double> reporter) {
            // Ensure that the input values are in range.
            if (first < 0 || last < 0) {
                throw ref new InvalidArgumentException();
            }
            // Perform the computation in parallel.
            concurrent_vector<int> primes;
            long operation = 0;
            long range = last - first + 1;
            double lastPercent = 0.0;
    
            parallel_for(first, last + 1, [this, &primes, &operation, range, &lastPercent, reporter](int n) 
            {
                // Report progress message.
                double progress = 100.0 * InterlockedIncrement(&operation) / range;
                if (progress >= lastPercent)
                {
                    reporter.report(progress);
                    lastPercent += 1.0;
                }
    
                // If the value is prime, add it to the local vector.
                if (is_prime(n))
                {
                    primes.push_back(n);
                    m_dispatcher->RunAsync( CoreDispatcherPriority::Normal,
                         ref new DispatchedHandler([this, n]() 
                           {
                                 this->primeFoundEvent(n);
                           }, Platform::CallbackContext::Any));
    
                }
            });
            reporter.report(100.0);
        });
    }
    
    

Criando um projeto em JavaScript

Para criar um projeto em JavaScript

  1. No Gerenciador de Soluções, no menu de atalhos do nó Solução, escolha Adicionar, Novo Projeto.

  2. Expanda JavaScript e escolha Aplicativo em Branco.

  3. Aceite o nome padrão de App1 escolhendo o botão OK.

  4. Clique com o botão direito do mouse no nó do projeto App1 e escolha Definir como Projeto de Inicialização.

  5. Adicione uma referência de projeto a CppLib:

    1. No menu de atalhos do nó Referências, escolha Adicionar Referência.

    2. No painel esquerdo da caixa de diálogo Gerenciador de Referências, selecione Solução e Projetos.

    3. No painel central, selecione CppLib e escolha o botão OK.

Adicionando HTML que invoca manipuladores de eventos JavaScript

Cole este HTML no nó <body> da página default.html:

<div id="LogButtonDiv">
     <button id="logButton" onclick="LogButton_Click()">Logarithms using AMP</button>
 </div>
 <div id="LogResultDiv">
     <p id="logResult"></p>
 </div>
 <div id="OrderedPrimeButtonDiv">
     <button id="orderedPrimeButton" onclick="ButtonOrdered_Click()">Primes using parallel_for with sort</button>
 </div>
 <div id="OrderedPrimeProgress">
     <progress id="OrderedPrimesProgressBar" value="0" max="100"></progress>
 </div>
 <div id="OrderedPrimeResultDiv">
     <p id="orderedPrimes">
         Primes found (ordered):
     </p>
 </div>
 <div id="UnorderedPrimeButtonDiv">
     <button id="ButtonUnordered" onclick="ButtonUnordered_Click()">Primes returned as they are produced.</button>
 </div>
 <div id="UnorderedPrimeDiv">
     <progress id="UnorderedPrimesProgressBar" value="0" max="100"></progress>
 </div>
 <div id="UnorderedPrime">
     <p id="unorderedPrimes">
         Primes found (unordered):
     </p>
 </div>

Adicionando os estilos

Remova o estilo body e adicione estes estilos a default.css:


#LogButtonDiv { background: maroon; border: orange solid 1px; -ms-grid-row: 1; /* default is 1 */; -ms-grid-column: 1; /* default is 1 */ } #LogResultDiv { background: black; border: red solid 1px; -ms-grid-row: 1; -ms-grid-column: 2; } #UnorderedPrimeButtonDiv, #OrderedPrimeButtonDiv { background: green; border: orange solid 1px; -ms-grid-row: 2; -ms-grid-column:1; } #UnorderedPrimeDiv, #OrderedPrimeDiv { background: maroon; border: red solid 1px; -ms-grid-row: 2; -ms-grid-column:1; } #UnorderedPrimeProgress, #OrderedPrimeProgress { background: lightgray; border: red solid 1px; -ms-grid-row: 2; -ms-grid-column: 2; -ms-grid-column-span: 2; } #UnorderedPrimeResult, #OrderedPrimeResult { background: black; border: red solid 1px; -ms-grid-row: 2; -ms-grid-column: 3; }

Adicionando manipuladores de eventos de JavaScript que chamam na DLL do componente

Adicione as seguintes funções ao final do arquivo default.js. Essas funções são chamadas quando os botões na página principal são escolhidos. Observe como o JavaScript ativa a classe C++ e, em seguida, chama seus métodos e usa os valores de retorno para preencher os rótulos HTML.

var nativeObject = new cpplib.Class1(); function LogButton_Click() { var val = nativeObject.computeResult(0); var result = ""; for (i = 0; i < val.length; i++) { result += val[i] + "<br/>"; } document.getElementById('logResult').innerHTML = result; } function ButtonOrdered_Click() { document.getElementById('orderedPrimes').innerHTML = "Primes found (ordered): "; var asyncResult = nativeObject.getPrimesOrdered(2, 1000).then( function (v) { for (var i = 0; i < v.length; i++) document.getElementById('orderedPrimes').innerHTML += v[i] + " "; }, function (error) { document.getElementById('orderedPrimes').innerHTML += " " + error.description; }, function (p) { var progressBar = document.getElementById( "OrderedPrimesProgressBar"); progressBar.value = p; }); } function ButtonUnordered_Click() { document.getElementById('unorderedPrimes').innerHTML = "Primes found (unordered): "; nativeObject.onprimefoundevent = handler_unordered; var asyncResult = nativeObject.getPrimesUnordered(2, 1000).then( function () { }, function (error) { document.getElementById("unorderedPrimes").innerHTML += " " + error.description; }, function (p) { var progressBar = document.getElementById("UnorderedPrimesProgressBar"); progressBar.value = p; }); } var handler_unordered = function (n) { document.getElementById('unorderedPrimes').innerHTML += n.target.toString() + " "; };

Executando o aplicativo

Pressione F5.

Inspecionando o componente no Pesquisador de Objetos (opcional)

No Pesquisador de Objetos, você pode inspecionar todos os tipos do Tempo de Execução do Windows que são definidos em arquivos .winmd. Isso inclui os tipos no namespace Platform e no namespace padrão. No entanto, os tipos no namespace Platform::Collections são definidos no arquivo de cabeçalho collections.h, não em um arquivo winmd. Portanto, esses tipos não aparecem no Pesquisador de Objetos.

Para inspecionar seu componente no Pesquisador de Objetos

  1. Na barra de menus do Visual Studio, escolha Exibir, Outras Janelas, Pesquisador de Objetos.

  2. No painel esquerdo do Pesquisador de Objetos, expanda o nó CppLib para mostrar os tipos e métodos definidos no seu componente.

Dicas de depuração

Para obter uma melhor experiência de depuração, baixe os símbolos de depuração dos servidores públicos de símbolos da Microsoft. No menu principal, escolha Ferramentas e selecione Opções. Na janela Opções, expanda Depuração e selecione Símbolos. Marque a caixa ao lado de Servidores de Símbolo da Microsoft e escolha OK. Pode levar algum tempo para baixá-los na primeira vez. Para melhorar o desempenho na próxima vez que você pressionar F5, use o espaço fornecido para especificar uma pasta local onde armazenar os símbolos em cache.

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, no menu de atalhos do nó de projeto em JavaScript no Gerenciador de Soluções, 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 do 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 ComputeResult C++ deve ser referenciado como computeResult 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

Referência

Roadmap for Windows Store apps using C++

Outros recursos

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