Creare un'app .NET C# con interoperabilità WinUI 3 e Win32
In questo articolo viene illustrato come creare un'applicazione C# .NET di base con funzionalità di interoperabilità WinUI 3 e Win32 usando Platform Invocation Services ( PInvoke).
Prerequisiti
App C#/.NET gestita di base
Per questo esempio, si specificheranno la posizione e le dimensioni della finestra dell'app, questa verrà convertita e ridimensionata secondo i valori DPI appropriati, saranno disabilitati i pulsanti di riduzione a icona e ingrandimento della finestra e infine si eseguirà una query sul processo corrente per visualizzare l'elenco di moduli caricati nel processo.
Si creerà l'app di esempio dall'applicazione modello iniziale (vedere Prerequisiti). Vedere anche Modelli WinUI 3 in Visual Studio.
Il file MainWindow.xaml
Con WinUI 3, è possibile creare istanze della classe Window nel markup XAML.
La classe Window XAML è stata estesa per supportare le finestre desktop, trasformandola in un'astrazione di ognuna delle implementazioni di finestre di basso livello usate dai modelli di app UWP e desktop. In particolare, CoreWindow per piattaforma UWP e handle di finestra (o HWND) per Win32.
Il codice seguente mostra il file MainWindow.xaml dall'app modello iniziale, che usa la classe Window come elemento radice per l'app.
<Window
x:Class="WinUI_3_basic_win32_interop.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:WinUI_3_basic_win32_interop"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<Button x:Name="myButton" Click="myButton_Click">Click Me</Button>
</StackPanel>
</Window>
Configurazione
Per chiamare le API Win32 esposte in User32.dll, aggiungere il pacchetto NuGet PInvoke.User32 open source al progetto VS (dai menu di Visual Studio, selezionare Strumenti -> Gestione pacchetti NuGet -> Gestisci pacchetti NuGet per la soluzione... e cercare "Pinvoke.User32"). Per ulteriori dettagli, vedere Chiamata di funzioni native da codice gestito.
Gestione pacchetti NuGet con PInvoke.User32 selezionata.Verificare che l'installazione sia riuscita controllando la cartella Pacchetti nel progetto VS.
Pacchetti di Esplora soluzioni con PInvoke.User32.Fare quindi doppio clic sul file di progetto dell'applicazione (o fare clic con il pulsante destro del mouse e selezionare "Modifica file di progetto") per aprire il file in un editor di testo e verificare che il file di progetto includa ora il NuGet
PackageReference
per "PInvoke.User32".<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>WinExe</OutputType> <TargetFramework>net6.0-windows10.0.19041.0</TargetFramework> <TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion> <RootNamespace>WinUI_3_basic_win32_interop</RootNamespace> <ApplicationManifest>app.manifest</ApplicationManifest> <Platforms>x86;x64;arm64</Platforms> <RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers> <UseWinUI>true</UseWinUI> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.ProjectReunion" Version="0.8.1" /> <PackageReference Include="Microsoft.ProjectReunion.Foundation" Version="0.8.1" /> <PackageReference Include="Microsoft.ProjectReunion.WinUI" Version="0.8.1" /> <PackageReference Include="PInvoke.User32" Version="0.7.104" /> <Manifest Include="$(ApplicationManifest)" /> </ItemGroup> </Project>
Codice
Nel
App.xaml.cs
file code-behind si ottiene un handle per la finestra usando il metodo di interoperabilità COM WindowNative.GetWindowHandle WinRT (vedere Recuperare un handle di finestra (HWND)).Questo metodo viene chiamato dal gestore OnLaunched dell'app, come illustrato di seguito:
/// <summary> /// Invoked when the application is launched normally by the end user. Other entry points /// will be used such as when the application is launched to open a specific file. /// </summary> /// <param name="args">Details about the launch request and process.</param> protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args) { m_window = new MainWindow(); var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(m_window); SetWindowDetails(hwnd, 800, 600); m_window.Activate(); }
Quindi si chiama un metodo
SetWindowDetails
, trasmettendo l'handle Window e le dimensioni preferite. Ricordarsi di aggiungere la direttivausing static PInvoke.User32;
.In questo metodo:
- Si chiama GetDpiForWindow per ottenere il valore dei punti per pollice (dpi) per la finestra (Win32 usa pixel attuali mentre WinUI 3 usa pixel effettivi). Questo valore dpi viene usato per calcolare il fattore di scala e applicarlo alla larghezza e all'altezza specificata per la finestra.
- Si chiama quindi SetWindowPos per specificare la posizione della finestra desiderata.
- Infine, si chiama SetWindowLong per disabilitare i pulsanti Riduzione a icona e Ingrandimento.
private static void SetWindowDetails(IntPtr hwnd, int width, int height) { var dpi = GetDpiForWindow(hwnd); float scalingFactor = (float)dpi / 96; width = (int)(width * scalingFactor); height = (int)(height * scalingFactor); _ = SetWindowPos(hwnd, SpecialWindowHandles.HWND_TOP, 0, 0, width, height, SetWindowPosFlags.SWP_NOMOVE); _ = SetWindowLong(hwnd, WindowLongIndexFlags.GWL_STYLE, (SetWindowLongFlags)(GetWindowLong(hwnd, WindowLongIndexFlags.GWL_STYLE) & ~(int)SetWindowLongFlags.WS_MINIMIZEBOX & ~(int)SetWindowLongFlags.WS_MAXIMIZEBOX)); }
Nel file MainWindow.xaml viene usato un contentDialog con ScrollViewer per visualizzare l'elenco di tutti i moduli caricati per il processo corrente.
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center"> <Button x:Name="myButton" Click="myButton_Click">Display loaded modules</Button> <ContentDialog x:Name="contentDialog" CloseButtonText="Close"> <ScrollViewer> <TextBlock x:Name="cdTextBlock" TextWrapping="Wrap" /> </ScrollViewer> </ContentDialog> </StackPanel>
Sostituire quindi il
MyButton_Click
gestore eventi con il codice seguente.In questo caso si ottiene un riferimento al processo corrente chiamando GetCurrentProcess. Si scorre quindi la raccolta di Moduli e si aggiunge il nome file di ogni ProcessModule alla stringa di visualizzazione.
private async void myButton_Click(object sender, RoutedEventArgs e) { myButton.Content = "Clicked"; var description = new System.Text.StringBuilder(); var process = System.Diagnostics.Process.GetCurrentProcess(); foreach (System.Diagnostics.ProcessModule module in process.Modules) { description.AppendLine(module.FileName); } cdTextBlock.Text = description.ToString(); await contentDialog.ShowAsync(); }
Compila ed esegui l'app.
Dopo aver visualizzato la finestra, selezionare il pulsante "Visualizza moduli caricati".
Applicazione di interoperabilità Win32 di base descritta in questo argomento.
Riepilogo
In questo argomento è stato illustrato l'accesso all'implementazione della finestra sottostante (in questo caso Win32 e HWND) e all'uso delle API Win32 insieme alle API WinRT. In questo modo viene illustrato come usare il codice dell'applicazione desktop esistente durante la creazione di nuove app desktop WinUI 3.
Per un esempio più completo, vedere l'esempio della raccolta AppWindow nel repository GitHub degli esempi di Windows App SDK.