Mercurial > hg > ucis.core
annotate Remoting/RemotingManager.cs @ 111:df53bdd49507 default tip
Merge
author | Ivo Smits <Ivo@UCIS.nl> |
---|---|
date | Fri, 07 Nov 2014 18:37:39 +0100 |
parents | 5e717aac4c1d |
children |
rev | line source |
---|---|
13 | 1 ???using System; |
2 using System.Collections.Generic; | |
3 using System.IO; | |
4 using System.Reflection; | |
5 using System.Reflection.Emit; | |
6 using System.Runtime.Remoting; | |
7 using System.Runtime.Remoting.Messaging; | |
8 using System.Runtime.Remoting.Proxies; | |
9 using System.Runtime.Serialization; | |
10 using System.Threading; | |
11 using UCIS.Util; | |
12 using SysThreadPool = System.Threading.ThreadPool; | |
13 | |
14 //[assembly: InternalsVisibleToAttribute("UCIS.Remoting.Proxies")] | |
15 | |
16 namespace UCIS.Remoting { | |
17 public class RemotingManager { | |
18 Dictionary<UInt32, PendingRemoteCall> pendingCalls = new Dictionary<uint, PendingRemoteCall>(); | |
19 Dictionary<Thread, UInt32> waitingCallThreads = new Dictionary<Thread, UInt32>(); | |
110
5e717aac4c1d
Improvements in RemotingManager (close event) and Windows Named Pipe (accept time-out)
Ivo Smits <Ivo@UCIS.nl>
parents:
14
diff
changeset
|
20 public Boolean Closed { get; private set; } |
13 | 21 |
22 IDictionary<String, Object> incomingCallContext = new Dictionary<String, Object>(); | |
23 [ThreadStatic] | |
24 static IDictionary<String, Object> currentCallContext; | |
25 | |
26 public event Action<String> OnDebugLog; | |
27 public event Action<Exception> OnErrorLog; | |
110
5e717aac4c1d
Improvements in RemotingManager (close event) and Windows Named Pipe (accept time-out)
Ivo Smits <Ivo@UCIS.nl>
parents:
14
diff
changeset
|
28 public event Action<RemotingManager> OnClosed; |
13 | 29 |
30 private void DebugLog(String text, params Object[] args) { | |
31 if (OnDebugLog != null) OnDebugLog(String.Format(text, args)); | |
32 } | |
33 private void ErrorLog(Exception ex) { | |
34 if (OnErrorLog != null) OnErrorLog(ex); | |
35 } | |
36 | |
37 public Object LocalRoot { get; set; } | |
38 | |
39 public IDictionary<String, Object> CallContext { get { return incomingCallContext; } } | |
40 public static IDictionary<String, Object> CurrentCallContext { get { return currentCallContext; } } | |
41 | |
42 public RemotingManager(PacketStream stream) : this(stream, null) { } | |
43 public RemotingManager(PacketStream stream, Object localRoot) { | |
44 this.stream = stream; | |
45 this.LocalRoot = localRoot; | |
110
5e717aac4c1d
Improvements in RemotingManager (close event) and Windows Named Pipe (accept time-out)
Ivo Smits <Ivo@UCIS.nl>
parents:
14
diff
changeset
|
46 this.Closed = false; |
13 | 47 stream.BeginReadPacketFast(ReceiveCallback, null); |
48 } | |
49 | |
50 #region I/O, multiplexing, encoding | |
51 PacketStream stream; | |
52 public PacketStream Stream { get { return stream; } } | |
53 int streamIndex = 0; | |
54 Dictionary<int, StreamChannel> streamChannels = new Dictionary<int, StreamChannel>(); | |
55 | |
56 class StreamChannel : QueuedPacketStream { | |
57 public RemotingManager Manager { get; private set; } | |
58 public int StreamID { get; private set; } | |
59 public StreamChannel OtherSide { get; set; } | |
60 | |
61 public StreamChannel(RemotingManager conn, int sid) { | |
62 this.Manager = conn; | |
63 this.StreamID = sid; | |
64 } | |
65 | |
66 internal void AddBuffer(Byte[] buffer, int offset, int count) { | |
67 if (Closed) return; | |
68 base.AddReadBufferCopy(buffer, offset, count); | |
69 } | |
70 | |
71 public override bool CanRead { get { return !Closed; } } | |
72 public override bool CanWrite { get { return !Closed; } } | |
73 | |
74 public override void Flush() { } | |
75 | |
76 public override void Write(byte[] buffer, int offset, int count) { | |
77 if (Closed) throw new ObjectDisposedException("BaseStream", "The connection has been closed"); | |
78 Manager.WriteStreamChannelPacket(StreamID ^ 0x4000, buffer, offset, count); | |
79 } | |
80 | |
81 public override void Close() { | |
82 StreamChannel other = OtherSide; | |
83 MultiplexorClosed(); | |
84 if (other != null) other.CloseInternal(); | |
85 lock (Manager.streamChannels) Manager.streamChannels.Remove(StreamID); | |
86 } | |
87 public void CloseInternal() { | |
88 MultiplexorClosed(); | |
89 lock (Manager.streamChannels) Manager.streamChannels.Remove(StreamID); | |
90 } | |
91 internal void MultiplexorClosed() { | |
92 base.Close(); | |
93 OtherSide = null; | |
94 } | |
95 } | |
96 | |
97 private void ReceiveCallback(IAsyncResult ar) { | |
98 if (ar.CompletedSynchronously) { | |
99 SysThreadPool.QueueUserWorkItem(ReceiveCallbackA, ar); | |
100 } else { | |
101 ReceiveCallbackB(ar); | |
102 } | |
103 } | |
104 private void ReceiveCallbackA(Object state) { | |
105 ReceiveCallbackB((IAsyncResult)state); | |
106 } | |
107 private void ReceiveCallbackB(IAsyncResult ar) { | |
108 try { | |
109 ArraySegment<Byte> packet = stream.EndReadPacketFast(ar); | |
110 Byte[] array = packet.Array; | |
111 if (packet.Count < 2) throw new ArgumentOutOfRangeException("packet.Count", "Packet is too small"); | |
112 int offset = packet.Offset; | |
113 int sid = (array[offset + 0] << 8) | (array[offset + 1] << 0); | |
114 if ((sid & 0x8000) != 0) { | |
115 StreamChannel substr; | |
116 if (streamChannels.TryGetValue(sid, out substr)) substr.AddBuffer(array, offset + 2, packet.Count - 2); | |
117 } | |
118 stream.BeginReadPacketFast(ReceiveCallback, null); | |
119 if (sid == 0) { | |
120 Object obj; | |
121 using (MemoryStream ms = new MemoryStream(packet.Array, packet.Offset + 2, packet.Count - 2, false)) obj = Deserialize(ms); | |
122 ReceiveObject(obj); | |
123 } | |
124 } catch (Exception ex) { | |
125 Closed = true; | |
126 stream.Close(); | |
127 lock (ObjectReferencesByID) { | |
128 ObjectReferencesByID.Clear(); | |
129 RemoteObjectReferences.Clear(); | |
130 } | |
131 lock (pendingCalls) { | |
132 foreach (PendingRemoteCall call in pendingCalls.Values) call.SetError(new InvalidOperationException("The connection has been closed")); | |
133 pendingCalls.Clear(); | |
134 } | |
135 lock (streamChannels) { | |
136 foreach (StreamChannel s in streamChannels.Values) s.MultiplexorClosed(); | |
137 streamChannels.Clear(); | |
138 } | |
139 ErrorLog(ex); | |
110
5e717aac4c1d
Improvements in RemotingManager (close event) and Windows Named Pipe (accept time-out)
Ivo Smits <Ivo@UCIS.nl>
parents:
14
diff
changeset
|
140 if (OnClosed != null) OnClosed(this); |
13 | 141 } |
142 } | |
143 private void SendObject(Object obj) { | |
144 if (Closed) throw new ObjectDisposedException("RemotingManager", "The connection has been closed"); | |
145 using (MemoryStream ms = new MemoryStream()) { | |
146 ms.WriteByte(0); | |
147 ms.WriteByte(0); | |
148 Serialize(ms, obj); | |
149 lock (stream) ms.WriteTo(stream); | |
150 } | |
151 } | |
152 | |
153 private void WriteStreamChannelPacket(int sid, Byte[] buffer, int offset, int count) { | |
154 Byte[] store = new Byte[count + 2]; | |
155 store[0] = (Byte)(sid >> 8); | |
156 store[1] = (Byte)(sid >> 0); | |
157 Buffer.BlockCopy(buffer, offset, store, 2, count); | |
158 lock (stream) stream.Write(store, 0, store.Length); | |
159 } | |
160 | |
161 public PacketStream GetStreamPair(out PacketStream remote) { | |
162 StreamChannel stream; | |
163 int sid; | |
164 lock (streamChannels) { | |
165 if (Closed) throw new ObjectDisposedException("BaseStream", "Reading from the base stream failed"); | |
166 while (true) { | |
167 sid = Interlocked.Increment(ref streamIndex); | |
168 if ((sid & 0xc000) != 0) streamIndex = sid = 0; | |
169 sid |= 0x8000; | |
170 if (!streamChannels.ContainsKey(sid)) break; | |
171 } | |
172 stream = new StreamChannel(this, sid); | |
173 streamChannels.Add(sid, stream); | |
174 } | |
175 stream.OtherSide = (StreamChannel)SyncCall(new CreateStreamRequest() { StreamObject = stream, StreamID = sid | 0x4000 }); | |
176 remote = stream.OtherSide; | |
177 return stream; | |
178 } | |
179 #endregion | |
180 | |
181 #region Incoming call processing | |
182 private void ReceiveObject(Object obj) { | |
183 if (obj is ReferenceReleaseRequest) { | |
184 ReferenceReleaseRequest req = (ReferenceReleaseRequest)obj; | |
185 lock (ObjectReferencesByID) { | |
186 RemoteObjectReference objref = (RemoteObjectReference)ObjectReferencesByID[req.ObjectID]; | |
187 if (objref.Release(req.ReferenceCount) == 0) { | |
188 ObjectReferencesByID.Remove(objref.ID); | |
189 RemoteObjectReferences.Remove(objref); | |
190 DebugLog("Release remoted object {0} with reference count {1}; {2} objects referenced", objref.ID, req.ReferenceCount, RemoteObjectReferences.Count); | |
191 } | |
192 } | |
193 } else if (obj is SynchronousCall) { | |
194 SynchronousCall sc = (SynchronousCall)obj; | |
195 if (sc.IsResult) { | |
196 PendingRemoteCall call; | |
197 lock (pendingCalls) { | |
198 call = pendingCalls[sc.CallID]; | |
199 pendingCalls.Remove(call.ID); | |
200 } | |
201 call.SetResult(sc.Data); | |
202 } else if (sc.HasPreviousCallID) { | |
203 PendingRemoteCall call; | |
204 lock (pendingCalls) call = pendingCalls[sc.PreviousCallID]; | |
205 if (!call.SetNextCall(sc)) { | |
206 ProcessRemoteCallRequest(sc); | |
207 } | |
208 } else { | |
209 ProcessRemoteCallRequest(sc); | |
210 } | |
211 } else { | |
212 throw new InvalidDataException("Unexpected object type"); | |
213 } | |
214 } | |
215 | |
216 private void ProcessRemoteCallRequest(SynchronousCall call) { | |
217 UInt32 prevcallid; | |
218 Boolean hasprevcallid; | |
219 Thread currentThread = Thread.CurrentThread; | |
220 lock (waitingCallThreads) { | |
221 hasprevcallid = waitingCallThreads.TryGetValue(currentThread, out prevcallid); | |
222 waitingCallThreads[currentThread] = call.CallID; | |
223 } | |
224 IDictionary<String, Object> prevCallContext = currentCallContext; | |
225 currentCallContext = incomingCallContext; | |
226 try { | |
227 call.Data = ProcessRemoteCallRequestA(call.Data); | |
228 } finally { | |
229 currentCallContext = prevCallContext; | |
230 lock (waitingCallThreads) { | |
231 if (hasprevcallid) waitingCallThreads[currentThread] = prevcallid; | |
232 else waitingCallThreads.Remove(currentThread); | |
233 } | |
234 } | |
235 call.IsResult = true; | |
236 SendObject(call); | |
237 } | |
238 | |
239 private Object FixReturnType(Object value, Type expectedType) { | |
240 if (value == null) return value; | |
241 Type valueType = value.GetType(); | |
242 if (valueType != expectedType) { | |
243 if (valueType.IsArray) { | |
244 Array retarray = value as Array; | |
245 if (retarray != null) { | |
246 if (!valueType.GetElementType().IsPublic && retarray.Rank == 1 && | |
247 ( | |
248 expectedType.IsArray || | |
249 (expectedType.IsGenericType && (expectedType.GetGenericTypeDefinition() == typeof(IEnumerable<>) || expectedType.GetGenericTypeDefinition() == typeof(ICollection<>) || expectedType.GetGenericTypeDefinition() == typeof(IList<>))) | |
250 ) | |
251 ) { | |
252 Type btype = expectedType.IsArray ? expectedType.GetElementType() : expectedType.GetGenericArguments()[0]; | |
253 Array r = Array.CreateInstance(btype, retarray.Length); | |
254 retarray.CopyTo(r, 0); | |
255 value = r; | |
256 } | |
257 } | |
258 } | |
259 } | |
260 return value; | |
261 } | |
262 private Object ProcessRemoteMethodCallRequestA(Object ret) { | |
263 if (ret is DelegateCallRequest) { | |
264 DelegateCallRequest call = (DelegateCallRequest)ret; | |
265 Object target = call.Delegate; | |
266 DebugLog("Remote delegate call on {0}", target); | |
267 if (ReferenceEquals(target, null)) throw new NullReferenceException("target"); | |
268 if (!(target is Delegate)) throw new InvalidCastException("target"); | |
269 Object[] args = call.Arguments; | |
270 return ((Delegate)target).DynamicInvoke(args); | |
271 } else if (ret is MethodCallRequest) { | |
272 MethodCallRequest call = (MethodCallRequest)ret; | |
273 Object target = call.Object; | |
274 if (ReferenceEquals(target, null)) throw new NullReferenceException("target"); | |
275 Type intf = call.Type ?? target.GetType(); | |
276 DebugLog("Remote call {0}.{1} on {2}", intf.FullName, call.MethodName, target); | |
277 if (!intf.IsInstanceOfType(target)) throw new InvalidCastException("target"); | |
278 MethodInfo meth; | |
279 if (call.MethodSignature != null) { | |
280 meth = intf.GetMethod(call.MethodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, call.MethodSignature, null); | |
281 } else { | |
282 meth = intf.GetMethod(call.MethodName, BindingFlags.Instance | BindingFlags.Public); | |
283 } | |
284 if (meth == null) throw new NullReferenceException("method"); | |
285 Object[] args = call.Arguments; | |
286 Object retval; | |
287 if (meth.Name == "GetType" && meth == typeof(Object).GetMethod("GetType", Type.EmptyTypes)) { | |
288 if (target.GetType().IsPublic) retval = target.GetType(); | |
289 else retval = intf; | |
290 } else { | |
291 retval = meth.Invoke(target, args); | |
292 } | |
293 //Todo: causes lots of redundant data transfers, do something about it! | |
294 /*Object[] rargs = new Object[0]; | |
295 for (int i = 0; i < args.Length; i++) { | |
296 if (args[i] == null) continue; | |
297 if (args[i].GetType().IsArray) { | |
298 Array.Resize(ref rargs, i + 1); | |
299 rargs[i] = args[i]; | |
300 } | |
301 } | |
302 resp.ReturnArguments = rargs;*/ | |
303 return FixReturnType(retval, meth.ReturnType); | |
304 } else if (ret is PropertyAccessRequest) { | |
305 PropertyAccessRequest call = (PropertyAccessRequest)ret; | |
306 Object target = call.Object; | |
307 if (ReferenceEquals(target, null)) throw new NullReferenceException("target"); | |
308 Type intf = call.Type ?? target.GetType(); | |
309 DebugLog("Remote property access {0}.{1} on {2}", intf.FullName, call.PropertyName, target); | |
310 if (!intf.IsInstanceOfType(target)) throw new InvalidCastException("target"); | |
311 PropertyInfo meth = intf.GetProperty(call.PropertyName, BindingFlags.Instance | BindingFlags.Public); | |
312 if (meth == null) throw new NullReferenceException("property"); | |
313 if (call.SetValue) { | |
314 meth.SetValue(target, call.Value, null); | |
315 return null; | |
316 } else { | |
317 return FixReturnType(meth.GetValue(target, null), meth.PropertyType); | |
318 } | |
319 } else { | |
320 throw new InvalidDataException("Unexpected object type"); | |
321 } | |
322 } | |
323 private Object ProcessRemoteCallRequestA(Object ret) { | |
324 if (ret is DelegateCallRequest || ret is MethodCallRequest || ret is PropertyAccessRequest) { | |
325 MethodCallResponse resp = new MethodCallResponse(); | |
326 try { | |
327 resp.ReturnValue = ProcessRemoteMethodCallRequestA(ret); | |
328 } catch (Exception ex) { | |
329 resp.Exception = ex; | |
330 ErrorLog(ex); | |
331 } | |
332 return resp; | |
333 } else if (ret is GetRootRequest) { | |
334 DebugLog("Remote root request"); | |
335 return LocalRoot; | |
336 } else if (ret is ObjectCanCastToRequest) { | |
337 ObjectCanCastToRequest ctr = (ObjectCanCastToRequest)ret; | |
338 Type intf = ctr.Type; | |
339 Object target = ctr.Object; | |
340 DebugLog("Remote type check for {0} on {1}", intf.Name, target); | |
341 return intf != null && intf.IsInstanceOfType(target); | |
342 } else if (ret is CreateStreamRequest) { | |
343 CreateStreamRequest csr = (CreateStreamRequest)ret; | |
344 StreamChannel ss = new StreamChannel(this, csr.StreamID); | |
345 ss.OtherSide = csr.StreamObject; | |
346 lock (streamChannels) streamChannels.Add(csr.StreamID, ss); | |
347 return ss; | |
348 } else { | |
349 throw new InvalidDataException("Unexpected object type"); | |
350 } | |
351 } | |
352 #endregion | |
353 | |
354 #region Outgoing calls | |
355 UInt32 callID = 0; | |
356 private UInt32 AllocatePendingCallID(PendingRemoteCall call) { | |
357 UInt32 cid; | |
358 lock (pendingCalls) { | |
359 while (true) { | |
360 cid = ++callID; | |
361 if (!pendingCalls.ContainsKey(cid)) break; | |
362 Monitor.Wait(pendingCalls); | |
363 } | |
364 call.ID = cid; | |
365 pendingCalls.Add(cid, call); | |
366 } | |
367 return cid; | |
368 } | |
369 private Object SyncCall(Object req) { | |
370 UInt32 previousCall; | |
371 Boolean hasPreviousCall; | |
372 lock (waitingCallThreads) hasPreviousCall = waitingCallThreads.TryGetValue(Thread.CurrentThread, out previousCall); | |
373 PendingRemoteCall pending = new PendingRemoteCall() { Completed = false, WaitHandle = new ManualResetEvent(false), CallData = req }; | |
374 AllocatePendingCallID(pending); | |
375 SynchronousCall call = new SynchronousCall() { IsResult = false, CallID = pending.ID, Data = req, HasPreviousCallID = false }; | |
376 if (hasPreviousCall) { | |
377 call.HasPreviousCallID = true; | |
378 call.PreviousCallID = previousCall; | |
379 } | |
380 SendObject(call); | |
381 while (true) { | |
382 pending.WaitHandle.WaitOne(); | |
383 if (pending.Completed) break; | |
384 pending.WaitHandle.Reset(); | |
385 if (pending.NextCall == null) throw new InvalidOperationException("Operation did not complete"); | |
386 SynchronousCall sc = pending.NextCall.Value; | |
387 pending.NextCall = null; | |
388 ProcessRemoteCallRequest(sc); | |
389 } | |
390 pending.WaitHandle.Close(); | |
391 if (pending.Error != null) throw pending.Error; | |
392 return pending.Response; | |
393 } | |
394 private void AsyncCall(Object req) { | |
395 UInt32 cid = AllocatePendingCallID(new PendingRemoteCall() { Completed = false, CallData = req }); | |
396 SendObject(new SynchronousCall() { IsResult = false, CallID = cid, Data = req, HasPreviousCallID = false }); | |
397 } | |
398 | |
399 public Object RemoteRoot { | |
400 get { | |
401 return SyncCall(new GetRootRequest()); | |
402 } | |
403 } | |
404 | |
405 public static void AsyncCall(Delegate f, params Object[] args) { | |
406 IProxyBase proxy; | |
407 if ((proxy = GetRealProxyForObject(f)) != null) { | |
408 proxy.Manager.AsyncCall(new DelegateCallRequest() { Delegate = proxy, Arguments = args }); | |
409 } else if ((proxy = GetRealProxyForObject(f.Target)) != null) { | |
410 MethodCallRequest call = new MethodCallRequest() { Object = proxy, Type = f.Method.DeclaringType, MethodName = f.Method.Name, Arguments = args }; | |
411 call.MethodSignature = ConvertMethodParameterTypeArray(f.Method); | |
412 proxy.Manager.AsyncCall(call); | |
413 } else { | |
414 throw new InvalidOperationException("Delegate is not a proxy for a remote object"); | |
415 } | |
416 } | |
417 | |
418 private static Type[] ConvertMethodParameterTypeArray(MethodInfo method) { | |
419 ParameterInfo[] parameters = method.GetParameters(); | |
420 Type[] types = new Type[parameters.Length]; | |
421 for (int i = 0; i < types.Length; i++) types[i] = parameters[i].ParameterType; | |
422 return types; | |
423 } | |
424 private void ProxyCallCheckReturnType(Object value, Type type) { | |
425 if (type == typeof(void)) return; | |
426 if (type.IsInstanceOfType(value)) return; | |
427 if (value == null && !type.IsValueType) return; | |
428 throw new InvalidCastException("Type returned by remote procedure does not match expected type."); | |
429 } | |
430 private void ProxyCallFixReturnArguments(Object[] args, Object[] rargs) { | |
431 if (rargs == null) return; | |
432 for (int i = 0; i < rargs.Length; i++) { | |
433 if (rargs[i] == null) continue; | |
434 if (args[i] != null && args[i].GetType().IsArray) { | |
435 ((Array)rargs[i]).CopyTo((Array)args[i], 0); | |
436 } | |
437 } | |
438 } | |
439 private Object ProxyMakeCallDelegate(DelegateProxy obj, Object[] args) { | |
440 DelegateCallRequest call = new DelegateCallRequest() { Delegate = obj, Arguments = args }; | |
441 MethodCallResponse resp = (MethodCallResponse)SyncCall(call); | |
442 if (resp.Exception != null) throw new Exception("Remote exception", resp.Exception); | |
443 ProxyCallFixReturnArguments(args, resp.ReturnArguments); | |
444 ProxyCallCheckReturnType(resp.ReturnValue, obj.MethodSignature.ReturnType); | |
445 return resp.ReturnValue; | |
446 } | |
447 private Object ProxyMakeCall(IProxyBase obj, MethodInfo method, Object[] args) { | |
448 /*if (args.Length == 1 && method.ReturnType == typeof(void) && args[0] != null && args[0] is Delegate) { | |
449 foreach (EventInfo ei in type.GetEvents()) { | |
450 if (ei.EventHandlerType.IsInstanceOfType(args[0])) { | |
451 if (method == ei.GetAddMethod()) { | |
452 Console.WriteLine("ADD EVENT"); | |
453 } else if (method == ei.GetRemoveMethod()) { | |
454 Console.WriteLine("REMOVE EVENT"); | |
455 } | |
456 } | |
457 } | |
458 }*/ | |
459 MethodCallRequest call = new MethodCallRequest() { Object = obj, Type = method.DeclaringType, MethodName = method.Name, Arguments = args }; | |
460 call.MethodSignature = ConvertMethodParameterTypeArray(method); | |
461 MethodCallResponse resp = (MethodCallResponse)SyncCall(call); | |
462 if (resp.Exception != null) throw new Exception("Remote exception", resp.Exception); | |
463 ProxyCallFixReturnArguments(args, resp.ReturnArguments); | |
464 ProxyCallCheckReturnType(resp.ReturnValue, method.ReturnType); | |
465 return resp.ReturnValue; | |
466 } | |
467 private Boolean ProxyCanCastTo(IProxyBase obj, Type type) { | |
468 return (Boolean)SyncCall(new ObjectCanCastToRequest() { Object = obj, Type = type }); | |
469 } | |
470 private void ProxyReleaseObject(ProxyObjectReference objref) { | |
471 if (objref == null) return; | |
472 ProxyReleaseObject(objref, objref.RefCnt); | |
473 } | |
474 private void ProxyReleaseObject(ProxyObjectReference objref, int refcnt) { | |
475 lock (ObjectReferencesByID) { | |
476 int newrefcnt = objref.Release(refcnt); | |
477 IObjectReferenceBase regobjref; | |
478 if (newrefcnt <= 0 && ObjectReferencesByID.TryGetValue(objref.ID, out regobjref) && objref == regobjref) ObjectReferencesByID.Remove(objref.ID); | |
479 } | |
480 try { | |
481 if (Closed) return; | |
482 SendObject(new ReferenceReleaseRequest() { ObjectID = objref.RemoteID, ReferenceCount = refcnt }); | |
483 } catch (Exception) { } //Assume the exception happened because the connection is closed. Otherwise memory leak... | |
484 } | |
485 | |
486 class PendingRemoteCall { | |
487 public UInt32 ID = 0; | |
488 public ManualResetEvent WaitHandle = null; | |
489 public Object Response = null; | |
490 public Exception Error = null; | |
491 public Boolean Completed = false; | |
492 public SynchronousCall? NextCall = null; | |
493 public Object CallData = null; | |
494 | |
495 public void SetError(Exception error) { | |
496 this.Error = error; | |
497 Completed = true; | |
498 if (WaitHandle != null) WaitHandle.Set(); | |
499 } | |
500 public void SetResult(Object result) { | |
501 this.Response = result; | |
502 Completed = true; | |
503 if (WaitHandle != null) WaitHandle.Set(); | |
504 } | |
505 public Boolean SetNextCall(SynchronousCall nextcall) { | |
506 if (WaitHandle == null) return false; | |
507 this.NextCall = nextcall; | |
508 WaitHandle.Set(); | |
509 return true; | |
510 } | |
511 } | |
512 #endregion | |
513 | |
514 #region Object serialization support | |
515 private void Serialize(Stream stream, Object obj) { | |
516 BinaryWriter writer = new BinaryWriter(stream); | |
517 Serialize(writer, obj); | |
518 writer.Flush(); | |
519 } | |
520 private void Serialize(BinaryWriter writer, Object obj) { | |
521 if (ReferenceEquals(obj, null)) { | |
522 writer.Write((Byte)0); | |
523 } else if (GetRealProxyForObject(obj) != null) { | |
524 GetObjectData(obj, writer); | |
525 } else { | |
526 Type type = obj.GetType(); | |
527 if (type == typeof(Boolean)) { | |
528 writer.Write((Byte)1); | |
529 writer.Write((Boolean)obj); | |
530 } else if (type == typeof(Byte)) { | |
531 writer.Write((Byte)2); | |
532 writer.Write((Byte)obj); | |
533 } else if (type == typeof(Char)) { | |
534 writer.Write((Byte)3); | |
535 writer.Write((Int16)obj); | |
536 } else if (type == typeof(Decimal)) { | |
537 writer.Write((Byte)4); | |
538 writer.Write((Decimal)obj); | |
539 } else if (type == typeof(Double)) { | |
540 writer.Write((Byte)5); | |
541 writer.Write((Double)obj); | |
542 } else if (type == typeof(Int16)) { | |
543 writer.Write((Byte)6); | |
544 writer.Write((Int16)obj); | |
545 } else if (type == typeof(Int32)) { | |
546 writer.Write((Byte)7); | |
547 writer.Write((Int32)obj); | |
548 } else if (type == typeof(Int64)) { | |
549 writer.Write((Byte)8); | |
550 writer.Write((Int64)obj); | |
551 } else if (type == typeof(SByte)) { | |
552 writer.Write((Byte)9); | |
553 writer.Write((SByte)obj); | |
554 } else if (type == typeof(Single)) { | |
555 writer.Write((Byte)10); | |
556 writer.Write((Single)obj); | |
557 } else if (type == typeof(String)) { | |
558 writer.Write((Byte)11); | |
559 writer.Write((String)obj); | |
560 } else if (type == typeof(UInt16)) { | |
561 writer.Write((Byte)12); | |
562 writer.Write((UInt16)obj); | |
563 } else if (type == typeof(UInt32)) { | |
564 writer.Write((Byte)13); | |
565 writer.Write((UInt32)obj); | |
566 } else if (type == typeof(UInt64)) { | |
567 writer.Write((Byte)14); | |
568 writer.Write((UInt64)obj); | |
14 | 569 } else if (type == typeof(DateTime)) { |
570 writer.Write((Byte)32); | |
571 writer.Write((Int64)((DateTime)obj).ToBinary()); | |
13 | 572 } else if (type.IsSubclassOf(typeof(Type))) { |
573 writer.Write((Byte)165); | |
574 SerializeType(writer, (Type)obj); | |
575 } else if (type.IsArray) { | |
576 writer.Write((Byte)164); | |
577 Array arr = (Array)obj; | |
578 writer.Write((int)arr.Length); | |
579 SerializeType(writer, type.GetElementType()); | |
580 for (int i = 0; i < arr.Length; i++) { | |
581 Serialize(writer, arr.GetValue(i)); | |
582 } | |
583 } else if (type.IsPrimitive) { | |
584 throw new NotSupportedException(); | |
585 } else if (typeof(Delegate).IsAssignableFrom(type) || type.IsMarshalByRef) { | |
586 GetObjectData(obj, writer); | |
587 } else if (obj is ISerializable) { | |
588 SerializationInfo si = new SerializationInfo(type, new FormatterConverter()); | |
589 ((ISerializable)obj).GetObjectData(si, new StreamingContext(StreamingContextStates.All)); | |
14 | 590 writer.Write((Byte)128); |
13 | 591 SerializeType(writer, Type.GetType(si.FullTypeName + "," + si.AssemblyName)); |
592 writer.Write((int)si.MemberCount); | |
593 foreach (SerializationEntry se in si) { | |
594 writer.Write(se.Name); | |
595 Serialize(writer, se.Value); | |
596 } | |
597 } else if (type.IsSerializable) { | |
598 MemberInfo[] members = FormatterServices.GetSerializableMembers(type); | |
599 Object[] values = FormatterServices.GetObjectData(obj, members); | |
600 writer.Write((Byte)128); | |
601 SerializeType(writer, type); | |
14 | 602 writer.Write((int)members.Length); |
13 | 603 for (int i = 0; i < members.Length; i++) { |
14 | 604 writer.Write(members[i].Name); |
13 | 605 Serialize(writer, values[i]); |
606 } | |
607 } else { | |
608 GetObjectData(obj, writer); | |
609 } | |
610 } | |
611 } | |
612 private void SerializeType(BinaryWriter writer, Type t) { | |
613 writer.Write(t.FullName); | |
614 writer.Write(t.Assembly.FullName); | |
615 } | |
616 private Object Deserialize(Stream stream) { | |
617 BinaryReader reader = new BinaryReader(stream); | |
618 return Deserialize(reader); | |
619 } | |
620 private Object Deserialize(BinaryReader reader) { | |
621 Byte t = reader.ReadByte(); | |
622 if (t == 0) return null; | |
623 if (t == 1) return reader.ReadBoolean(); | |
624 if (t == 2) return reader.ReadByte(); | |
625 if (t == 3) return (Char)reader.ReadInt16(); | |
626 if (t == 4) return reader.ReadDecimal(); | |
627 if (t == 5) return reader.ReadDouble(); | |
628 if (t == 6) return reader.ReadInt16(); | |
629 if (t == 7) return reader.ReadInt32(); | |
630 if (t == 8) return reader.ReadInt64(); | |
631 if (t == 9) return reader.ReadSByte(); | |
632 if (t == 10) return reader.ReadSingle(); | |
633 if (t == 11) return reader.ReadString(); | |
634 if (t == 12) return reader.ReadUInt16(); | |
635 if (t == 13) return reader.ReadUInt32(); | |
636 if (t == 14) return reader.ReadUInt64(); | |
14 | 637 if (t == 32) return DateTime.FromBinary(reader.ReadInt64()); |
13 | 638 if (t == 128) { |
639 Type type = DeserializeType(reader); | |
14 | 640 int cnt = reader.ReadInt32(); |
641 Object inst; | |
642 StreamingContext sc = new StreamingContext(StreamingContextStates.All); | |
643 if (typeof(ISerializable).IsAssignableFrom(type) && type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(SerializationInfo), typeof(StreamingContext) }, null) != null) { | |
644 SerializationInfo si = new SerializationInfo(type, new FormatterConverter()); | |
645 for (int i = 0; i < cnt; i++) { | |
646 String name = reader.ReadString(); | |
647 Object value = Deserialize(reader); | |
648 si.AddValue(name, value); | |
649 } | |
650 inst = Activator.CreateInstance(type, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new Object[] { si, sc }, null); | |
651 } else if (type.IsSerializable) { | |
652 inst = FormatterServices.GetUninitializedObject(type); | |
653 List<MemberInfo> members = new List<MemberInfo>(); | |
654 List<Object> values = new List<object>(); | |
655 for (int i = 0; i < cnt; i++) { | |
13 | 656 String mname = reader.ReadString(); |
657 Object value = Deserialize(reader); | |
658 MemberInfo[] mms = type.GetMember(mname, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); | |
659 if (mms.Length != 1) throw new InvalidOperationException(); | |
660 members.Add(mms[0]); | |
661 values.Add(value); | |
14 | 662 } |
663 FormatterServices.PopulateObjectMembers(inst, members.ToArray(), values.ToArray()); | |
664 } else { | |
665 throw new InvalidOperationException("Type " + type.Name + " is not serializable"); | |
13 | 666 } |
14 | 667 IObjectReference objref = inst as IObjectReference; |
668 if (objref != null) inst = objref.GetRealObject(sc); | |
13 | 669 return inst; |
670 } | |
671 if (t == 129 || t == 130 || t == 131) { | |
672 return SetObjectData(t, reader); | |
673 } | |
674 if (t == 164) { | |
675 int len = reader.ReadInt32(); | |
676 Type type = DeserializeType(reader); Array arr = Array.CreateInstance(type, len); | |
677 for (int i = 0; i < len; i++) arr.SetValue(Deserialize(reader), i); | |
678 return arr; | |
679 } | |
680 if (t == 165) { | |
681 return DeserializeType(reader); | |
682 } | |
683 throw new NotSupportedException(); | |
684 } | |
685 private Type DeserializeType(BinaryReader reader) { | |
686 String name = reader.ReadString(); | |
687 String aname = reader.ReadString(); | |
688 Type t = Type.GetType(name + ", " + aname, false); | |
689 if (t != null) return t; | |
690 foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) { | |
691 t = assembly.GetType(name, false); | |
692 if (t != null) return t; | |
693 } | |
694 return t; | |
695 } | |
696 | |
697 Dictionary<UInt32, IObjectReferenceBase> ObjectReferencesByID = new Dictionary<uint, IObjectReferenceBase>(); | |
698 List<RemoteObjectReference> RemoteObjectReferences = new List<RemoteObjectReference>(); | |
699 uint remoteReferenceIndex = 0; | |
700 void GetObjectData(object obj, BinaryWriter w) { | |
701 IObjectReferenceBase rref = null; | |
702 IProxyBase proxy = GetRealProxyForObject(obj); | |
703 if (proxy != null && proxy.Manager == this) { | |
704 rref = proxy.ObjectReference; | |
705 w.Write((Byte)131); | |
706 w.Write((UInt32)rref.RemoteID); | |
707 } else { | |
708 Boolean isDelegate = obj is Delegate; | |
709 lock (ObjectReferencesByID) { | |
710 foreach (IObjectReferenceBase objref in RemoteObjectReferences) { | |
711 Object other = objref.Target; | |
712 if (ReferenceEquals(other, obj) || (isDelegate && Delegate.Equals(other, obj))) { | |
713 rref = objref; | |
714 break; | |
715 } | |
716 } | |
717 if (rref == null) { | |
718 UInt32 objid; | |
719 lock (ObjectReferencesByID) { | |
720 while (true) { | |
721 remoteReferenceIndex++; | |
722 objid = remoteReferenceIndex; | |
723 if ((objid & 0x80000000) != 0) { | |
724 remoteReferenceIndex = 0; | |
725 continue; | |
726 } | |
727 if (!ObjectReferencesByID.ContainsKey(objid)) break; | |
728 Monitor.Wait(ObjectReferencesByID); | |
729 } | |
730 } | |
731 rref = new RemoteObjectReference(objid, obj); | |
732 ObjectReferencesByID.Add(objid, rref); | |
733 RemoteObjectReferences.Add((RemoteObjectReference)rref); | |
734 } | |
735 rref.AddRef(); | |
736 } | |
737 if (isDelegate) { | |
738 w.Write((Byte)130); | |
739 w.Write((UInt32)rref.RemoteID); | |
740 SerializeType(w, obj.GetType()); | |
741 } else { | |
742 w.Write((Byte)129); | |
743 w.Write((UInt32)rref.RemoteID); | |
744 } | |
745 } | |
746 } | |
747 object SetObjectData(Byte t, BinaryReader r) { | |
748 UInt32 objid = r.ReadUInt32(); | |
749 Type deltype = null; | |
750 if (t == 130) deltype = DeserializeType(r); | |
751 lock (ObjectReferencesByID) { | |
752 Object target = null; | |
753 IObjectReferenceBase rref; | |
754 if (ObjectReferencesByID.TryGetValue(objid, out rref)) target = rref.Target; | |
755 if (t == 131) { | |
756 if (target == null) throw new InvalidOperationException("Object not found"); | |
757 } else { | |
758 if (target == null) { | |
759 if ((objid & 0x80000000) == 0) throw new InvalidOperationException("The Object ID is invalid"); | |
760 IProxyBase proxy; | |
761 if (t == 130) { | |
762 proxy = CreateDelegateProxy(objid, deltype); | |
763 } else { | |
764 String[] iftypes = null; //(String[])info.GetValue("InterfaceTypes", typeof(String[])); | |
765 proxy = CreateObjectProxy(objid, iftypes); | |
766 } | |
767 rref = proxy.ObjectReference; | |
768 ObjectReferencesByID[objid] = rref; | |
769 target = proxy.GetTransparentProxy(); | |
770 } | |
771 rref.AddRef(); | |
772 } | |
773 return target; | |
774 } | |
775 } | |
776 #endregion | |
777 | |
778 #region RPC messages | |
779 [Serializable] | |
780 struct SynchronousCall { | |
781 public UInt32 CallID; | |
782 public Boolean IsResult; | |
783 public Object Data; | |
784 public Boolean HasPreviousCallID; | |
785 public UInt32 PreviousCallID; | |
786 } | |
787 | |
788 [Serializable] | |
789 struct MethodCallRequest { | |
790 public Object Object; | |
791 public Type Type; | |
792 public String MethodName; | |
793 public Type[] MethodSignature; | |
794 public Object[] Arguments; | |
795 } | |
796 [Serializable] | |
797 struct MethodCallResponse { | |
798 public Exception Exception; | |
799 public Object ReturnValue; | |
800 public Object[] ReturnArguments; | |
801 } | |
802 [Serializable] | |
803 struct PropertyAccessRequest { | |
804 public Object Object; | |
805 public Type Type; | |
806 public String PropertyName; | |
807 public Boolean SetValue; | |
808 public Object Value; | |
809 } | |
810 [Serializable] | |
811 struct ReferenceReleaseRequest { | |
812 public UInt32 ObjectID; | |
813 public Int32 ReferenceCount; | |
814 } | |
815 [Serializable] | |
816 struct GetRootRequest { | |
817 } | |
818 [Serializable] | |
819 struct ObjectCanCastToRequest { | |
820 public Object Object; | |
821 public Type Type; | |
822 } | |
823 [Serializable] | |
824 struct CreateStreamRequest { | |
825 public StreamChannel StreamObject; | |
826 public int StreamID; | |
827 } | |
828 [Serializable] | |
829 struct DelegateCallRequest { | |
830 public Object Delegate; | |
831 public Object[] Arguments; | |
832 } | |
833 #endregion | |
834 | |
835 #region Proxy magic | |
836 static IProxyBase GetRealProxyForObject(Object obj) { | |
837 if (RemotingServices.IsTransparentProxy(obj)) return RemotingServices.GetRealProxy(obj) as FWProxy; | |
838 IProxyBase obj_IProxyBase = obj as IProxyBase; | |
839 if (obj_IProxyBase != null) return obj_IProxyBase; | |
840 Delegate obj_Delegate = obj as Delegate; | |
841 if (obj_Delegate != null) { | |
842 DelegateProxy pb = obj_Delegate.Target as DelegateProxy; | |
843 if (pb != null) return pb; | |
844 } | |
845 return null; | |
846 } | |
847 public static RemotingManager GetManagerForObjectProxy(Object obj) { | |
848 IProxyBase prox = GetRealProxyForObject(obj); | |
849 if (prox == null) return null; | |
850 return prox.Manager; | |
851 } | |
852 | |
853 interface IObjectReferenceBase { | |
854 UInt32 ID { get; } | |
855 Object Target { get; } | |
856 int AddRef(); | |
857 int RefCnt { get; } | |
858 Boolean IsLocal { get; } | |
859 UInt32 RemoteID { get; } | |
860 } | |
861 | |
862 class RemoteObjectReference : IObjectReferenceBase { | |
863 int refcnt = 0; | |
864 public UInt32 ID { get; private set; } | |
865 public Object Target { get; private set; } | |
866 public int RefCnt { get { return refcnt; } } | |
867 public int AddRef() { return Interlocked.Increment(ref refcnt); } | |
868 public int Release(int count) { return Interlocked.Add(ref refcnt, -count); } | |
869 public Boolean IsLocal { get { return true; } } | |
870 public UInt32 RemoteID { get { return ID | 0x80000000; } } | |
871 public RemoteObjectReference(UInt32 id, Object obj) { | |
872 if ((id & 0x80000000) != 0) throw new InvalidOperationException("The Object ID is invalid"); | |
873 this.ID = id; | |
874 this.Target = obj; | |
875 } | |
876 } | |
877 | |
878 interface IProxyBase { | |
879 RemotingManager Manager { get; } | |
880 UInt32 ID { get; } | |
881 ProxyObjectReference ObjectReference { get; } | |
882 Object GetTransparentProxy(); | |
883 } | |
884 class ProxyObjectReference : IObjectReferenceBase { | |
885 int refcnt = 0; | |
886 WeakReference targetref; | |
887 public UInt32 ID { get; private set; } | |
888 public RemotingManager Manager { get; private set; } | |
889 public Object Target { | |
890 get { | |
891 IProxyBase proxy = this.Proxy; | |
892 if (proxy == null) return null; | |
893 return proxy.GetTransparentProxy(); | |
894 } | |
895 } | |
896 public IProxyBase Proxy { | |
897 get { | |
898 if (targetref == null) return null; | |
899 return (IProxyBase)targetref.Target; | |
900 } | |
901 } | |
902 public int RefCnt { get { return refcnt; } } | |
903 public int AddRef() { | |
904 int newcnt = Interlocked.Increment(ref refcnt); | |
905 if (newcnt > 10000 && Interlocked.CompareExchange(ref refcnt, 1, newcnt) == newcnt) { | |
906 Manager.ProxyReleaseObject(this, newcnt - 1); | |
907 newcnt = 1; | |
908 } | |
909 return newcnt; | |
910 } | |
911 public int Release(int count) { return Interlocked.Add(ref refcnt, -count); } | |
912 public Boolean IsLocal { get { return false; } } | |
913 public UInt32 RemoteID { get { return ID & 0x7FFFFFFF; } } | |
914 public ProxyObjectReference(IProxyBase proxy) { | |
915 this.Manager = proxy.Manager; | |
916 this.ID = proxy.ID; | |
917 if ((ID & 0x80000000) == 0) throw new InvalidOperationException("The Object ID is invalid"); | |
918 targetref = new WeakReference(proxy); | |
919 } | |
920 } | |
921 class DelegateProxy : IProxyBase { | |
922 public ProxyObjectReference ObjectReference { get; private set; } | |
923 public RemotingManager Manager { get; private set; } | |
924 public UInt32 ID { get; private set; } | |
925 public Delegate Target { get; private set; } | |
926 public MethodInfo MethodSignature { get; private set; } | |
927 public Object GetTransparentProxy() { return Target; } | |
928 | |
929 public DelegateProxy(RemotingManager manager, UInt32 objid, MethodInfo methodinfo, Type delegateType, DynamicMethod methodBuilder) { | |
930 this.Manager = manager; | |
931 this.ID = objid; | |
932 this.MethodSignature = methodinfo; | |
933 Delegate mi = methodBuilder.CreateDelegate(delegateType, this); | |
934 ObjectReference = new ProxyObjectReference(this); | |
935 } | |
936 ~DelegateProxy() { | |
937 Manager.ProxyReleaseObject(ObjectReference); | |
938 } | |
939 private Object DoCall(Object[] args) { | |
940 return Manager.ProxyMakeCallDelegate(this, args); | |
941 } | |
942 } | |
943 class FWProxy : RealProxy, IRemotingTypeInfo, IProxyBase { | |
944 public RemotingManager Manager { get; private set; } | |
945 public UInt32 ID { get; private set; } | |
946 public ProxyObjectReference ObjectReference { get; private set; } | |
947 | |
948 public FWProxy(RemotingManager manager, UInt32 objid) : base(typeof(MarshalByRefObject)) { | |
949 this.Manager = manager; | |
950 this.ID = objid; | |
951 this.ObjectReference = new ProxyObjectReference(this); | |
952 } | |
953 | |
954 ~FWProxy() { | |
955 Manager.ProxyReleaseObject(ObjectReference); | |
956 } | |
957 | |
958 public string TypeName { get; set; } | |
959 | |
960 public override IMessage Invoke(IMessage msg) { | |
961 IMethodCallMessage methodCallMessage = msg as IMethodCallMessage; | |
962 if (methodCallMessage != null) { | |
963 Object r = Manager.ProxyMakeCall(this, (MethodInfo)methodCallMessage.MethodBase, methodCallMessage.Args); | |
964 return new ReturnMessage(r, null, 0, null, methodCallMessage); | |
965 } | |
966 throw new NotImplementedException(); | |
967 } | |
968 | |
969 public bool CanCastTo(Type fromType, object o) { | |
970 if (fromType == typeof(ISerializable)) return false; | |
971 return Manager.ProxyCanCastTo(this, fromType); | |
972 } | |
973 } | |
974 class ProxyBase : IProxyBase { | |
975 public RemotingManager Manager { get; private set; } | |
976 public UInt32 ID { get; private set; } | |
977 public ProxyObjectReference ObjectReference { get; private set; } | |
978 public Object GetTransparentProxy() { return this; } | |
979 | |
980 public void Init(RemotingManager manager, UInt32 objid) { | |
981 this.Manager = manager; | |
982 this.ID = objid; | |
983 this.ObjectReference = new ProxyObjectReference(this); | |
984 } | |
985 | |
986 protected Object DoCall(RuntimeMethodHandle methodh, Object[] args) { | |
987 MethodInfo meth = (MethodInfo)MethodInfo.GetMethodFromHandle(methodh); | |
988 return Manager.ProxyMakeCall(this, meth, args); | |
989 } | |
990 | |
991 ~ProxyBase() { | |
992 Manager.ProxyReleaseObject(ObjectReference); | |
993 } | |
994 } | |
995 | |
996 private IProxyBase CreateDelegateProxy(UInt32 objid, Type deltype) { | |
997 MethodInfo newMethod = deltype.GetMethod("Invoke"); | |
998 ParameterInfo[] parameters = newMethod.GetParameters(); | |
999 Type[] mparams = ArrayUtil.Merge(new Type[1] { typeof(DelegateProxy) }, Array.ConvertAll(parameters, delegate(ParameterInfo pi) { return pi.ParameterType; })); | |
1000 DynamicMethod methodBuilder = new DynamicMethod(String.Empty, newMethod.ReturnType, mparams, typeof(DelegateProxy)); | |
1001 ILGenerator ilGenerator = methodBuilder.GetILGenerator(); | |
1002 GenerateProxyMethodCode(ilGenerator, parameters, newMethod.ReturnType, typeof(DelegateProxy), "DoCall", null); | |
1003 return new DelegateProxy(this, objid, newMethod, deltype, methodBuilder); | |
1004 } | |
1005 | |
1006 private IProxyBase CreateObjectProxy(UInt32 objid, String[] typeNames) { | |
1007 DebugLog("Create proxy for remote object {0}", objid); | |
1008 IProxyBase proxy; | |
1009 if (true) { | |
1010 proxy = new FWProxy(this, objid); | |
1011 } else { | |
1012 Type[] types = new Type[typeNames.Length]; | |
1013 int j = 0; | |
1014 for (int i = 0; i < typeNames.Length; i++) { | |
1015 Type t = Type.GetType(typeNames[i], false); | |
1016 if (t == null || !t.IsInterface) continue; | |
1017 types[j] = t; | |
1018 j++; | |
1019 } | |
1020 Array.Resize(ref types, j); | |
1021 Type proxyType = GetProxyType(types); | |
1022 proxy = (ProxyBase)Activator.CreateInstance(proxyType); | |
1023 ((ProxyBase)proxy).Init(this, objid); | |
1024 } | |
1025 return proxy; | |
1026 } | |
1027 | |
1028 private static ModuleBuilder moduleBuilder = null; | |
1029 private static Dictionary<String, Type> proxyCache = null; | |
1030 private static Type GetProxyType(Type[] interfaceTypes) { | |
1031 Type proxyType; | |
1032 String key = String.Join("&", Array.ConvertAll(interfaceTypes, delegate(Type t) { return t.Name; })); | |
1033 lock (typeof(RemotingManager)) if (proxyCache == null) proxyCache = new Dictionary<String, Type>(); | |
1034 lock (proxyCache) { | |
1035 if (!proxyCache.TryGetValue(key, out proxyType)) { | |
1036 proxyType = GenerateProxyType(key, interfaceTypes); | |
1037 proxyCache.Add(key, proxyType); | |
1038 } | |
1039 } | |
1040 return proxyType; | |
1041 } | |
1042 private static Type GenerateProxyType(String name, Type[] interfaceTypes) { | |
1043 lock (typeof(RemotingManager)) { | |
1044 if (moduleBuilder == null) { | |
1045 AssemblyBuilder assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("UCIS.Remoting.Proxies"), AssemblyBuilderAccess.Run); | |
1046 moduleBuilder = assembly.DefineDynamicModule("UCIS.Remoting.Proxies", false); | |
1047 } | |
1048 } | |
1049 TypeBuilder typeBuilder = moduleBuilder.DefineType( | |
1050 name.Length == 0 ? "UndefinedProxy" : name, //mono does not like types with no name! | |
1051 TypeAttributes.NotPublic | TypeAttributes.Sealed, | |
1052 typeof(ProxyBase), | |
1053 interfaceTypes); | |
1054 foreach (Type interfaceType in interfaceTypes) { | |
1055 foreach (MethodInfo method in interfaceType.GetMethods()) { | |
1056 GenerateProxyMethod(typeBuilder, method); | |
1057 } | |
1058 } | |
1059 return typeBuilder.CreateType(); | |
1060 } | |
1061 private static void GenerateProxyMethod(TypeBuilder typeBuilder, MethodInfo newMethod) { | |
1062 if (newMethod.IsGenericMethod) newMethod = newMethod.GetGenericMethodDefinition(); | |
1063 ParameterInfo[] parameters = newMethod.GetParameters(); | |
1064 Type[] parameterTypes = Array.ConvertAll(parameters, delegate(ParameterInfo parameter) { return parameter.ParameterType; }); | |
1065 | |
1066 MethodBuilder methodBuilder = typeBuilder.DefineMethod( | |
1067 "Impl_" + newMethod.DeclaringType.Name + "_" + newMethod.Name, | |
1068 MethodAttributes.Public | MethodAttributes.Virtual, | |
1069 newMethod.ReturnType, | |
1070 parameterTypes); | |
1071 typeBuilder.DefineMethodOverride(methodBuilder, newMethod); | |
1072 | |
1073 if (newMethod.IsGenericMethod) { | |
1074 methodBuilder.DefineGenericParameters(Array.ConvertAll(newMethod.GetGenericArguments(), delegate(Type type) { return type.Name; })); | |
1075 } | |
1076 | |
1077 ILGenerator ilGenerator = methodBuilder.GetILGenerator(); | |
1078 GenerateProxyMethodCode(ilGenerator, parameters, newMethod.ReturnType, typeof(ProxyBase), "DoCall", newMethod); | |
1079 } | |
1080 private static void GenerateProxyMethodCode(ILGenerator ilGenerator, ParameterInfo[] parameters, Type returnType, Type baseType, String baseMethod, MethodInfo methodRef) { | |
1081 LocalBuilder localBuilder = ilGenerator.DeclareLocal(typeof(Object[])); | |
1082 ilGenerator.Emit(OpCodes.Ldc_I4, parameters.Length); | |
1083 ilGenerator.Emit(OpCodes.Newarr, typeof(Object)); | |
1084 ilGenerator.Emit(OpCodes.Stloc, localBuilder); | |
1085 for (int i = 0; i < parameters.Length; i++) { | |
1086 if (parameters[i].ParameterType.IsByRef) continue; | |
1087 ilGenerator.Emit(OpCodes.Ldloc, localBuilder); | |
1088 ilGenerator.Emit(OpCodes.Ldc_I4, i); | |
1089 ilGenerator.Emit(OpCodes.Ldarg, i + 1); | |
1090 if (parameters[i].ParameterType.IsValueType) ilGenerator.Emit(OpCodes.Box, parameters[i].ParameterType); | |
1091 ilGenerator.Emit(OpCodes.Stelem_Ref); | |
1092 } | |
1093 ilGenerator.Emit(OpCodes.Ldarg_0); | |
1094 if (methodRef != null) ilGenerator.Emit(OpCodes.Ldtoken, methodRef); | |
1095 ilGenerator.Emit(OpCodes.Ldloc, localBuilder); | |
1096 ilGenerator.Emit(OpCodes.Call, baseType.GetMethod(baseMethod, BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.NonPublic)); | |
1097 if (returnType == typeof(void)) { | |
1098 ilGenerator.Emit(OpCodes.Pop); | |
1099 } else if (returnType.IsValueType) { | |
1100 ilGenerator.Emit(OpCodes.Unbox_Any, returnType); | |
1101 } | |
1102 ilGenerator.Emit(OpCodes.Ret); | |
1103 } | |
1104 #endregion | |
1105 } | |
1106 } |