mirror of
https://github.com/nsnail/dot.git
synced 2025-06-17 21:13:21 +08:00
Merge branch 'dev' into tk
This commit is contained in:
commit
bd2f88a1d6
@ -24,5 +24,5 @@ internal static class AssemblyInfo
|
|||||||
public const string ASSEMBLY_PRODUCT = "dot";
|
public const string ASSEMBLY_PRODUCT = "dot";
|
||||||
public const string ASSEMBLY_TITLE = "功能全面的实用工具 - 程序员的瑞士军刀";
|
public const string ASSEMBLY_TITLE = "功能全面的实用工具 - 程序员的瑞士军刀";
|
||||||
public const string ASSEMBLY_VERSION = _VERSION;
|
public const string ASSEMBLY_VERSION = _VERSION;
|
||||||
private const string _VERSION = "1.1.6";
|
private const string _VERSION = "1.1.7.1";
|
||||||
}
|
}
|
@ -1,7 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"
|
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root" xmlns="">
|
||||||
id="root" xmlns="">
|
|
||||||
<xsd:element name="root" msdata:IsDataSet="true"></xsd:element>
|
<xsd:element name="root" msdata:IsDataSet="true"></xsd:element>
|
||||||
</xsd:schema>
|
</xsd:schema>
|
||||||
<resheader name="resmimetype">
|
<resheader name="resmimetype">
|
||||||
@ -254,4 +253,16 @@
|
|||||||
<data name="BufferSize" xml:space="preserve">
|
<data name="BufferSize" xml:space="preserve">
|
||||||
<value>Buffer size (kilobytes)</value>
|
<value>Buffer size (kilobytes)</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Translating" xml:space="preserve">
|
||||||
|
<value>In translation...</value>
|
||||||
|
</data>
|
||||||
|
<data name="HideTranslate" xml:space="preserve">
|
||||||
|
<value>Press [green] Esc[/] to hide the translation</value>
|
||||||
|
</data>
|
||||||
|
<data name="StartTranslate" xml:space="preserve">
|
||||||
|
<value>Select the text and press [green] Capslock[/] to start the translation</value>
|
||||||
|
</data>
|
||||||
|
<data name="TranslateTool" xml:space="preserve">
|
||||||
|
<value>Translation tools</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -267,4 +267,13 @@
|
|||||||
<data name="Translating" xml:space="preserve">
|
<data name="Translating" xml:space="preserve">
|
||||||
<value>翻译中...</value>
|
<value>翻译中...</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="HideTranslate" xml:space="preserve">
|
||||||
|
<value>按下 [green]Esc[/] 隐藏译文</value>
|
||||||
|
</data>
|
||||||
|
<data name="StartTranslate" xml:space="preserve">
|
||||||
|
<value>选中文本按下 [green]Capslock[/] 开始翻译</value>
|
||||||
|
</data>
|
||||||
|
<data name="TranslateTool" xml:space="preserve">
|
||||||
|
<value>翻译工具</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
71
src/Native/KeyboardHook.cs
Normal file
71
src/Native/KeyboardHook.cs
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
// ReSharper disable ClassNeverInstantiated.Global
|
||||||
|
|
||||||
|
#if NET7_0_WINDOWS
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Dot.Native;
|
||||||
|
|
||||||
|
internal sealed class KeyboardHook : IDisposable
|
||||||
|
{
|
||||||
|
private readonly nint _hookId;
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
|
public KeyboardHook()
|
||||||
|
{
|
||||||
|
_hookId = SetHook(HookCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
~KeyboardHook()
|
||||||
|
{
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public delegate bool KeyUpEventHandler(object sender, Win32.KbdllhooksStruct e);
|
||||||
|
|
||||||
|
public KeyUpEventHandler KeyUpEvent { get; set; }
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static nint SetHook(Win32.HookProc lpfn)
|
||||||
|
{
|
||||||
|
using var process = Process.GetCurrentProcess();
|
||||||
|
using var module = process.MainModule;
|
||||||
|
return Win32.SetWindowsHookExA(Win32.WH_KEYBOARD_LL, lpfn, module!.BaseAddress, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (_disposed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (disposing) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_hookId != default) {
|
||||||
|
Win32.UnhookWindowsHookExA(_hookId);
|
||||||
|
}
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private nint HookCallback(int nCode, nint wParam, nint lParam)
|
||||||
|
{
|
||||||
|
if (nCode >= 0 && wParam == Win32.WM_KEYDOWN) {
|
||||||
|
var hookStruct = (Win32.KbdllhooksStruct)Marshal.PtrToStructure(lParam, typeof(Win32.KbdllhooksStruct))!;
|
||||||
|
if (KeyUpEvent?.Invoke(null, hookStruct) ?? false) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Win32.CallNextHookEx(_hookId, nCode, wParam, lParam);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
172
src/Native/VkCode.cs
Normal file
172
src/Native/VkCode.cs
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
|
namespace Dot.Native;
|
||||||
|
|
||||||
|
public class VkCode
|
||||||
|
{
|
||||||
|
public const int VK_A = 0x41; // A 键
|
||||||
|
public const int VK_ACCEPT = 0x1E; // IME 接受
|
||||||
|
public const int VK_ADD = 0x6B; // 添加密钥
|
||||||
|
public const int VK_APPS = 0x5D; // 应用程序键 (自然键盘)
|
||||||
|
public const int VK_ATTN = 0xF6; // Attn 键
|
||||||
|
public const int VK_B = 0x42; // B 键
|
||||||
|
public const int VK_BACK = 0x08; // BACKSPACE 密钥
|
||||||
|
public const int VK_BROWSER_BACK = 0xA6; // 浏览器后退键
|
||||||
|
public const int VK_BROWSER_FAVORITES = 0xAB; // 浏览器收藏键
|
||||||
|
public const int VK_BROWSER_FORWARD = 0xA7; // 浏览器前进键
|
||||||
|
public const int VK_BROWSER_HOME = 0xAC; // 浏览器“开始”和“主页”键
|
||||||
|
public const int VK_BROWSER_REFRESH = 0xA8; // 浏览器刷新键
|
||||||
|
public const int VK_BROWSER_SEARCH = 0xAA; // 浏览器搜索键
|
||||||
|
public const int VK_BROWSER_STOP = 0xA9; // 浏览器停止键
|
||||||
|
public const int VK_C = 0x43; // C 键
|
||||||
|
public const int VK_CANCEL = 0x03; // 控制中断处理
|
||||||
|
public const int VK_CAPITAL = 0x14; // CAPS LOCK 键
|
||||||
|
public const int VK_CLEAR = 0x0C; // CLEAR 键
|
||||||
|
public const int VK_CONTROL = 0x11; // Ctrl 键
|
||||||
|
public const int VK_CONVERT = 0x1C; // IME 转换
|
||||||
|
public const int VK_CRSEL = 0xF7; // CrSel 键
|
||||||
|
public const int VK_D = 0x44; // D 键
|
||||||
|
public const int VK_DECIMAL = 0x6E; // 十进制键
|
||||||
|
public const int VK_DELETE = 0x2E; // DEL 键
|
||||||
|
public const int VK_DIVIDE = 0x6F; // 除键
|
||||||
|
public const int VK_DOWN = 0x28; // 向下键
|
||||||
|
public const int VK_E = 0x45; // E 键
|
||||||
|
public const int VK_END = 0x23; // END 键
|
||||||
|
public const int VK_EREOF = 0xF9; // 擦除 EOF 密钥
|
||||||
|
public const int VK_ESCAPE = 0x1B; // ESC 键
|
||||||
|
public const int VK_EXECUTE = 0x2B; // EXECUTE 键
|
||||||
|
public const int VK_EXSEL = 0xF8; // ExSel 密钥
|
||||||
|
public const int VK_F = 0x46; // F 键
|
||||||
|
public const int VK_F1 = 0x70; // F1 键
|
||||||
|
public const int VK_F10 = 0x79; // F10 键
|
||||||
|
public const int VK_F11 = 0x7A; // F11 键
|
||||||
|
public const int VK_F12 = 0x7B; // F12 键
|
||||||
|
public const int VK_F13 = 0x7C; // F13 键
|
||||||
|
public const int VK_F14 = 0x7D; // F14 键
|
||||||
|
public const int VK_F15 = 0x7E; // F15 键
|
||||||
|
public const int VK_F16 = 0x7F; // F16 键
|
||||||
|
public const int VK_F17 = 0x80; // F17 键
|
||||||
|
public const int VK_F18 = 0x81; // F18 键
|
||||||
|
public const int VK_F19 = 0x82; // F19 键
|
||||||
|
public const int VK_F2 = 0x71; // F2 键
|
||||||
|
public const int VK_F20 = 0x83; // F20 键
|
||||||
|
public const int VK_F21 = 0x84; // F21 键
|
||||||
|
public const int VK_F22 = 0x85; // F22 键
|
||||||
|
public const int VK_F23 = 0x86; // F23 键
|
||||||
|
public const int VK_F24 = 0x87; // F24 键
|
||||||
|
public const int VK_F3 = 0x72; // F3 键
|
||||||
|
public const int VK_F4 = 0x73; // F4 键
|
||||||
|
public const int VK_F5 = 0x74; // F5 键
|
||||||
|
public const int VK_F6 = 0x75; // F6 键
|
||||||
|
public const int VK_F7 = 0x76; // F7 键
|
||||||
|
public const int VK_F8 = 0x77; // F8 键
|
||||||
|
public const int VK_F9 = 0x78; // F9 键
|
||||||
|
public const int VK_FINAL = 0x18; // IME 最终模式
|
||||||
|
public const int VK_G = 0x47; // G 键
|
||||||
|
public const int VK_H = 0x48; // H 键
|
||||||
|
public const int VK_HANGUEL = 0x15; // IME 朝鲜文库埃尔模式 (保持兼容性;使用 VK_HANGUL)
|
||||||
|
public const int VK_HANGUL = 0x15; // IME Hanguel 模式
|
||||||
|
public const int VK_HANJA = 0x19; // IME Hanja 模式
|
||||||
|
public const int VK_HELP = 0x2F; // 帮助密钥
|
||||||
|
public const int VK_HOME = 0x24; // HOME 键
|
||||||
|
public const int VK_I = 0x49; // I 键
|
||||||
|
public const int VK_IME_OFF = 0x1A; // IME 关闭
|
||||||
|
public const int VK_IME_ON = 0x16; // IME On
|
||||||
|
public const int VK_INSERT = 0x2D; // INS 密钥
|
||||||
|
public const int VK_J = 0x4A; // J 键
|
||||||
|
public const int VK_JUNJA = 0x17; // IME Junja 模式
|
||||||
|
public const int VK_K = 0x4B; // K 键
|
||||||
|
public const int VK_KANA = 0x15; // IME Kana 模式
|
||||||
|
public const int VK_KANJI = 0x19; // IME Kanji 模式
|
||||||
|
public const int VK_L = 0x4C; // L 键
|
||||||
|
public const int VK_LAUNCH_APP1 = 0xB6; // 启动应用程序 1 键
|
||||||
|
public const int VK_LAUNCH_APP2 = 0xB7; // 启动应用程序 2 键
|
||||||
|
public const int VK_LAUNCH_MAIL = 0xB4; // 启动邮件键
|
||||||
|
public const int VK_LAUNCH_MEDIA_SELECT = 0xB5; // 选择媒体键
|
||||||
|
public const int VK_LBUTTON = 0x01; // 鼠标左键
|
||||||
|
public const int VK_LCONTROL = 0xA2; // 左 Ctrl 键
|
||||||
|
public const int VK_LEFT = 0x25; // 向左键
|
||||||
|
public const int VK_LMENU = 0xA4; // 左 Alt 键
|
||||||
|
public const int VK_LSHIFT = 0xA0; // 左 SHIFT 键
|
||||||
|
public const int VK_LWIN = 0x5B; // 左Windows键 (自然键盘)
|
||||||
|
public const int VK_M = 0x4D; // M 键
|
||||||
|
public const int VK_MBUTTON = 0x04; // 中间鼠标按钮 (三键鼠标)
|
||||||
|
public const int VK_MEDIA_NEXT_TRACK = 0xB0; // 下一曲目键
|
||||||
|
public const int VK_MEDIA_PLAY_PAUSE = 0xB3; // 播放/暂停媒体键
|
||||||
|
public const int VK_MEDIA_PREV_TRACK = 0xB1; // 上一曲目键
|
||||||
|
public const int VK_MEDIA_STOP = 0xB2; // 停止媒体键
|
||||||
|
public const int VK_MENU = 0x12; // Alt 键
|
||||||
|
public const int VK_MODECHANGE = 0x1F; // IME 模式更改请求
|
||||||
|
public const int VK_MULTIPLY = 0x6A; // 乘键
|
||||||
|
public const int VK_N = 0x4E; // N 键
|
||||||
|
public const int VK_NEXT = 0x22; // PAGE DOWN 键
|
||||||
|
public const int VK_NONAME = 0xFC; // 预留
|
||||||
|
public const int VK_NONCONVERT = 0x1D; // IME 不转换
|
||||||
|
public const int VK_NUMLOCK = 0x90; // NUM LOCK 密钥
|
||||||
|
public const int VK_NUMPAD0 = 0x60; // 数字键盘 0 键
|
||||||
|
public const int VK_NUMPAD1 = 0x61; // 数字键盘 1 键
|
||||||
|
public const int VK_NUMPAD2 = 0x62; // 数字键盘 2 键
|
||||||
|
public const int VK_NUMPAD3 = 0x63; // 数字键盘 3 键
|
||||||
|
public const int VK_NUMPAD4 = 0x64; // 数字键盘 4 键
|
||||||
|
public const int VK_NUMPAD5 = 0x65; // 数字键盘 5 键
|
||||||
|
public const int VK_NUMPAD6 = 0x66; // 数字键盘 6 键
|
||||||
|
public const int VK_NUMPAD7 = 0x67; // 数字键盘 7 键
|
||||||
|
public const int VK_NUMPAD8 = 0x68; // 数字键盘 8 键
|
||||||
|
public const int VK_NUMPAD9 = 0x69; // 数字键盘 9 键
|
||||||
|
public const int VK_O = 0x4F; // O 键
|
||||||
|
public const int VK_OEM_1 = 0xBA; // 用于其他字符;它可能因键盘而异。 对于美国标准键盘,“;:”键
|
||||||
|
public const int VK_OEM_102 = 0xE2; // <>美国标准键盘上的键,或\\|非美国 102 键键盘上的键
|
||||||
|
public const int VK_OEM_2 = 0xBF; // 用于其他字符;它可能因键盘而异。 对于美国标准键盘,“/?” key
|
||||||
|
public const int VK_OEM_3 = 0xC0; // 用于其他字符;它可能因键盘而异。 对于美国标准键盘,“~”键
|
||||||
|
public const int VK_OEM_4 = 0xDB; // 用于其他字符;它可能因键盘而异。 对于美国标准键盘,“[{”键
|
||||||
|
public const int VK_OEM_5 = 0xDC; // 用于其他字符;它可能因键盘而异。 对于美国标准键盘,“\|”键
|
||||||
|
public const int VK_OEM_6 = 0xDD; // 用于其他字符;它可能因键盘而异。 对于美国标准键盘,“]}”键
|
||||||
|
public const int VK_OEM_7 = 0xDE; // 用于其他字符;它可能因键盘而异。 对于美国标准键盘,“单引号/双引号”键
|
||||||
|
public const int VK_OEM_8 = 0xDF; // 用于其他字符;它可能因键盘而异。
|
||||||
|
public const int VK_OEM_CLEAR = 0xFE; // 清除键
|
||||||
|
public const int VK_OEM_COMMA = 0xBC; // 对于任何国家/地区,“,键
|
||||||
|
public const int VK_OEM_MINUS = 0xBD; // 对于任何国家/地区,“-”键
|
||||||
|
public const int VK_OEM_PERIOD = 0xBE; // 对于任何国家/地区,“.”键
|
||||||
|
public const int VK_OEM_PLUS = 0xBB; // 对于任何国家/地区,“+”键
|
||||||
|
public const int VK_P = 0x50; // P 键
|
||||||
|
public const int VK_PA1 = 0xFD; // PA1 键
|
||||||
|
public const int VK_PACKET = 0xE7; // 用于将 Unicode 字符当作键击传递。
|
||||||
|
public const int VK_PAUSE = 0x13; // PAUSE 键
|
||||||
|
public const int VK_PLAY = 0xFA; // 播放键
|
||||||
|
public const int VK_PRINT = 0x2A; // PRINT 键
|
||||||
|
public const int VK_PRIOR = 0x21; // PAGE UP 键
|
||||||
|
public const int VK_PROCESSKEY = 0xE5; // IME PROCESS 密钥
|
||||||
|
public const int VK_Q = 0x51; // Q 键
|
||||||
|
public const int VK_R = 0x52; // R 键
|
||||||
|
public const int VK_RBUTTON = 0x02; // 鼠标右键
|
||||||
|
public const int VK_RCONTROL = 0xA3; // 右 Ctrl 键
|
||||||
|
public const int VK_RETURN = 0x0D; // Enter 键
|
||||||
|
public const int VK_RIGHT = 0x27; // 向右键
|
||||||
|
public const int VK_RMENU = 0xA5; // 右 ALT 键
|
||||||
|
public const int VK_RSHIFT = 0xA1; // 右 SHIFT 键
|
||||||
|
public const int VK_RWIN = 0x5C; // 右Windows键 (自然键盘)
|
||||||
|
public const int VK_S = 0x53; // S 键
|
||||||
|
public const int VK_SCROLL = 0x91; // SCROLL LOCK 键
|
||||||
|
public const int VK_SELECT = 0x29; // SELECT 键
|
||||||
|
public const int VK_SEPARATOR = 0x6C; // 分隔符键
|
||||||
|
public const int VK_SHIFT = 0x10; // SHIFT 键
|
||||||
|
public const int VK_SLEEP = 0x5F; // 计算机休眠键
|
||||||
|
public const int VK_SNAPSHOT = 0x2C; // 打印屏幕键
|
||||||
|
public const int VK_SPACE = 0x20; // 空格键
|
||||||
|
public const int VK_SUBTRACT = 0x6D; // 减去键
|
||||||
|
public const int VK_T = 0x54; // T 键
|
||||||
|
public const int VK_TAB = 0x09; // Tab 键
|
||||||
|
public const int VK_U = 0x55; // U 键
|
||||||
|
public const int VK_UP = 0x26; // 向上键
|
||||||
|
public const int VK_V = 0x56; // V 键
|
||||||
|
public const int VK_VOLUME_DOWN = 0xAE; // 音量减小键
|
||||||
|
public const int VK_VOLUME_MUTE = 0xAD; // 静音键
|
||||||
|
public const int VK_VOLUME_UP = 0xAF; // 音量增加键
|
||||||
|
public const int VK_W = 0x57; // W 键
|
||||||
|
public const int VK_X = 0x58; // X 键
|
||||||
|
public const int VK_XBUTTON1 = 0x05; // X1 鼠标按钮
|
||||||
|
public const int VK_XBUTTON2 = 0x06; // X2 鼠标按钮
|
||||||
|
public const int VK_Y = 0x59; // Y 键
|
||||||
|
public const int VK_Z = 0x5A; // Z 键
|
||||||
|
public const int VK_ZOOM = 0xFB; // 缩放键
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
// ReSharper disable UnusedMember.Global
|
// ReSharper disable UnusedMember.Global
|
||||||
// ReSharper disable UnusedMethodReturnValue.Global
|
// ReSharper disable UnusedMethodReturnValue.Global
|
||||||
|
|
||||||
|
#pragma warning disable SA1307
|
||||||
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Dot.Native;
|
namespace Dot.Native;
|
||||||
@ -9,10 +11,16 @@ internal static partial class Win32
|
|||||||
{
|
{
|
||||||
public const int CS_DROP_SHADOW = 0x20000;
|
public const int CS_DROP_SHADOW = 0x20000;
|
||||||
public const int GCL_STYLE = -26;
|
public const int GCL_STYLE = -26;
|
||||||
|
public const int HC_ACTION = 0;
|
||||||
|
public const int INPUT_KEYBOARD = 1;
|
||||||
|
public const int KEYEVENTF_KEYUP = 0x0002;
|
||||||
public const int SW_HIDE = 0;
|
public const int SW_HIDE = 0;
|
||||||
|
public const int WH_KEYBOARD_LL = 13;
|
||||||
public const int WH_MOUSE_LL = 14;
|
public const int WH_MOUSE_LL = 14;
|
||||||
public const int WM_CHANGECBCHAIN = 0x030D;
|
public const int WM_CHANGECBCHAIN = 0x030D;
|
||||||
public const int WM_DRAWCLIPBOARD = 0x308;
|
public const int WM_DRAWCLIPBOARD = 0x308;
|
||||||
|
public const int WM_KEYDOWN = 0x0100;
|
||||||
|
public const int WM_KEYUP = 0x0101;
|
||||||
public const int WM_LBUTTONDOWN = 0x0201;
|
public const int WM_LBUTTONDOWN = 0x0201;
|
||||||
public const int WM_MOUSEMOVE = 0x0200;
|
public const int WM_MOUSEMOVE = 0x0200;
|
||||||
private const string _GDI32_DLL = "gdi32.dll";
|
private const string _GDI32_DLL = "gdi32.dll";
|
||||||
@ -49,6 +57,10 @@ internal static partial class Win32
|
|||||||
[LibraryImport(_USER32_DLL)]
|
[LibraryImport(_USER32_DLL)]
|
||||||
internal static partial int ReleaseDC(nint hWnd, nint dc);
|
internal static partial int ReleaseDC(nint hWnd, nint dc);
|
||||||
|
|
||||||
|
[LibraryImport(_USER32_DLL)]
|
||||||
|
internal static partial uint SendInput(uint cInputs, [MarshalAs(UnmanagedType.LPArray)] InputStruct[] inputs
|
||||||
|
, int cbSize);
|
||||||
|
|
||||||
[LibraryImport(_USER32_DLL)]
|
[LibraryImport(_USER32_DLL)]
|
||||||
internal static partial int SendMessageA(nint hwnd, uint wMsg, nint wParam, nint lParam);
|
internal static partial int SendMessageA(nint hwnd, uint wMsg, nint wParam, nint lParam);
|
||||||
|
|
||||||
@ -72,10 +84,37 @@ internal static partial class Win32
|
|||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
internal static partial bool UnhookWindowsHookExA(nint hhk);
|
internal static partial bool UnhookWindowsHookExA(nint hhk);
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Explicit)]
|
||||||
|
public struct InputStruct
|
||||||
|
{
|
||||||
|
[FieldOffset(8)] public KeybdInputStruct ki;
|
||||||
|
[FieldOffset(0)] public uint type;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Explicit)]
|
||||||
|
public struct KbdllhooksStruct
|
||||||
|
{
|
||||||
|
[FieldOffset(16)] public nint dwExtraInfo;
|
||||||
|
[FieldOffset(8)] public uint flags;
|
||||||
|
[FieldOffset(4)] public uint scanCode;
|
||||||
|
[FieldOffset(12)] public uint time;
|
||||||
|
[FieldOffset(0)] public uint vkCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Explicit)]
|
||||||
|
public struct KeybdInputStruct
|
||||||
|
{
|
||||||
|
[FieldOffset(20)] public long _; // 补位以匹配 UNION的MOUSEINPUT参数 (28bytes)
|
||||||
|
[FieldOffset(12)] public nint dwExtraInfo;
|
||||||
|
[FieldOffset(4)] public uint dwFlags;
|
||||||
|
[FieldOffset(8)] public uint time;
|
||||||
|
[FieldOffset(2)] public ushort wScan;
|
||||||
|
[FieldOffset(0)] public ushort wVk;
|
||||||
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Explicit)]
|
[StructLayout(LayoutKind.Explicit)]
|
||||||
internal ref struct Systemtime
|
internal ref struct Systemtime
|
||||||
{
|
{
|
||||||
#pragma warning disable SA1307
|
|
||||||
[FieldOffset(6)] public ushort wDay;
|
[FieldOffset(6)] public ushort wDay;
|
||||||
[FieldOffset(4)] public ushort wDayOfWeek;
|
[FieldOffset(4)] public ushort wDayOfWeek;
|
||||||
[FieldOffset(8)] public ushort wHour;
|
[FieldOffset(8)] public ushort wHour;
|
||||||
@ -84,6 +123,5 @@ internal static partial class Win32
|
|||||||
[FieldOffset(2)] public ushort wMonth;
|
[FieldOffset(2)] public ushort wMonth;
|
||||||
[FieldOffset(12)] public ushort wSecond;
|
[FieldOffset(12)] public ushort wSecond;
|
||||||
[FieldOffset(0)] public ushort wYear;
|
[FieldOffset(0)] public ushort wYear;
|
||||||
#pragma warning restore SA1307
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,9 +9,13 @@ internal static class BaiduSignCracker
|
|||||||
|
|
||||||
public static string Sign(string text)
|
public static string Sign(string text)
|
||||||
{
|
{
|
||||||
var e = new List<int>(text.Length);
|
var hash = text.Length > 30
|
||||||
for (var i = 0; i < text.Length; i++) {
|
? string.Concat(text.AsSpan()[..10], text.AsSpan(text.Length / 2 - 5, 10), text.AsSpan()[^10..])
|
||||||
var k = (int)text[i];
|
: text;
|
||||||
|
|
||||||
|
var e = new List<int>(hash.Length);
|
||||||
|
for (var i = 0; i < hash.Length; i++) {
|
||||||
|
var k = (int)hash[i];
|
||||||
switch (k) {
|
switch (k) {
|
||||||
case < 128:
|
case < 128:
|
||||||
e.Add(k);
|
e.Add(k);
|
||||||
@ -20,8 +24,8 @@ internal static class BaiduSignCracker
|
|||||||
e.Add((k >> 6) | 192);
|
e.Add((k >> 6) | 192);
|
||||||
break;
|
break;
|
||||||
default: {
|
default: {
|
||||||
if ((k & 64512) == 55296 && i + 1 < text.Length && (text[i + 1] & 64512) == 56320) {
|
if ((k & 64512) == 55296 && i + 1 < hash.Length && (hash[i + 1] & 64512) == 56320) {
|
||||||
k = 65536 + ((k & 1023) << 10) + (text[++i] & 1023);
|
k = 65536 + ((k & 1023) << 10) + (hash[++i] & 1023);
|
||||||
e.Add((k >> 18) | 240);
|
e.Add((k >> 18) | 240);
|
||||||
e.Add(((k >> 12) & 63) | 128);
|
e.Add(((k >> 12) & 63) | 128);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
#if NET7_0_WINDOWS
|
#if NET7_0_WINDOWS
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Dot.Native;
|
using Dot.Native;
|
||||||
using Dot.Tran.Dto;
|
using Dot.Tran.Dto;
|
||||||
@ -9,22 +11,21 @@ using Size = System.Drawing.Size;
|
|||||||
|
|
||||||
namespace Dot.Tran;
|
namespace Dot.Tran;
|
||||||
|
|
||||||
|
[SupportedOSPlatform(nameof(OSPlatform.Windows))]
|
||||||
internal sealed partial class FrmMain : Form
|
internal sealed partial class FrmMain : Form
|
||||||
{
|
{
|
||||||
private const int _HIDING_MIL_SECS = 5000; // 隐藏窗体时间(秒
|
|
||||||
private const int _RETRY_WAIT_MIL_SEC = 1000; // 重试等待时间(秒)
|
private const int _RETRY_WAIT_MIL_SEC = 1000; // 重试等待时间(秒)
|
||||||
private const string _TRANSLATE_API_URL = "https://fanyi.baidu.com/v2transapi";
|
private const string _TRANSLATE_API_URL = $"{_TRANSLATE_HOME_URL}/v2transapi";
|
||||||
private const string _TRANSLATE_HOME_URL = "https://fanyi.baidu.com";
|
private const string _TRANSLATE_HOME_URL = "https://fanyi.baidu.com";
|
||||||
private const double _WINDOW_OPACITY = .5; // 窗体透明度
|
|
||||||
private static ManualResetEvent _mre = new(false); // 隐藏窗体侦测线程信号
|
|
||||||
private readonly HttpClient _httpClient = new();
|
|
||||||
private readonly Label _labelDest = new(); // 显示翻译内容的文本框
|
|
||||||
|
|
||||||
|
private readonly HttpClient _httpClient = new();
|
||||||
|
private readonly KeyboardHook _keyboardHook = new();
|
||||||
|
private readonly Label _labelDest = new(); // 显示翻译内容的文本框
|
||||||
private readonly MouseHook _mouseHook = new();
|
private readonly MouseHook _mouseHook = new();
|
||||||
private readonly Size _mouseMargin = new(10, 10);
|
private readonly Size _mouseMargin = new(10, 10);
|
||||||
|
private bool _capsLockPressed; // 大写键按下进行翻译,区分ctrl+c
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
private DateTime? _latestActiveTime; // 窗体最后激活时间
|
private nint _nextClipViewer; // 下一个剪贴板监视链对象句柄
|
||||||
private nint _nextHwnd; // 下一个剪贴板监视链对象句柄
|
|
||||||
private string _token = "ae72ebad4113270fd26ada5125301268";
|
private string _token = "ae72ebad4113270fd26ada5125301268";
|
||||||
|
|
||||||
public FrmMain()
|
public FrmMain()
|
||||||
@ -34,9 +35,7 @@ internal sealed partial class FrmMain : Form
|
|||||||
InitLabelDest();
|
InitLabelDest();
|
||||||
InitHttpClient();
|
InitHttpClient();
|
||||||
|
|
||||||
_nextHwnd = Win32.SetClipboardViewer(Handle);
|
_nextClipViewer = Win32.SetClipboardViewer(Handle);
|
||||||
|
|
||||||
Task.Run(HideWindow);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~FrmMain()
|
~FrmMain()
|
||||||
@ -53,14 +52,13 @@ internal sealed partial class FrmMain : Form
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (disposing) {
|
if (disposing) {
|
||||||
_mre = null; // 结束定时隐藏窗体线程
|
|
||||||
_mre?.Dispose();
|
|
||||||
_httpClient?.Dispose();
|
_httpClient?.Dispose();
|
||||||
_labelDest?.Dispose();
|
_labelDest?.Dispose();
|
||||||
_mouseHook?.Dispose();
|
_mouseHook?.Dispose();
|
||||||
|
_keyboardHook?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
Win32.ChangeClipboardChain(Handle, _nextHwnd); // 从剪贴板监视链移除本窗体
|
Win32.ChangeClipboardChain(Handle, _nextClipViewer); // 从剪贴板监视链移除本窗体
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,18 +66,21 @@ internal sealed partial class FrmMain : Form
|
|||||||
{
|
{
|
||||||
void SendToNext(Message message)
|
void SendToNext(Message message)
|
||||||
{
|
{
|
||||||
_ = Win32.SendMessageA(_nextHwnd, (uint)message.Msg, message.WParam, message.LParam);
|
_ = Win32.SendMessageA(_nextClipViewer, (uint)message.Msg, message.WParam, message.LParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (m.Msg) {
|
switch (m.Msg) {
|
||||||
case Win32.WM_DRAWCLIPBOARD:
|
case Win32.WM_DRAWCLIPBOARD:
|
||||||
|
if (_capsLockPressed) {
|
||||||
|
_capsLockPressed = false;
|
||||||
DisplayClipboardData();
|
DisplayClipboardData();
|
||||||
|
}
|
||||||
|
|
||||||
SendToNext(m);
|
SendToNext(m);
|
||||||
break;
|
break;
|
||||||
case Win32.WM_CHANGECBCHAIN:
|
case Win32.WM_CHANGECBCHAIN:
|
||||||
Console.WriteLine(Win32.WM_CHANGECBCHAIN);
|
if (m.WParam == _nextClipViewer) {
|
||||||
if (m.WParam == _nextHwnd) {
|
_nextClipViewer = m.LParam;
|
||||||
_nextHwnd = m.LParam;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
SendToNext(m);
|
SendToNext(m);
|
||||||
@ -93,7 +94,7 @@ internal sealed partial class FrmMain : Form
|
|||||||
}
|
}
|
||||||
|
|
||||||
[GeneratedRegex("token: '(\\w+)'")]
|
[GeneratedRegex("token: '(\\w+)'")]
|
||||||
private static partial Regex MyRegex();
|
private static partial Regex TokenRegex();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 显示剪贴板内容.
|
/// 显示剪贴板内容.
|
||||||
@ -107,7 +108,8 @@ internal sealed partial class FrmMain : Form
|
|||||||
|
|
||||||
_labelDest.Text = Str.Translating;
|
_labelDest.Text = Str.Translating;
|
||||||
Task.Run(() => {
|
Task.Run(() => {
|
||||||
var translateText = _labelDest.Text = TranslateText(clipText);
|
var translateText = TranslateText(clipText);
|
||||||
|
ClipboardService.SetText(translateText);
|
||||||
Invoke(() => { _labelDest.Text = translateText; });
|
Invoke(() => { _labelDest.Text = translateText; });
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -115,44 +117,58 @@ internal sealed partial class FrmMain : Form
|
|||||||
point.Offset(new Point(_mouseMargin));
|
point.Offset(new Point(_mouseMargin));
|
||||||
Location = point;
|
Location = point;
|
||||||
Show();
|
Show();
|
||||||
_latestActiveTime = DateTime.Now;
|
|
||||||
_mre.Set();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task HideWindow()
|
|
||||||
{
|
|
||||||
while (_mre is not null) {
|
|
||||||
while (Visible) {
|
|
||||||
await Task.Delay(100);
|
|
||||||
if (_latestActiveTime is null ||
|
|
||||||
!((DateTime.Now - _latestActiveTime.Value).TotalMilliseconds > _HIDING_MIL_SECS)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Invoke(Hide);
|
|
||||||
}
|
|
||||||
|
|
||||||
_mre.WaitOne();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitForm()
|
private void InitForm()
|
||||||
{
|
{
|
||||||
AutoSize = true;
|
AutoSize = true;
|
||||||
AutoSizeMode = AutoSizeMode.GrowAndShrink;
|
AutoSizeMode = AutoSizeMode.GrowAndShrink;
|
||||||
|
MaximumSize = Screen.FromHandle(Handle).Bounds.Size / 2;
|
||||||
FormBorderStyle = FormBorderStyle.None;
|
FormBorderStyle = FormBorderStyle.None;
|
||||||
Opacity = _WINDOW_OPACITY;
|
|
||||||
TopMost = true;
|
TopMost = true;
|
||||||
Visible = false;
|
Visible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitHook()
|
private unsafe void InitHook()
|
||||||
{
|
{
|
||||||
_mouseHook.MouseMoveEvent += (_, e) => {
|
_mouseHook.MouseMoveEvent += (_, e) => {
|
||||||
var point = new Point(e.X, e.Y);
|
var point = new Point(e.X, e.Y);
|
||||||
point.Offset(new Point(_mouseMargin));
|
point.Offset(new Point(_mouseMargin));
|
||||||
Location = point;
|
Location = point;
|
||||||
};
|
};
|
||||||
|
_keyboardHook.KeyUpEvent += (_, e) => {
|
||||||
|
switch (e.vkCode) {
|
||||||
|
case VkCode.VK_CAPITAL: {
|
||||||
|
var keyInputs = new Win32.InputStruct[4];
|
||||||
|
|
||||||
|
keyInputs[0].type = Win32.INPUT_KEYBOARD;
|
||||||
|
keyInputs[0].ki.wVk = VkCode.VK_CONTROL;
|
||||||
|
|
||||||
|
keyInputs[1].type = Win32.INPUT_KEYBOARD;
|
||||||
|
keyInputs[1].ki.wVk = VkCode.VK_C;
|
||||||
|
|
||||||
|
keyInputs[2].type = Win32.INPUT_KEYBOARD;
|
||||||
|
keyInputs[2].ki.wVk = VkCode.VK_C;
|
||||||
|
keyInputs[2].ki.dwFlags = Win32.KEYEVENTF_KEYUP;
|
||||||
|
|
||||||
|
keyInputs[3].type = Win32.INPUT_KEYBOARD;
|
||||||
|
keyInputs[3].ki.wVk = VkCode.VK_CONTROL;
|
||||||
|
keyInputs[3].ki.dwFlags = Win32.KEYEVENTF_KEYUP;
|
||||||
|
|
||||||
|
Win32.SendInput((uint)keyInputs.Length, keyInputs, sizeof(Win32.InputStruct));
|
||||||
|
_capsLockPressed = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case VkCode.VK_ESCAPE:
|
||||||
|
Hide();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitHttpClient()
|
private void InitHttpClient()
|
||||||
@ -164,6 +180,7 @@ internal sealed partial class FrmMain : Form
|
|||||||
|
|
||||||
private void InitLabelDest()
|
private void InitLabelDest()
|
||||||
{
|
{
|
||||||
|
_labelDest.Font = new Font(_labelDest.Font.FontFamily, 16);
|
||||||
_labelDest.BorderStyle = BorderStyle.None;
|
_labelDest.BorderStyle = BorderStyle.None;
|
||||||
_labelDest.Dock = DockStyle.Fill;
|
_labelDest.Dock = DockStyle.Fill;
|
||||||
_labelDest.AutoSize = true;
|
_labelDest.AutoSize = true;
|
||||||
@ -173,11 +190,7 @@ internal sealed partial class FrmMain : Form
|
|||||||
private string TranslateText(string sourceText)
|
private string TranslateText(string sourceText)
|
||||||
{
|
{
|
||||||
while (true) {
|
while (true) {
|
||||||
var hash = sourceText.Length > 30
|
var sign = BaiduSignCracker.Sign(sourceText);
|
||||||
? string.Concat(sourceText.AsSpan()[..10], sourceText.AsSpan(sourceText.Length / 2 - 5, 10)
|
|
||||||
, sourceText.AsSpan()[^10..])
|
|
||||||
: sourceText;
|
|
||||||
var sign = BaiduSignCracker.Sign(hash);
|
|
||||||
var content = new FormUrlEncodedContent(new List<KeyValuePair<string, string>> {
|
var content = new FormUrlEncodedContent(new List<KeyValuePair<string, string>> {
|
||||||
new("from", "auto")
|
new("from", "auto")
|
||||||
, new("to", "zh")
|
, new("to", "zh")
|
||||||
@ -204,7 +217,7 @@ internal sealed partial class FrmMain : Form
|
|||||||
_httpClient.DefaultRequestHeaders.Remove(nameof(Cookie));
|
_httpClient.DefaultRequestHeaders.Remove(nameof(Cookie));
|
||||||
_httpClient.DefaultRequestHeaders.Add(nameof(Cookie), cookie);
|
_httpClient.DefaultRequestHeaders.Add(nameof(Cookie), cookie);
|
||||||
var html = _httpClient.GetStringAsync(_TRANSLATE_HOME_URL).Result;
|
var html = _httpClient.GetStringAsync(_TRANSLATE_HOME_URL).Result;
|
||||||
_token = MyRegex().Match(html).Groups[1].Value;
|
_token = TokenRegex().Match(html).Groups[1].Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,14 +4,18 @@ using System.Runtime.Versioning;
|
|||||||
|
|
||||||
namespace Dot.Tran;
|
namespace Dot.Tran;
|
||||||
|
|
||||||
|
[Description(nameof(Str.TranslateTool))]
|
||||||
|
[Localization(typeof(Str))]
|
||||||
internal sealed class Main : ToolBase<Option>
|
internal sealed class Main : ToolBase<Option>
|
||||||
{
|
{
|
||||||
[SupportedOSPlatform(nameof(OSPlatform.Windows))]
|
[SupportedOSPlatform(nameof(OSPlatform.Windows))]
|
||||||
protected override Task Core()
|
protected override Task Core()
|
||||||
{
|
{
|
||||||
|
AnsiConsole.MarkupLine(Str.StartTranslate);
|
||||||
|
AnsiConsole.MarkupLine(Str.HideTranslate);
|
||||||
var th = new Thread(() => {
|
var th = new Thread(() => {
|
||||||
using var frm = new FrmMain();
|
using var frm = new FrmMain();
|
||||||
Application.Run(frm);
|
Application.Run();
|
||||||
});
|
});
|
||||||
th.SetApartmentState(ApartmentState.STA);
|
th.SetApartmentState(ApartmentState.STA);
|
||||||
th.Start();
|
th.Start();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user