Files
LanMountainDesktop/LanMountainDesktop.Platform.Windows/WindowsMainWindowDesktopLayerService.cs
lincube 9b7a35ddd3 refactor: 新建 Platform 平台差异层,主工程 P/Invoke 全部迁出
- 新增 Platform.Abstractions(接口+NoOp+PlatformLog 日志桥)、
  Platform.Windows、Platform.MacOS、Platform.Android 四个项目
- 迁移电源管理、原生对话框、桌面层嵌入、窗口置底/区域穿透、
  DWM 互操作、图标服务、包标识查询等全部 Windows P/Invoke 实现
- 主工程保留静态工厂门面,调用点不变;DllImport 扫描为零
- 更新 WindowPassthroughServiceTests 指向新位置(20/20 通过)
2026-07-26 22:14:46 +09:00

239 lines
7.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Runtime.InteropServices;
using Avalonia.Controls;
using LanMountainDesktop.Platform.Abstractions;
namespace LanMountainDesktop.Platform.Windows;
/// <summary>
/// Windows 桌面层实现将窗口设为桌面图标宿主SHELLDLL_DefView的子窗口。
/// 自 LanMountainDesktop.Services.MainWindowDesktopLayerService 迁移,行为不变。
/// </summary>
public sealed class WindowsMainWindowDesktopLayerService : IMainWindowDesktopLayerService
{
private const int GWL_STYLE = -16;
private const int GWL_EXSTYLE = -20;
private const long WS_CHILD = 0x40000000L;
private const long WS_POPUP = 0x80000000L;
private const long WS_CAPTION = 0x00C00000L;
private const long WS_THICKFRAME = 0x00040000L;
private const long WS_MINIMIZEBOX = 0x00020000L;
private const long WS_MAXIMIZEBOX = 0x00010000L;
private const long WS_SYSMENU = 0x00080000L;
private const uint SWP_NOSIZE = 0x0001;
private const uint SWP_NOMOVE = 0x0002;
private const uint SWP_NOACTIVATE = 0x0010;
private const uint SWP_SHOWWINDOW = 0x0040;
private const uint SWP_FRAMECHANGED = 0x0020;
private static readonly IntPtr HWND_TOP = IntPtr.Zero;
private static readonly IntPtr HWND_BOTTOM = new(1);
private readonly object _gate = new();
private readonly Dictionary<IntPtr, WindowRestoreState> _restoreStates = [];
public bool IsSupported => true;
public void EnableOrRefresh(Window window)
{
ArgumentNullException.ThrowIfNull(window);
var handle = GetWindowHandle(window);
if (handle == IntPtr.Zero)
{
window.Opened -= OnDeferredOpened;
window.Opened += OnDeferredOpened;
return;
}
EnableOrRefresh(handle);
}
public void Disable(Window window)
{
ArgumentNullException.ThrowIfNull(window);
window.Opened -= OnDeferredOpened;
var handle = GetWindowHandle(window);
if (handle == IntPtr.Zero)
{
return;
}
WindowRestoreState? restoreState;
lock (_gate)
{
if (!_restoreStates.Remove(handle, out restoreState))
{
return;
}
}
try
{
_ = SetParent(handle, restoreState.Parent);
SetWindowLongPtr(handle, GWL_STYLE, restoreState.Style);
SetWindowLongPtr(handle, GWL_EXSTYLE, restoreState.ExStyle);
_ = SetWindowPos(
handle,
HWND_TOP,
0,
0,
0,
0,
SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_FRAMECHANGED | SWP_SHOWWINDOW);
PlatformLog.Info("MainWindowDesktopLayer", $"Disabled desktop layer. Window={handle}.");
}
catch (Exception ex)
{
PlatformLog.Warn("MainWindowDesktopLayer", $"Failed to disable desktop layer. Window={handle}.", ex);
}
}
private void OnDeferredOpened(object? sender, EventArgs e)
{
if (sender is not Window window)
{
return;
}
window.Opened -= OnDeferredOpened;
EnableOrRefresh(window);
}
private void EnableOrRefresh(IntPtr handle)
{
if (handle == IntPtr.Zero || !IsWindow(handle))
{
return;
}
SaveRestoreStateIfNeeded(handle);
var desktopHost = ResolveDesktopIconHost();
if (desktopHost != IntPtr.Zero && IsWindow(desktopHost))
{
ApplyDesktopChildStyle(handle);
if (GetParent(handle) != desktopHost)
{
_ = SetParent(handle, desktopHost);
}
_ = SetWindowPos(
handle,
HWND_TOP,
0,
0,
0,
0,
SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_FRAMECHANGED | SWP_SHOWWINDOW);
PlatformLog.Info("MainWindowDesktopLayer", $"Enabled desktop layer. Window={handle}; Host={desktopHost}.");
return;
}
_ = SetWindowPos(handle, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_SHOWWINDOW);
PlatformLog.Warn("MainWindowDesktopLayer", $"Desktop icon host not found. Falling back to HWND_BOTTOM. Window={handle}.");
}
private void SaveRestoreStateIfNeeded(IntPtr handle)
{
lock (_gate)
{
if (_restoreStates.ContainsKey(handle))
{
return;
}
_restoreStates[handle] = new WindowRestoreState(
GetParent(handle),
GetWindowLongPtr(handle, GWL_STYLE),
GetWindowLongPtr(handle, GWL_EXSTYLE));
}
}
private static void ApplyDesktopChildStyle(IntPtr handle)
{
var style = GetWindowLongPtr(handle, GWL_STYLE).ToInt64();
style |= WS_CHILD;
style &= ~(WS_POPUP | WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU);
SetWindowLongPtr(handle, GWL_STYLE, new IntPtr(style));
}
private static IntPtr ResolveDesktopIconHost()
{
var topLevelWindows = new List<IntPtr>();
EnumWindows((handle, _) =>
{
topLevelWindows.Add(handle);
return true;
}, IntPtr.Zero);
foreach (var topLevelWindow in topLevelWindows)
{
var worker = FindWindowEx(topLevelWindow, IntPtr.Zero, "WorkerW", null);
if (worker == IntPtr.Zero)
{
continue;
}
var defView = FindWindowEx(worker, IntPtr.Zero, "SHELLDLL_DefView", null);
if (defView != IntPtr.Zero)
{
return defView;
}
}
foreach (var topLevelWindow in topLevelWindows)
{
var defView = FindWindowEx(topLevelWindow, IntPtr.Zero, "SHELLDLL_DefView", null);
if (defView != IntPtr.Zero)
{
return defView;
}
}
return IntPtr.Zero;
}
private static IntPtr GetWindowHandle(Window window)
{
try
{
return window.TryGetPlatformHandle()?.Handle ?? IntPtr.Zero;
}
catch
{
return IntPtr.Zero;
}
}
private sealed record WindowRestoreState(IntPtr Parent, IntPtr Style, IntPtr ExStyle);
private delegate bool EnumWindowsProc(IntPtr handle, IntPtr lParam);
[DllImport("user32.dll", EntryPoint = "GetWindowLongPtr")]
private static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr")]
private static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
[DllImport("user32.dll")]
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint flags);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll")]
private static extern IntPtr GetParent(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern bool IsWindow(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindowEx(IntPtr hParent, IntPtr hChildAfter, string? lpszClass, string? lpszWindow);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
}