-
Notifications
You must be signed in to change notification settings - Fork 0
/
InputMonitorService.cs
126 lines (101 loc) · 4.34 KB
/
InputMonitorService.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using Squared.Task;
using Squared.Task.IO;
using Squared.Task.Http;
using System.Windows.Forms;
using System.Web;
using System.IO;
using Newtonsoft.Json.Linq;
using System.Linq;
using ICSharpCode.SharpZipLib.GZip;
using System.Diagnostics;
using Newtonsoft.Json;
using System.Net.Sockets;
using System.Net;
using System.Runtime.InteropServices;
namespace Tsunagaro {
public class InputMonitorService {
public const double ChildRestartTimeoutSeconds = 60;
public readonly TaskScheduler Scheduler;
public InputMonitorService (TaskScheduler scheduler) {
Scheduler = scheduler;
}
public IEnumerator<object> Initialize () {
Scheduler.Start(MainTask(), TaskExecutionPolicy.RunAsBackgroundTask);
// Program.Control.Handlers.Add("/input", ServeInput);
yield break;
}
private IEnumerator<object> ChildHandler (Process proc, Future<TcpClient> fClient) {
try {
Console.WriteLine("Started input hook w/pid {0}; waiting for return connection", proc.Id);
yield return fClient;
var client = fClient.Result;
const int PacketSize = 512;
var buffer = new byte[PacketSize];
int MessageSize = Marshal.SizeOf(typeof(Win32.InputEvent));
var outboundPayload = new Dictionary<string, object> {
{"Events", null},
{"State", null}
};
using (client)
using (var adapter = new SocketDataAdapter(client.Client, false))
while (true) {
// Read a single packet of up to PacketSize bytes.
// InputHook sets DontFragment so we are going to get a complete set of messages.
var fBytesRead = adapter.Read(buffer, 0, PacketSize);
yield return fBytesRead;
if (fBytesRead.Failed)
yield break;
outboundPayload["Events"] = Convert.ToBase64String(buffer, 0, fBytesRead.Result, Base64FormattingOptions.None);
// Now we rebroadcast the packet over RPC to our peers
yield return Program.Peer.Broadcast("RemoteInput", outboundPayload);
}
} finally {
bool hasExited = true;
try {
hasExited = proc.HasExited;
} catch {
}
if (!hasExited) {
Console.WriteLine("Terminating input hook");
proc.Kill();
} else {
Console.WriteLine("Input hook process exited");
}
proc.Dispose();
}
}
public IEnumerator<object> MainTask () {
var listener = new TcpListener(0);
listener.Start();
var port = ((IPEndPoint)listener.Server.LocalEndPoint).Port;
var psi = new ProcessStartInfo("InputHook.exe", port.ToString()) {
UseShellExecute = false,
RedirectStandardOutput = false,
RedirectStandardError = false,
RedirectStandardInput = true,
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden
};
while (true) {
var fProc = Future.RunInThread(() => Process.Start(psi));
yield return fProc;
var fClient = listener.AcceptIncomingConnection();
using (var fChildHandler = Scheduler.Start(ChildHandler(fProc.Result, fClient))) {
// If the process is terminated prematurely, kill the child handler
var fTerminated = Future.RunInThread(fProc.Result.WaitForExit);
fTerminated.RegisterOnComplete(
(_) => fChildHandler.Dispose()
);
// Run the child handler until it completes
yield return fChildHandler;
}
// The child died for some reason, let's wait a moment before restarting it...
yield return new Sleep(ChildRestartTimeoutSeconds);
}
}
}
}