185 lines
6.3 KiB
C#
185 lines
6.3 KiB
C#
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 partial 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);
|
|
|
|
[LibraryImport("user32.dll")]
|
|
[return: MarshalAs(UnmanagedType.Bool)]
|
|
private static partial bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
|
|
|
|
[LibraryImport("user32.dll")]
|
|
[return: MarshalAs(UnmanagedType.Bool)]
|
|
private static partial bool IsWindowVisible(IntPtr hWnd);
|
|
|
|
[LibraryImport("user32.dll", EntryPoint = "GetWindowTextW", SetLastError = true)]
|
|
private static partial int GetWindowText(IntPtr hWnd, [Out] char[] lpString, int nMaxCount);
|
|
|
|
[LibraryImport("user32.dll", EntryPoint = "GetWindowTextLengthW", SetLastError = true)]
|
|
private static partial int GetWindowTextLength(IntPtr hWnd);
|
|
|
|
[LibraryImport("user32.dll", EntryPoint = "SendMessageW")]
|
|
private static partial IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
|
|
|
|
[LibraryImport("user32.dll", EntryPoint = "GetClassLongPtrW")]
|
|
private static partial IntPtr GetClassLongPtr64(IntPtr hWnd, int nIndex);
|
|
|
|
[LibraryImport("user32.dll", EntryPoint = "GetClassLongW")]
|
|
private static partial uint GetClassLong32(IntPtr hWnd, int nIndex);
|
|
|
|
[LibraryImport("user32.dll", SetLastError = true)]
|
|
private static partial uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
|
|
|
|
[LibraryImport("shell32.dll", EntryPoint = "ExtractIconW", StringMarshalling = StringMarshalling.Utf16)]
|
|
private static partial 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
|
|
var length = GetWindowTextLength(hWnd);
|
|
var titleBuffer = new char[length + 1];
|
|
|
|
var actualLength = GetWindowText(hWnd, titleBuffer, titleBuffer.Length);
|
|
var title = actualLength > 0 ? new string(titleBuffer, 0, actualLength) : string.Empty;
|
|
|
|
// Fetch numeric process identifier
|
|
GetWindowThreadProcessId(hWnd, out var 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)
|
|
{
|
|
var 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;
|
|
}
|
|
}
|
|
} |