Domande frequenti su C++/WinRT

Risposte alle domande che potresti porti sulla creazione e sull'utilizzo delle API Windows Runtime con C++/WinRT.

Importante

Per le note sulla versione relative a C++/WinRT, vedi Funzionalità nuove e aggiornate di C++/WinRT 2.0.

Nota

Se la domanda riguarda un messaggio di errore visualizzato, vedi anche l'argomento Risoluzione dei problemi di C++/WinRT.

Dove si possono trovare app di esempio C++/WinRT?

Come posso impostare una versione più recente di Windows SDK come destinazione per il mio progetto C++/WinRT?

Perché il mio nuovo progetto non viene compilato dopo il passaggio a C++/WinRT 2.0?

Per informazioni su tutte le modifiche (incluse quelle che possono causare interruzioni), vedi Novità e modifiche in C++/WinRT 2.0. Ad esempio, se usi un'istruzione for basata su intervallo in una raccolta di Windows Runtime, ora dovrai usare #include <winrt/Windows.Foundation.Collections.h>.

Per quale motivo il mio nuovo progetto non viene compilato? Uso Visual Studio 2017 (versione 15.8.0 o successiva ) e la versione 17134 dell'SDK

Se usi Visual Studio 2017 (versione 15.8.0 o successive) con Windows SDK versione 10.0.17134.0 (Windows 10 versione 1803) come destinazione, è possibile che la compilazione di un nuovo progetto C++/WinRT non riesca con l'errore "errore C3861: 'from_abi': identificatore non trovato" e con altri errori con origine in base.h. La soluzione è selezionare una versione successiva (più conforme) di Windows SDK come destinazione oppure impostare la proprietà del progetto C/C++>Linguaggio>Modalità di conformità: No (elimina anche /permissive- se compare nella proprietà del progetto C/C++>Riga di comando in Opzioni aggiuntive).

Come posso risolvere l'errore di compilazione "The C++/WinRT VSIX no longer provides project build support. Please add a project reference to the Microsoft.Windows.CppWinRT Nuget package" (C++/WinRT VSIX non offre più il supporto per la compilazione dei progetti. Aggiungi un riferimento al progetto nel pacchetto NuGet Microsoft.Windows.CppWinRT)?

Installa il pacchetto NuGet Microsoft.Windows.CppWinRT nel progetto. Per informazioni dettagliate, vedi Versioni precedenti dell'estensione VSIX.

Come posso personalizzare il supporto per la compilazione nel pacchetto NuGet?

Il supporto per la compilazione C++/WinRT (props/targets) è documentato nel readme del pacchetto NuGet Microsoft.Windows.CppWinRT.

Quali sono i requisiti per l'estensione per Visual Studio (VSIX) C++/WinRT?

Per la versione 1.0.190128.4 dell'estensione VSIX e versioni successive, vedi Supporto di Visual Studio per C++/WinRT. Per le altre versioni, vedi Versioni precedenti dell'estensione VSIX.

Che cos'è una classe di runtime?

Una classe di runtime è un tipo che può essere attivato e utilizzato mediante le interfacce COM moderne, in genere tra limiti eseguibili. Una classe di runtime può tuttavia essere usata anche nell'unità di compilazione che la implementa. Devi dichiarare una classe di runtime nel linguaggio di definizione dell'interfaccia (IDL, Interface Definition Language) e puoi implementarla in codice C++ standard con C++/WinRT.

Cosa indicano il tipo proiettato e il tipo di implementazione?

Se intendi solo utilizzare una classe Windows Runtime (classe di runtime), avrai a che fare solo con tipi proiettati. C++/WinRT è una proiezione di linguaggio, pertanto i tipi proiettati fanno parte della superficie di Windows Runtime proiettata in C++ con C++/WinRT. Per altri dettagli, vedi Utilizzare API con C++/WinRT.

Il tipo di implementazione contiene l'implementazione di una classe di runtime, pertanto è disponibile solo nel progetto che implementa la classe di runtime. Per lavorare a un progetto che implementa classi di runtime (un progetto di componente Windows Runtime o un progetto che usa l'interfaccia utente XAML), è importante avere una certa familiarità con la differenza tra tipo di implementazione per una classe di runtime e tipo proiettato che rappresenta la classe di runtime proiettata in C++/WinRT. Per altri dettagli, vedi Creare API con C++/WinRT.

Devo dichiarare un costruttore nell'IDL della classe di runtime?

Solo se la classe di runtime è progettata per essere utilizzata dall'esterno dell'unità di compilazione di implementazione (è un componente Windows Runtime destinato all'uso generale da parte di app client Windows Runtime). Per dettagli completi sullo scopo e sulle conseguenze della dichiarazione di costruttori in IDL, vedi Costruttori della classe di runtime.

Perché il compilatore restituisce un errore "C3779:consume_Something: funzione che restituisce "auto" non può essere usata prima di essere definita?

Stai usando un oggetto Windows Runtime senza aver prima incluso il file di intestazione dello spazio dei nomi corrispondente. Includi l'intestazione denominata per lo spazio dei nomi dell'API e ricompila. Per altre info, vedi Intestazioni di proiezione C++/WinRT.

Per quale motivo il linker restituisce un errore "LNK2019: Unresolved external symbol" (Simbolo esterno non risolto)?

Se il simbolo non risolto è una funzione libera di Windows Runtime, ad esempio RoInitialize, dovrai collegare in modo esplicito la raccolta generica WindowsApp.lib nel tuo progetto. La proiezione C++/WinRT dipende da alcune di questi punti di ingresso e funzioni libere (non membro). Se usi uno dei modelli di progetto di Estensione C++/WinRT per Visual Studio (VSIX) per l'applicazione, la raccolta WindowsApp.lib viene collegata automaticamente. In caso contrario, puoi usare le impostazioni di collegamento del progetto per includerla o farlo nel codice sorgente.

#pragma comment(lib, "windowsapp")

È importante risolvere eventuali errori del linker collegando WindowsApp.lib invece di una libreria a collegamento statico alternativa. In caso contrario, l'applicazione non supererà i test del kit di certificazione app Windows usati da Visual Studio e da Microsoft Store per convalidare gli invii e questo significa che non sarà di conseguenza possibile l'inserimento corretto della tua applicazione in Microsoft Store.

Se il simbolo non risolto è un costruttore, potrebbe essere stato dimenticato di includere il file di intestazione dello spazio dei nomi per la classe costruita. Includere l'intestazione denominata per lo spazio dei nomi della classe e ricompilare. Per altre info, vedi Intestazioni di proiezione C++/WinRT.

Per quale motivo viene restituita un'eccezione "classe non registrata"?

In questo caso, il sintomo consiste nel fatto che, durante la creazione di una classe di runtime o l'accesso a un membro statico, viene visualizzata un'eccezione generata in fase di runtime con il valore di HRESULT REGDB_E_CLASSNOTREGISTERED.

Una delle cause di questa eccezione è data dall'impossibilità di caricare il componente Windows Runtime. Assicurati che il file di metadati di Windows Runtime del componente (.winmd) abbia lo stesso nome del file binario del componente (.dll), che è anche il nome del progetto e il nome dello spazio dei nomi radice. Assicurati inoltre che i metadati di Windows Runtime e il file binario siano stati copiati correttamente dal processo di compilazione nella cartella Appx dell'app d'uso. Verifica infine che il file AppxManifest.xml dell'app d'uso (anch'esso nella cartella Appx) contenga un elemento <InProcessServer> che dichiara correttamente la classe attivabile e il nome del file binario.

Costruzione uniforme Questo errore può verificarsi anche se si prova a creare un'istanza di una classe di runtime implementata in locale usando uno dei costruttori del tipo proiettato (diverso dal costruttore std::nullptr_t). Per eseguire questa operazione è necessaria la funzionalità di C++/WinRT 2.0 che viene spesso definita costruzione uniforme. Se vuoi acconsentire esplicitamente all'uso di questa funzionalità, per altre informazioni ed esempi di codice, vedi Acconsentire esplicitamente alla costruzione uniforme e all'accesso diretto all'implementazione.

Se vuoi sapere come creare istanze delle classi di runtime implementate in locale che non richiedono la costruzione uniforme, vedi Controlli XAML, binding a una proprietà C++/WinRT.

Devo implementare Windows::Foundation::IClosable e, in caso affermativo, come?

Se hai una classe di runtime che libera risorse all'interno del distruttore e tale classe di runtime è progettata per essere utilizzata dall'esterno della relativa unità di implementazione (si tratta di un componente Windows Runtime destinato all'uso generale da parte di app client Windows Runtime), è consigliabile implementare anche IClosable per supportare l'utilizzo della classe di runtime in linguaggi senza finalizzazione deterministica. Assicurati che le risorse vengano liberate sia che venga chiamato il distruttore, IClosable::Close o entrambi. È possibile chiamare IClosable::Close un numero arbitrario di volte.

Devo chiamare IClosable::Close sulle classi di runtime utilizzate?

IClosable esiste per supportare i linguaggi senza finalizzazione deterministica. Quindi, di solito, non è necessario chiamare IClosable::Close da C++/WinRT. Considera tuttavia le seguenti eccezioni a tale regola generale.

  • In casi molto rari che prevedono race condition di arresto o condizioni di blocco non è necessario chiamare IClosable::Close. Ad esempio, se usi tipi Windows.UI.Composition, possono verificarsi casi in cui vuoi eliminare gli oggetti in una sequenza definita, anziché consentire che l'operazione venga eseguita in automatico tramite la distruzione del wrapper C++/WinRT.
  • Se non puoi garantire di disporre dell'ultimo riferimento rimanente a un oggetto (perché lo hai passato ad altre API, che potrebbero mantenere un riferimento), è utile chiamare IClosable::Close.
  • In caso di dubbi, è più sicuro chiamare IClosable::Close manualmente invece di attendere che il wrapper lo chiami per la distruzione.

Di conseguenza, se sei certo di avere l'ultimo riferimento, puoi lasciare che il distruttore del wrapper esegua l'operazione. Per chiudere prima che l'ultimo riferimento scompaia, devi chiamare Close. Per essere indipendente dalle eccezioni, devi chiamare Close in un tipo RAII (Resource-Acquisition-is-Initialization), in modo che la chiusura venga eseguita durante la rimozione. C++/WinRT non ha un wrapper unique_close, ma puoi crearne uno personale.

Posso usare LLVM/Clang per compilare con C++/WinRT?

La toolchain LLVM/Clang non è supportata per C++/WinRT, ma viene usata internamente per convalidare la conformità di C++/WinRT agli standard. Ad esempio, se vuoi emulare ciò che avviene internamente, puoi provare un esperimento come quello descritto di seguito.

Vai alla pagina dei download di LLVM, cerca Download LLVM 6.0.0>Pre-Built Binaries (File binari predefiniti) e scarica Clang for Windows (64-bit). Durante l'installazione, scegli di aggiungere LLVM alla variabile di sistema PATH per poterlo richiamare da un prompt dei comandi. Ai fini di questo esperimento, puoi ignorare qualsiasi errore "Impossibile trovare la directory dei set di strumenti MSBuild" e/o "Installazione dell'integrazione MSVC non riuscita", se vengono visualizzati. Esistono diversi modi per richiamare LLVM/Clang. L'esempio seguente ne mostra solo uno.

C:\ExperimentWithLLVMClang>type main.cpp
// main.cpp
#pragma comment(lib, "windowsapp")
#pragma comment(lib, "ole32")

#include <winrt/Windows.Foundation.h>
#include <stdio.h>
#include <iostream>

using namespace winrt;

int main()
{
    winrt::init_apartment();
    Windows::Foundation::Uri rssFeedUri{ L"https://blogs.windows.com/feed" };
    std::wcout << rssFeedUri.Domain().c_str() << std::endl;
}

C:\ExperimentWithLLVMClang>clang-cl main.cpp /EHsc /I ..\.. -Xclang -std=c++17 -Xclang -Wno-delete-non-virtual-dtor -o app.exe

C:\ExperimentWithLLVMClang>app
windows.com

Dato che C++/WinRT usa funzionalità dello standard C++ 17, dovrai usare tutti i flag del compilatore necessari per ottenere tale supporto. I flag sono diversi a seconda del compilatore.

Visual Studio è lo strumento di sviluppo supportato e consigliato per C++/WinRT. Vedi Supporto di Visual Studio per C++/WinRT .

Per qualche motivo la funzione di implementazione generata per una proprietà di sola lettura ha il qualificatore const?

Quando dichiari una proprietà di sola lettura in MIDL 3.0, è previsto che lo strumento cppwinrt.exe generi automaticamente una funzione di implementazione qualificata con const (una funzione const gestisce il puntatore this come const).

È certamente consigliabile usare const laddove possibile, ma lo strumento cppwinrt.exe non tenta di distinguere tra le funzioni di implementazione che potrebbero essere plausibilmente const e quelle che potrebbero non esserlo. Puoi scegliere di impostare tutte le funzioni di implementazione come const, come in questo esempio.

struct MyStringable : winrt::implements<MyStringable, winrt::Windows::Foundation::IStringable>
{
    winrt::hstring ToString() const
    {
        return L"MyStringable";
    }
};

Puoi rimuovere il qualificatore const per ToString, se decidi che è necessario modificare uno stato di oggetto nella relativa implementazione. Imposta però ogni funzione membro come const o non const. In altre parole, non eseguire l'overload di una funzione di implementazione in const.

A parte le funzioni di implementazione, un'altra posizione in cui entra in gioco const sono le proiezioni di funzione di Windows Runtime. Considera questo codice.

int main()
{
    winrt::Windows::Foundation::IStringable s{ winrt::make<MyStringable>() };
    auto result{ s.ToString() };
}

Per la chiamata a ToString precedente, il comando Vai a dichiarazione in Visual Studio mostra che la proiezione di IStringable::ToString di Windows Runtime in C++/WinRT ha questo aspetto.

winrt::hstring ToString() const;

Le funzioni nella proiezione sono const, indipendentemente da come scegli di qualificarne l'implementazione. Dietro le quinte, la proiezione chiama l'interfaccia ABI (Application Binary Interface) che corrisponde a una chiamata tramite un puntatore di interfaccia COM. L'unico stato con cui interagisce l'elemento ToString proiettato è il puntatore di interfaccia COM e di certo non occorre modificare tale puntatore, quindi la funzione è const. In questo modo puoi avere la certezza che non verrà modificato nulla del riferimento a IStringable chiamato e che sia possibile chiamare ToString anche con un riferimento const a IStringable.

Tieni presente che questi esempi di const sono dettagli di implementazione di proiezioni e implementazioni C++/WinRT. Rappresentano buone norme di scrittura del codice a tuo vantaggio. In COM o nell'interfaccia ABI di Windows Runtime (per le funzioni membro) non esiste qualcosa di simile a const.

Esistono raccomandazioni per ridurre le dimensioni del codice per i file binari C++/WinRT?

Quando si usano oggetti Windows Runtime, dovresti evitare il modello di codifica illustrato di seguito perché può avere un impatto negativo sull'applicazione causando la generazione di più codice binario del necessario.

anobject.b().c().d();
anobject.b().c().e();
anobject.b().c().f();

Nel mondo Windows Runtime il compilatore non è in grado di memorizzare nella cache il valore di c() o le interfacce per ogni metodo chiamato tramite un riferimento indiretto ('.'). Se non intervieni, ciò comporta più chiamate virtuali e l'overhead di conteggio dei riferimenti. Il modello precedente potrebbe facilmente generare il doppio del codice strettamente necessario. Preferisci invece il modello illustrato di seguito ove possibile. Questo modello genera molto meno codice e può anche migliorare drasticamente le prestazioni in fase di esecuzione.

auto a{ anobject.b().c() };
a.d();
a.e();
a.f();

Il modello consigliato illustrato in precedenza non si applica solo a C++/WinRT ma a tutte le proiezioni del linguaggio di Windows Runtime.

Come si converte una stringa in un tipo (ad esempio per lo spostamento)?

Alla fine dell'esempio di codice della visualizzazione di spostamento (in gran parte in C#), è presente un frammento di codice C++/WinRT che illustra come eseguire questa operazione.

Come posso risolvere le ambiguità con GetCurrentTime e/o TRY?

Il file di intestazione winrt/Windows.UI.Xaml.Media.Animation.h dichiara un metodo denominato GetCurrentTime, mentre windows.h (tramite winbase.h) definisce una macro denominata GetCurrentTime. Quando si verifica un conflitto tra i due, il compilatore C++ genera l'errore "C4002: troppi argomenti per la chiamata 'GetCurrentTime' della macro simile a funzione".

Analogamente, winrt/Windows.Globalization.h dichiara un metodo denominato TRY, mentre afx.h definisce una macro denominata TRY. In caso di conflitto, il compilatore C++ genera l'errore "C2334: token imprevisti prima di '{'. Il corpo apparente della funzione verrà ignorato".

Per risolvere uno dei problemi oppure entrambi, puoi eseguire questa operazione.

#pragma push_macro("GetCurrentTime")
#pragma push_macro("TRY")
#undef GetCurrentTime
#undef TRY
#include <winrt/include_your_cppwinrt_headers_here.h>
#include <winrt/include_your_cppwinrt_headers_here.h>
#pragma pop_macro("TRY")
#pragma pop_macro("GetCurrentTime")

In che modo è possibile velocizzare il caricamento dei simboli?

In Visual Studio, Strumenti>Opzioni>Debug>Simboli> selezionare Carica solo i moduli specificati. È quindi possibile fare clic con il pulsante destro del mouse sulle DDL nell'elenco stack e caricare singoli moduli.

Nota

Se questo argomento non risponde alla domanda, potresti trovare assistenza visitando il sito della community degli sviluppatori Visual Studio C++ o tramite il tag c++-winrt in Stack Overflow.