Condividi tramite


Procedura dettagliata: creazione di un componente Windows Runtime di base in C++ e chiamata da JavaScript

In questa procedura dettagliata viene illustrato come creare la DLL di un componente di Windows Runtime di base che è possibile chiamare da JavaScript, C# o Visual Basic. Prima di iniziare questa procedura dettagliata, assicurati di avere compreso concetti quali interfaccia binaria astratta (ABI), classi di riferimento e estensioni dei componenti di Visual C++ che semplificano l'uso delle classi di riferimento. Per ulteriori informazioni, vedi Creazione di componenti Windows Runtime in C++ e Riferimenti al linguaggio Visual C++ (C++/CX).

Creazione di un progetto di componente C++

In questo esempio abbiamo creato prima il progetto di componente, ma se vuoi puoi creare prima il progetto JavaScript. L'ordine non ha importanza.

Nota che la classe principale del componente contiene esempi di definizioni di proprietà e metodi e una dichiarazione di evento, forniti al solo scopo illustrativo. Non sono necessari e in questo esempio sostituiremo tutto il codice generato con il nostro codice.

Per creare un progetto di componente C++

  1. Dalla barra dei menu di Visual Studio scegli File, Nuovo, Progetto.

  2. Nel riquadro sinistro della finestra di dialogo Nuovo progetto espandi Visual C++, quindi seleziona il nodo per le applicazioni Windows Store.

  3. Nel riquadro centrale seleziona Componente di Windows Runtime, quindi assegna al progetto il nome CppLib.

  4. Scegli OK.

Aggiunta di una classe attivabile al componente

Una classe attivabile è una classe che JavaScript può creare utilizzando un'espressione new. Nel componente dichiara la classe come public ref class sealed. Di fatto, i file Class1.h e cpp dispongono già di una classe di riferimento. Puoi modificare il nome, ma in questo esempio utilizzeremo il nome predefinito, Class1. Se necessario, puoi definire classi di riferimento aggiuntive nel componente. Per ulteriori informazioni sulle classi ref, vedi Sistema di tipi (C++/CX).

Aggiunta delle istruzioni #include e using necessarie

Per aggiungere le istruzioni #include e using necessarie

  1. Aggiungi queste direttive #include a Class1.h:

    #include <collection.h>
    #include <amp.h>
    #include <amp_math.h>
    
  2. Aggiungi queste direttive #include a Class1.cpp:

    #include <ppltasks.h>
    #include <concurrent_vector.h>
    
  3. Aggiungi queste istruzioni using a Class1.cpp:

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

Aggiunta di un metodo pubblico sincrono alla classe

I metodi che vengono eseguiti rapidamente possono essere implementati come metodi sincroni. Vengono eseguiti sullo stesso thread del chiamante JavaScript. Tutti i tipi di parametro e i valori restituiti sui metodi pubblici devono essere tipi compatibili con Windows Runtime.

Per aggiungere un metodo pubblico sincrono alla classe

  1. Aggiungi queste dichiarazioni alla classe in 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. Aggiungi queste implementazioni a 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;
    }
    

Aggiunta di un metodo pubblico asincrono alla classe

Implementare metodi la cui esecuzione come metodi asincroni potrebbe richiedere del tempo. Un metodo asincrono viene eseguito su un thread in background e non blocca il thread JavaScript. Crea il metodo pubblico async utilizzando internamente il metodo create_async. JavaScript chiama il metodo come qualsiasi suggerimento. Come con un metodo sincrono, tutti i tipi di parametro devono essere tipi compatibili con Windows Runtime. Su un metodo asincrono, il tipo restituito deve essere uno dei seguenti:

  • IAsyncAction per i metodi async che non restituiscono valori e forniscono informazioni sullo stato di avanzamento.

  • IAsyncActionWithProgress per i metodi async che non restituiscono valori, ma forniscono informazioni sullo stato di avanzamento.

  • IAsyncOperation<T> per i metodi async che restituiscono un valore T, ma non forniscono informazioni sullo stato di avanzamento.

  • IAsyncOperationWithProgress<T> per i metodi async che restituiscono un valore T e forniscono informazioni sullo stato di avanzamento.

Per ulteriori informazioni, vedi Creazione di operazioni asincrone in C++ per le applicazioni Windows Store.

Per aggiungere un metodo asincrono che restituisce un risultato e fornisce informazioni sullo stato di avanzamento

  1. Aggiungi queste dichiarazioni alla classe in 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. Aggiungi queste implementazioni a 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;
        });
    }
    

Per aggiungere un metodo asincrono che genera eventi e fornisce informazioni sullo stato di avanzamento

  1. Aggiungi queste dichiarazioni pubbliche alla classe in Class1.h:

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

    Aggiungi la seguente dichiarazione privata:

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

    Aggiungi il seguente delegato all'ambito dello spazio dei nomi in Class1.h:

    public delegate void PrimeFoundHandler(int i);
    

    Utilizziamo questo oggetto per eseguire il marshalling degli eventi dalla funzione parallela al thread UI.

  2. Aggiungi queste implementazioni a 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);
        });
    }
    
    

Creazione di un progetto di JavaScript

Per creare un progetto di JavaScript

  1. In Esplora soluzioni scegli Aggiungi, Nuovo progetto dal menu di scelta rapida del nodo Soluzione.

  2. Espandi JavaScript e scegli Applicazione vuota.

  3. Accetta il nome predefinito App1 scegliendo il pulsante OK.

  4. Fai clic con il pulsante destro del mouse sul nodo del progetto App1 e scegli Imposta come progetto di avvio.

  5. Aggiungi un riferimento al progetto a CppLib:

    1. Scegli Aggiungi riferimento dal menu di scelta rapida del nodo Riferimenti.

    2. Nel riquadro sinistro della finestra di dialogo Gestione riferimenti seleziona Soluzione, quindi Progetti.

    3. Nel riquadro centrale seleziona CppLib e scegli il pulsante OK.

Aggiunta di codice HTML per richiamare i gestori di eventi JavaScript

Incolla il codice HTML nel nodo <body> della pagina 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>

Aggiunta di stili

Rimuovi lo stile body, quindi aggiungi questi stili 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; }

Aggiunta dei gestori di eventi JavaScript che chiamano la DLL del componente

Aggiungi le seguenti funzioni alla fine del file default.js. Queste funzioni vengono chiamate quando vengono scelti i pulsanti nella pagina principale. Nota come JavaScript attiva la classe C++ e chiama i relativi metodi e come utilizza i valori restituiti per popolare le etichette 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() + " "; };

Esecuzione dell'app

Premi F5.

Controllo del componente nel Visualizzatore oggetti (facoltativo)

Nel Visualizzatore oggetti puoi controllare tutti i tipi di Windows Runtime definiti nei file winmd, sono inclusi i tipi nello spazio dei nomi Platform e nello spazio dei nomi predefinito. Tuttavia, i tipi nello spazio dei nomi Platform::Collections sono definiti nel file di intestazione collections.h, non in un file winmd. Di conseguenza, questi tipi non vengono visualizzati nel Visualizzatore oggetti.

Per controllare il componente nel Visualizzatore oggetti

  1. Sulla barra dei menu di Visual Studio scegli Visualizza, Altre finestre, Visualizzatore oggetti.

  2. Nel riquadro sinistro del Visualizzatore oggetti espandi il nodo CppLib per mostrare i tipi e i metodi definiti nel componente.

Suggerimenti relativi al debug

Per una migliore esperienza di debug, scarica i simboli di debug dai server di simboli Microsoft pubblici. Dal menu Strumenti scegli Opzioni. Nella finestra Opzioni espandi Debug e seleziona Simboli. Seleziona la casella accanto a Server dei simboli Microsoft e scegli OK. Il primo download potrebbe richiedere del tempo. Per ottenere prestazioni migliori quando premerai F5 la volta successiva, utilizza lo spazio disponibile per specificare una directory locale in cui memorizzare nella cache i simboli.

Quando esegui il debug di una soluzione JavaScript che dispone di una DLL componente, puoi impostare il debugger per abilitare l'esecuzione passo passo tramite script o l'esecuzione passo passo nel codice nativo nel componente, ma non entrambe contemporaneamente. Per modificare l'impostazione, dal menu di scelta rapida per il nodo del progetto di JavaScript in Esplora soluzioni, scegli Proprietà, Debug, Tipo di debugger.

Assicurati di selezionare le funzionalità appropriate nella finestra di progettazione del pacchetto. Ad esempio, se tenti di aprire un file utilizzando le API di Windows Runtime, verifica di selezionare la casella di controllo Accesso raccolta documenti nel riquadro Funzionalità della finestra di progettazione del pacchetto.

Se il codice JavaScript non sembra riconoscere le proprietà o i metodi pubblici nel componente, verifica di utilizzare la convenzione Camel in JavaScript. Ad esempio, è necessario fare riferimento al metodo ComputeResult C++ come computeResult in JavaScript.

Se rimuovi il progetto di Componente di Windows Runtime C++ da una soluzione, devi anche rimuovere manualmente il riferimento al progetto dal progetto di JavaScript. Se non esegui questa operazione, non potrai eseguire le operazioni di debug o compilazione successive. Se necessario, puoi aggiungere un riferimento all'assembly alla DLL.

Vedere anche

Riferimenti

Roadmap for Windows Store apps using C++

Altre risorse

Sviluppo dell'utilità di ottimizzazione dei viaggi di Bing Mappe, un'app di Windows Store in JavaScript e C++