Generare una proiezione C# da un componente C++/WinRT, distribuito come NuGet per le app .NET

In questo argomento viene illustrato come usare C#/WinRT per generare un assembly di proiezione .NET C# (o interoperabilità) da un componente Windows Runtime C++/WinRT e distribuirlo come pacchetto NuGet per le applicazioni .NET.

In .NET 6 e versioni successive, l'utilizzo dei file di metadati di Windows (WinMD) non è più supportato (vedi Supporto predefinito per WinRT viene rimosso da .NET). Lo strumento C#/WinRT può invece essere usato per generare un assembly di proiezione per qualsiasi file WinMD, il che consente quindi l'utilizzo di componenti WinRT dalle applicazioni .NET. Un assembly di proiezione è noto anche come assembly di interoperabilità. Questa procedura dettagliata illustra come eseguire le operazioni seguenti:

  • Usare il pacchetto C#/WinRT per generare una proiezione C# da un componente C++/WinRT.
  • Distribuire il componente, insieme all'assembly di proiezione, come pacchetto NuGet.
  • Utilizzare il pacchetto NuGet da un'applicazione console .NET.

Prerequisiti

Questa procedura dettagliata e l'esempio corrispondente richiedono gli strumenti e i componenti seguenti:

  • Visual Studio 2022 (o Visual Studio 2019) con il carico di lavoro della piattaforma UWP (Universal Windows Platform) installato. In Dettagli di installazione>Sviluppo di app per la piattaforma UWP (Universal Windows Platform) seleziona una o più opzioni di Strumenti della piattaforma UWP (Universal Windows Platform) C++ (v14x).
  • .NET 6.0 SDK o versione successiva.

Solo Visual Studio 2019. L'estensione VSIX C++/WinRT, che offre modelli di progetto C++/WinRT in Visual Studio. I modelli di progetto sono incorporati in Visual Studio 2022.

In questa procedura dettagliata si utilizzeranno Visual Studio 2022 e .NET 6.

Importante

Inoltre, sarà necessario scaricare o clonare il codice di esempio per questo argomento dall'esempio di proiezione C#/WinRT in GitHub. Vai su CsWinRTe fai clic sul pulsante Codice verde per ottenere l'URL git clone. Assicurarsi di leggere il file README.md per l'esempio in questione.

Creare un semplice componente Windows Runtime C++/WinRT

Per seguire questa procedura dettagliata, è necessario innanzitutto disporre di un componente Windows Runtime (WRC) C++/WinRT da cui generare l'assembly di proiezione C#.

Questa procedura dettagliata usa SimpleMathComponent WRC dall'esempio di proiezione C#/WinRT in GitHub, che è già stato scaricato o clonato. SimpleMathComponent è stato creato dal modello di progetto di Visual Studio componente Windows Runtime (C++/WinRT) (incluso in Visual Studio 2022 o con l'estensione VSIX C++/WinRT).

Per aprire il progetto SimpleMathComponent in Visual Studio, apri il file \CsWinRT\src\Samples\NetProjectionSample\CppWinRTComponentProjectionSample.sln, disponibile nel download o nel clone del repository.

Il codice in questo progetto fornisce la funzionalità per le operazioni matematiche di base illustrate nel file di intestazione riportato di seguito.

// SimpleMath.h
...
namespace winrt::SimpleMathComponent::implementation
{
    struct SimpleMath: SimpleMathT<SimpleMath>
    {
        SimpleMath() = default;
        double add(double firstNumber, double secondNumber);
        double subtract(double firstNumber, double secondNumber);
        double multiply(double firstNumber, double secondNumber);
        double divide(double firstNumber, double secondNumber);
    };
}

È possibile verificare che la proprietà Compatibile con desktop di Windows sia impostata su per il progetto componente Windows Runtime con C++/WinRT SimpleMathComponent. A tale scopo, nelle proprietà del progetto per SimpleMathComponent, in Configurazione proprietà>Generale>Impostazioni predefinite, impostare la proprietà Compatibile con desktop di Windows su Sì. Ciò garantisce che i file binari di runtime corretti vengano caricati per l'utilizzo di app desktop .NET.

Desktop Compatible property page

Per istruzioni più dettagliate sulla creazione di un componente C++/WinRT e sulla generazione di un file WinMD, vedi Componenti Windows Runtime con C++/WinRT.

Nota

Se stai implementando IInspectable::GetRuntimeClassName nel tuo componente, deve restituire un nome di classe WinRT valido. Poiché C#/WinRT usa la stringa del nome della classe per l'interoperabilità, un nome di classe di runtime non corretto genererà un'eccezione InvalidCastException.

Aggiungere un progetto di proiezione alla soluzione del componente

Prima di tutto, con la soluzione CppWinRTComponentProjectionSample ancora aperta in Visual Studio, rimuovere il progetto SimpleMathProjection da tale soluzione. Eliminare quindi dal file system la cartella SimpleMathProjection (o, se si preferisce, rinominarla). Tali passaggi sono necessari per poter seguire passo passo questa procedura dettagliata.

  1. Aggiungere un nuovo progetto della libreria C# alla soluzione.

    1. In Esplora soluzioni fai clic con il pulsante destro del mouse sul nodo della soluzione e poi su Aggiungi>Nuovo progetto.
    2. Nella finestra di dialogo Aggiungi un nuovo progetto, digitare Libreria di classi nella casella di ricerca. Scegliere C# dall'elenco dei linguaggi e quindi Windows dall'elenco delle piattaforme. Scegliere il modello di progetto C# denominato semplicemente Libreria di classi (senza prefissi né suffissi) e fare clic su Avanti.
    3. Assegnare al nuovo progetto il nome SimpleMathProjection. Il percorso deve essere già impostato sulla stessa cartella \CsWinRT\src\Samples\NetProjectionSample in cui si trova la cartella SimpleMathComponent, ma verificare che sia effettivamente così. Fare clic su Avanti.
    4. Nella pagina Informazioni aggiuntive, selezionare .NET 6.0 (supporto a lungo termine), quindi scegliere Crea.
  2. Eliminare il file stub Class1.cs dal progetto.

  3. Usare la procedura seguente per installare il pacchetto NuGet C#/WinRT.

    1. In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto SimpleMathProjection e scegliere Gestisci pacchetti NuGet.
    2. Nella scheda Sfoglia digitare o incollare Microsoft.Windows.CsWinRT nella casella di ricerca, selezionare l'elemento con la versione più recente nei risultati della ricerca e quindi fare clic su Installa per installare il pacchetto nel progetto SimpleMathProjection.
  4. Aggiungere a SimpleMathProjection un riferimento al progetto SimpleMathComponent. In Solution Explorer, right-click the Dependencies node under the SimpleMathProjection project node, select Add Project Reference, and select the SimpleMathComponent project >OK.

Non provare ancora a compilare il progetto. Questa operazione verrà eseguita in un passaggio successivo.

Fino a questo punto, Esplora soluzioni dovrebbe avere un aspetto simile a questo (i numeri di versione saranno diversi).

Solution Explorer showing projection project dependencies

Compilare progetti all'esterno dell'origine

Per la soluzione CppWinRTComponentProjectionSample nell'esempio di proiezione C#/WinRT (scaricato o clonato da GitHub e ora aperto), il percorso di output della compilazione è configurato con il file Directory.Build.props per la compilazione fuori origine. Ciò significa che i file dall'output di compilazione vengono generati all'esterno della cartella di origine. Ti consigliamo di creare un'origine quando usi lo strumento C#/WinRT. Ciò impedisce al compilatore C# di raccogliere inavvertitamente tutti i file*.cs nella directory radice del progetto, che può causare errori di tipo duplicati (ad esempio durante la compilazione per più configurazioni e/o piattaforme).

Anche se questa operazione è già configurata per la soluzione CppWinRTComponentProjectionSample, seguire la procedura seguente per impratichirsi con la configurazione manualmente.

Per configurare la soluzione per la compilazione fuori origine:

  1. Tenendo aperta la soluzione CppWinRTComponentProjectionSample, fare clic con il pulsante destro del mouse sul nodo della soluzione e selezionare Aggiungi>Nuovo elemento. Selezionare l'elemento File XML e denominarlo Directory.Build.props (senza estensione .xml). Fare clic su per sovrascrivere il file esistente.

  2. Sostituire il contenuto di Directory.Build.props con la configurazione seguente.

    <Project>
      <PropertyGroup>
        <BuildOutDir>$([MSBuild]::NormalizeDirectory('$(SolutionDir)', '_build', '$(Platform)', '$(Configuration)'))</BuildOutDir>
        <OutDir>$([MSBuild]::NormalizeDirectory('$(BuildOutDir)', '$(MSBuildProjectName)', 'bin'))</OutDir>
        <IntDir>$([MSBuild]::NormalizeDirectory('$(BuildOutDir)', '$(MSBuildProjectName)', 'obj'))</IntDir>
      </PropertyGroup>
    </Project>
    
  3. Salvare e chiudere il file Directory.Build.props.

Modificare il file di progetto per eseguire C#/WinRT

Prima di poter richiamare lo strumento cswinrt.exe per generare l'assembly di proiezione, è necessario modificare il file di progetto per specificare alcune proprietà del progetto.

  1. In Esplora soluzioni fare doppio clic sul nodo SimpleMathProjection per aprire il file di progetto nell'editor.

  2. Aggiornare l'elemento TargetFramework per specificare una versione specifica di Windows SDK. In questo modo vengono aggiunte dipendenze assembly necessarie per il supporto all'interoperabilità e alla proiezione. Questo esempio è destinato alla versione di Windows SDK net6.0-windows10.0.19041.0 (nota anche come Windows 10, versione 2004). Impostare l'elemento Platform su AnyCPU in modo che sia possibile fare riferimento all'assembly di proiezione risultante da qualsiasi architettura dell'app. Per consentire alle applicazioni di riferimento di supportare versioni precedenti di Windows SDK, è inoltre possibile impostare la proprietà TargetPlatformMinimumVersion.

    <PropertyGroup>
      <TargetFramework>net6.0-windows10.0.19041.0</TargetFramework>
      <!-- Set Platform to AnyCPU to allow consumption of the projection assembly from any architecture. -->
      <Platform>AnyCPU</Platform>
    </PropertyGroup>
    

    Nota

    Per questa procedura dettagliata e il codice di esempio correlato, la soluzione viene compilata per x64 e Rilascio. Si noti che il progetto SimpleMathProjection è configurato per la compilazione per AnyCPU per tutte le configurazioni dell'architettura della soluzione.

  3. Aggiungere un secondo elemento PropertyGroup (immediatamente dopo il primo) che imposta diverse proprietà C#/WinRT.

    <PropertyGroup>
      <CsWinRTIncludes>SimpleMathComponent</CsWinRTIncludes>
      <CsWinRTGeneratedFilesDir>$(OutDir)</CsWinRTGeneratedFilesDir>
    </PropertyGroup>
    

    Ecco alcuni dettagli sulle impostazioni in questo esempio:

    • La proprietà CsWinRTIncludes specifica gli spazi dei nomi da proiettare.
    • La proprietà CsWinRTGeneratedFilesDir imposta la directory di output in cui vengono generati i file di origine della proiezione. Questa proprietà è impostata su OutDir, definita in Directory.Build.props dalla sezione precedente.
  4. Salvare e chiudere il file SimpleMathProjection.csproj e fare clic su Ricarica progetti, se necessario.

Creare un pacchetto NuGet con la proiezione

Per distribuire l'assembly di proiezione per gli sviluppatori di applicazioni .NET, è possibile creare automaticamente un pacchetto NuGet durante la compilazione della soluzione aggiungendo alcune proprietà ulteriori del progetto. Per le destinazioni .NET, il pacchetto NuGet deve includere l'assembly di proiezione e l'assembly di implementazione del componente.

  1. Usare la procedura seguente per aggiungere un file di specifiche NuGet (.nuspec) al progetto SimpleMathProjection.

    1. In Esplora soluzioni fare clic con il pulsante destro del mouse sul nodo SimpleMathProjection, scegliere Aggiungi>nuova cartella e assegnare alla cartella il nome nuget.
    2. Fare clic con il pulsante destro del mouse sulla cartella nuget , scegliere Aggiungi>nuovo elemento, scegliere File XML e denominarloSimpleMathProjection.nuspec.
  2. In Esplora soluzioni fare doppio clic sul nodo SimpleMathProjection per aprire il file di progetto nell'editor. Aggiungere il seguente gruppo di proprietà al file SimpleMathProjection.csproj (subito dopo i due elementi PropertyGroup esistenti) per generare automaticamente il pacchetto. Queste proprietà specificano NuspecFile e la directory per generare il pacchetto NuGet.

    <PropertyGroup>
      <GeneratedNugetDir>.\nuget\</GeneratedNugetDir>
      <NuspecFile>$(GeneratedNugetDir)SimpleMathProjection.nuspec</NuspecFile>
      <OutputPath>$(GeneratedNugetDir)</OutputPath>
      <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
    </PropertyGroup>
    

    Nota

    Se si preferisce generare un pacchetto separatamente, è anche possibile scegliere di eseguire lo strumento nuget.exe dalla riga di comando. Per altre informazioni sulla creazione di un pacchetto NuGet, vedi Creare un pacchetto usando l'interfaccia della riga di comando di nuget.exe.

  3. Aprire il file SimpleMathProjection.nuspec per modificare le proprietà di creazione del pacchetto e incollare il codice seguente. Il frammento di codice seguente è una specifica NuGet di esempio per la distribuzione di SimpleMathComponent a più framework di destinazione. Si noti che l'assembly di proiezione SimpleMathProjection.dll viene specificato invece di SimpleMathComponent.winmd per la destinazione lib\net6.0-windows10.0.19041.0\SimpleMathProjection.dll. Questo comportamento è una novità di .NET 6 e versioni successive ed è abilitato da C#/WinRT. Anche l'assembly di implementazione, SimpleMathComponent.dll, deve essere distribuito e verrà caricato in fase di esecuzione.

    <?xml version="1.0" encoding="utf-8"?>
    <package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
      <metadata>
        <id>SimpleMathComponent</id>
        <version>0.1.0-prerelease</version>
        <authors>Contoso Math Inc.</authors>
        <description>A simple component with basic math operations</description>
        <dependencies>
          <group targetFramework="net6.0-windows10.0.19041.0" />
          <group targetFramework=".NETCoreApp3.0" />
          <group targetFramework="UAP10.0" />
          <group targetFramework=".NETFramework4.6" />
        </dependencies>
      </metadata>
      <files>
        <!--Support .NET 6, .NET Core 3, UAP, .NET Framework 4.6, C++ -->
        <!--Architecture-neutral assemblies-->
        <file src="..\..\_build\AnyCPU\Release\SimpleMathProjection\bin\SimpleMathProjection.dll" target="lib\net6.0-windows10.0.19041.0\SimpleMathProjection.dll" />
        <file src="..\..\_build\x64\Release\SimpleMathComponent\bin\SimpleMathComponent\SimpleMathComponent.winmd" target="lib\netcoreapp3.0\SimpleMathComponent.winmd" />
        <file src="..\..\_build\x64\Release\SimpleMathComponent\bin\SimpleMathComponent\SimpleMathComponent.winmd" target="lib\uap10.0\SimpleMathComponent.winmd" />
        <file src="..\..\_build\x64\Release\SimpleMathComponent\bin\SimpleMathComponent\SimpleMathComponent.winmd" target="lib\net46\SimpleMathComponent.winmd" />
        <!--Architecture-specific implementation DLLs should be copied into RID-relative folders-->
        <file src="..\..\_build\x64\Release\SimpleMathComponent\bin\SimpleMathComponent\SimpleMathComponent.dll" target="runtimes\win10-x64\native\SimpleMathComponent.dll" />
        <!--To support x86 and Arm64, build SimpleMathComponent for those other architectures and uncomment the entries below.-->
        <!--<file src="..\..\_build\Win32\Release\SimpleMathComponent\bin\SimpleMathComponent\SimpleMathComponent.dll" target="runtimes\win10-x86\native\SimpleMathComponent.dll" />-->
        <!--<file src="..\..\_build\arm64\Release\SimpleMathComponent\bin\SimpleMathComponent\SimpleMathComponent.dll" target="runtimes\win10-arm64\native\SimpleMathComponent.dll" />-->
      </files>
    </package>
    

    Nota

    SimpleMathComponent.dll, l'assembly di implementazione per il componente, è specifico dell'architettura. Se si supportano altre piattaforme (ad esempio, x86 o Arm64), è prima necessario compilare SimpleMathComponent per le piattaforme desiderate e aggiungere questi file di assembly alla cartella rid-relativa appropriata. L'assembly di proiezione SimpleMathProjection.dll e il componente SimpleMathComponent.winmd sono entrambi indipendenti dall'architettura.

  4. Salvare e chiudere i file appena modificati.

Compilare la soluzione per generare la proiezione e il pacchetto NuGet

Prima di compilare la soluzione, assicurarsi di controllare le impostazioni di Gestione di configurazione in Visual Studio, alla voce Compila>Gestione di configurazione. Per questa procedura dettagliata, impostare Configurazione su Rilascioe Piattaforma su x64 per la soluzione.

A questo punto è ora possibile compilare la soluzione. Fare clic con il pulsante destro del mouse sul nodo della soluzione e selezionare Compila soluzione. Innanzitutto, verrà compilato il progetto SimpleMathComponent e successivamente il progetto SimpleMathProjection. L'assembly di implementazione e WinMD del componente (SimpleMathComponent.winmd e SimpleMathComponent.dll), i file di origine della proiezione e l'assembly di proiezione ( SimpleMathProjection.dll) verranno tutti generati nella directory di output _build. Sarà inoltre possibile visualizzare il pacchetto NuGet generato, SimpleMathComponent0.1.0-prerelease.nupkg, nella cartella \SimpleMathProjection\nuget.

Importante

Se uno dei file indicati in precedenza non viene generato, compilare la soluzione una seconda volta. Potrebbe anche essere necessario chiudere e riaprire la soluzione prima della ricompilazione.

Potrebbe essere necessario chiudere e riaprire la soluzione perché .nupkg venga visualizzato in Visual Studio come illustrato (oppure selezionare e quindi deselezionare Mostra tutti i file).

Solution Explorer showing projection generation

Fare riferimento al pacchetto NuGet in un'applicazione console C# .NET 6

Per utilizzare SimpleMathComponent da un progetto .NET, è sufficiente aggiungere a un nuovo progetto .NET un riferimento al pacchetto NuGet SimpleMathComponent0.1.0-prerelease.nupkg creato nella sezione precedente. I passaggi seguenti illustrano come eseguire questa operazione creando una semplice app console in una soluzione separata.

  1. Usare la procedura seguente per creare una nuova soluzione contenente un progetto app console C#. La creazione di questo progetto in una nuova soluzione consente di ripristinare il pacchetto NuGet SimpleMathComponent in modo indipendente.

    Importante

    Verrà creato questo nuovo progetto di app console all'interno della cartella \CsWinRT\src\Samples\NetProjectionSample, disponibile nel download o nel clone dell'esempio di proiezione C#/WinRT.

    1. In una nuova istanza di Visual Studio, selezionare File>Nuovo>Progetto.
    2. Nella finestra di dialogo Crea un nuovo progetto, cercare il modello di progetto App console. Scegliere il modello di progetto C# denominato semplicemente App console (senza prefissi né suffissi) e fare clic su Avanti. Se si usa Visual Studio 2019, il modello di progetto è Applicazione console.
    3. Denominare il nuovo progetto SampleConsoleApp, impostarne il percorso sulla stessa cartella \CsWinRT\src\Samples\NetProjectionSample in cui si trovano le cartelle SimpleMathComponent e SimpleMathProjection e fare clic su Avanti.
    4. Nella pagina Informazioni aggiuntive, selezionare .NET 6.0 (supporto a lungo termine), quindi scegliere Crea.
  2. In Esplora soluzioni, fare doppio clic sul nodo SampleConsoleApp per aprire il file di progetto SampleConsoleApp.csproj e modificare le proprietà TargetFramework e Platform in modo che vengano visualizzate come illustrato nell'elenco seguente. Aggiungere l'elemento Platform se non è presente.

    <PropertyGroup>
      <OutputType>Exe</OutputType>
      <TargetFramework>net6.0-windows10.0.19041.0</TargetFramework>
      <Platform>x64</Platform>
    </PropertyGroup>
    
  3. Con il file di progetto SampleConsoleApp.csproj ancora aperto, si aggiungerà quindi al progetto SampleConsoleApp un riferimento al pacchetto NuGet SimpleMathComponent. Per ripristinare SimpleMathComponent NuGet durante la compilazione del progetto, è possibile usare la proprietà RestoreSources con il percorso della cartella nuget nella soluzione del componente. Copiare la configurazione seguente e incollarla in SampleConsoleApp.csproj (all'interno dell'elemento Project ).

    <PropertyGroup>
      <RestoreSources>
        https://api.nuget.org/v3/index.json;
        ../SimpleMathProjection/nuget
      </RestoreSources>
    </PropertyGroup>
    
    <ItemGroup>
      <PackageReference Include="SimpleMathComponent" Version="0.1.0-prerelease" />
    </ItemGroup>
    

    Importante

    Il RestoreSources percorso del pacchetto SimpleMathComponent illustrato sopra è impostato su ../SimpleMathProjection/nuget. Tale percorso è corretto purché siano stati eseguiti i passaggi di questa procedura dettagliata, in modo che i progetti SimpleMathComponent e SampleConsoleApp si trovino entrambi nella stessa cartella (in questo caso la cartella NetProjectionSample). Se è stato fatto qualcosa di diverso, sarà necessario regolare il percorso di conseguenza. In alternativa, è possibile aggiungere un feed di pacchetti NuGet locale alla soluzione.

  4. Modificare il file Program.cs per usare la funzionalità fornita da SimpleMathComponent.

    var x = new SimpleMathComponent.SimpleMath();
    Console.WriteLine("Adding 5.5 + 6.5 ...");
    Console.WriteLine(x.add(5.5, 6.5).ToString());
    
  5. Salvare e chiudere i file appena modificati ed eseguire l'app console. Verrà visualizzato il seguente output:

    Console NET5 output

Problemi noti

  • Durante la compilazione del progetto di proiezione, è possibile che venga visualizzato un errore simile al seguente: Errore MSB3271 Si è verificato un errore tra l'architettura del processore del progetto in fase di compilazione "MSIL" e l'architettura del processore , "x86", del file di implementazione ".. \SimpleMathComponent.dll" per ".. \SimpleMathComponent.winmd". Questa mancata corrispondenza può causare errori di runtime. Prendere in considerazione la modifica dell'architettura del processore di destinazione del progetto tramite Configuration Manager, in modo da allineare le architetture del processore tra il progetto e il file di implementazione oppure scegliere un file winmd con un file di implementazione con un'architettura del processore corrispondente all'architettura del processore di destinazione del progetto. Per risolvere questo errore, aggiungere la proprietà seguente al file di progetto della libreria C#:
    <PropertyGroup>
        <!-- Workaround for MSB3271 error on processor architecture mismatch -->
        <ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
    </PropertyGroup>
    

Altre considerazioni

L'assembly di proiezione (o interoperabilità) C# che abbiamo illustrato in questo argomento su come procedere alla creazione è piuttosto semplice, ma non ha dipendenze da altri componenti. Tuttavia, per generare una proiezione C# per un componente C++/WinRT con riferimenti ai tipi di Windows App SDK, nel progetto di proiezione è necessario aggiungere un riferimento al pacchetto NuGet di Windows App SDK. Se mancano riferimenti di questo tipo, verranno visualizzati errori come "Impossibile trovare il tipo <T> ".

Un'altra operazione eseguita in questo argomento consiste nel distribuire la proiezione come pacchetto NuGet. Questo è attualmente necessario.

Risorse