Condividi tramite


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

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

In .NET 6 e versioni successive, il consumo di file di metadati Windows (WinMD) non è più supportato (vedere Built-in support for WinRT viene rimosso da .NET). In alternativa, lo strumento C#/WinRT può essere usato per generare un assembly di proiezione per qualsiasi file WinMD, che consente quindi l'utilizzo di componenti WinRT da 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 da un'applicazione console .NET.

Prerequisiti

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

  • Visual Studio 2022 o versione successiva con il carico di lavoro di sviluppo piattaforma UWP (Universal Windows Platform) installato. In Installation Details>piattaforma UWP (Universal Windows Platform) development, controllare l'opzione C++ (v14x) piattaforma UWP (Universal Windows Platform) tools.
  • .NET 8.0 SDK (LTS) o versione successiva.

In questa procedura dettagliata si userà Visual Studio 2022 o versione successiva e .NET 8.

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.

Creare un semplice componente Windows Runtime C++/WinRT

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

Questa procedura dettagliata utilizza il SimpleMathComponent WRC dal campione di proiezione C#/WinRT su GitHub, che hai già scaricato o clonato. SimpleMathComponent è stato creato dal modello di progetto Windows Runtime Component (C++/WinRT) Visual Studio.

Per aprire il progetto SimpleMathComponent in Visual Studio, aprire 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 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 Compatible sia impostata su Yes per il progetto componente SimpleMathComponent C++/WinRT Windows Runtime. A tale scopo, nelle proprietà project per SimpleMathComponent in Proprietà di configurazione>General>Project Defaults impostare la proprietà Windows Desktop Compatible su Yes. 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 ancora 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 Project.
    2. Nella finestra di dialogo Aggiungi un nuovo progetto digitare Libreria di Classi nella casella di ricerca. Scegliere C# dall'elenco delle lingue 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 8.0 (supporto a lungo termine) e quindi scegliere Crea.
  2. Eliminare lo stub del file Class1.cs dal progetto.

  3. Seguire questa procedura 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 Browse digitare o incollare Microsoft.Windows. CsWinRT nella casella di ricerca selezionare l'elemento con la versione più recente e quindi fare clic su Install 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 Dependencies nel nodo SimpleMathProjection project, selezionare Aggiungi riferimento Project e selezionare SimpleMathComponent project >OK.

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

Fino a questo punto, il Esplora soluzioni dovrebbe essere simile a questo (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'esempio di proiezione C#/WinRT (che hai scaricato o clonato da GitHub e che ora hai aperto), il percorso di output della compilazione è configurato con il file Directory.Build.props per compilare fuori 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 come destinazione una versione specifica 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 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 Windows SDK, è anche possibile impostare la proprietà TargetPlatformMinimumVersion.

    <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 i target .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 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 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 è nuovo in .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 compilare la soluzione, assicurarsi di controllare le impostazioni Gestione configurazione in Visual Studio, in Build>Gestione configurazione. Per questa procedura dettagliata, impostare la Configuration su Release e la 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 la soluzione affinché il .nupkg venga visualizzato in Visual Studio come illustrato (o selezionare e quindi deselezionare Mostra tutti i file).

Esplora soluzioni mostra la generazione di proiezioni

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

Per utilizzare SimpleMathComponent da un progetto di .NET, è sufficiente aggiungere a un nuovo progetto .NET un riferimento al pacchetto NuGet SimpleMathComponent0.1.0-prerelease.nupkg pacchetto NuGet 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 selezionare File>New>Project.
    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 si usa Visual Studio 2019, il modello di progetto è Console Application.
    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 8.0 (supporto a lungo termine) e 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 siano 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 a: Error 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 il Gestione configurazione 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>
    

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 SDK per app di Windows, nel progetto di proiezione è necessario aggiungere un riferimento al pacchetto NuGet SDK per app di Windows. 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