Initial commit

This commit is contained in:
2026-09-18 14:26:02 +03:00 Unverified
commit 171f94414d
22 changed files with 1340 additions and 0 deletions
+365
View File
@@ -0,0 +1,365 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Oo]ut/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
onnx-models
+16
View File
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<Application
x:Class="IPClab1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:IPClab1">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
<!-- Other merged dictionaries here -->
</ResourceDictionary.MergedDictionaries>
<!-- Other app resources here -->
</ResourceDictionary>
</Application.Resources>
</Application>
+69
View File
@@ -0,0 +1,69 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using IPClab1.Services;
using IPClab1.ViewModels;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using Microsoft.UI.Xaml.Shapes;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.Foundation;
using Windows.Foundation.Collections;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace IPClab1;
/// <summary>
/// Provides application-specific behavior to supplement the default Application class.
/// </summary>
public partial class App : Application
{
public static IServiceProvider Services
{
get;
private set;
}
private Window? _window;
/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
public App()
{
InitializeComponent();
Services = ConfigureServices();
}
/// <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)
{
_window = Services.GetRequiredService<MainWindow>();
_window.Activate();
}
private static ServiceProvider ConfigureServices()
{
var services = new ServiceCollection();
services.AddTransient<WindowListService>();
services.AddTransient<WindowInteractionService>();
services.AddTransient<MainWindow>();
services.AddTransient<MainWindowViewModel>();
return services.BuildServiceProvider();
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 432 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 637 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 283 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

+14
View File
@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IPClab1.Enums;
public enum WindowState
{
Minimize,
Maximize,
Restore
}
+67
View File
@@ -0,0 +1,67 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
<LangVersion>14.0</LangVersion>
<RootNamespace>IPClab1</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>
<WinUISDKReferences>false</WinUISDKReferences>
<EnableMsixTooling>true</EnableMsixTooling>
<Nullable>enable</Nullable>
<WindowsPackageType>None</WindowsPackageType>
<WindowsAppSdkBootstrapInitialize>true</WindowsAppSdkBootstrapInitialize>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</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="CommunityToolkit.Mvvm" Version="8.4.2" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.12" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.28000.2705" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="2.5.1" />
<PackageReference Include="System.Drawing.Common" Version="10.0.12" />
</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>
+13
View File
@@ -0,0 +1,13 @@
<Solution>
<Configurations>
<Platform Name="ARM64" />
<Platform Name="x64" />
<Platform Name="x86" />
</Configurations>
<Project Path="IPClab1.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
<Platform Solution="*|x86" Project="x86" />
<Deploy />
</Project>
</Solution>
+149
View File
@@ -0,0 +1,149 @@
<?xml version="1.0" encoding="utf-8"?>
<Window
x:Class="IPClab1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:IPClab1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:m="using:IPClab1.Models"
xmlns:e="using:IPClab1.Enums"
mc:Ignorable="d"
Title="IPClab1">
<Window.SystemBackdrop>
<MicaBackdrop />
</Window.SystemBackdrop>
<Grid VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TitleBar IsBackButtonVisible="False" IsPaneToggleButtonVisible="False" x:Name="AppTitleBar"/>
<Grid Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ScrollView ContentOrientation="Vertical">
<StackPanel Padding="36">
<TextBlock Text="1. Выберите окно:" Margin="0,0,0,36" Style="{ThemeResource TitleTextBlockStyle}"/>
<GridView x:Name="WindowGrid" ItemsSource="{x:Bind ViewModel.Windows, Mode=OneWay}" SizeChanged="GridView_SizeChanged" CornerRadius="8"
Background="{ThemeResource LayerFillColorDefaultBrush}" Padding="8,8,8,4" SelectionChanged="WindowGrid_SelectionChanged">
<GridView.ItemContainerStyle>
<Style TargetType="GridViewItem" BasedOn="{StaticResource DefaultGridViewItemStyle}">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</GridView.ItemContainerStyle>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid Orientation="Horizontal" ItemHeight="64"/>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
<GridView.ItemTemplate>
<DataTemplate x:DataType="m:WindowInfo">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch" VerticalAlignment="Center">
<Image Source="{x:Bind IconSource}" Height="32" Width="32" Margin="12,0,12,0"/>
<StackPanel Orientation="Vertical">
<TextBlock Text="{x:Bind WindowTitle}" Style="{ThemeResource BodyTextBlockStyle}"/>
<TextBlock Text="{x:Bind ProcessId.ToString()}" Style="{ThemeResource CaptionTextBlockStyle}"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
</StackPanel>
</ScrollView>
<ScrollView Grid.Column="1" >
<StackPanel Padding="36">
<TextBlock Text="2. Выберите взаимодействие:" Margin="0,0,0,36" Style="{ThemeResource TitleTextBlockStyle}"/>
<Grid x:Name="ActionsGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition x:Name="ActionRow0" Height="Auto"/>
<RowDefinition x:Name="ActionRow1" Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Border Margin="12" Background="{ThemeResource LayerFillColorDefaultBrush}" SizeChanged="RowContent_SizeChanged" CornerRadius="8">
<StackPanel Padding="16">
<TextBlock Text="Симулировать нажатие" Margin="0,0,0,16" Style="{ThemeResource SubtitleTextBlockStyle}"/>
<Grid Margin="24,0,24,16">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal">
<TextBlock Text="X:" Style="{ThemeResource BodyStrongTextBlockStyle}" VerticalAlignment="Center" Margin="0,0,8,0"/>
<NumberBox SmallChange="1" Minimum="0" Value="{x:Bind ViewModel.X, Mode=TwoWay}" Maximum="{x:Bind ViewModel.Width, Mode=OneWay}" SpinButtonPlacementMode="Compact" VerticalAlignment="Center"/>
</StackPanel>
<StackPanel Grid.Column="1" HorizontalAlignment="Right" Orientation="Horizontal">
<TextBlock Text="Y:" Style="{ThemeResource BodyStrongTextBlockStyle}" VerticalAlignment="Center" Margin="0,0,8,0"/>
<NumberBox SmallChange="1" Minimum="0" Value="{x:Bind ViewModel.Y, Mode=TwoWay}" Maximum="{x:Bind ViewModel.Height, Mode=OneWay}" SpinButtonPlacementMode="Compact" VerticalAlignment="Center"/>
</StackPanel>
</Grid>
<Button Command="{x:Bind ViewModel.SendClickCommand}" HorizontalAlignment="Stretch" Content="Отправить" Margin="0,0,0,16"/>
<InfoBar IsOpen="True" Message="Работает не очень" IsClosable="False" HorizontalAlignment="Center"/>
</StackPanel>
</Border>
<Border Grid.Column="1" Margin="12" Background="{ThemeResource LayerFillColorDefaultBrush}" SizeChanged="RowContent_SizeChanged" CornerRadius="8">
<StackPanel Padding="16">
<TextBlock Text="Заменить заголовок" Margin="0,0,0,16" Style="{ThemeResource SubtitleTextBlockStyle}"/>
<TextBox Text="{x:Bind ViewModel.NewTitle, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="0,0,0,16"/>
<Button Command="{x:Bind ViewModel.SetNewTitleCommand}" HorizontalAlignment="Stretch" Content="Отправить"/>
</StackPanel>
</Border>
<Border Grid.Row="1" Margin="12" Background="{ThemeResource LayerFillColorDefaultBrush}" SizeChanged="RowContent_SizeChanged" CornerRadius="8">
<StackPanel Padding="16">
<TextBlock Text="Сменить состояние" Margin="0,0,0,16" Style="{ThemeResource SubtitleTextBlockStyle}"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button Content="Свернуть" Margin="12" HorizontalAlignment="Center" Command="{x:Bind ViewModel.SetWindowStateCommand}" CommandParameter="{x:Bind e:WindowState.Minimize}"/>
<Button Grid.Column="1" Content="Восстановить" Margin="12" HorizontalAlignment="Center" Command="{x:Bind ViewModel.SetWindowStateCommand}" CommandParameter="{x:Bind e:WindowState.Restore}"/>
<Button Grid.Row="1" Content="Развернуть" Margin="12" HorizontalAlignment="Center" Command="{x:Bind ViewModel.SetWindowStateCommand}" CommandParameter="{x:Bind e:WindowState.Maximize}"/>
<Border Grid.Row="1" Grid.Column="1" Background="{ThemeResource SystemFillColorCriticalBackgroundBrush}" Padding="12" HorizontalAlignment="Center" CornerRadius="4">
<Button Content="Закрыть" Command="{x:Bind ViewModel.CloseWindowCommand}" Click="CloseButton_Click"/>
</Border>
</Grid>
</StackPanel>
</Border>
<Border Grid.Row="1" Grid.Column="1" Margin="12" Background="{ThemeResource LayerFillColorDefaultBrush}" SizeChanged="RowContent_SizeChanged" CornerRadius="8">
<StackPanel Padding="16">
<TextBlock Text="Информация" Margin="0,0,0,16" Style="{ThemeResource SubtitleTextBlockStyle}"/>
<TextBlock Text="{x:Bind ViewModel.InfoString, Mode=OneWay}" FontFamily="Cascadia Code, Consolas, monospace" TextWrapping="Wrap"/>
</StackPanel>
</Border>
<Border Grid.Row="2" Grid.ColumnSpan="2"/>
</Grid>
</StackPanel>
</ScrollView>
<Border Grid.Row="1" Grid.ColumnSpan="2"
Background="{ThemeResource LayerFillColorDefault}"
BorderBrush="{ThemeResource DividerStrokeColorDefaultBrush}"
BorderThickness="0,1,0,0"
Padding="24">
<StackPanel Orientation="Horizontal">
<Button Content="Получить список окон" Command="{x:Bind ViewModel.RefreshWindowsCommand}" Style="{StaticResource AccentButtonStyle}"/>
<TextBlock Text="Только видимые" VerticalAlignment="Center" Margin="24,0,12,2"/>
<ToggleSwitch OnContent="Да" OffContent="Нет" IsOn="{x:Bind ViewModel.VisibleOnly, Mode=TwoWay}"/>
</StackPanel>
</Border>
</Grid>
</Grid>
</Window>
+98
View File
@@ -0,0 +1,98 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using IPClab1.Models;
using IPClab1.ViewModels;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using Windows.Foundation;
using Windows.Foundation.Collections;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace IPClab1;
/// <summary>
/// An empty window that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainWindow : Window
{
private readonly DispatcherTimer timer = new() { Interval = TimeSpan.FromMilliseconds(100) };
public MainWindowViewModel ViewModel;
public MainWindow(MainWindowViewModel viewModel)
{
InitializeComponent();
ViewModel = viewModel;
ExtendsContentIntoTitleBar = true;
SetTitleBar(AppTitleBar);
timer.Tick += Timer_Tick;
timer.Start();
}
private void Timer_Tick(object? sender, object e)
{
AppTitleBar.Title = Title;
ViewModel.RefreshInfo();
}
private void GridView_SizeChanged(object sender, SizeChangedEventArgs e)
{
var gridView = sender as GridView;
double availableWidth = e.NewSize.Width - gridView.Padding.Left - gridView.Padding.Right;
double minItemWidth = 250; // The absolute minimum width you want an item to be
double baseSpacing = 8; // Your baseline gap width
// Calculate how many columns can mathematically fit
int columns = (int)Math.Floor((availableWidth + baseSpacing) / (minItemWidth + baseSpacing));
columns = Math.Max(1, columns); // Ensure at least 1 column
// Dynamically calculate the item width to absorb the remaining space
double dynamicWidth = ((availableWidth + baseSpacing) / columns) - baseSpacing;
if (WindowGrid.ItemsPanelRoot is ItemsWrapGrid wrapGrid)
{
wrapGrid.ItemWidth = dynamicWidth;
}
}
private void WindowGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems.Count > 0) ViewModel.SelectedWindow = (WindowInfo)e.AddedItems[0];
}
private void RowContent_SizeChanged(object sender, SizeChangedEventArgs e)
{
// 1. Get the actual natural height of the content in both rows
// (Assuming the direct children of Row 0 and Row 1 are FrameworkElements)
double row0Height = ((FrameworkElement)ActionsGrid.Children[0]).ActualHeight;
double row1Height = ((FrameworkElement)ActionsGrid.Children[1]).ActualHeight;
double row2Height = ((FrameworkElement)ActionsGrid.Children[2]).ActualHeight;
double row3Height = ((FrameworkElement)ActionsGrid.Children[3]).ActualHeight;
// 2. Find which one is larger
double maxHeight = Math.Max(Math.Max(row0Height, row1Height), Math.Max(row2Height, row3Height));
// 3. Force both rows to match the largest height
ActionRow0.MinHeight = maxHeight;
ActionRow1.MinHeight = maxHeight;
// Note: Row 2 will automatically shrink to 0 height if Row 0 and Row 1
// consume the entire available height of the parent Grid.
}
private void CloseButton_Click(object sender, RoutedEventArgs e)
{
ViewModel.Windows.Remove((WindowInfo)WindowGrid.SelectedItem);
WindowGrid.SelectedIndex = -1;
}
}
+36
View File
@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Channels;
using System.Threading.Tasks;
using Microsoft.UI.Xaml.Media.Imaging;
namespace IPClab1.Models;
public class WindowInfo
{
public IntPtr Hwnd
{
get;
set;
}
public uint ProcessId
{
get;
set;
}
public string WindowTitle
{
get;
set;
}
public SoftwareBitmapSource IconSource
{
get;
set;
}
}
+53
View File
@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
xmlns:systemai="http://schemas.microsoft.com/appx/manifest/systemai/windows10"
IgnorableNamespaces="uap rescap systemai">
<Identity
Name="97d7a681-9417-442c-954b-e3b3a3a9d28e"
Publisher="CN=retrd"
Version="1.0.0.0" />
<mp:PhoneIdentity PhoneProductId="97d7a681-9417-442c-954b-e3b3a3a9d28e" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
<Properties>
<DisplayName>IPClab1</DisplayName>
<PublisherDisplayName>retrd</PublisherDisplayName>
<Logo>Assets\StoreLogo.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.26226.0" />
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.26226.0" />
</Dependencies>
<Resources>
<Resource Language="x-generate"/>
</Resources>
<Applications>
<Application Id="App"
Executable="$targetnametoken$.exe"
EntryPoint="$targetentrypoint$">
<uap:VisualElements
DisplayName="IPClab1"
Description="IPClab1"
BackgroundColor="transparent"
Square150x150Logo="Assets\Square150x150Logo.png"
Square44x44Logo="Assets\Square44x44Logo.png">
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png" />
<uap:SplashScreen Image="Assets\SplashScreen.png" />
</uap:VisualElements>
</Application>
</Applications>
<Capabilities>
<rescap:Capability Name="runFullTrust" />
<systemai:Capability Name="systemAIModels"/>
</Capabilities>
</Package>
+10
View File
@@ -0,0 +1,10 @@
{
"profiles": {
"IPClab1 (Package)": {
"commandName": "MsixPackage"
},
"IPClab1 (Unpackaged)": {
"commandName": "Project"
}
}
}
+110
View File
@@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using IPClab1.Enums;
namespace IPClab1.Services;
public class WindowInteractionService
{
private const uint WM_MOUSEMOVE = 0x0200;
private const uint WM_LBUTTONDOWN = 0x0201;
private const uint WM_LBUTTONUP = 0x0202;
private const uint WM_SETTEXT = 0x000C;
private const uint WM_CLOSE = 0x0010;
private const uint WM_SYSCOMMAND = 0x0112;
private const int SC_MINIMIZE = 0xF020;
private const int SC_MAXIMIZE = 0xF030;
private const int SC_RESTORE = 0xF120;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, string lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
// 1. Get Class Name
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
// 2. Get Geometry
[DllImport("user32.dll", SetLastError = true)]
private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
// 3. Get Parent
[DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
private static extern IntPtr GetParent(IntPtr hWnd);
public void SimulateClick(IntPtr hwnd, int x, int y)
{
IntPtr lParam = (IntPtr)((y << 16) | (x & 0xFFFF));
PostMessage(hwnd, WM_MOUSEMOVE, IntPtr.Zero, lParam);
PostMessage(hwnd, WM_LBUTTONDOWN, (IntPtr)1, lParam);
PostMessage(hwnd, WM_LBUTTONUP, IntPtr.Zero, lParam);
}
public void SetText(IntPtr hwnd, string text)
{
if (string.IsNullOrEmpty(text)) return;
SendMessage(hwnd, WM_SETTEXT, IntPtr.Zero, text);
}
public void SetWindowState(IntPtr hwnd, WindowState state)
{
IntPtr wParam = state switch
{
WindowState.Minimize => (IntPtr)SC_MINIMIZE,
WindowState.Maximize => (IntPtr)SC_MAXIMIZE,
WindowState.Restore => (IntPtr)SC_RESTORE,
_ => IntPtr.Zero
};
if (wParam != IntPtr.Zero)
{
PostMessage(hwnd, WM_SYSCOMMAND, wParam, IntPtr.Zero);
}
}
public void CloseWindow(IntPtr hwnd)
{
PostMessage(hwnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
}
public (int, int) GetWindowSize(IntPtr hwnd)
{
GetWindowRect(hwnd, out RECT rect);
return (rect.Width, rect.Height);
}
public string GetWindowDetails(IntPtr hwnd)
{
StringBuilder classNameBuffer = new StringBuilder(256);
GetClassName(hwnd, classNameBuffer, classNameBuffer.Capacity);
string className = classNameBuffer.ToString();
GetWindowRect(hwnd, out RECT rect);
IntPtr parentHWnd = GetParent(hwnd);
bool isChild = (parentHWnd != IntPtr.Zero);
return @$"Класс окна: {className}
Положение: (x, y) = ({rect.Left}, {rect.Top})
Размер: (w, h) = ({rect.Width}, {rect.Height})
Является дочерним: {(isChild ? "да" : "нет")}{(isChild ? $"\nРодительское окно: 0x{parentHWnd.ToInt64():X}" : "")}";
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
public int Width => Right - Left;
public int Height => Bottom - Top;
}
}
+184
View File
@@ -0,0 +1,184 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using IPClab1.Models;
using Microsoft.UI.Xaml.Media.Imaging;
using Windows.Graphics.Imaging;
namespace IPClab1.Services;
public class WindowListService
{
#region Win32 P/Invoke Declarations
private const uint WM_GETICON = 0x007F;
private const IntPtr ICON_SMALL = 0;
private const IntPtr ICON_BIG = 1;
private const IntPtr ICON_SMALL2 = 2;
private const int GCLP_HICON = -14;
private const int GCLP_HICONSM = -34;
private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", EntryPoint = "GetClassLongPtr", CharSet = CharSet.Auto)]
private static extern IntPtr GetClassLongPtr64(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", EntryPoint = "GetClassLong", CharSet = CharSet.Auto)]
private static extern uint GetClassLong32(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", SetLastError = true)]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("shell32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr ExtractIcon(IntPtr hInst, string lpszExeFileName, int nIconIndex);
private IntPtr GetClassLongPtr(IntPtr hWnd, int nIndex)
{
return IntPtr.Size == 8 ? GetClassLongPtr64(hWnd, nIndex) : new IntPtr(GetClassLong32(hWnd, nIndex));
}
#endregion
/// <summary>
/// Retrieves a list of structs representing all visible top-level windows with titles and native WinUI 3 icons.
/// </summary>
public async Task<List<WindowInfo>> GetVisibleWindowsAsync(bool onlyVisible)
{
var windowList = new List<WindowInfo>();
var handles = new List<IntPtr>();
// 1. Gather all raw visible window handles that actually contain titles
EnumWindows((hWnd, lParam) =>
{
if ((IsWindowVisible(hWnd) || !onlyVisible) && GetWindowTextLength(hWnd) > 0)
{
handles.Add(hWnd);
}
return true;
}, IntPtr.Zero);
// 2. Iterate and async decode titles, process info, and UI image references
foreach (var hWnd in handles)
{
// Fetch window text title cleanly
int length = GetWindowTextLength(hWnd);
var builder = new StringBuilder(length + 1);
GetWindowText(hWnd, builder, builder.Capacity);
string title = builder.ToString();
// Fetch numeric process identifier
GetWindowThreadProcessId(hWnd, out uint processId);
// Fetch and map the icon image synchronously/asynchronously into WinUI structures
SoftwareBitmapSource iconSource = await GetWinUi3IconAsync(hWnd);
windowList.Add(new WindowInfo
{
Hwnd = hWnd,
ProcessId = processId,
WindowTitle = title,
IconSource = iconSource
});
}
return windowList;
}
private Icon GetIconFromHwnd(IntPtr hWnd)
{
IntPtr hIcon = SendMessage(hWnd, WM_GETICON, ICON_BIG, IntPtr.Zero);
if (hIcon == IntPtr.Zero)
hIcon = SendMessage(hWnd, WM_GETICON, ICON_SMALL2, IntPtr.Zero);
if (hIcon == IntPtr.Zero)
hIcon = GetClassLongPtr(hWnd, GCLP_HICON);
if (hIcon == IntPtr.Zero)
hIcon = GetClassLongPtr(hWnd, GCLP_HICONSM);
if (hIcon != IntPtr.Zero)
{
try
{
using var rawIcon = Icon.FromHandle(hIcon);
return (Icon)rawIcon.Clone();
}
catch { /* Proceed to alternate extraction fallback */ }
}
try
{
GetWindowThreadProcessId(hWnd, out uint processId);
if (processId != 0)
{
using var proc = Process.GetProcessById((int)processId);
string exePath = proc.MainModule?.FileName;
if (!string.IsNullOrEmpty(exePath) && File.Exists(exePath))
{
IntPtr hExtracted = ExtractIcon(IntPtr.Zero, exePath, 0);
if (hExtracted != IntPtr.Zero && hExtracted != new IntPtr(1))
{
using var rawIcon = Icon.FromHandle(hExtracted);
return (Icon)rawIcon.Clone();
}
}
}
}
catch { /* Protection fallback */ }
return null;
}
private async Task<SoftwareBitmapSource> GetWinUi3IconAsync(IntPtr hWnd)
{
using Icon icon = GetIconFromHwnd(hWnd);
if (icon == null) return null;
try
{
using Bitmap bitmap = icon.ToBitmap();
using var stream = new MemoryStream();
bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
stream.Position = 0;
var decoder = await BitmapDecoder.CreateAsync(stream.AsRandomAccessStream());
var softwareBitmap = await decoder.GetSoftwareBitmapAsync(
BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Premultiplied);
var source = new SoftwareBitmapSource();
await source.SetBitmapAsync(softwareBitmap);
return source;
}
catch
{
return null;
}
}
}
+137
View File
@@ -0,0 +1,137 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using IPClab1.Enums;
using IPClab1.Models;
using IPClab1.Services;
namespace IPClab1.ViewModels;
public partial class MainWindowViewModel(WindowListService windowListService, WindowInteractionService windowInteractionService) : ObservableObject
{
private readonly WindowListService _windowListService = windowListService;
private readonly WindowInteractionService _windowInteractionService = windowInteractionService;
[ObservableProperty]
public ObservableCollection<WindowInfo> _windows = [];
[ObservableProperty]
public partial bool VisibleOnly
{
get;
set;
}
[RelayCommand]
public async Task RefreshWindows()
{
Windows.Clear();
foreach (WindowInfo windowInfo in await _windowListService.GetVisibleWindowsAsync(VisibleOnly))
Windows.Add(windowInfo);
}
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(SetNewTitleCommand))]
[NotifyCanExecuteChangedFor(nameof(SetWindowStateCommand))]
[NotifyCanExecuteChangedFor(nameof(CloseWindowCommand))]
[NotifyCanExecuteChangedFor(nameof(SendClickCommand))]
public partial WindowInfo SelectedWindow
{
get;
set;
}
partial void OnSelectedWindowChanged(WindowInfo value)
{
(Width, Height) = _windowInteractionService.GetWindowSize(value.Hwnd);
}
[ObservableProperty]
public partial int Width
{
get;
set;
}
[ObservableProperty]
public partial int Height
{
get;
set;
}
[ObservableProperty]
public partial int X
{
get;
set;
}
[ObservableProperty]
public partial int Y
{
get;
set;
}
[RelayCommand(CanExecute = nameof(IsWindowSelected))]
public void SendClick()
{
_windowInteractionService.SimulateClick(SelectedWindow.Hwnd, X, Y);
}
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(SetNewTitleCommand))]
public partial string NewTitle
{
get;
set;
}
private bool CanSetNewTitle()
{
return !string.IsNullOrEmpty(NewTitle) && SelectedWindow != null;
}
[RelayCommand(CanExecute = nameof(CanSetNewTitle))]
public void SetNewTitle()
{
_windowInteractionService.SetText(SelectedWindow.Hwnd, NewTitle);
Windows[Windows.IndexOf(SelectedWindow)].WindowTitle = NewTitle;
}
private bool IsWindowSelected()
{
return SelectedWindow != null;
}
[RelayCommand(CanExecute = nameof(IsWindowSelected))]
public void SetWindowState(WindowState windowState)
{
_windowInteractionService.SetWindowState(SelectedWindow.Hwnd, windowState);
}
[RelayCommand(CanExecute = nameof(IsWindowSelected))]
public void CloseWindow()
{
_windowInteractionService.CloseWindow(SelectedWindow.Hwnd);
SelectedWindow = null;
}
[ObservableProperty]
public partial string InfoString
{
get;
set;
}
public void RefreshInfo()
{
if (SelectedWindow != null) InfoString = _windowInteractionService.GetWindowDetails(SelectedWindow.Hwnd);
}
}
+19
View File
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="IPClab1.app"/>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- The ID below informs the system that this application is compatible with OS features first introduced in Windows 10.
It is necessary to support features in unpackaged applications, for example the custom titlebar implementation.
For more info see https://docs.microsoft.com/windows/apps/windows-app-sdk/use-windows-app-sdk-run-time#declare-os-compatibility-in-your-application-manifest -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
</windowsSettings>
</application>
</assembly>