add:基于IPC的IACore在net6的实现

This commit is contained in:
2026-05-02 00:40:49 +08:00
parent fff52aa282
commit 94142ec8a5
5 changed files with 15 additions and 88 deletions
+8 -3
View File
@@ -1204,9 +1204,14 @@ namespace Ink_Canvas
try try
{ {
LogHelper.WriteLogToFile("启动 IACore IPC 辅助进程"); var shapeMode = ShapeRecognitionRouter.FromSettingsInt(
bool ipcStarted = IpcIACoreClient.Instance.Start(); Ink_Canvas.Windows.SettingsViews.Helpers.SettingsManager.Settings?.InkToShape?.ShapeRecognitionEngine ?? 0);
LogHelper.WriteLogToFile($"IACore IPC 辅助进程{(ipcStarted ? "" : "使 IACore 退")}"); if (!ShapeRecognitionRouter.ResolveUseWinRt(shapeMode))
{
LogHelper.WriteLogToFile("启动 IACore IPC 辅助进程");
bool ipcStarted = IpcIACoreClient.Instance.Start();
LogHelper.WriteLogToFile($"IACore IPC 辅助进程{(ipcStarted ? "" : "")}");
}
} }
catch (Exception ex) catch (Exception ex)
{ {
+1 -10
View File
@@ -1,4 +1,3 @@
using Ink_Canvas.Controls;
using System; using System;
using System.Windows; using System.Windows;
using System.Windows.Media; using System.Windows.Media;
@@ -8,15 +7,7 @@ namespace Ink_Canvas.Helpers
{ {
internal class AnimationsHelper internal class AnimationsHelper
{ {
private static UIElement ResolveAnimationTarget(UIElement element) private static UIElement ResolveAnimationTarget(UIElement element) => element;
{
if (element is BoardMenuFrame frame)
{
frame.ApplyTemplate();
return frame.AnimationTarget ?? element;
}
return element;
}
public static void ShowWithFadeIn(UIElement element, double duration = 0.15) public static void ShowWithFadeIn(UIElement element, double duration = 0.15)
{ {
+4 -51
View File
@@ -1,24 +1,16 @@
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.IO.Pipes; using System.IO.Pipes;
using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Ink; using System.Windows.Ink;
using System.Windows.Media; using System.Windows.Media;
namespace Ink_Canvas.Helpers namespace Ink_Canvas.Helpers
{ {
/// <summary>
/// 通过 Named Pipe IPC 调用 32 位辅助进程 InkCanvasForClass.IACoreHelper.exe 进行 IACore 形状识别。
/// 单例,线程安全,辅助进程崩溃后自动重启。
/// </summary>
public sealed class IpcIACoreClient : IDisposable public sealed class IpcIACoreClient : IDisposable
{ {
// ── 单例 ──────────────────────────────────────────────────────────────
private static IpcIACoreClient _instance; private static IpcIACoreClient _instance;
private static readonly object _instanceLock = new object(); private static readonly object _instanceLock = new object();
@@ -34,35 +26,26 @@ namespace Ink_Canvas.Helpers
} }
} }
// ── 状态 ──────────────────────────────────────────────────────────────
private Process _helperProcess; private Process _helperProcess;
private readonly object _pipeLock = new object(); private readonly object _pipeLock = new object();
private bool _disposed; private bool _disposed;
private bool _available; private bool _available;
// 辅助进程 EXE 的路径(与主 EXE 同目录)
private static string HelperExePath => private static string HelperExePath =>
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "InkCanvasForClass.IACoreHelper.exe"); Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "InkCanvasForClass.IACoreHelper.exe");
// Named Pipe 名称(含主进程 PID,保证唯一)
private string PipeName => private string PipeName =>
string.Format("ICC_IACoreHelper_{0}", Process.GetCurrentProcess().Id); string.Format("ICC_IACoreHelper_{0}", Process.GetCurrentProcess().Id);
private IpcIACoreClient() { } private IpcIACoreClient() { }
// ── 公开 API ──────────────────────────────────────────────────────────
/// <summary>启动辅助进程,返回是否成功。</summary>
public bool Start() public bool Start()
{ {
if (_disposed) return false; if (_disposed) return false;
// 已运行则无需重启,避免 Warmup + App 两处调用导致重复启动
if (IsAvailable) return true; if (IsAvailable) return true;
if (!File.Exists(HelperExePath)) if (!File.Exists(HelperExePath))
{ {
LogHelper.WriteLogToFile($"[IACoreIPC] 辅助进程不存在: {HelperExePath}", LogHelper.LogType.Warning);
_available = false; _available = false;
return false; return false;
} }
@@ -70,13 +53,8 @@ namespace Ink_Canvas.Helpers
return LaunchHelper(); return LaunchHelper();
} }
/// <summary>是否就绪(辅助进程运行中)。</summary>
public bool IsAvailable => _available && _helperProcess != null && !_helperProcess.HasExited; public bool IsAvailable => _available && _helperProcess != null && !_helperProcess.HasExited;
/// <summary>
/// 发送笔画到辅助进程进行 IACore 形状识别。
/// 超时或失败时返回 <see cref="InkShapeRecognitionResult.Empty"/>。
/// </summary>
public InkShapeRecognitionResult Recognize(StrokeCollection strokes) public InkShapeRecognitionResult Recognize(StrokeCollection strokes)
{ {
if (strokes == null || strokes.Count == 0) if (strokes == null || strokes.Count == 0)
@@ -84,32 +62,22 @@ namespace Ink_Canvas.Helpers
EnsureHelperAlive(); EnsureHelperAlive();
if (!IsAvailable) if (!IsAvailable)
{
LogHelper.WriteLogToFile($"[IACoreIPC] Recognize 跳过:辅助进程不可用(strokes={strokes.Count}", LogHelper.LogType.Warning);
return InkShapeRecognitionResult.Empty; return InkShapeRecognitionResult.Empty;
}
lock (_pipeLock) lock (_pipeLock)
{ {
try try
{ {
LogHelper.WriteLogToFile($"[IACoreIPC] Recognize 请求:strokes={strokes.Count}"); return SendRecognizeRequest(strokes);
var result = SendRecognizeRequest(strokes);
LogHelper.WriteLogToFile(
$"[IACoreIPC] Recognize 响应:IsSuccess={result.IsSuccess}, Shape={result.ShapeName}, StrokesToRemove={result.StrokesToRemove?.Count ?? 0}");
return result;
} }
catch (Exception ex) catch
{ {
LogHelper.WriteLogToFile($"[IACoreIPC] 识别失败: {ex.GetType().Name}: {ex.Message}", LogHelper.LogType.Error);
KillHelper(); KillHelper();
return InkShapeRecognitionResult.Empty; return InkShapeRecognitionResult.Empty;
} }
} }
} }
// ── 内部实现 ──────────────────────────────────────────────────────────
private bool LaunchHelper() private bool LaunchHelper()
{ {
try try
@@ -133,16 +101,12 @@ namespace Ink_Canvas.Helpers
_helperProcess.EnableRaisingEvents = true; _helperProcess.EnableRaisingEvents = true;
_helperProcess.Exited += OnHelperExited; _helperProcess.Exited += OnHelperExited;
// 等待 Pipe 就绪(最多 3 s)
bool pipeReady = WaitForPipe(3000); bool pipeReady = WaitForPipe(3000);
_available = pipeReady; _available = pipeReady;
LogHelper.WriteLogToFile($"[IACoreIPC] 辅助进程启动{(pipeReady ? "" : "Pipe ")}PID={_helperProcess?.Id}");
return pipeReady; return pipeReady;
} }
catch (Exception ex) catch
{ {
LogHelper.WriteLogToFile($"[IACoreIPC] 启动辅助进程失败: {ex.Message}", LogHelper.LogType.Error);
_available = false; _available = false;
return false; return false;
} }
@@ -161,7 +125,6 @@ namespace Ink_Canvas.Helpers
using (var probe = new NamedPipeClientStream(".", PipeName, PipeDirection.InOut)) using (var probe = new NamedPipeClientStream(".", PipeName, PipeDirection.InOut))
{ {
probe.Connect(200); probe.Connect(200);
// 连接成功后立即断开,下次 Recognize 会重新连接
return true; return true;
} }
} }
@@ -183,7 +146,6 @@ namespace Ink_Canvas.Helpers
using (var writer = new BinaryWriter(client, System.Text.Encoding.UTF8, leaveOpen: true)) using (var writer = new BinaryWriter(client, System.Text.Encoding.UTF8, leaveOpen: true))
using (var reader = new BinaryReader(client, System.Text.Encoding.UTF8, leaveOpen: true)) using (var reader = new BinaryReader(client, System.Text.Encoding.UTF8, leaveOpen: true))
{ {
// 序列化请求
writer.Write(CmdRecognize); writer.Write(CmdRecognize);
writer.Write(strokes.Count); writer.Write(strokes.Count);
foreach (var stroke in strokes) foreach (var stroke in strokes)
@@ -199,7 +161,6 @@ namespace Ink_Canvas.Helpers
} }
writer.Flush(); writer.Flush();
// 读取响应
bool success = reader.ReadBoolean(); bool success = reader.ReadBoolean();
string shape = reader.ReadString(); string shape = reader.ReadString();
float cx = reader.ReadSingle(); float cx = reader.ReadSingle();
@@ -220,7 +181,6 @@ namespace Ink_Canvas.Helpers
if (!success || string.IsNullOrEmpty(shape)) if (!success || string.IsNullOrEmpty(shape))
return InkShapeRecognitionResult.Empty; return InkShapeRecognitionResult.Empty;
// 根据下标还原参与识别的笔画子集
var recognized = new StrokeCollection(); var recognized = new StrokeCollection();
foreach (int idx in indices) foreach (int idx in indices)
if (idx >= 0 && idx < strokes.Count) if (idx >= 0 && idx < strokes.Count)
@@ -240,16 +200,12 @@ namespace Ink_Canvas.Helpers
private void EnsureHelperAlive() private void EnsureHelperAlive()
{ {
if (!IsAvailable) if (!IsAvailable)
{
LogHelper.WriteLogToFile("[IACoreIPC] 辅助进程不可用,尝试重启...", LogHelper.LogType.Warning);
LaunchHelper(); LaunchHelper();
}
} }
private void OnHelperExited(object sender, EventArgs e) private void OnHelperExited(object sender, EventArgs e)
{ {
_available = false; _available = false;
LogHelper.WriteLogToFile("[IACoreIPC] 辅助进程意外退出", LogHelper.LogType.Warning);
} }
private void KillHelper() private void KillHelper()
@@ -257,12 +213,10 @@ namespace Ink_Canvas.Helpers
if (_helperProcess == null) return; if (_helperProcess == null) return;
try try
{ {
// 先解除事件订阅,避免主动 Kill 时触发 OnHelperExited 误报
try { _helperProcess.Exited -= OnHelperExited; } catch { } try { _helperProcess.Exited -= OnHelperExited; } catch { }
if (!_helperProcess.HasExited) if (!_helperProcess.HasExited)
{ {
// 发送优雅关闭命令
try try
{ {
using (var client = new NamedPipeClientStream(".", PipeName, PipeDirection.InOut)) using (var client = new NamedPipeClientStream(".", PipeName, PipeDirection.InOut))
@@ -272,7 +226,7 @@ namespace Ink_Canvas.Helpers
w.Write(CmdShutdown); w.Write(CmdShutdown);
} }
} }
catch { /* 忽略,下面直接 Kill */ } catch { }
if (!_helperProcess.WaitForExit(800)) if (!_helperProcess.WaitForExit(800))
_helperProcess.Kill(); _helperProcess.Kill();
@@ -294,7 +248,6 @@ namespace Ink_Canvas.Helpers
KillHelper(); KillHelper();
} }
// ── 协议常量(与 IACoreHelper/IpcProtocol.cs 保持一致) ─────────────
private const int IpcTimeoutMs = 5000; private const int IpcTimeoutMs = 5000;
private const byte CmdRecognize = 0x01; private const byte CmdRecognize = 0x01;
private const byte CmdShutdown = 0xFF; private const byte CmdShutdown = 0xFF;
+1 -1
View File
@@ -589,7 +589,7 @@
<value>识别引擎</value> <value>识别引擎</value>
</data> </data>
<data name="InkRecog_ShapeEngineHint" xml:space="preserve"> <data name="InkRecog_ShapeEngineHint" xml:space="preserve">
<value> 自动:64 位进程使用 WinRTWindows 10+),32 位使用 IACore。可强制指定 IACore 或 WinRT。</value> <value> 自动:在 Windows 10+ 使用 WinRT,否则使用 IACore。可强制指定 IACore 或 WinRT。IACore 通过 IPC 辅助进程运行,在 32/64 位主进程下均可用。</value>
</data> </data>
<data name="InkRecog_ShapeEngineAuto" xml:space="preserve"> <data name="InkRecog_ShapeEngineAuto" xml:space="preserve">
<value>自动</value> <value>自动</value>
+1 -23
View File
@@ -2,7 +2,6 @@ using System;
using System.IO; using System.IO;
using System.IO.Pipes; using System.IO.Pipes;
using System.Linq; using System.Linq;
using System.Windows;
using System.Windows.Ink; using System.Windows.Ink;
using System.Windows.Input; using System.Windows.Input;
@@ -10,29 +9,16 @@ namespace InkCanvasForClass.IACoreHelper
{ {
internal static class Program internal static class Program
{ {
private static readonly string LogPath = Path.Combine(
AppDomain.CurrentDomain.BaseDirectory, "IACoreHelper.log");
private static void Log(string msg)
{
try { File.AppendAllText(LogPath, $"{DateTime.Now:HH:mm:ss.fff} {msg}{Environment.NewLine}"); }
catch { }
}
[STAThread] [STAThread]
static void Main(string[] args) static void Main(string[] args)
{ {
Log($"=== Helper started, args=[{string.Join(",", args)}], cwd={Environment.CurrentDirectory}, baseDir={AppDomain.CurrentDomain.BaseDirectory} ===");
if (args.Length < 1 || !int.TryParse(args[0], out int parentPid)) if (args.Length < 1 || !int.TryParse(args[0], out int parentPid))
{ {
Console.Error.WriteLine("Usage: IACoreHelper.exe <parentPid>"); Console.Error.WriteLine("Usage: IACoreHelper.exe <parentPid>");
Log("Missing parentPid argument, exiting");
return; return;
} }
string pipeName = string.Format(IpcConstants.PipeName, parentPid); string pipeName = string.Format(IpcConstants.PipeName, parentPid);
Log($"Pipe name: {pipeName}");
try try
{ {
@@ -40,7 +26,6 @@ namespace InkCanvasForClass.IACoreHelper
} }
catch (Exception ex) catch (Exception ex)
{ {
Log($"FATAL: {ex}");
Console.Error.WriteLine("IACoreHelper fatal: " + ex.Message); Console.Error.WriteLine("IACoreHelper fatal: " + ex.Message);
} }
} }
@@ -82,7 +67,6 @@ namespace InkCanvasForClass.IACoreHelper
Console.Error.WriteLine("IACoreHelper pipe error: " + ex.Message); Console.Error.WriteLine("IACoreHelper pipe error: " + ex.Message);
} }
} }
// 每次连接后重新监听,支持多次调用
} }
} }
@@ -90,19 +74,14 @@ namespace InkCanvasForClass.IACoreHelper
{ {
try try
{ {
Log($"HandleRecognize: strokes={request.Strokes?.Length ?? 0}");
var strokes = BuildStrokeCollection(request); var strokes = BuildStrokeCollection(request);
Log($"Built StrokeCollection: count={strokes.Count}");
if (strokes.Count == 0) if (strokes.Count == 0)
return new RecognizeResponse { Success = false, ShapeName = string.Empty }; return new RecognizeResponse { Success = false, ShapeName = string.Empty };
var result = RecognizeCore(strokes); return RecognizeCore(strokes);
Log($"RecognizeCore result: success={result.Success}, shape={result.ShapeName}");
return result;
} }
catch (Exception ex) catch (Exception ex)
{ {
Log($"HandleRecognize EXCEPTION: {ex}");
Console.Error.WriteLine("IACoreHelper recognize error: " + ex.Message); Console.Error.WriteLine("IACoreHelper recognize error: " + ex.Message);
return new RecognizeResponse { Success = false, ShapeName = string.Empty }; return new RecognizeResponse { Success = false, ShapeName = string.Empty };
} }
@@ -189,7 +168,6 @@ namespace InkCanvasForClass.IACoreHelper
if (hot != null) if (hot != null)
for (int i = 0; i < hot.Count; i++) { hotX[i] = (float)hot[i].X; hotY[i] = (float)hot[i].Y; } for (int i = 0; i < hot.Count; i++) { hotX[i] = (float)hot[i].X; hotY[i] = (float)hot[i].Y; }
// 计算参与识别的笔画在原始集合中的下标
var participatingStrokes = analysisAlternate.Strokes; var participatingStrokes = analysisAlternate.Strokes;
int[] strokeIndices = new int[participatingStrokes?.Count ?? 0]; int[] strokeIndices = new int[participatingStrokes?.Count ?? 0];
if (participatingStrokes != null) if (participatingStrokes != null)