Condividi tramite


Generare una proiezione in C# da un componente C++/WinRT e distribuirla come NuGet per le app .NET

In questo argomento viene illustrato come usare C#/WinRT per generare un assembly di proiezione C# .NET (o di 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 (per ulteriori informazioni, vedere Il 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, 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, in un pacchetto NuGet.
  • Utilizzare il pacchetto NuGet in 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 per lo sviluppo della piattaforma UWP (Universal Windows Platform) installato. In Dettagli installazione>sviluppo della piattaforma UWP (Universal Windows Platform), selezionare l'opzione C++ (v14x) degli strumenti della piattaforma UWP (Universal Windows Platform).
  • .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 userà Visual Studio 2022 e .NET 6.

Importante

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

** Crea un semplice componente C++/WinRT di Windows Runtime

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 l'SimpleMathComponent WRC dall'esempio di proiezione C#/WinRT in GitHub, già scaricato o clonato. SimpleMathComponent è stato creato dal componente Windows Runtime (C++/WinRT) modello di progetto di Visual Studio (fornito con 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, che troverai 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 seguente.

// 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à Windows Desktop compatibile sia impostata su per il progetto del componente SimpleMathComponent Windows Runtime C++/WinRT. A tale scopo, nelle proprietà del progetto per SimpleMathComponent, sotto Proprietà di Configurazione>Generale>Impostazioni Predefinite del Progetto, impostare la proprietà Compatibile con Windows Desktop su . Ciò garantisce che i file binari di runtime corretti vengano caricati per l'utilizzo di app desktop .NET.

Pagina delle proprietà desktop compatibile

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.

Annotazioni

Se stai implementando IInspectable::GetRuntimeClassName nel 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 InvalidCastException.

Aggiungere un progetto di proiezione alla soluzione del componente

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

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

    1. In Esplora soluzioni fare clic con il pulsante destro del mouse sul nodo della soluzione e scegliere 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 scegliere Windows dall'elenco delle piattaforme. Scegliere il modello di progetto C# chiamato semplicemente Libreria di classi (senza prefissi né suffissi) e fare clic su Avanti.
    3. Assegnare al nuovo progetto il nome SimpleMathProjection. La posizione dovrebbe essere già impostata sulla stessa cartella \CsWinRT\src\Samples\NetProjectionSample in cui si trova la cartella SimpleMathComponent; ma assicurati di confermarlo. Fare quindi clic su Avanti.
    4. Nella pagina Informazioni aggiuntive selezionare .NET 6.0 (supporto a lungo termine)e quindi scegliere Crea.
  2. Eliminare lo stub del file 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 selezionare Gestisci pacchetti NuGet.
    2. Nella scheda Sfoglia, digitare o incollare Microsoft.Windows.CsWinRT nella casella di ricerca. Nei risultati della ricerca, selezionare l'elemento con la versione più recente e poi fare clic su Installa per installare il pacchetto nel progetto SimpleMathProjection.
  4. Aggiungere a SimpleMathProjection un riferimento al progetto SimpleMathComponent . In Esplora soluzioni, fare clic con il pulsante destro del mouse sul nodo Dipendenze sotto il nodo del progetto SimpleMathProjection, selezionare Aggiungi,Riferimento progetto e selezionare il progetto SimpleMathComponent>, quindi fare clic suOK.

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

Fino a questo punto, la esplora soluzioni dovrebbe essere simile a questa (i numeri di versione saranno diversi).

Esplora soluzioni che mostra le dipendenze del progetto di proiezione

Compilare progetti all'esterno dell'origine

Per la soluzione CppWinRTComponentProjectionSample nell' di esempio di proiezione C#/WinRT (scaricata o clonata da GitHub), il percorso di output della compilazione è configurato con il file Directory.Build.props per compilare dall'origine. Ciò significa che i file dall'output di compilazione vengono generati all'esterno della cartella di origine. Ti consigliamo di compilare fuori dalla directory del codice sorgente 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 duplicato (ad esempio durante la compilazione per più configurazioni e/o piattaforme).

Anche se è già configurata per la soluzione CppWinRTComponentProjectionSample, segui la procedura qui sotto per fare pratica configurandola tu stesso.

Per configurare la soluzione per la compilazione fuori origine:

  1. Con la soluzione CppWinRTComponentProjectionSample ancora aperta, 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 cswinrt.exe strumento 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 si aggiungono le dipendenze dell'assembly necessarie per il supporto all'interoperabilità e alla proiezione. Questo esempio è destinato alla versione 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, è anche possibile impostare la TargetPlatformMinimumVersion proprietà .

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

    Annotazioni

    Per questa procedura dettagliata e il codice di esempio correlato, la soluzione viene compilata per x64, e Release. Si noti che il progetto SimpleMathProjection è configurato per compilare su AnyCPU in tutte le configurazioni di architettura della soluzione.

  3. Aggiungere un secondo elemento PropertyGroup (subito 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 quali namespace mostrare.
    • La CsWinRTGeneratedFilesDir proprietà 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, se necessario, fare clic per ricaricare i progetti.

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à aggiuntive del progetto. Per le destinazioni .NET, il pacchetto NuGet deve includere l'assembly di proiezione e l'assembly di implementazione del componente.

  1. Utilizzare la seguente procedura per aggiungere un file spec NuGet (.nuspec) al progetto SimpleMathProjection .

    1. In Esplora soluzioni, fare clic con il pulsante destro del mouse sul nodo SimpleMathProjection, scegliere Aggiungi>Nuova cartellae 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 denominarlo SimpleMathProjection.nuspec.
  2. In Esplora Soluzioni, fare doppio clic sul nodo SimpleMathProjection per aprire il file di progetto nell'editor. Aggiungi il seguente gruppo di proprietà al SimpleMathProjection.csproj (subito dopo i due elementi PropertyGroup esistenti) per generare automaticamente il pacchetto. Queste proprietà specificano il 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>
    

    Annotazioni

    Se si preferisce generare un pacchetto separatamente, è anche possibile scegliere di eseguire lo nuget.exe strumento dalla riga di comando. Per ulteriori informazioni sulla creazione di un pacchetto NuGet, vedere Creare un pacchetto con la CLI 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è specificato invece di SimpleMathComponent.winmd per il target 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>
    

    Annotazioni

    SimpleMathComponent.dll, l'assieme di implementazione per il componente è specifico per l'architettura. Se supporti altre piattaforme (ad esempio, x86 o Arm64), devi prima generare SimpleMathComponent per le piattaforme desiderate e aggiungere questi file di assembly alla cartella appropriata relativa a RID. 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 costruire la soluzione, assicurati di controllare le impostazioni del Configuration Manager in Visual Studio, sotto Build>Configuration Manager. Per questa procedura dettagliata, impostare la di configurazione su Release e Platform 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 scegliere Compila soluzione. Per prima cosa verrà compilato il progetto SimpleMathComponent, e quindi 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à anche 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 il progetto affinché il .nupkg venga visualizzato in Visual Studio come mostrato (o semplicemente selezionare e poi deselezionare Mostra tutti i file).

Esplora soluzioni che mostra la generazione di proiezioni

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

Per consumare SimpleMathComponent da un progetto .NET, puoi semplicemente aggiungere a un nuovo progetto .NET un riferimento al pacchetto NuGet SimpleMathComponent0.1.0-prerelease.nupkg che abbiamo 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 di app console C# (la creazione di questo progetto in una nuova soluzione consente di ripristinare il pacchetto SimpleMathComponent NuGet in modo indipendente).

    Importante

    Creeremo questo nuovo progetto Console App all'interno della cartella \CsWinRT\src\Samples\NetProjectionSample, che troverai nel tuo file scaricato o clonato dell'esempio di proiezione C#/WinRT .

    1. In una nuova istanza di Visual Studio, seleziona File>Nuovo>Progetto.
    2. Nella finestra di dialogo Crea un nuovo progetto cercare il modello di progetto applicazione console. Scegliere il modello di progetto C# denominato semplicemente app console (senza prefissi né suffissi) e fare clic su Avanti. Se usi 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)e quindi scegliere Crea.
  2. In Esplora soluzionifare 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 SampleConsoleApp.csproj file di progetto ancora aperto, aggiungeremo quindi al progetto SampleConsoleApp un riferimento al pacchetto SimpleMathComponent NuGet. Per ripristinare il SimpleMathComponent NuGet quando si compila il progetto, è possibile usare la proprietà con il percorso alla 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 percorso per il pacchetto SimpleMathComponent illustrato in precedenza è impostato su . Questo percorso è corretto a condizione che tu abbia seguito i passaggi di questa procedura dettagliata, in modo che i progetti SimpleMathComponent e SampleConsoleApp si trovino entrambi nella stessa cartella (la cartella NetProjectionSample, in questo caso). Se hai fatto qualcosa di diverso, dovrai 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. Salva e chiudi i file che hai appena modificato, quindi costruisci ed esegui l'app console. Dovresti vedere l'output qui sotto.

    di output della console NET5

Problemi noti

  • Quando si compila il progetto di proiezione, è possibile che venga visualizzato un errore simile al seguente: Errore MSB3271 Si è verificato un disallineamento tra l'architettura del processore del progetto in costruzione "MSIL" e l'architettura del processore "x86" del file di implementazione "..\SimpleMathComponent.dll" per "..\SimpleMathComponent.winmd". Questa mancata corrispondenza può causare errori di runtime. Si consideri la modifica dell'architettura del processore di destinazione del progetto tramite il Configuration Manager in modo da allineare le architetture dei processori tra il progetto e il file di implementazione, oppure scegliere un file winmd con un file di implementazione dotato di un'architettura di processore corrispondente all'architettura del processore di destinazione del progetto. Per aggirare 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>
    

Considerazioni aggiuntive

L'assembly di proiezione (o di interoperabilità) C# che abbiamo mostrato come creare in questo argomento è piuttosto semplice: 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. Tale è attualmente necessario.

Risorse