Files
community/Ink Canvas/Helpers/IpcIACoreClient.cs
T

255 lines
8.0 KiB
C#
Raw Normal View History

2026-05-01 23:54:24 +08:00
using System;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Threading;
using System.Windows;
using System.Windows.Ink;
using System.Windows.Media;
namespace Ink_Canvas.Helpers
{
public sealed class IpcIACoreClient : IDisposable
{
private static IpcIACoreClient _instance;
private static readonly object _instanceLock = new object();
public static IpcIACoreClient Instance
{
get
{
if (_instance == null)
lock (_instanceLock)
if (_instance == null)
_instance = new IpcIACoreClient();
return _instance;
}
}
private Process _helperProcess;
private readonly object _pipeLock = new object();
private bool _disposed;
private bool _available;
private static string HelperExePath =>
2026-05-02 00:53:49 +08:00
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "InkCanvas.IACoreHelper.exe");
2026-05-01 23:54:24 +08:00
private string PipeName =>
string.Format("ICC_IACoreHelper_{0}", Process.GetCurrentProcess().Id);
private IpcIACoreClient() { }
public bool Start()
{
if (_disposed) return false;
if (IsAvailable) return true;
if (!File.Exists(HelperExePath))
{
_available = false;
return false;
}
return LaunchHelper();
}
public bool IsAvailable => _available && _helperProcess != null && !_helperProcess.HasExited;
public InkShapeRecognitionResult Recognize(StrokeCollection strokes)
{
if (strokes == null || strokes.Count == 0)
return InkShapeRecognitionResult.Empty;
EnsureHelperAlive();
if (!IsAvailable)
return InkShapeRecognitionResult.Empty;
lock (_pipeLock)
{
try
{
2026-05-02 00:40:49 +08:00
return SendRecognizeRequest(strokes);
2026-05-01 23:54:24 +08:00
}
2026-05-02 00:40:49 +08:00
catch
2026-05-01 23:54:24 +08:00
{
KillHelper();
return InkShapeRecognitionResult.Empty;
}
}
}
private bool LaunchHelper()
{
try
{
KillHelper();
var psi = new ProcessStartInfo
{
FileName = HelperExePath,
Arguments = Process.GetCurrentProcess().Id.ToString(),
UseShellExecute = false,
CreateNoWindow = true,
WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory
};
_helperProcess = Process.Start(psi);
if (_helperProcess == null)
{
_available = false;
return false;
}
_helperProcess.EnableRaisingEvents = true;
_helperProcess.Exited += OnHelperExited;
bool pipeReady = WaitForPipe(3000);
_available = pipeReady;
return pipeReady;
}
2026-05-02 00:40:49 +08:00
catch
2026-05-01 23:54:24 +08:00
{
_available = false;
return false;
}
}
private bool WaitForPipe(int timeoutMs)
{
int elapsed = 0;
while (elapsed < timeoutMs)
{
if (_helperProcess == null || _helperProcess.HasExited)
return false;
try
{
using (var probe = new NamedPipeClientStream(".", PipeName, PipeDirection.InOut))
{
probe.Connect(200);
return true;
}
}
catch
{
Thread.Sleep(100);
elapsed += 300;
}
}
return false;
}
private InkShapeRecognitionResult SendRecognizeRequest(StrokeCollection strokes)
{
using (var client = new NamedPipeClientStream(".", PipeName, PipeDirection.InOut))
{
client.Connect(IpcTimeoutMs);
using (var writer = new BinaryWriter(client, System.Text.Encoding.UTF8, leaveOpen: true))
using (var reader = new BinaryReader(client, System.Text.Encoding.UTF8, leaveOpen: true))
{
writer.Write(CmdRecognize);
writer.Write(strokes.Count);
foreach (var stroke in strokes)
{
var pts = stroke.StylusPoints;
writer.Write(pts.Count);
foreach (var pt in pts)
{
writer.Write((float)pt.X);
writer.Write((float)pt.Y);
writer.Write(pt.PressureFactor);
}
}
writer.Flush();
bool success = reader.ReadBoolean();
string shape = reader.ReadString();
float cx = reader.ReadSingle();
float cy = reader.ReadSingle();
float width = reader.ReadSingle();
float height = reader.ReadSingle();
int hotLen = reader.ReadInt32();
var hotPoints = new PointCollection();
for (int i = 0; i < hotLen; i++)
hotPoints.Add(new Point(reader.ReadSingle(), reader.ReadSingle()));
int idxLen = reader.ReadInt32();
var indices = new int[idxLen];
for (int i = 0; i < idxLen; i++)
indices[i] = reader.ReadInt32();
if (!success || string.IsNullOrEmpty(shape))
return InkShapeRecognitionResult.Empty;
var recognized = new StrokeCollection();
foreach (int idx in indices)
if (idx >= 0 && idx < strokes.Count)
recognized.Add(strokes[idx]);
return new InkShapeRecognitionResult(
shape,
new Point(cx, cy),
hotPoints,
width,
height,
recognized);
}
}
}
private void EnsureHelperAlive()
{
if (!IsAvailable)
LaunchHelper();
}
private void OnHelperExited(object sender, EventArgs e)
{
_available = false;
}
private void KillHelper()
{
if (_helperProcess == null) return;
try
{
try { _helperProcess.Exited -= OnHelperExited; } catch { }
if (!_helperProcess.HasExited)
{
try
{
using (var client = new NamedPipeClientStream(".", PipeName, PipeDirection.InOut))
{
client.Connect(500);
using (var w = new BinaryWriter(client))
w.Write(CmdShutdown);
}
}
2026-05-02 00:40:49 +08:00
catch { }
2026-05-01 23:54:24 +08:00
if (!_helperProcess.WaitForExit(800))
_helperProcess.Kill();
}
}
catch { }
finally
{
_helperProcess?.Dispose();
_helperProcess = null;
_available = false;
}
}
public void Dispose()
{
if (_disposed) return;
_disposed = true;
KillHelper();
}
private const int IpcTimeoutMs = 5000;
private const byte CmdRecognize = 0x01;
private const byte CmdShutdown = 0xFF;
}
}