255 lines
8.0 KiB
C#
255 lines
8.0 KiB
C#
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 =>
|
|
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "InkCanvas.IACoreHelper.exe");
|
|
|
|
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
|
|
{
|
|
return SendRecognizeRequest(strokes);
|
|
}
|
|
catch
|
|
{
|
|
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;
|
|
}
|
|
catch
|
|
{
|
|
_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);
|
|
}
|
|
}
|
|
catch { }
|
|
|
|
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;
|
|
}
|
|
} |