Delen via


Een C# .NET-app bouwen met WinUI 3 en Win32-interop

In dit onderwerp wordt uitgelegd hoe u een eenvoudige C# .NET--toepassing bouwt met WinUI 3- en Win32-interopmogelijkheden met platforminvocation Services (PInvoke-).

Voorwaarden

  1. Beginnen met het ontwikkelen van Windows-apps

Basis C#/.NET-beheerde app

In dit voorbeeld geven we de locatie en grootte van het app-venster op, converteren en schalen we dit voor de juiste DPI, schakelen we de minimaliseer- en maximaliseerknoppen van het venster uit, en voeren we ten slotte een query uit op het huidige proces om een lijst weer te geven van de modules die in het huidige proces zijn geladen.

We gaan onze voorbeeld-app opbouwen vanuit de initiƫle sjabloonapplicatie (zie Vereisten). Zie ook WinUI 3-sjablonen in Visual Studio.

Het bestand MainWindow.xaml

Met WinUI 3 kunt u exemplaren van de Window-klasse maken in XAML-markeringen.

De XAML Window-klasse is uitgebreid ter ondersteuning van bureaubladvensters, waardoor deze wordt omgezet in een abstractie van elk van de implementaties op laag niveau die worden gebruikt door de UWP- en desktop-app-modellen. CoreWindow voor UWP en venstergrepen (of HWNDs) voor Win32.

De volgende code toont het Bestand MainWindow.xaml uit de eerste sjabloon-app, die gebruikmaakt van de Window-klasse als het hoofdelement voor de 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>

Configuratie

  1. Als u Win32-API's wilt aanroepen die zijn geƫxporteerd uit User32.dll, kunt u de C#/Win32 P/Invoke Source Generator gebruiken in uw Visual Studio-project. Klik op Tools>NuGet Package Manager>NuGet-pakketten voor oplossing beheren...en (zoek op het tabblad Bladeren) naar Microsoft.Windows.CsWin32-. Zie Systeemeigen functies aanroepen vanuit beheerde codevoor meer informatie.

    U kunt eventueel bevestigen dat de installatie is geslaagd door te bevestigen dat Microsoft.Windows.CsWin32 wordt vermeld onder de Dependencies>Pakketten knooppunt in Solution Explorer.

    U kunt eventueel ook dubbelklikken op het projectbestand van de toepassing (of met de rechtermuisknop klikken en projectbestand bewerken) selecteren om het bestand te openen in een teksteditor en bevestigen dat het projectbestand nu een NuGet-PackageReference bevat voor Microsoft.Windows.CsWin32.

    <Project Sdk="Microsoft.NET.Sdk">
      <PropertyGroup>
        <OutputType>WinExe</OutputType>
        <TargetFramework>net8.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>win-x86;win-x64;win-arm64</RuntimeIdentifiers>
        <PublishProfile>win-$(Platform).pubxml</PublishProfile>
        <UseWinUI>true</UseWinUI>
        <EnableMsixTooling>true</EnableMsixTooling>
        <Nullable>enable</Nullable>
      </PropertyGroup>
    
      <ItemGroup>
        <Content Include="Assets\SplashScreen.scale-200.png" />
        <Content Include="Assets\LockScreenLogo.scale-200.png" />
        <Content Include="Assets\Square150x150Logo.scale-200.png" />
        <Content Include="Assets\Square44x44Logo.scale-200.png" />
        <Content Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" />
        <Content Include="Assets\StoreLogo.png" />
        <Content Include="Assets\Wide310x150Logo.scale-200.png" />
      </ItemGroup>
    
      <ItemGroup>
        <Manifest Include="$(ApplicationManifest)" />
      </ItemGroup>
    
      <!--
        Defining the "Msix" ProjectCapability here allows the Single-project MSIX Packaging
        Tools extension to be activated for this project even if the Windows App SDK Nuget
        package has not yet been restored.
      -->
      <ItemGroup Condition="'$(DisableMsixProjectCapabilityAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'">
        <ProjectCapability Include="Msix" />
      </ItemGroup>
      <ItemGroup>
        <PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.183">
          <PrivateAssets>all</PrivateAssets>
          <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
        </PackageReference>
        <PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.1742" />
        <PackageReference Include="Microsoft.WindowsAppSDK" Version="1.6.250205002" />
      </ItemGroup>
    
      <!--
        Defining the "HasPackageAndPublishMenuAddedByProject" property here allows the Solution
        Explorer "Package and Publish" context menu entry to be enabled for this project even if
        the Windows App SDK Nuget package has not yet been restored.
      -->
      <PropertyGroup Condition="'$(DisableHasPackageAndPublishMenuAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'">
        <HasPackageAndPublishMenu>true</HasPackageAndPublishMenu>
      </PropertyGroup>
    
      <!-- Publish Properties -->
      <PropertyGroup>
        <PublishReadyToRun Condition="'$(Configuration)' == 'Debug'">False</PublishReadyToRun>
        <PublishReadyToRun Condition="'$(Configuration)' != 'Debug'">True</PublishReadyToRun>
        <PublishTrimmed Condition="'$(Configuration)' == 'Debug'">False</PublishTrimmed>
        <PublishTrimmed Condition="'$(Configuration)' != 'Debug'">True</PublishTrimmed>
      </PropertyGroup>
    </Project>
    
  2. Voeg een tekstbestand toe aan uw project en geef het NativeMethods.txteen naam. De inhoud van dit bestand informeert de C#/Win32 P/Invoke Source Generator de functies en typen waarvoor u P/Invoke-broncode wilt genereren. Met andere woorden, welke functies en typen u gaat aanroepen en gebruiken in uw C#-code.

    GetDpiForWindow
    GetWindowLong
    SetWindowPos
    SetWindowLong
    HWND_TOP
    WINDOW_STYLE
    

Code

  1. In het App.xaml.cs code-behind-bestand verkrijgen we een handle voor het Window door gebruik te maken van de WindowNative.GetWindowHandle WinRT COM-interop-methode (zie Retrieve a window handle (HWND)).

    Deze methode wordt aangeroepen vanuit de OnLaunched handler van de app, zoals hier wordt weergegeven:

    /// <summary>
    /// Invoked when the application is launched.
    /// </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();
    }
    
  2. Vervolgens wordt een SetWindowDetails methode aangeroepen, waarbij de venstergreep en voorkeursdimensies worden doorgegeven.

    In deze methode:

    • We noemen GetDpiForWindow om de dpi-waarde (dots per inch) voor het venster op te halen (Win32 gebruikt fysieke pixels, terwijl WinUI 3 effectieve pixels gebruikt). Deze dpi-waarde wordt gebruikt om de schaalfactor te berekenen en toe te passen op de breedte en hoogte die voor het venster is opgegeven.
    • Vervolgens roepen we SetWindowPos aan om de gewenste locatie van het venster op te geven.
    • Ten slotte roepen we SetWindowLong aan om de knoppen Minimaliseren en Maximaliseren uit te schakelen.
    private static void SetWindowDetails(IntPtr hwnd, int width, int height)
    {
        var dpi = Windows.Win32.PInvoke.GetDpiForWindow((Windows.Win32.Foundation.HWND)hwnd);
        float scalingFactor = (float)dpi / 96;
        width = (int)(width * scalingFactor);
        height = (int)(height * scalingFactor);
    
        _ = Windows.Win32.PInvoke.SetWindowPos((Windows.Win32.Foundation.HWND)hwnd,
                                    Windows.Win32.Foundation.HWND.HWND_TOP,
                                    0, 0, width, height,
                                    Windows.Win32.UI.WindowsAndMessaging.SET_WINDOW_POS_FLAGS.SWP_NOMOVE);
    
        var nIndex = Windows.Win32.PInvoke.GetWindowLong((Windows.Win32.Foundation.HWND)hwnd,
                  Windows.Win32.UI.WindowsAndMessaging.WINDOW_LONG_PTR_INDEX.GWL_STYLE) &
                  ~(int)Windows.Win32.UI.WindowsAndMessaging.WINDOW_STYLE.WS_MINIMIZEBOX &
                  ~(int)Windows.Win32.UI.WindowsAndMessaging.WINDOW_STYLE.WS_MAXIMIZEBOX;
    
        _ = Windows.Win32.PInvoke.SetWindowLong((Windows.Win32.Foundation.HWND)hwnd,
               Windows.Win32.UI.WindowsAndMessaging.WINDOW_LONG_PTR_INDEX.GWL_STYLE,
               nIndex);
    }
    
  3. In het bestand MainWindow.xaml gebruiken we een ContentDialog- met een ScrollViewer- om een lijst weer te geven met alle modules die voor het huidige proces zijn geladen.

    <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>
    
  4. Vervolgens vervangen we de MyButton_Click gebeurtenis-handler door de volgende code.

    Hier krijgen we een verwijzing naar het huidige proces door GetCurrentProcessaan te roepen. Vervolgens doorlopen we de verzameling Modules en voegen we de bestandsnaam van elke ProcessModule toe aan onze weergavetekenreeks.

    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();
    }
    
  5. Compileer en voer de app uit.

  6. Nadat het venster wordt weergegeven, selecteert u de knop 'Geladen modules weergeven'.

    Schermopname van de eenvoudige Win32-interoperabiliteitstoepassing die in dit onderwerp wordt beschreven.
    De eenvoudige Win32-interoperabiliteitstoepassing die in dit onderwerp wordt beschreven.

Samenvatting

In dit onderwerp hebben we het gebruik van de onderliggende vensterimplementatie besproken (in dit geval Win32 en HWND's) en het gebruik van Win32-API's, samen met de WinRT-API's. Dit laat zien hoe u bestaande bureaubladtoepassingscode kunt gebruiken bij het maken van nieuwe WinUI 3-bureaublad-apps.

Zie het AppWindow-galerievoorbeeld in de Voorbeelden van de Windows App SDK GitHub-opslagplaats voor een uitgebreider voorbeeld.

Een voorbeeld om de titelbalk van het venster aan te passen

In dit tweede voorbeeld laten we zien hoe u de titelbalk en de inhoud van het venster kunt aanpassen. Bekijk de volgende onderwerpen voordat u deze volgt:

Een nieuw project maken

  1. Maak in Visual Studio een nieuw C# of C++/WinRT-project op basis van de Blanco App, Verpakt (WinUI 3 in Desktop) projectsjabloon.

Configuratie

  1. Raadpleeg opnieuw het Microsoft.Windows.CsWin32 NuGet-pakket, net zoals in het eerste voorbeeld.

  2. Voeg een NativeMethods.txt tekstbestand toe aan uw project.

    LoadImage
    SendMessage
    SetWindowText
    WM_SETICON
    

MainWindow.xaml

Notitie

Als u een pictogrambestand nodig hebt voor deze handleiding, kunt u het computer.ico bestand downloaden van de WirelessHostednetwork voorbeeld app. Plaats dat bestand in uw Assets map en voeg het bestand als inhoud toe aan uw project. Vervolgens kunt u naar het bestand verwijzen met behulp van de URL Assets/computer.ico.

Anders kunt u een pictogrambestand gebruiken dat u al hebt en wijzig de twee verwijzingen ernaar in de onderstaande codevermeldingen.

  1. In de onderstaande codevermelding ziet u dat we in MainWindow.xaml twee knoppen hebben toegevoegd en voor elk Click-handler hebben opgegeven. In de klik handler voor de eerste knop (basicButton_Click), stellen we het titelbalkpictogram en de tekst in. In de tweede (customButton_Click) laten we een significantere aanpassing zien door de titelbalk te vervangen door de inhoud van de StackPanel- met de naam customTitleBarPanel.
<?xml version="1.0" encoding="utf-8"?>
<Window
    x:Class="window_titlebar.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:window_titlebar"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Title="Basic WinUI 3 Window title bar sample">

    <Grid x:Name="rootElement" RowDefinitions="100, *, 100, *">

        <StackPanel x:Name="customTitleBarPanel" Grid.Row="0" Orientation="Horizontal" HorizontalAlignment="Stretch" VerticalAlignment="Top" Visibility="Collapsed">
            <Image Source="Images/windowIcon.gif" />
            <TextBlock VerticalAlignment="Center" Text="Full customization of title bar"/>
        </StackPanel>

        <StackPanel x:Name="buttonPanel"  Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Center">
            <Button x:Name="basicButton" Click="basicButton_Click" Margin="25">Set the Window title and icon</Button>
            <Button x:Name="customButton" Click="customButton_Click" Margin="25">Customize the window title bar</Button>
        </StackPanel>

    </Grid>
</Window>

MainWindow.xaml.cs/cpp

  1. In de onderstaande codevermelding voor de basicButton_Click handler , om de aangepaste titelbalk verborgen te houden, vouwen we de eigenschap customTitleBarPanelStackPanelsamen en stellen we de eigenschap ExtendContentIntoTitleBar in op false.
  2. Vervolgens roepen we IWindowNative::get_WindowHandle (voor C# met behulp van de helpermethode voor interop GetWindowHandle) aan om de venstergreep (HWND-) van het hoofdvenster op te halen.
  3. Vervolgens stellen we het toepassingspictogram (voor C# met behulp van het PInvoke.User32 NuGet-pakket) in door de functies LoadImage en SendMessage- aan te roepen.
  4. Ten slotte roepen we SetWindowText- aan om de titelbalktekenreeks bij te werken.
private void basicButton_Click(object sender, RoutedEventArgs e)
{
    // Ensure the custom title bar content is not displayed.
    customTitleBarPanel.Visibility = Visibility.Collapsed;

    // Disable custom title bar content.
    ExtendsContentIntoTitleBar = false;

    //Get the Window's HWND
    var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this);

    var hIcon = Windows.Win32.PInvoke.LoadImage(
        null,
        "Images/windowIcon.ico",
        Windows.Win32.UI.WindowsAndMessaging.GDI_IMAGE_TYPE.IMAGE_ICON,
        20, 20,
        Windows.Win32.UI.WindowsAndMessaging.IMAGE_FLAGS.LR_LOADFROMFILE);

    Windows.Win32.PInvoke.SendMessage(
        (Windows.Win32.Foundation.HWND)hwnd,
        Windows.Win32.PInvoke.WM_SETICON,
        (Windows.Win32.Foundation.WPARAM)0,
        (Windows.Win32.Foundation.LPARAM)hIcon.DangerousGetHandle());

    Windows.Win32.PInvoke.SetWindowText((Windows.Win32.Foundation.HWND)hwnd, "Basic customization of title bar");
}
// pch.h
...
#include <microsoft.ui.xaml.window.h>
...

// MainWindow.xaml.h
...
void basicButton_Click(Windows::Foundation::IInspectable const& sender, Microsoft::UI::Xaml::RoutedEventArgs const& args);
...

// MainWindow.xaml.cpp
void MainWindow::basicButton_Click(IInspectable const&, RoutedEventArgs const&)
{
    // Ensure the that custom title bar content is not displayed.
    customTitleBarPanel().Visibility(Visibility::Collapsed);

    // Disable custom title bar content.
    ExtendsContentIntoTitleBar(false);

    // Get the window's HWND
    auto windowNative{ this->m_inner.as<::IWindowNative>() };
    HWND hWnd{ 0 };
    windowNative->get_WindowHandle(&hWnd);

    HICON icon{ reinterpret_cast<HICON>(::LoadImage(nullptr, L"Assets/computer.ico", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE)) };
    ::SendMessage(hWnd, WM_SETICON, 0, (LPARAM)icon);

    this->Title(L"Basic customization of title bar");
}
  1. In de customButton_Click handler stellen we de zichtbaarheid van de customTitleBarPanelStackPanel in op Visible.
  2. Vervolgens stellen we de eigenschap ExtendContentIntoTitleBar in op trueen roepen SetTitleBar aan om de customTitleBarPanelStackPanel weer te geven als de aangepaste titelbalk.
private void customButton_Click(object sender, RoutedEventArgs e)
{
    customTitleBarPanel.Visibility = Visibility.Visible;

    // Enable custom title bar content.
    ExtendsContentIntoTitleBar = true;
    // Set the content of the custom title bar.
    SetTitleBar(customTitleBarPanel);
}
// MainWindow.xaml.h
...
void customButton_Click(Windows::Foundation::IInspectable const& sender, Microsoft::UI::Xaml::RoutedEventArgs const& args);
...

// MainWindow.xaml.cpp
void MainWindow::customButton_Click(IInspectable const&, RoutedEventArgs const&)
{
    customTitleBarPanel().Visibility(Visibility::Visible);

    // Enable custom title bar content.
    ExtendsContentIntoTitleBar(true);

    // Set the content of the custom title bar.
    SetTitleBar(customTitleBarPanel());
}

App.xaml

  1. Direct na de App.xaml-opmerking in het <!-- Other app resources here -->-bestand hebben we een aantal op maat gemaakte penselen toegevoegd voor de titelbalk, zoals hieronder weergegeven.
<?xml version="1.0" encoding="utf-8"?>
<Application
    x:Class="window_titlebar.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:window_titlebar">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
                <!-- Other merged dictionaries here -->
            </ResourceDictionary.MergedDictionaries>
            <!-- Other app resources here -->
            <SolidColorBrush x:Key="WindowCaptionBackground">Green</SolidColorBrush>
            <SolidColorBrush x:Key="WindowCaptionBackgroundDisabled">LightGreen</SolidColorBrush>
            <SolidColorBrush x:Key="WindowCaptionForeground">Red</SolidColorBrush>
            <SolidColorBrush x:Key="WindowCaptionForegroundDisabled">Pink</SolidColorBrush>
        </ResourceDictionary>
    </Application.Resources>
</Application>
  1. Als u deze stappen in uw eigen app hebt gevolgd, kunt u uw project nu bouwen en de app uitvoeren. U ziet een toepassingsvenster dat lijkt op het volgende (met het pictogram van de aangepaste app):

    sjabloon-app zonder aanpassing.
    Sjabloon-app.

  • Dit is de standaard aangepaste titelbalk:

    sjabloon-app met aangepast toepassingspictogram.
    sjabloon-app met aangepast toepassingspictogram.

  • Dit is de volledig aangepaste titelbalk:

    Sjabloon-app met aangepaste titelbalk.
    Sjabloon-app met aangepaste titelbalk.

Zie ook