9bb4f85802
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
92 lines
3.1 KiB
C#
92 lines
3.1 KiB
C#
using System.Diagnostics;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
|
|
namespace CVRManager.Services;
|
|
|
|
public static class GameWindow
|
|
{
|
|
const uint WM_SETICON = 0x0080;
|
|
const int ICON_SMALL = 0;
|
|
const int ICON_BIG = 1;
|
|
const int SM_CXICON = 11;
|
|
const int SM_CXSMICON = 49;
|
|
|
|
delegate bool EnumProc(IntPtr hwnd, IntPtr lParam);
|
|
|
|
[DllImport("user32.dll")]
|
|
static extern bool EnumWindows(EnumProc proc, IntPtr lParam);
|
|
|
|
[DllImport("user32.dll")]
|
|
static extern uint GetWindowThreadProcessId(IntPtr hwnd, out uint pid);
|
|
|
|
[DllImport("user32.dll")]
|
|
static extern bool IsWindowVisible(IntPtr hwnd);
|
|
|
|
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
|
|
static extern int GetClassName(IntPtr hwnd, StringBuilder name, int max);
|
|
|
|
[DllImport("user32.dll", SetLastError = true)]
|
|
static extern IntPtr SendMessageTimeout(IntPtr hwnd, uint msg, IntPtr wParam, IntPtr lParam, uint flags, uint timeout, out IntPtr result);
|
|
|
|
const uint SMTO_ABORTIFHUNG = 2;
|
|
|
|
[DllImport("user32.dll")]
|
|
static extern int GetSystemMetrics(int index);
|
|
|
|
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
|
|
static extern uint PrivateExtractIcons(string file, int index, int cx, int cy, IntPtr[] icons, uint[] ids, uint count, uint flags);
|
|
|
|
static IntPtr FindUnityWindow(int pid)
|
|
{
|
|
var found = IntPtr.Zero;
|
|
var name = new StringBuilder(64);
|
|
EnumWindows((hwnd, _) =>
|
|
{
|
|
GetWindowThreadProcessId(hwnd, out var owner);
|
|
if (owner != pid || !IsWindowVisible(hwnd)) return true;
|
|
name.Clear();
|
|
GetClassName(hwnd, name, name.Capacity);
|
|
if (name.ToString() == "UnityWndClass") found = hwnd;
|
|
return true;
|
|
}, IntPtr.Zero);
|
|
return found;
|
|
}
|
|
|
|
static void SetIcons(IntPtr hwnd, string exe)
|
|
{
|
|
foreach (var (kind, size) in new[] { (ICON_SMALL, GetSystemMetrics(SM_CXSMICON)), (ICON_BIG, GetSystemMetrics(SM_CXICON)) })
|
|
{
|
|
var icons = new IntPtr[1];
|
|
var ids = new uint[1];
|
|
if (PrivateExtractIcons(exe, 0, size, size, icons, ids, 1, 0) > 0 && icons[0] != IntPtr.Zero)
|
|
SendMessageTimeout(hwnd, WM_SETICON, (IntPtr)kind, icons[0], SMTO_ABORTIFHUNG, 1000, out _);
|
|
}
|
|
}
|
|
|
|
public static async Task ApplyIconWhenReadyAsync(Process proc, string exe)
|
|
{
|
|
if (!OperatingSystem.IsWindows()) return;
|
|
try
|
|
{
|
|
var hwnd = IntPtr.Zero;
|
|
for (var i = 0; i < 240 && hwnd == IntPtr.Zero; i++)
|
|
{
|
|
if (proc.HasExited) return;
|
|
hwnd = FindUnityWindow(proc.Id);
|
|
if (hwnd == IntPtr.Zero) await Task.Delay(250);
|
|
}
|
|
if (hwnd == IntPtr.Zero) return;
|
|
|
|
foreach (var wait in new[] { 0, 3000, 7000, 20000 })
|
|
{
|
|
await Task.Delay(wait);
|
|
if (proc.HasExited) return;
|
|
var current = FindUnityWindow(proc.Id);
|
|
if (current != IntPtr.Zero) SetIcons(current, exe);
|
|
}
|
|
}
|
|
catch (InvalidOperationException) { }
|
|
}
|
|
}
|