LumaTunnel
【WinUI】LumaTunnel 是一个面向个人多设备的 Windows 代理系统。客户端在本机提供 HTTP/HTTPS CONNECT 与 SOCKS5 TCP 代理,并通过一个受信任 TLS 证书保护的 WSS 会话,将多个 TCP 流复用到自建 Windows Server 节点。
关注
0
Fork
0
Star
0
using System.Runtime.InteropServices;
using LumaTunnel.Client.Interface;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using XFEExtension.NetCore.WinUIHelper.Implements.Services;
namespace LumaTunnel.Client.Implements;
public sealed class TrayService : GlobalServiceBase, ITrayService
{
private const uint CallbackMessage = 0x8001;
private const uint LeftDoubleClick = 0x0203;
private readonly ManualResetEventSlim _ready = new();
private Thread? _thread;
private IntPtr _window;
private IntPtr _icon;
private WndProc? _wndProc;
private DispatcherQueue? _dispatcher;
private Action? _showWindow;
public void Initialize(Window window, Action showWindow)
{
if (_thread is not null)
return;
_dispatcher = window.DispatcherQueue;
_showWindow = showWindow;
_thread = new Thread(MessageLoop) { IsBackground = true, Name = "LumaTunnel.Tray" };
_thread.Start();
_ready.Wait(TimeSpan.FromSeconds(3));
}
public void Dispose()
{
if (_window != IntPtr.Zero)
PostMessage(_window, 0x0010, IntPtr.Zero, IntPtr.Zero);
_thread?.Join(TimeSpan.FromSeconds(2));
_ready.Dispose();
GC.SuppressFinalize(this);
}
private void MessageLoop()
{
_wndProc = WindowProcedure;
var className = "LumaTunnelTrayWindow." + Environment.ProcessId;
var windowClass = new WindowClass
{
ClassName = className,
Instance = GetModuleHandle(null),
WindowProcedure = Marshal.GetFunctionPointerForDelegate(_wndProc)
};
RegisterClass(ref windowClass);
_window = CreateWindowEx(0, className, className, 0, 0, 0, 0, 0, new IntPtr(-3), IntPtr.Zero, windowClass.Instance, IntPtr.Zero);
_icon = LoadImage(IntPtr.Zero, Path.Combine(AppContext.BaseDirectory, "Assets", "LumaTunnel.ico"), 1, 0, 0, 0x0010 | 0x0040);
var data = CreateNotifyData(0);
ShellNotifyIcon(0, ref data);
_ready.Set();
while (GetMessage(out var message, IntPtr.Zero, 0, 0) > 0)
{
TranslateMessage(ref message);
DispatchMessage(ref message);
}
data = CreateNotifyData(0);
ShellNotifyIcon(2, ref data);
if (_icon != IntPtr.Zero) DestroyIcon(_icon);
_window = IntPtr.Zero;
}
private IntPtr WindowProcedure(IntPtr window, uint message, IntPtr wParam, IntPtr lParam)
{
if (message == CallbackMessage && unchecked((uint)lParam.ToInt64()) == LeftDoubleClick)
_dispatcher?.TryEnqueue(() => _showWindow?.Invoke());
if (message == 0x0002)
PostQuitMessage(0);
return DefWindowProc(window, message, wParam, lParam);
}
private NotifyIconData CreateNotifyData(uint flags) => new()
{
Size = Marshal.SizeOf<NotifyIconData>(),
Window = _window,
Id = 1,
Flags = flags == 0 ? 0x1u | 0x2u | 0x4u : flags,
CallbackMessage = CallbackMessage,
Icon = _icon,
Tip = "LumaTunnel 光隧",
Info = string.Empty,
InfoTitle = string.Empty
};
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct WindowClass
{
public uint Style;
public IntPtr WindowProcedure;
public int ClassExtra;
public int WindowExtra;
public IntPtr Instance;
public IntPtr Icon;
public IntPtr Cursor;
public IntPtr Background;
[MarshalAs(UnmanagedType.LPWStr)] public string? MenuName;
[MarshalAs(UnmanagedType.LPWStr)] public string ClassName;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct NotifyIconData
{
public int Size;
public IntPtr Window;
public uint Id;
public uint Flags;
public uint CallbackMessage;
public IntPtr Icon;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] public string Tip;
public uint State;
public uint StateMask;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string Info;
public uint TimeoutOrVersion;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)] public string InfoTitle;
public uint InfoFlags;
public Guid Item;
public IntPtr BalloonIcon;
}
[StructLayout(LayoutKind.Sequential)]
private struct Message { public IntPtr Window; public uint Value; public UIntPtr WParam; public IntPtr LParam; public uint Time; public int X; public int Y; }
private delegate IntPtr WndProc(IntPtr window, uint message, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Unicode)] private static extern ushort RegisterClass(ref WindowClass windowClass);
[DllImport("user32.dll", CharSet = CharSet.Unicode)] private static extern IntPtr CreateWindowEx(uint exStyle, string className, string windowName, uint style, int x, int y, int width, int height, IntPtr parent, IntPtr menu, IntPtr instance, IntPtr parameter);
[DllImport("user32.dll")] private static extern IntPtr DefWindowProc(IntPtr window, uint message, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")] private static extern int GetMessage(out Message message, IntPtr window, uint min, uint max);
[DllImport("user32.dll")] private static extern bool TranslateMessage(ref Message message);
[DllImport("user32.dll")] private static extern IntPtr DispatchMessage(ref Message message);
[DllImport("user32.dll")] private static extern void PostQuitMessage(int exitCode);
[DllImport("user32.dll")] private static extern bool PostMessage(IntPtr window, uint message, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Unicode)] private static extern IntPtr LoadImage(IntPtr instance, string name, uint type, int width, int height, uint load);
[DllImport("user32.dll")] private static extern bool DestroyIcon(IntPtr icon);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)] private static extern IntPtr GetModuleHandle(string? moduleName);
[DllImport("shell32.dll", CharSet = CharSet.Unicode)] private static extern bool ShellNotifyIcon(uint message, ref NotifyIconData data);
}
using System.Runtime.InteropServices;
using LumaTunnel.Client.Interface;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using XFEExtension.NetCore.WinUIHelper.Implements.Services;
namespace LumaTunnel.Client.Implements;
public sealed class TrayService : GlobalServiceBase, ITrayService
{
private const uint CallbackMessage = 0x8001;
private const uint LeftDoubleClick = 0x0203;
private readonly ManualResetEventSlim _ready = new();
private Thread? _thread;
private IntPtr _window;
private IntPtr _icon;
private WndProc? _wndProc;
private DispatcherQueue? _dispatcher;
private Action? _showWindow;
public void Initialize(Window window, Action showWindow)
{
if (_thread is not null)
return;
_dispatcher = window.DispatcherQueue;
_showWindow = showWindow;
_thread = new Thread(MessageLoop) { IsBackground = true, Name = "LumaTunnel.Tray" };
_thread.Start();
_ready.Wait(TimeSpan.FromSeconds(3));
}
public void Dispose()
{
if (_window != IntPtr.Zero)
PostMessage(_window, 0x0010, IntPtr.Zero, IntPtr.Zero);
_thread?.Join(TimeSpan.FromSeconds(2));
_ready.Dispose();
GC.SuppressFinalize(this);
}
private void MessageLoop()
{
_wndProc = WindowProcedure;
var className = "LumaTunnelTrayWindow." + Environment.ProcessId;
var windowClass = new WindowClass
{
ClassName = className,
Instance = GetModuleHandle(null),
WindowProcedure = Marshal.GetFunctionPointerForDelegate(_wndProc)
};
RegisterClass(ref windowClass);
_window = CreateWindowEx(0, className, className, 0, 0, 0, 0, 0, new IntPtr(-3), IntPtr.Zero, windowClass.Instance, IntPtr.Zero);
_icon = LoadImage(IntPtr.Zero, Path.Combine(AppContext.BaseDirectory, "Assets", "LumaTunnel.ico"), 1, 0, 0, 0x0010 | 0x0040);
var data = CreateNotifyData(0);
ShellNotifyIcon(0, ref data);
_ready.Set();
while (GetMessage(out var message, IntPtr.Zero, 0, 0) > 0)
{
TranslateMessage(ref message);
DispatchMessage(ref message);
}
data = CreateNotifyData(0);
ShellNotifyIcon(2, ref data);
if (_icon != IntPtr.Zero) DestroyIcon(_icon);
_window = IntPtr.Zero;
}
private IntPtr WindowProcedure(IntPtr window, uint message, IntPtr wParam, IntPtr lParam)
{
if (message == CallbackMessage && unchecked((uint)lParam.ToInt64()) == LeftDoubleClick)
_dispatcher?.TryEnqueue(() => _showWindow?.Invoke());
if (message == 0x0002)
PostQuitMessage(0);
return DefWindowProc(window, message, wParam, lParam);
}
private NotifyIconData CreateNotifyData(uint flags) => new()
{
Size = Marshal.SizeOf<NotifyIconData>(),
Window = _window,
Id = 1,
Flags = flags == 0 ? 0x1u | 0x2u | 0x4u : flags,
CallbackMessage = CallbackMessage,
Icon = _icon,
Tip = "LumaTunnel 光隧",
Info = string.Empty,
InfoTitle = string.Empty
};
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct WindowClass
{
public uint Style;
public IntPtr WindowProcedure;
public int ClassExtra;
public int WindowExtra;
public IntPtr Instance;
public IntPtr Icon;
public IntPtr Cursor;
public IntPtr Background;
[MarshalAs(UnmanagedType.LPWStr)] public string? MenuName;
[MarshalAs(UnmanagedType.LPWStr)] public string ClassName;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct NotifyIconData
{
public int Size;
public IntPtr Window;
public uint Id;
public uint Flags;
public uint CallbackMessage;
public IntPtr Icon;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] public string Tip;
public uint State;
public uint StateMask;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string Info;
public uint TimeoutOrVersion;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)] public string InfoTitle;
public uint InfoFlags;
public Guid Item;
public IntPtr BalloonIcon;
}
[StructLayout(LayoutKind.Sequential)]
private struct Message { public IntPtr Window; public uint Value; public UIntPtr WParam; public IntPtr LParam; public uint Time; public int X; public int Y; }
private delegate IntPtr WndProc(IntPtr window, uint message, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Unicode)] private static extern ushort RegisterClass(ref WindowClass windowClass);
[DllImport("user32.dll", CharSet = CharSet.Unicode)] private static extern IntPtr CreateWindowEx(uint exStyle, string className, string windowName, uint style, int x, int y, int width, int height, IntPtr parent, IntPtr menu, IntPtr instance, IntPtr parameter);
[DllImport("user32.dll")] private static extern IntPtr DefWindowProc(IntPtr window, uint message, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")] private static extern int GetMessage(out Message message, IntPtr window, uint min, uint max);
[DllImport("user32.dll")] private static extern bool TranslateMessage(ref Message message);
[DllImport("user32.dll")] private static extern IntPtr DispatchMessage(ref Message message);
[DllImport("user32.dll")] private static extern void PostQuitMessage(int exitCode);
[DllImport("user32.dll")] private static extern bool PostMessage(IntPtr window, uint message, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Unicode)] private static extern IntPtr LoadImage(IntPtr instance, string name, uint type, int width, int height, uint load);
[DllImport("user32.dll")] private static extern bool DestroyIcon(IntPtr icon);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)] private static extern IntPtr GetModuleHandle(string? moduleName);
[DllImport("shell32.dll", CharSet = CharSet.Unicode)] private static extern bool ShellNotifyIcon(uint message, ref NotifyIconData data);
}