mirror of
https://github.com/nsnail/dot.git
synced 2025-06-17 13:03:22 +08:00
< ver> + 1.1.7
<feat> + 翻译工具
This commit is contained in:
commit
5a499428a2
@ -1,8 +1,4 @@
|
||||
<wpf:ResourceDictionary xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xml:space="preserve">
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/ReSpeller/ReSpellerEnabled/@EntryValue">False</s:Boolean>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_BLANK_LINES_IN_CODE/@EntryValue">1</s:Int64>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_BLANK_LINES_IN_DECLARATIONS/@EntryValue">1</s:Int64>
|
||||
@ -16,7 +12,6 @@
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateConstants/@EntryIndexedValue"><Policy Inspect="True" Prefix="_" Suffix="" Style="AA_BB" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticReadonly/@EntryIndexedValue"><Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=StaticReadonly/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||
|
||||
<s:String x:Key="/Default/CodeStyle/CSharpFileLayoutPatterns/Pattern/@EntryValue"><?xml version="1.0" encoding="utf-16"?>
|
||||
<Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns">
|
||||
<TypePattern>
|
||||
@ -57,5 +52,4 @@
|
||||
</Entry>
|
||||
</TypePattern>
|
||||
</Patterns></s:String>
|
||||
|
||||
</wpf:ResourceDictionary>
|
@ -1 +1,2 @@
|
||||
git reset --hard | git clean -d -fx .
|
||||
git reset --hard
|
||||
git clean -d -fx .
|
@ -24,5 +24,5 @@ internal static class AssemblyInfo
|
||||
public const string ASSEMBLY_PRODUCT = "dot";
|
||||
public const string ASSEMBLY_TITLE = "功能全面的实用工具 - 程序员的瑞士军刀";
|
||||
public const string ASSEMBLY_VERSION = _VERSION;
|
||||
private const string _VERSION = "1.1.6";
|
||||
private const string _VERSION = "1.1.7";
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
// ReSharper disable ClassNeverInstantiated.Global
|
||||
|
||||
#if NET7_0_WINDOWS
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Dot.Color;
|
||||
|
||||
internal sealed class MouseHook : IDisposable
|
||||
{
|
||||
private const int _WH_MOUSE_LL = 14;
|
||||
private const int _WM_LBUTTONDOWN = 0x0201;
|
||||
private const int _WM_MOUSEMOVE = 0x0200;
|
||||
private readonly nint _hookId;
|
||||
private bool _disposed;
|
||||
|
||||
public MouseHook()
|
||||
{
|
||||
_hookId = SetHook(HookCallback);
|
||||
}
|
||||
|
||||
~MouseHook()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public event MouseEventHandler MouseEvent;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private static nint SetHook(Func<int, nint, nint, nint> proc)
|
||||
{
|
||||
using var curProcess = Process.GetCurrentProcess();
|
||||
using var curModule = curProcess.MainModule!;
|
||||
return Win32.SetWindowsHookEx(_WH_MOUSE_LL, proc, Win32.GetModuleHandle(curModule.ModuleName), 0);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (disposing) {
|
||||
//
|
||||
}
|
||||
|
||||
if (_hookId != default) {
|
||||
Win32.UnhookWindowsHookEx(_hookId);
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
private nint HookCallback(int nCode, nint wParam, nint lParam)
|
||||
{
|
||||
if (nCode < 0 || (wParam != _WM_MOUSEMOVE && wParam != _WM_LBUTTONDOWN)) {
|
||||
return Win32.CallNextHookEx(_hookId, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
var hookStruct = (Msllhookstruct)Marshal.PtrToStructure(lParam, typeof(Msllhookstruct))!;
|
||||
MouseEvent?.Invoke(null, new MouseEventArgs( //
|
||||
wParam == _WM_MOUSEMOVE ? MouseButtons.None : MouseButtons.Left //
|
||||
, 0 //
|
||||
, hookStruct.X //
|
||||
, hookStruct.Y //
|
||||
, 0));
|
||||
return Win32.CallNextHookEx(_hookId, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
private readonly struct Msllhookstruct
|
||||
{
|
||||
[FieldOffset(0)] public readonly int X;
|
||||
[FieldOffset(4)] public readonly int Y;
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,6 +1,7 @@
|
||||
#if NET7_0_WINDOWS
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
using Dot.Native;
|
||||
using TextCopy;
|
||||
|
||||
namespace Dot.Color;
|
||||
|
73
src/Native/MouseHook.cs
Normal file
73
src/Native/MouseHook.cs
Normal file
@ -0,0 +1,73 @@
|
||||
// ReSharper disable ClassNeverInstantiated.Global
|
||||
|
||||
#if NET7_0_WINDOWS
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Dot.Native;
|
||||
|
||||
internal sealed class MouseHook : IDisposable
|
||||
{
|
||||
private readonly nint _hookId;
|
||||
private bool _disposed;
|
||||
|
||||
public MouseHook()
|
||||
{
|
||||
_hookId = SetHook(HookCallback);
|
||||
}
|
||||
|
||||
~MouseHook()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public event MouseEventHandler MouseMoveEvent;
|
||||
|
||||
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_MOUSE_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 (wParam == Win32.WM_MOUSEMOVE) {
|
||||
var hookStruct = (Msllhookstruct)Marshal.PtrToStructure(lParam, typeof(Msllhookstruct))!;
|
||||
MouseMoveEvent?.Invoke(null, new MouseEventArgs(MouseButtons.None, 0, hookStruct.X, hookStruct.Y, 0));
|
||||
}
|
||||
|
||||
return Win32.CallNextHookEx(_hookId, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
private readonly struct Msllhookstruct
|
||||
{
|
||||
[FieldOffset(0)] public readonly int X;
|
||||
[FieldOffset(4)] public readonly int Y;
|
||||
}
|
||||
}
|
||||
#endif
|
@ -3,17 +3,23 @@
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Dot;
|
||||
namespace Dot.Native;
|
||||
|
||||
internal static partial class Win32
|
||||
{
|
||||
public const int SW_HIDE = 0;
|
||||
public const int WM_CHANGECBCHAIN = 0x030D;
|
||||
public const int WM_DRAWCLIPBOARD = 0x308;
|
||||
public const int CS_DROP_SHADOW = 0x20000;
|
||||
public const int GCL_STYLE = -26;
|
||||
public const int SW_HIDE = 0;
|
||||
public const int WH_MOUSE_LL = 14;
|
||||
public const int WM_CHANGECBCHAIN = 0x030D;
|
||||
public const int WM_DRAWCLIPBOARD = 0x308;
|
||||
public const int WM_LBUTTONDOWN = 0x0201;
|
||||
public const int WM_MOUSEMOVE = 0x0200;
|
||||
private const string _GDI32_DLL = "gdi32.dll";
|
||||
private const string _KERNEL32_DLL = "kernel32.dll";
|
||||
private const string _USER32_DLL = "user32.dll";
|
||||
|
||||
private const string _GDI32_DLL = "gdi32.dll";
|
||||
private const string _KERNEL32_DLL = "kernel32.dll";
|
||||
private const string _USER32_DLL = "user32.dll";
|
||||
public delegate nint HookProc(int nCode, nint wParam, nint lParam);
|
||||
|
||||
[LibraryImport(_USER32_DLL)]
|
||||
internal static partial nint CallNextHookEx(nint hhk, int nCode, nint wParam, nint lParam);
|
||||
@ -22,6 +28,9 @@ internal static partial class Win32
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static partial bool ChangeClipboardChain(nint hWndRemove, nint hWndNewNext);
|
||||
|
||||
[LibraryImport(_USER32_DLL)]
|
||||
internal static partial int GetClassLongA(nint hWnd, int nIndex);
|
||||
|
||||
[LibraryImport(_KERNEL32_DLL)]
|
||||
internal static partial nint GetConsoleWindow();
|
||||
|
||||
@ -29,7 +38,7 @@ internal static partial class Win32
|
||||
internal static partial nint GetDesktopWindow();
|
||||
|
||||
[LibraryImport(_KERNEL32_DLL, StringMarshalling = StringMarshalling.Utf16)]
|
||||
internal static partial nint GetModuleHandle(string lpModuleName);
|
||||
internal static partial nint GetModuleHandleA(string lpModuleName);
|
||||
|
||||
[LibraryImport(_GDI32_DLL)]
|
||||
internal static partial uint GetPixel(nint dc, int x, int y);
|
||||
@ -43,6 +52,9 @@ internal static partial class Win32
|
||||
[LibraryImport(_USER32_DLL)]
|
||||
internal static partial int SendMessageA(nint hwnd, uint wMsg, nint wParam, nint lParam);
|
||||
|
||||
[LibraryImport(_USER32_DLL)]
|
||||
internal static partial int SetClassLongA(nint hWnd, int nIndex, int dwNewLong);
|
||||
|
||||
[LibraryImport(_USER32_DLL)]
|
||||
internal static partial int SetClipboardViewer(nint hWnd);
|
||||
|
||||
@ -50,8 +62,7 @@ internal static partial class Win32
|
||||
internal static partial void SetLocalTime(Systemtime st);
|
||||
|
||||
[LibraryImport(_USER32_DLL)]
|
||||
internal static partial nint SetWindowsHookEx(int idHook, Func<int, nint, nint, nint> lpfn, nint hMod
|
||||
, uint dwThreadId);
|
||||
internal static partial nint SetWindowsHookExA(int idHook, HookProc lpfn, nint hMod, uint dwThreadId);
|
||||
|
||||
[LibraryImport(_USER32_DLL)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
@ -59,7 +70,7 @@ internal static partial class Win32
|
||||
|
||||
[LibraryImport(_USER32_DLL)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static partial bool UnhookWindowsHookEx(nint hhk);
|
||||
internal static partial bool UnhookWindowsHookExA(nint hhk);
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
internal ref struct Systemtime
|
@ -1,22 +0,0 @@
|
||||
namespace Dot.Text;
|
||||
|
||||
internal sealed partial class Main
|
||||
{
|
||||
private ref struct Output
|
||||
{
|
||||
public ReadOnlySpan<char> Base64;
|
||||
public ReadOnlySpan<char> Base64DeCode;
|
||||
public ReadOnlySpan<char> Base64DeCodeHex;
|
||||
public ReadOnlySpan<char> EncodingName;
|
||||
public ReadOnlySpan<char> Hex;
|
||||
public ReadOnlySpan<char> HtmlDecode;
|
||||
public ReadOnlySpan<char> HtmlEncode;
|
||||
public ReadOnlySpan<char> Md5;
|
||||
public ReadOnlySpan<char> OriginText;
|
||||
public ReadOnlySpan<char> Sha1;
|
||||
public ReadOnlySpan<char> Sha256;
|
||||
public ReadOnlySpan<char> Sha512;
|
||||
public ReadOnlySpan<char> UrlDecode;
|
||||
public ReadOnlySpan<char> UrlEncode;
|
||||
}
|
||||
}
|
127
src/Text/Main.cs
127
src/Text/Main.cs
@ -12,7 +12,7 @@ namespace Dot.Text;
|
||||
|
||||
[Description(nameof(Str.TextTool))]
|
||||
[Localization(typeof(Str))]
|
||||
internal sealed partial class Main : ToolBase<Option>
|
||||
internal sealed class Main : ToolBase<Option>
|
||||
{
|
||||
#if NET7_0_WINDOWS
|
||||
protected override async Task Core()
|
||||
@ -39,20 +39,29 @@ internal sealed partial class Main : ToolBase<Option>
|
||||
{
|
||||
Output ret;
|
||||
var inputHex = text.Hex(enc);
|
||||
ret.Base64DeCodeHex = ReadOnlySpan<char>.Empty;
|
||||
ret.Base64DeCode = ReadOnlySpan<char>.Empty;
|
||||
ret.EncodingName = enc.EncodingName;
|
||||
ret.OriginText = text;
|
||||
ret.Hex = inputHex.String();
|
||||
ret.Base64 = text.Base64(enc);
|
||||
ret.Md5 = MD5.HashData(inputHex).String();
|
||||
ret.Sha1 = SHA1.HashData(inputHex).String();
|
||||
ret.Sha256 = SHA256.HashData(inputHex).String();
|
||||
ret.Sha512 = SHA512.HashData(inputHex).String();
|
||||
ret.UrlEncode = text.Url();
|
||||
ret.UrlDecode = text.UrlDe();
|
||||
ret.HtmlDecode = text.HtmlDe();
|
||||
ret.HtmlEncode = text.Html();
|
||||
ret.Base64DeCodeHex = ReadOnlySpan<char>.Empty;
|
||||
ret.Base64DeCode = ReadOnlySpan<char>.Empty;
|
||||
ret.EncodingName = enc.EncodingName;
|
||||
ret.Hex = inputHex.String();
|
||||
ret.Base64 = text.Base64(enc);
|
||||
ret.Md5 = MD5.HashData(inputHex).String();
|
||||
ret.Sha1 = SHA1.HashData(inputHex).String();
|
||||
ret.Sha256 = SHA256.HashData(inputHex).String();
|
||||
ret.Sha512 = SHA512.HashData(inputHex).String();
|
||||
ret.UrlEncode = text.Url();
|
||||
ret.UrlDecode = text.UrlDe();
|
||||
ret.HtmlDecode = text.HtmlDe();
|
||||
ret.HtmlEncode = text.Html();
|
||||
ret.PercentUnicode = default;
|
||||
ret.AndUnicode = default;
|
||||
ret.BacksLantUnicode = default;
|
||||
ret.UnicodeDecode = text.UnicodeDe();
|
||||
|
||||
if (Equals(enc, Encoding.BigEndianUnicode)) {
|
||||
ret.PercentUnicode = inputHex.String(false, "%u", 2);
|
||||
ret.AndUnicode = inputHex.String(false, @";&#x", 2)[1..] + ";";
|
||||
ret.BacksLantUnicode = inputHex.String(false, @"\u", 2);
|
||||
}
|
||||
|
||||
if (!text.IsBase64String()) {
|
||||
return ret;
|
||||
@ -76,6 +85,37 @@ internal sealed partial class Main : ToolBase<Option>
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static string BuildTemplate(Output o)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
sb.AppendLine( //
|
||||
CultureInfo.InvariantCulture
|
||||
, $"{new string('-', 20)} {o.EncodingName} {new string('-', 30 - o.EncodingName.Length)}");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $"hex: {o.Hex}");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $"base64: {o.Base64}");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $"base64-decode-hex: {o.Base64DeCodeHex}");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $"base64-decode: {o.Base64DeCode}");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $"md5: {o.Md5}");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $"sha1: {o.Sha1}");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $"sha256: {o.Sha256}");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $"sha512: {o.Sha512}");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $"url-encode: {o.UrlEncode}");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $"url-decode: {o.UrlDecode}");
|
||||
|
||||
if (o.EncodingName.Equals(Encoding.BigEndianUnicode.EncodingName, StringComparison.OrdinalIgnoreCase)) {
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $"unicode-percent: {o.PercentUnicode}");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $"unicode-and: {o.AndUnicode}");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $"unicode-backslant: {o.BacksLantUnicode}");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $"unicode-decode: {o.UnicodeDecode}");
|
||||
}
|
||||
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $"html-encode: {o.HtmlEncode}");
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $"html-decode: {o.HtmlDecode}");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static void ParseAndShow(string text)
|
||||
{
|
||||
var ansi = BuildOutput(text, Encoding.GetEncoding("gbk"));
|
||||
@ -83,35 +123,40 @@ internal sealed partial class Main : ToolBase<Option>
|
||||
var unicodeLittleEndian = BuildOutput(text, Encoding.Unicode);
|
||||
var unicodeBigEndian = BuildOutput(text, Encoding.BigEndianUnicode);
|
||||
|
||||
PrintOutput(ansi);
|
||||
PrintOutput(utf8);
|
||||
PrintOutput(unicodeLittleEndian);
|
||||
PrintOutput(unicodeBigEndian);
|
||||
}
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine(BuildTemplate(ansi));
|
||||
sb.AppendLine(BuildTemplate(utf8));
|
||||
sb.AppendLine(BuildTemplate(unicodeLittleEndian));
|
||||
sb.AppendLine(BuildTemplate(unicodeBigEndian));
|
||||
var str = sb.ToString();
|
||||
|
||||
Console.WriteLine(str);
|
||||
|
||||
private static void PrintOutput(Output o)
|
||||
{
|
||||
var outputTemp = $"""
|
||||
origin-text: {o.OriginText}
|
||||
{new string('-', 20)} {o.EncodingName} {new string('-', 30 - o.EncodingName.Length)}
|
||||
hex: {o.Hex}
|
||||
base64: {o.Base64}
|
||||
base64-decode-hex: {o.Base64DeCodeHex}
|
||||
base64-decode: {o.Base64DeCode}
|
||||
md5: {o.Md5}
|
||||
sha1: {o.Sha1}
|
||||
sha256: {o.Sha256}
|
||||
sha512: {o.Sha512}
|
||||
url-encode: {o.UrlEncode}
|
||||
url-decode: {o.UrlDecode}
|
||||
html-encode: {o.HtmlEncode}
|
||||
html-decode: {o.HtmlDecode}
|
||||
""";
|
||||
Console.WriteLine(outputTemp);
|
||||
#if NET7_0_WINDOWS
|
||||
var file = Path.Combine(Path.GetTempPath(), $"{System.Guid.NewGuid()}.html");
|
||||
File.WriteAllText(file, outputTemp.Text2Html());
|
||||
var file = Path.Combine(Path.GetTempPath(), $"{System.Guid.NewGuid()}.txt");
|
||||
File.WriteAllText(file, str);
|
||||
Process.Start("explorer", file);
|
||||
#endif
|
||||
}
|
||||
|
||||
private ref struct Output
|
||||
{
|
||||
public ReadOnlySpan<char> AndUnicode;
|
||||
public ReadOnlySpan<char> BacksLantUnicode;
|
||||
public ReadOnlySpan<char> Base64;
|
||||
public ReadOnlySpan<char> Base64DeCode;
|
||||
public ReadOnlySpan<char> Base64DeCodeHex;
|
||||
public ReadOnlySpan<char> EncodingName;
|
||||
public ReadOnlySpan<char> Hex;
|
||||
public ReadOnlySpan<char> HtmlDecode;
|
||||
public ReadOnlySpan<char> HtmlEncode;
|
||||
public ReadOnlySpan<char> Md5;
|
||||
public ReadOnlySpan<char> PercentUnicode;
|
||||
public ReadOnlySpan<char> Sha1;
|
||||
public ReadOnlySpan<char> Sha256;
|
||||
public ReadOnlySpan<char> Sha512;
|
||||
public ReadOnlySpan<char> UnicodeDecode;
|
||||
public ReadOnlySpan<char> UrlDecode;
|
||||
public ReadOnlySpan<char> UrlEncode;
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
// ReSharper disable ClassNeverInstantiated.Global
|
||||
|
||||
using System.Net.Sockets;
|
||||
using Dot.Native;
|
||||
|
||||
namespace Dot.Time;
|
||||
|
||||
|
60
src/Tran/BaiduSignCracker.cs
Normal file
60
src/Tran/BaiduSignCracker.cs
Normal file
@ -0,0 +1,60 @@
|
||||
using NSExt.Extensions;
|
||||
|
||||
namespace Dot.Tran;
|
||||
|
||||
internal static class BaiduSignCracker
|
||||
{
|
||||
private const int _MAGIC_NUMBER_1 = 320305;
|
||||
private const int _MAGIC_NUMBER_2 = 131321201;
|
||||
|
||||
public static string Sign(string text)
|
||||
{
|
||||
var e = new List<int>(text.Length);
|
||||
for (var i = 0; i < text.Length; i++) {
|
||||
var k = (int)text[i];
|
||||
switch (k) {
|
||||
case < 128:
|
||||
e.Add(k);
|
||||
break;
|
||||
case < 2048:
|
||||
e.Add((k >> 6) | 192);
|
||||
break;
|
||||
default: {
|
||||
if ((k & 64512) == 55296 && i + 1 < text.Length && (text[i + 1] & 64512) == 56320) {
|
||||
k = 65536 + ((k & 1023) << 10) + (text[++i] & 1023);
|
||||
e.Add((k >> 18) | 240);
|
||||
e.Add(((k >> 12) & 63) | 128);
|
||||
}
|
||||
else {
|
||||
e.Add((k >> 12) | 224);
|
||||
e.Add(((k >> 6) & 63) | 128);
|
||||
e.Add((k & 63) | 128);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var ret = e.Aggregate(_MAGIC_NUMBER_1, (accumulator, source) => Compute(accumulator + source, "+-a^+6"));
|
||||
ret = Compute(ret, "+-3^+b+-f");
|
||||
ret ^= _MAGIC_NUMBER_2;
|
||||
var longRet = ret < 0 ? 1L + (ret & int.MaxValue) + int.MaxValue : ret;
|
||||
longRet %= 1_000_000;
|
||||
return $"{longRet}.{longRet ^ _MAGIC_NUMBER_1}";
|
||||
}
|
||||
|
||||
private static int Compute(int number, string password)
|
||||
{
|
||||
unchecked {
|
||||
for (var i = 0; i < password.Length - 2; i += 3) {
|
||||
var c = password[i + 2];
|
||||
var moveBit = c >= 'a' ? c - 87 : c.ToString().Int32();
|
||||
var d = password[i + 1] == '+' ? number >>> moveBit : number << moveBit;
|
||||
number = password[i] == '+' ? (number + d) & (int)uint.MaxValue : number ^ d;
|
||||
}
|
||||
}
|
||||
|
||||
return number;
|
||||
}
|
||||
}
|
62
src/Tran/Dto/BaiduTranslateResultDto.cs
Normal file
62
src/Tran/Dto/BaiduTranslateResultDto.cs
Normal file
@ -0,0 +1,62 @@
|
||||
// ReSharper disable InconsistentNaming
|
||||
// ReSharper disable ClassNeverInstantiated.Global
|
||||
|
||||
#pragma warning disable IDE1006,SA1300
|
||||
namespace Dot.Tran.Dto;
|
||||
|
||||
internal sealed record BaiduTranslateResultDto
|
||||
{
|
||||
public sealed record DataItem
|
||||
{
|
||||
public string dst { get; set; }
|
||||
|
||||
public int prefixWrap { get; set; }
|
||||
|
||||
public string result { get; set; }
|
||||
|
||||
public string src { get; set; }
|
||||
}
|
||||
|
||||
public sealed record PhoneticItem
|
||||
{
|
||||
public string src_str { get; set; }
|
||||
|
||||
public string trg_str { get; set; }
|
||||
}
|
||||
|
||||
public sealed record Root
|
||||
{
|
||||
public string errmsg { get; set; }
|
||||
|
||||
public int errno { get; set; }
|
||||
|
||||
public int error { get; set; }
|
||||
|
||||
public string errShowMsg { get; set; }
|
||||
|
||||
public string from { get; set; }
|
||||
|
||||
public long logid { get; set; }
|
||||
|
||||
public string query { get; set; }
|
||||
|
||||
public string to { get; set; }
|
||||
|
||||
public Trans_result trans_result { get; set; }
|
||||
}
|
||||
|
||||
public sealed record Trans_result
|
||||
{
|
||||
public List<DataItem> data { get; set; }
|
||||
|
||||
public string from { get; set; }
|
||||
|
||||
public List<PhoneticItem> phonetic { get; set; }
|
||||
|
||||
public int status { get; set; }
|
||||
|
||||
public string to { get; set; }
|
||||
|
||||
public int type { get; set; }
|
||||
}
|
||||
}
|
@ -1,37 +1,38 @@
|
||||
#if NET7_0_WINDOWS
|
||||
using System.Net;
|
||||
using System.Text.RegularExpressions;
|
||||
using Dot.Native;
|
||||
using Dot.Tran.Dto;
|
||||
using NSExt.Extensions;
|
||||
using Colors = System.Drawing.Color;
|
||||
using Padding = System.Windows.Forms.Padding;
|
||||
using TextCopy;
|
||||
using Size = System.Drawing.Size;
|
||||
|
||||
namespace Dot.Tran;
|
||||
|
||||
internal sealed class FrmMain : Form
|
||||
internal sealed partial class FrmMain : Form
|
||||
{
|
||||
private const int _HIDING_MIL_SECS = 1000; // 鼠标离开超过此时间后隐藏窗体
|
||||
private const string _TOKEN_URL = "https://fanyi.qq.com/api/reauth12f";
|
||||
private const string _URL = "https://fanyi.qq.com/api/translate";
|
||||
private static ManualResetEvent _mre = new(false); // 隐藏窗体侦测线程信号
|
||||
private const int _HIDING_MIL_SECS = 5000; // 隐藏窗体时间(秒
|
||||
private const int _RETRY_WAIT_MIL_SEC = 1000; // 重试等待时间(秒)
|
||||
private const string _TRANSLATE_API_URL = "https://fanyi.baidu.com/v2transapi";
|
||||
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 Colors _bgColor // 背景颜色
|
||||
= Colors.FromArgb(0xff, 0x1e, 0x1e, 0x1e);
|
||||
|
||||
private readonly Colors _foreColor = Colors.White; // 字体颜色
|
||||
|
||||
private readonly HttpClient _httpClient = new();
|
||||
private readonly RichTextBox _richTextSource = new(); // 显示剪贴板内容的富文本框
|
||||
private readonly TextBox _textDest = new(); // 显示翻译内容的文本框
|
||||
private readonly Padding _windowPadding = new(10, 10, 10, 10); // 窗体边距
|
||||
private readonly Size _windowSize = new(640, 360); // 窗体大小
|
||||
private DateTime? _mouseLeaveTime; // 鼠标离开窗体时间
|
||||
private nint _nextHwnd; // 下一个剪贴板监视链对象句柄
|
||||
private readonly MouseHook _mouseHook = new();
|
||||
private readonly Size _mouseMargin = new(10, 10);
|
||||
private bool _disposed;
|
||||
private DateTime? _latestActiveTime; // 窗体最后激活时间
|
||||
private nint _nextHwnd; // 下一个剪贴板监视链对象句柄
|
||||
private string _token = "ae72ebad4113270fd26ada5125301268";
|
||||
|
||||
public FrmMain()
|
||||
{
|
||||
InitForm();
|
||||
|
||||
InitTextSource();
|
||||
InitTextDest();
|
||||
InitHook();
|
||||
InitLabelDest();
|
||||
InitHttpClient();
|
||||
|
||||
_nextHwnd = Win32.SetClipboardViewer(Handle);
|
||||
|
||||
@ -40,35 +41,27 @@ internal sealed class FrmMain : Form
|
||||
|
||||
~FrmMain()
|
||||
{
|
||||
_mre?.Dispose();
|
||||
_richTextSource?.Dispose();
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
protected override void OnClosed(EventArgs e)
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.OnClosed(e);
|
||||
base.Dispose(disposing);
|
||||
|
||||
// 结束定时隐藏窗体线程
|
||||
_mre = null;
|
||||
|
||||
// 从剪贴板监视链移除本窗体
|
||||
Win32.ChangeClipboardChain(Handle, _nextHwnd);
|
||||
}
|
||||
|
||||
protected override void OnMouseEnter(EventArgs e)
|
||||
{
|
||||
base.OnMouseEnter(e);
|
||||
_mouseLeaveTime = null;
|
||||
}
|
||||
|
||||
protected override void OnMouseLeave(EventArgs e)
|
||||
{
|
||||
base.OnMouseLeave(e);
|
||||
if (ClientRectangle.Contains(PointToClient(MousePosition))) {
|
||||
if (_disposed) {
|
||||
return;
|
||||
}
|
||||
|
||||
_mouseLeaveTime = DateTime.Now;
|
||||
if (disposing) {
|
||||
_mre = null; // 结束定时隐藏窗体线程
|
||||
_mre?.Dispose();
|
||||
_httpClient?.Dispose();
|
||||
_labelDest?.Dispose();
|
||||
_mouseHook?.Dispose();
|
||||
}
|
||||
|
||||
Win32.ChangeClipboardChain(Handle, _nextHwnd); // 从剪贴板监视链移除本窗体
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
protected override void WndProc(ref Message m)
|
||||
@ -84,6 +77,7 @@ internal sealed class FrmMain : Form
|
||||
SendToNext(m);
|
||||
break;
|
||||
case Win32.WM_CHANGECBCHAIN:
|
||||
Console.WriteLine(Win32.WM_CHANGECBCHAIN);
|
||||
if (m.WParam == _nextHwnd) {
|
||||
_nextHwnd = m.LParam;
|
||||
}
|
||||
@ -98,61 +92,44 @@ internal sealed class FrmMain : Form
|
||||
}
|
||||
}
|
||||
|
||||
[GeneratedRegex("token: '(\\w+)'")]
|
||||
private static partial Regex MyRegex();
|
||||
|
||||
/// <summary>
|
||||
/// 显示剪贴板内容.
|
||||
/// </summary>
|
||||
private void DisplayClipboardData()
|
||||
{
|
||||
var clipData = Clipboard.GetDataObject();
|
||||
if (clipData is null) {
|
||||
var clipText = ClipboardService.GetText();
|
||||
if (clipText.NullOrWhiteSpace()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (clipData.GetDataPresent(DataFormats.Rtf)) {
|
||||
_richTextSource.Rtf = clipData.GetData(DataFormats.Rtf) as string;
|
||||
}
|
||||
else if (clipData.GetDataPresent(DataFormats.Text)) {
|
||||
_richTextSource.Text = clipData.GetData(DataFormats.Text) as string;
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
_labelDest.Text = Str.Translating;
|
||||
Task.Run(() => {
|
||||
var translateText = _labelDest.Text = TranslateText(clipText);
|
||||
Invoke(() => { _labelDest.Text = translateText; });
|
||||
});
|
||||
|
||||
var mousePos = Cursor.Position;
|
||||
mousePos.Offset(-_windowPadding.Left, -_windowPadding.Top);
|
||||
Location = mousePos;
|
||||
Visible = true;
|
||||
_mouseLeaveTime = null;
|
||||
var point = Cursor.Position;
|
||||
point.Offset(new Point(_mouseMargin));
|
||||
Location = point;
|
||||
Show();
|
||||
_latestActiveTime = DateTime.Now;
|
||||
_mre.Set();
|
||||
}
|
||||
|
||||
private TokenStruct GetTranslateToken()
|
||||
{
|
||||
using var rsp = _httpClient.PostAsync(_TOKEN_URL, new StringContent(string.Empty)).Result;
|
||||
var rspStr = rsp.Content.ReadAsStringAsync().Result;
|
||||
return rspStr.Object<TokenStruct>();
|
||||
}
|
||||
|
||||
private int HalfHeight()
|
||||
{
|
||||
return (_windowSize.Height - _windowPadding.Top - _windowPadding.Bottom) / 2 - _windowPadding.Top / 2;
|
||||
}
|
||||
|
||||
private async Task HideWindow()
|
||||
{
|
||||
while (_mre is not null) {
|
||||
while (Visible) {
|
||||
await Task.Delay(100);
|
||||
if (_mouseLeaveTime is null ||
|
||||
!((DateTime.Now - _mouseLeaveTime.Value).TotalMilliseconds > _HIDING_MIL_SECS)) {
|
||||
if (_latestActiveTime is null ||
|
||||
!((DateTime.Now - _latestActiveTime.Value).TotalMilliseconds > _HIDING_MIL_SECS)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Invoke(() => {
|
||||
Visible = false;
|
||||
_richTextSource.Clear();
|
||||
_textDest.Clear();
|
||||
});
|
||||
Invoke(Hide);
|
||||
}
|
||||
|
||||
_mre.WaitOne();
|
||||
@ -161,73 +138,74 @@ internal sealed class FrmMain : Form
|
||||
|
||||
private void InitForm()
|
||||
{
|
||||
BackColor = _bgColor;
|
||||
AutoSize = true;
|
||||
AutoSizeMode = AutoSizeMode.GrowAndShrink;
|
||||
FormBorderStyle = FormBorderStyle.None;
|
||||
Padding = _windowPadding;
|
||||
Size = _windowSize;
|
||||
Opacity = _WINDOW_OPACITY;
|
||||
TopMost = true;
|
||||
Visible = false;
|
||||
}
|
||||
|
||||
private void InitTextDest()
|
||||
private void InitHook()
|
||||
{
|
||||
_textDest.BackColor = _bgColor;
|
||||
_textDest.BorderStyle = BorderStyle.None;
|
||||
_textDest.Dock = DockStyle.Bottom;
|
||||
_textDest.ForeColor = _foreColor;
|
||||
_textDest.Height = HalfHeight();
|
||||
_textDest.Margin = new Padding(0, _windowPadding.Top / 2, 0, 0);
|
||||
_textDest.MouseEnter += (_, e) => OnMouseEnter(e);
|
||||
_textDest.Multiline = true;
|
||||
_textDest.ScrollBars = ScrollBars.None;
|
||||
Controls.Add(_textDest);
|
||||
_mouseHook.MouseMoveEvent += (_, e) => {
|
||||
var point = new Point(e.X, e.Y);
|
||||
point.Offset(new Point(_mouseMargin));
|
||||
Location = point;
|
||||
};
|
||||
}
|
||||
|
||||
private void InitTextSource()
|
||||
private void InitHttpClient()
|
||||
{
|
||||
_richTextSource.TextChanged += (sender, e) => {
|
||||
if (_richTextSource.Text.NullOrWhiteSpace()) {
|
||||
return;
|
||||
}
|
||||
_httpClient.DefaultRequestHeaders.Add( //
|
||||
"User-Agent"
|
||||
, " Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36");
|
||||
}
|
||||
|
||||
_textDest.Text = Str.Translating;
|
||||
_textDest.Text = TranslateText(_richTextSource.Text);
|
||||
};
|
||||
_richTextSource.BackColor = _bgColor;
|
||||
_richTextSource.BorderStyle = BorderStyle.None;
|
||||
_richTextSource.Dock = DockStyle.Top;
|
||||
_richTextSource.ForeColor = _foreColor;
|
||||
_richTextSource.Height = HalfHeight();
|
||||
_richTextSource.Margin = new Padding(0, 0, 0, _windowPadding.Top / 2);
|
||||
_richTextSource.MouseEnter += (_, e) => OnMouseEnter(e);
|
||||
_richTextSource.ScrollBars = RichTextBoxScrollBars.None;
|
||||
Controls.Add(_richTextSource);
|
||||
private void InitLabelDest()
|
||||
{
|
||||
_labelDest.BorderStyle = BorderStyle.None;
|
||||
_labelDest.Dock = DockStyle.Fill;
|
||||
_labelDest.AutoSize = true;
|
||||
Controls.Add(_labelDest);
|
||||
}
|
||||
|
||||
private string TranslateText(string sourceText)
|
||||
{
|
||||
var token = GetTranslateToken();
|
||||
var content = new FormUrlEncodedContent(new List<KeyValuePair<string, string>> {
|
||||
new("source", "auto")
|
||||
, new("target", "zh")
|
||||
, new("qtv", token.Qtv.Url())
|
||||
, new("qtk", token.Qtk.Url())
|
||||
, new("sourceText", sourceText.Url())
|
||||
});
|
||||
var rsp = _httpClient.PostAsync(_URL, content).Result;
|
||||
var ret = rsp.Content.ReadAsStringAsync().Result;
|
||||
while (true) {
|
||||
var hash = sourceText.Length > 30
|
||||
? 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>> {
|
||||
new("from", "auto")
|
||||
, new("to", "zh")
|
||||
, new("query", sourceText)
|
||||
, new("simple_means_flag", "3")
|
||||
, new("sign", sign)
|
||||
, new("token", _token)
|
||||
, new("domain", "common")
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
var rsp = _httpClient.PostAsync(_TRANSLATE_API_URL, content).Result;
|
||||
var rspObj = rsp.Content.ReadAsStringAsync().Result.Object<BaiduTranslateResultDto.Root>();
|
||||
if (rspObj.error == 0) {
|
||||
return string.Join(Environment.NewLine, rspObj.trans_result.data.Select(x => x.dst));
|
||||
}
|
||||
|
||||
private readonly struct TokenStruct
|
||||
{
|
||||
// ReSharper disable UnusedAutoPropertyAccessor.Local
|
||||
public string Qtk { get; init; }
|
||||
Console.Error.WriteLine(rspObj.Json().UnicodeDe());
|
||||
Console.Error.WriteLine(rsp.Headers.Json());
|
||||
|
||||
public string Qtv { get; init; }
|
||||
|
||||
// ReSharper restore UnusedAutoPropertyAccessor.Local
|
||||
//cookie or token invalid
|
||||
Task.Delay(_RETRY_WAIT_MIL_SEC).Wait();
|
||||
var cookie = string.Join(
|
||||
';', rsp.Headers.First(x => x.Key == "Set-Cookie").Value.Select(x => x.Split(';').First()));
|
||||
_httpClient.DefaultRequestHeaders.Remove(nameof(Cookie));
|
||||
_httpClient.DefaultRequestHeaders.Add(nameof(Cookie), cookie);
|
||||
var html = _httpClient.GetStringAsync(_TRANSLATE_HOME_URL).Result;
|
||||
_token = MyRegex().Match(html).Groups[1].Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -9,7 +9,10 @@ internal sealed class Main : ToolBase<Option>
|
||||
[SupportedOSPlatform(nameof(OSPlatform.Windows))]
|
||||
protected override Task Core()
|
||||
{
|
||||
var th = new Thread(() => { Application.Run(new FrmMain()); });
|
||||
var th = new Thread(() => {
|
||||
using var frm = new FrmMain();
|
||||
Application.Run(frm);
|
||||
});
|
||||
th.SetApartmentState(ApartmentState.STA);
|
||||
th.Start();
|
||||
th.Join();
|
||||
|
@ -13,7 +13,7 @@
|
||||
<DefineConstants>$(DefineConstants);NET7_0_WINDOWS</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="NSExt" Version="1.0.9-alpha.0.1" />
|
||||
<PackageReference Include="NSExt" Version="1.0.11-alpha.0.1" />
|
||||
<PackageReference Include="Spectre.Console.Cli.NS" Version="0.45.1-preview.0.48" />
|
||||
<PackageReference Include="Spectre.Console.NS" Version="0.45.1-preview.0.48" />
|
||||
<PackageReference Condition="'$(TargetFramework)' == 'net7.0-windows'" Include="TextCopy" Version="6.2.0" />
|
||||
|
@ -11,7 +11,7 @@
|
||||
"packages": [
|
||||
{
|
||||
"packageName": "NSExt",
|
||||
"version": "1.0.9-alpha.0.1"
|
||||
"version": "1.0.11-alpha.0.1"
|
||||
},
|
||||
{
|
||||
"packageName": "Spectre.Console.Cli.NS",
|
||||
|
Loading…
x
Reference in New Issue
Block a user