Files
MRCC/launcher/PCL-CE/PCL.Core/Utils/OS/DragHelper.cs
T
xyou f70b061d1a
CI / Go Backend (push) Canceled after 0s
feat: 项目初始化 + 3D方块世界原型 + AI助搭系统
初始化 monorepo: Go后端(7微服务) + Unity客户端(9模块) + 启动器

HTML5原型: Three.js 3D体素世界, Perlin噪声地形, 原版材质, 22种方块

Minecraft创造模式背包: 双栏布局, 拖拽移动物品, 方向性元件引脚

AI助搭策划文档 + 客户端/服务端骨架 + Docker Compose + CI
2026-08-08 14:07:56 +08:00

203 lines
5.1 KiB
C#

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
namespace PCL.Core.Utils.OS;
// ReSharper disable InconsistentNaming
public partial class DragHelper
{
public event EventHandler? DragDrop;
public string[]? DropFilePaths { get; private set; }
public Point DropDragPoint { get; private set; }
public HwndSource? HwndSource { get; set; }
#region Public API
public void AddHook()
{
if (HwndSource is null)
throw new InvalidOperationException("HwndSource 未设置");
RemoveHook();
HwndSource.AddHook(WndProc);
IntPtr hwnd = HwndSource.Handle;
if (IsUserAnAdmin())
RevokeDragDrop(hwnd);
DragAcceptFiles(hwnd, true);
ChangeMessageFilter(hwnd);
}
public void RemoveHook()
{
if (HwndSource is null)
return;
HwndSource.RemoveHook(WndProc);
DragAcceptFiles(HwndSource.Handle, false);
}
#endregion
#region WndProc
private IntPtr WndProc(
IntPtr hwnd,
int msg,
IntPtr wParam,
IntPtr lParam,
ref bool handled)
{
if (TryGetDropInfo(msg, wParam, out var files, out var pt))
{
DropFilePaths = files;
DropDragPoint = new Point(pt.X, pt.Y);
DragDrop?.Invoke(this, EventArgs.Empty);
handled = true;
}
return IntPtr.Zero;
}
#endregion
#region Message filter (UAC)
private static unsafe void ChangeMessageFilter(IntPtr hwnd)
{
var ver = Environment.OSVersion.Version;
if (ver < new Version(6, 0))
return;
var win7OrHigher = ver >= new Version(6, 1);
var filter = new CHANGEFILTERSTRUCT
{
cbSize = (uint)sizeof(CHANGEFILTERSTRUCT)
};
uint[] messages = [
WM_DROPFILES,
WM_COPYGLOBALDATA,
WM_COPYDATA
];
foreach (var msg in messages)
{
var ok = win7OrHigher
? ChangeWindowMessageFilterEx(hwnd, msg, MSGFLT_ALLOW, ref filter)
: ChangeWindowMessageFilter(msg, MSGFLT_ADD);
if (!ok) throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
#endregion
#region Drop parsing
private static bool TryGetDropInfo(
int msg,
IntPtr hDrop,
out string[]? filePaths,
out DragPoint dropPoint)
{
filePaths = null;
dropPoint = default;
if (msg != WM_DROPFILES)
return false;
var count = DragQueryFile(hDrop, uint.MaxValue, IntPtr.Zero, 0);
filePaths = new string[count];
const int maxPath = 32768, smallerMaxPath = 1024;
Span<char> gBuffer = stackalloc char[smallerMaxPath];
for (uint i = 0; i < count; i++)
{
var len = DragQueryFile(hDrop, i, IntPtr.Zero, 0) + 1;
if (len > maxPath) len = maxPath;
var buffer = len <= smallerMaxPath ? gBuffer[..(int)len] : new char[len];
_ = DragQueryFile(hDrop, i, buffer, len);
filePaths[i] = new string(buffer[..(int)(len - 1)]);
}
DragFinish(hDrop);
return true;
}
#endregion
#region Win32
private const uint WM_COPYGLOBALDATA = 0x0049;
private const uint WM_COPYDATA = 0x004A;
private const uint WM_DROPFILES = 0x0233;
private const uint MSGFLT_ALLOW = 1;
private const uint MSGFLT_ADD = 1;
[LibraryImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static partial bool ChangeWindowMessageFilter(
uint msg,
uint flags);
[LibraryImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static partial bool ChangeWindowMessageFilterEx(
IntPtr hwnd,
uint msg,
uint action,
ref CHANGEFILTERSTRUCT filter);
[LibraryImport("shell32.dll")]
private static partial void DragAcceptFiles(
IntPtr hwnd,
[MarshalAs(UnmanagedType.Bool)] bool accept);
[LibraryImport("shell32.dll", EntryPoint = "DragQueryFileW", StringMarshalling = StringMarshalling.Utf16)]
private static partial uint DragQueryFile(IntPtr hDrop, uint iFile, Span<char> lpszFile, uint cch);
[LibraryImport("shell32.dll", EntryPoint = "DragQueryFileW")]
private static partial uint DragQueryFile(IntPtr hDrop, uint iFile, IntPtr lpszFile, uint cch);
[LibraryImport("shell32.dll")]
private static partial void DragFinish(IntPtr hDrop);
[LibraryImport("ole32.dll")]
private static partial int RevokeDragDrop(IntPtr hwnd);
[LibraryImport("shell32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static partial bool IsUserAnAdmin();
#endregion
#region Structs
[StructLayout(LayoutKind.Sequential)]
private struct DragPoint
{
public int X;
public int Y;
}
[StructLayout(LayoutKind.Sequential)]
private struct CHANGEFILTERSTRUCT
{
public uint cbSize;
public uint ExtStatus;
}
#endregion
}