Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Neste tópico, explicamos como construir uma aplicação básica C# .NET com capacidades de interoperabilidade WinUI e Win32 usando Platform Invocation Services (PInvoke).
Prerequisites
Aplicação básica gerida em C#/.NET
Para este exemplo, especificaremos o local e o tamanho da janela do aplicativo, converteremos e dimensionaremos para o DPI apropriado, desativaremos os botões minimizar e maximizar a janela e, finalmente, consultaremos o processo atual para mostrar uma lista dos módulos que são carregados no processo atual.
Vamos construir a nossa aplicação de exemplo a partir da aplicação modelo inicial (ver Pré-requisitos). Veja também os modelos WinUI em Visual Studio.
O arquivo MainWindow.xaml
Com o WinUI, pode criar instâncias da classe Window em marcação XAML.
A classe XAML Window foi estendida para suportar janelas de ambiente de trabalho, transformando-a numa abstração de cada uma das implementações de janelas de baixo nível usadas pelos modelos UWP e de aplicações de desktop. Especificamente, CoreWindow para UWP e identificadores de janela (ou HWNDs) para Win32.
O código seguinte mostra o ficheiro MainWindow.xaml da aplicação modelo inicial, que usa a classe Window como elemento raiz da aplicação.
<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>
Configuration
Para chamar APIs Win32 exportadas de
User32.dll, pode utilizar o Gerador de Código C#/Win32 P/Invoke no seu projeto do Visual Studio. Clique em Tools>NuGet Package Manager>Gerenciar pacotes NuGet para solução... , e (no separador Navegar) procurar por Microsoft.Windows.CsWin32. Para obter mais detalhes, consulte chamando funções nativas do Managed Code.Pode, opcionalmente, confirmar que a instalação foi bem-sucedida ao verificar se Microsoft.Windows.CsWin32 está listado no nó Dependências>Pacotes no Explorador de Soluções.
Também pode, opcionalmente, fazer duplo clique sobre o ficheiro de projeto da aplicação (ou clicar com o botão direito e selecionar Editar ficheiro de projeto) para abrir o ficheiro num editor de texto e confirmar que o ficheiro de projeto agora inclui um NuGet
PackageReferencepara "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>Adicione um ficheiro de texto ao seu project e nomee-o
NativeMethods.txt. O conteúdo deste arquivo informa o C#/Win32 P/Invoke Source Generator as funções e tipos para os quais você deseja que o código-fonte P/Invoke seja gerado. Em outras palavras, quais funções e tipos você chamará e usará em seu código C#.GetDpiForWindow GetWindowLong SetWindowPos SetWindowLong HWND_TOP WINDOW_STYLE
Code
No
App.xaml.csficheiro code-behind, obtém o handle para a Window usando o método COM interop WindowNative.GetWindowHandle do WinRT (ver Obtenha um handle da janela (HWND)).Esse método é chamado a partir do handler OnLaunched da aplicação, como mostrado aqui:
/// <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(); }Em seguida, chamamos um método
SetWindowDetails, passando o identificador da janela e as dimensões preferidas.Neste método:
- Chamamos o GetDpiForWindow para obter o valor de pontos por polegada (dpi) da janela (o Win32 usa píxeis físicos, enquanto o WinUI usa píxeis efetivos). Esse valor de dpi é usado para calcular o fator de escala e aplicá-lo à largura e altura especificadas para a janela.
- Depois chamamos SetWindowPos para especificar a localização desejada da janela.
- Por fim, chamamos o SetWindowLong para desativar os botões Minimizar e Maximizar .
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); }No ficheiro MainWindow.xaml, usamos um ContentDialog com um ScrollViewer para mostrar uma lista de todos os módulos carregados para o processo atual.
<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>Em seguida, substituímos o manipulador de eventos
MyButton_Clickpelo código a seguir.Aqui, obtemos uma referência ao processo atual chamando GetCurrentProcess. Depois iteramos pela coleção de Módulos e adicionamos o nome do ficheiro de cada ProcessModule à nossa cadeia de visualização.
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(); }Compile e execute o aplicativo.
Depois que a janela aparecer, selecione o botão "Exibir módulos carregados".
O aplicativo de interoperabilidade Win32 básico descrito neste tópico.
Summary
Neste tópico, abordamos o acesso à implementação da janela subjacente (neste caso, Win32 e HWNDs) e o uso de APIs do Win32 junto com as APIs do WinRT. Isto demonstra como pode usar código de aplicação de ambiente de trabalho existente ao criar novas aplicações de desktop WinUI.
Para uma amostra mais extensa, consulte a galeria de amostras AppWindow no repositório Windows App SDK Samples GitHub.
Um exemplo para personalizar a barra de título da janela
Neste segundo exemplo, mostramos como personalizar a barra de título da janela e seu conteúdo. Antes de acompanhá-lo, analise estes tópicos:
Crie um novo project
- No Visual Studio, crie um novo projeto em C# ou C++/WinRT a partir do modelo de projeto WinUI Blank App (Packaged ).
Configuration
Mais uma vez, consulte o pacote NuGet Microsoft.Windows.CsWin32 tal como fizemos no primeiro exemplo.
Adiciona um ficheiro de texto
NativeMethods.txtao teu project.LoadImage SendMessage SetWindowText WM_SETICON
MainWindow.xaml
Note
Se precisares de um ficheiro de ícone para usar com este guia, podes descarregar o ficheiro computer.ico da aplicação de exemplo WirelessHostednetwork. Coloque esse ficheiro na sua pasta Assets e adicione o ficheiro ao seu project como conteúdo. Em seguida, você poderá consultar o arquivo usando o url Assets/computer.ico.
Caso contrário, sinta-se à vontade para usar um arquivo de ícone que você já tem e alterar as duas referências a ele nas listagens de código abaixo.
- Na listagem de código abaixo, verá que no
MainWindow.xamladicionámos dois botões e especificámos manipuladores de clique para cada um. No controlador de cliques para o primeiro botão (basicButton_Click), definimos o ícone da barra de título e o texto. No segundo (customButton_Click), demonstramos uma personalização mais significativa ao substituir a barra de título pelo conteúdo do StackPaneldenominado 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
- Na listagem de código abaixo para o evento basicButton_Click — para manter a barra de título personalizada oculta — recolhemos o customTitleBarPanelStackPanel e configuramos a propriedade ExtendsContentIntoTitleBar para
false. - Depois chamamos IWindowNative::get_WindowHandle (para C#, usando o método auxiliar de interop GetWindowHandle) para recuperar o handle da janela (HWND) da janela principal.
- De seguida, definimos o ícone da aplicação (para C#, usando o pacote NuGet PInvoke.User32 ) chamando as funções LoadImage e SendMessage .
- Finalmente, chamamos SetWindowText para atualizar a cadeia de texto da barra de título.
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");
}
- No handler customButton_Click , definimos a visibilidade do customTitleBarPanelStackPanel para Visible.
- Depois definimos a propriedade ExtendsContentIntoTitleBar para
true, e chamamos SetTitleBar para mostrar o customTitleBarPanelStackPanel como a nossa barra de título personalizada.
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
- No arquivo
App.xaml, imediatamente após o comentário<!-- Other app resources here -->, adicionamos alguns pincéis coloridos personalizados para a barra de título, conforme mostrado abaixo.
<?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>
Se tem seguido estes passos na sua própria aplicação, pode agora construir o seu projeto e executá-lo. Você verá uma janela do aplicativo semelhante à seguinte (com o ícone do aplicativo personalizado):
Aplicação modelo.
Aqui está a barra de título personalizada básica:
Aplicativo modelo com ícone de aplicativo personalizado.Aqui está a barra de título totalmente personalizada:
Aplicativo modelo com barra de título personalizada.
Consulte também
Windows developer