-
Notifications
You must be signed in to change notification settings - Fork 0
/
InProcessAgent.cs
187 lines (162 loc) · 6.56 KB
/
InProcessAgent.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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Reflection;
using IPCTK.Messaging;
using IPCTK.Messaging.Messages;
namespace IPCTK
{
/// <summary>
/// Manages inter-process communication from the In-Process side.
/// </summary>
class InProcessAgent : IDisposable
{
/// <summary>
/// A static directory that manages all links between IPCTK-enabled
/// objects and InProcessAgents.
/// </summary>
private static Dictionary<Object, InProcessAgent> directory;
/// <summary>
/// Sends messages to the external process.
/// </summary>
private MessageSender messageSender;
/// <summary>
/// Receives messages from the external process.
/// </summary>
private MessageReceiver messageReceiver;
/// <summary>
/// Retrieve the <see cref="InProcessAgent"/> associated with the
/// IPCTK-enabled object <paramref name="obj"/>.
/// </summary>
/// <param name="obj">The object to retrieve the associated
/// <see cref="InProcessAgent"/> for.</param>
/// <returns>The assocated <see cref="InProcessAgent"/>.</returns>
public static InProcessAgent GetIPCAgent(Object obj)
{
if (!directory.ContainsKey(obj))
{
throw new ArgumentException("That object has no IPCInProcessAgent assocated. Call NewIPCAgent first.", "obj");
}
return directory[obj];
}
/// <summary>
/// Generate a new <see cref="InProcessAgent"/>, associated with <paramref name="obj"/>.
/// </summary>
/// <param name="obj">The object to assocated the <see cref="InProcessAgent"/> with.</param>
/// <returns>A new <see cref="InProcessAgent"/>.</returns>
public static InProcessAgent NewIPCAgent(Object obj)
{
var x = new InProcessAgent(obj.GetType());
directory.Add(obj, x);
return x;
}
//Static Constructor.
static InProcessAgent()
{
directory = new Dictionary<object, InProcessAgent>();
}
//Disable the no-argument constructor.
protected InProcessAgent() { }
//The process that this InProcessAgent should communicate with.
protected Process clientProcess;
//Pipes used for communication with external process.
protected AnonymousPipeServerStream pipeOut, pipeIn;
/// <summary>
/// Constructor. Starts external process.
/// </summary>
/// <param name="t">The type of the external class to manage.</param>
protected InProcessAgent(Type t)
{
//Fire up instance of exe, with pipes set up.
clientProcess = new Process();
clientProcess.StartInfo.FileName = t.Assembly.Location;
pipeOut = new AnonymousPipeServerStream(PipeDirection.Out, HandleInheritability.Inheritable);
pipeIn = new AnonymousPipeServerStream(PipeDirection.In, HandleInheritability.Inheritable);
clientProcess.StartInfo.Arguments = pipeOut.GetClientHandleAsString() + " " + pipeIn.GetClientHandleAsString();
clientProcess.StartInfo.UseShellExecute = false;
clientProcess.Start();
pipeOut.DisposeLocalCopyOfClientHandle();
pipeIn.DisposeLocalCopyOfClientHandle();
//initialise an instance of the object
messageSender = new MessageSender(pipeOut);
messageReceiver = new MessageReceiver(pipeIn);
}
/// <summary>
/// Run a method call for the object that the InProcessAgent is associated with.
/// </summary>
/// <param name="method">The method to call</param>
/// <param name="args">The arguments to the method.</param>
/// <returns>The return value of the method.</returns>
public Object Call(MethodBase method, Object[] args)
{
messageSender.SendMessage(new MethodCallMessage(method, args));
Message returnValue = messageReceiver.WaitForMessage();
if (!(returnValue is ReturnValueMessage))
throw new Exception("Unexpected reply from out-of-process client");
return (returnValue as ReturnValueMessage).ReturnValue;
}
/// <summary>
/// Tell the external process to construct a new object of type <paramref name="t"/>.
/// </summary>
/// <param name="t">The type of the object to instantiate remotely.</param>
/// <param name="args">Arguments to pass to the constructor.</param>
public void Construct(Type t, Object[] args)
{
messageSender.SendMessage(new InitMessage(t, args));
}
#region Disposing
private bool disposed = false;
/// <summary>
/// Free up resources. Politely exits the external process.
/// </summary>
public void Dispose()
{
Dispose(DisposeMethod.PoliteDispose);
}
/// <summary>
/// Free up resources. Has the option to kill the external process.
/// </summary>
/// <param name="disposeMethod">A value indicating whether the external process should be exited politely (via a signal) or killed.</param>
public void Dispose(DisposeMethod disposeMethod)
{
if (!disposed)
{
switch (disposeMethod)
{
case DisposeMethod.PoliteDispose:
//send terminate signal.
messageSender.SendMessage(new ExitMessage());
//memory cleanup
pipeIn.Close();
pipeOut.Close();
break;
case DisposeMethod.KillDispose:
pipeIn.Close();
pipeOut.Close();
clientProcess.Kill();
break;
}
disposed = true;
}
}
#endregion
}
/// <summary>
/// Indicates whether the external process should be exited politely (via a signal) or killed.
/// </summary>
enum DisposeMethod
{
/// <summary>
/// Exit the external process politely (send a signal asking for exit).
/// </summary>
PoliteDispose,
/// <summary>
/// Exit the external process by forcefully terminating it.
/// </summary>
KillDispose
}
}