In System.ServiceModel.Description:
[mono-project.git] / mcs / class / System.ServiceModel / System.ServiceModel / ServiceHostBase.cs
blob5add53257d0a5960505a1bd4546d649be9de7d99
1 //
2 // ServiceHostBase.cs
3 //
4 // Author:
5 // Atsushi Enomoto <atsushi@ximian.com>
6 //
7 // Copyright (C) 2005-2006 Novell, Inc. http://www.novell.com
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 using System;
29 using System.Collections.Generic;
30 using System.Collections.ObjectModel;
31 using System.ServiceModel.Channels;
32 using System.ServiceModel.Configuration;
33 using System.ServiceModel.Description;
34 using System.ServiceModel.Dispatcher;
35 using System.ServiceModel.Security;
36 using System.Reflection;
38 namespace System.ServiceModel
40 public abstract class ServiceHostBase
41 : CommunicationObject, IExtensibleObject<ServiceHostBase>, IDisposable
43 ServiceCredentials credentials;
44 ServiceDescription description;
45 UriSchemeKeyedCollection base_addresses;
46 TimeSpan open_timeout, close_timeout, instance_idle_timeout;
47 ServiceThrottle throttle;
48 List<InstanceContext> contexts;
49 ReadOnlyCollection<InstanceContext> exposed_contexts;
50 ChannelDispatcherCollection channel_dispatchers;
51 IDictionary<string,ContractDescription> contracts;
52 int flow_limit = int.MaxValue;
53 IExtensionCollection<ServiceHostBase> extensions;
55 protected ServiceHostBase ()
57 open_timeout = DefaultOpenTimeout;
58 close_timeout = DefaultCloseTimeout;
60 credentials = new ServiceCredentials ();
61 throttle = new ServiceThrottle ();
62 contexts = new List<InstanceContext> ();
63 exposed_contexts = new ReadOnlyCollection<InstanceContext> (contexts);
64 channel_dispatchers = new ChannelDispatcherCollection (this);
67 public event EventHandler<UnknownMessageReceivedEventArgs>
68 UnknownMessageReceived;
70 public ReadOnlyCollection<Uri> BaseAddresses {
71 get { return new ReadOnlyCollection<Uri> (base_addresses.InternalItems); }
74 internal Uri CreateUri (string sheme, Uri relatieUri) {
75 Uri baseUri = base_addresses.Contains (sheme) ? base_addresses [sheme] : null;
77 if (relatieUri == null)
78 return baseUri;
79 if (relatieUri.IsAbsoluteUri)
80 return relatieUri;
81 if (baseUri == null)
82 return null;
83 return new Uri (baseUri, relatieUri);
86 public ChannelDispatcherCollection ChannelDispatchers {
87 get { return channel_dispatchers; }
90 public ServiceAuthorizationBehavior Authorization {
91 get;
92 private set;
95 [MonoTODO]
96 public ServiceCredentials Credentials {
97 get { return credentials; }
100 public ServiceDescription Description {
101 get { return description; }
104 protected IDictionary<string,ContractDescription> ImplementedContracts {
105 get { return contracts; }
108 [MonoTODO]
109 public IExtensionCollection<ServiceHostBase> Extensions {
110 get {
111 if (extensions == null)
112 extensions = new ExtensionCollection<ServiceHostBase> (this);
113 return extensions;
117 protected internal override TimeSpan DefaultCloseTimeout {
118 get { return DefaultCommunicationTimeouts.Instance.CloseTimeout; }
121 protected internal override TimeSpan DefaultOpenTimeout {
122 get { return DefaultCommunicationTimeouts.Instance.OpenTimeout; }
125 public TimeSpan CloseTimeout {
126 get { return close_timeout; }
127 set { close_timeout = value; }
130 public TimeSpan OpenTimeout {
131 get { return open_timeout; }
132 set { open_timeout = value; }
135 public int ManualFlowControlLimit {
136 get { return flow_limit; }
137 set { flow_limit = value; }
140 public ServiceEndpoint AddServiceEndpoint (
141 string implementedContract, Binding binding, string address)
143 return AddServiceEndpoint (implementedContract,
144 binding,
145 new Uri (address, UriKind.RelativeOrAbsolute));
148 public ServiceEndpoint AddServiceEndpoint (
149 string implementedContract, Binding binding,
150 string address, Uri listenUri)
152 Uri uri = new Uri (address, UriKind.RelativeOrAbsolute);
153 return AddServiceEndpoint (
154 implementedContract, binding, uri, uri);
157 public ServiceEndpoint AddServiceEndpoint (
158 string implementedContract, Binding binding,
159 Uri address)
161 return AddServiceEndpoint (implementedContract, binding, address, address);
164 public ServiceEndpoint AddServiceEndpoint (
165 string implementedContract, Binding binding,
166 Uri address, Uri listenUri)
168 EndpointAddress ea = BuildEndpointAddress (address, binding);
169 ContractDescription cd = GetContract (implementedContract);
170 if (cd == null)
171 throw new InvalidOperationException (String.Format ("Contract '{0}' was not found in the implemented contracts in this service host.", implementedContract));
172 return AddServiceEndpointCore (cd, binding, ea, listenUri);
175 Type PopulateType (string typeName)
177 Type type = Type.GetType (typeName);
178 if (type != null)
179 return type;
180 foreach (ContractDescription cd in ImplementedContracts.Values) {
181 type = cd.ContractType.Assembly.GetType (typeName);
182 if (type != null)
183 return type;
185 return null;
188 ContractDescription GetContract (string typeName)
190 //FIXME: hack hack hack
191 ImplementedContracts ["IHttpGetHelpPageAndMetadataContract"] =
192 ContractDescription.GetContract (typeof (IHttpGetHelpPageAndMetadataContract));
194 // FIXME: As long as I tried, *only* IMetadataExchange
195 // is the exception case that does not require full
196 // type name. Hence I treat it as a special case.
197 if (typeName == ServiceMetadataBehavior.MexContractName) {
198 if (!Description.Behaviors.Contains (typeof (ServiceMetadataBehavior)) && Array.IndexOf (Description.ServiceType.GetInterfaces (), typeof (IMetadataExchange)) < 0)
199 throw new InvalidOperationException (
200 "Add ServiceMetadataBehavior to the ServiceHost to add a endpoint for IMetadataExchange contract.");
202 ImplementedContracts [ServiceMetadataBehavior.MexContractName] =
203 ContractDescription.GetContract (typeof (IMetadataExchange));
205 foreach (ContractDescription cd in ImplementedContracts.Values)
206 if (cd.ContractType == typeof (IMetadataExchange))
207 return cd;
208 return null;
211 Type type = PopulateType (typeName);
212 if (type == null)
213 return null;
215 foreach (ContractDescription cd in ImplementedContracts.Values) {
216 // FIXME: This check is a negative side effect
217 // of the above hack.
218 if (cd.ContractType == typeof (IMetadataExchange))
219 continue;
221 if (cd.ContractType == type ||
222 cd.ContractType.IsSubclassOf (type) ||
223 type.IsInterface && cd.ContractType.GetInterface (type.FullName) == type)
224 return cd;
226 return null;
229 internal EndpointAddress BuildEndpointAddress (Uri address, Binding binding)
231 if (!address.IsAbsoluteUri) {
232 // Find a Base address with matching scheme,
233 // and build new absolute address
234 if (!base_addresses.Contains (binding.Scheme))
235 throw new InvalidOperationException (String.Format ("Could not find base address that matches Scheme {0} for endpoint {1}", binding.Scheme, binding.Name));
237 Uri baseaddr = base_addresses [binding.Scheme];
239 if (!baseaddr.AbsoluteUri.EndsWith ("/"))
240 baseaddr = new Uri (baseaddr.AbsoluteUri + "/");
241 address = new Uri (baseaddr, address);
243 return new EndpointAddress (address);
246 internal ServiceEndpoint AddServiceEndpointCore (
247 ContractDescription cd, Binding binding, EndpointAddress address, Uri listenUri)
249 foreach (ServiceEndpoint e in Description.Endpoints)
250 if (e.Contract == cd)
251 return e;
252 ServiceEndpoint se = new ServiceEndpoint (cd, binding, address);
253 se.ListenUri = listenUri.IsAbsoluteUri ? listenUri : new Uri (address.Uri, listenUri);
254 Description.Endpoints.Add (se);
255 return se;
258 [MonoTODO]
259 protected virtual void ApplyConfiguration ()
261 if (Description == null)
262 throw new InvalidOperationException ("ApplyConfiguration requires that the Description property be initialized. Either provide a valid ServiceDescription in the CreateDescription method or override the ApplyConfiguration method to provide an alternative implementation");
264 ServiceElement service = GetServiceElement ();
266 //TODO: Should we call here LoadServiceElement ?
267 if (service != null) {
269 //base addresses
270 HostElement host = service.Host;
271 foreach (BaseAddressElement baseAddress in host.BaseAddresses) {
272 this.base_addresses.Add (new Uri (baseAddress.BaseAddress));
275 // services
276 foreach (ServiceEndpointElement endpoint in service.Endpoints) {
277 // FIXME: consider BindingName as well
278 ServiceEndpoint se = AddServiceEndpoint (
279 endpoint.Contract,
280 ConfigUtil.CreateBinding (endpoint.Binding, endpoint.BindingConfiguration),
281 endpoint.Address.ToString ());
283 // behaviors
284 // TODO: use EvaluationContext of ServiceElement.
285 ServiceBehaviorElement behavior = ConfigUtil.BehaviorsSection.ServiceBehaviors.Find (service.BehaviorConfiguration);
286 if (behavior != null) {
287 for (int i = 0; i < behavior.Count; i++) {
288 BehaviorExtensionElement bxel = behavior [i];
289 IServiceBehavior b = (IServiceBehavior) behavior [i].CreateBehavior ();
290 if (b != null)
291 Description.Behaviors.Add (b);
295 // TODO: consider commonBehaviors here
297 // ensure ServiceAuthorizationBehavior
298 Authorization = Description.Behaviors.Find<ServiceAuthorizationBehavior> ();
299 if (Authorization == null) {
300 Authorization = new ServiceAuthorizationBehavior ();
301 Description.Behaviors.Add (Authorization);
304 // ensure ServiceDebugBehavior
305 ServiceDebugBehavior debugBehavior = Description.Behaviors.Find<ServiceDebugBehavior> ();
306 if (debugBehavior == null) {
307 debugBehavior = new ServiceDebugBehavior ();
308 Description.Behaviors.Add (debugBehavior);
312 private ServiceElement GetServiceElement() {
313 Type serviceType = Description.ServiceType;
314 if (serviceType == null)
315 return null;
317 return ConfigUtil.ServicesSection.Services [serviceType.FullName];
320 internal ContractDescription GetContract (string name, string ns)
322 foreach (ContractDescription d in ImplementedContracts.Values)
323 if (d.Name == name && d.Namespace == ns)
324 return d;
325 return null;
328 protected abstract ServiceDescription CreateDescription (
329 out IDictionary<string,ContractDescription> implementedContracts);
331 protected void InitializeDescription (UriSchemeKeyedCollection baseAddresses)
333 this.base_addresses = baseAddresses;
334 IDictionary<string,ContractDescription> retContracts;
335 description = CreateDescription (out retContracts);
336 contracts = retContracts;
338 ApplyConfiguration ();
341 protected virtual void InitializeRuntime ()
343 //First validate the description, which should call all behaviors
344 //'Validate' method.
345 ValidateDescription ();
347 //Build all ChannelDispatchers, one dispatcher per user configured EndPoint.
348 //We must keep thet ServiceEndpoints as a seperate collection, since the user
349 //can change the collection in the description during the behaviors events.
350 Dictionary<ServiceEndpoint, ChannelDispatcher> endPointToDispatcher = new Dictionary<ServiceEndpoint,ChannelDispatcher>();
351 ServiceEndpoint[] endPoints = new ServiceEndpoint[Description.Endpoints.Count];
352 Description.Endpoints.CopyTo (endPoints, 0);
353 foreach (ServiceEndpoint se in endPoints) {
354 //Let all behaviors add their binding parameters
355 BindingParameterCollection commonParams =
356 new BindingParameterCollection ();
357 AddBindingParameters (commonParams, se);
358 ChannelDispatcher channel = BuildChannelDispatcher (se, commonParams);
359 ChannelDispatchers.Add (channel);
360 endPointToDispatcher[se] = channel;
363 //After the ChannelDispatchers are created, and attached to the service host
364 //Apply dispatching behaviors.
365 foreach (IServiceBehavior b in Description.Behaviors)
366 b.ApplyDispatchBehavior (Description, this);
368 foreach(KeyValuePair<ServiceEndpoint, ChannelDispatcher> val in endPointToDispatcher)
369 ApplyDispatchBehavior(val.Value, val.Key);
372 private void ValidateDescription ()
374 foreach (IServiceBehavior b in Description.Behaviors)
375 b.Validate (Description, this);
376 foreach (ServiceEndpoint endPoint in Description.Endpoints)
377 endPoint.Validate ();
380 private void ApplyDispatchBehavior (ChannelDispatcher dispatcher, ServiceEndpoint endPoint) {
381 foreach (IContractBehavior b in endPoint.Contract.Behaviors)
382 b.ApplyDispatchBehavior (endPoint.Contract, endPoint, dispatcher.Endpoints[0].DispatchRuntime);
383 foreach (IEndpointBehavior b in endPoint.Behaviors)
384 b.ApplyDispatchBehavior (endPoint, dispatcher.Endpoints [0]);
385 foreach (OperationDescription operation in endPoint.Contract.Operations) {
386 foreach (IOperationBehavior b in operation.Behaviors)
387 b.ApplyDispatchBehavior (operation, dispatcher.Endpoints [0].DispatchRuntime.Operations [operation.Name]);
392 internal ChannelDispatcher BuildChannelDispatcher (ServiceEndpoint se, BindingParameterCollection commonParams)
394 //User the binding parameters to build the channel listener and Dispatcher
395 IChannelListener lf = BuildListener (se, commonParams);
396 ChannelDispatcher cd = new ChannelDispatcher (
397 lf, se.Binding.Name);
398 cd.MessageVersion = se.Binding.MessageVersion;
399 if (cd.MessageVersion == null)
400 cd.MessageVersion = MessageVersion.Default;
402 //Attach one EndpointDispacher to the ChannelDispatcher
403 EndpointDispatcher endpoint_dispatcher =
404 new EndpointDispatcher (se.Address, se.Contract.Name, se.Contract.Namespace);
405 endpoint_dispatcher.DispatchRuntime.Type = Description.ServiceType;
406 endpoint_dispatcher.ContractFilter = GetContractFilter (se.Contract);
407 endpoint_dispatcher.AddressFilter = new EndpointAddressMessageFilter (se.Address);
408 endpoint_dispatcher.ChannelDispatcher = cd;
409 cd.Endpoints.Add (endpoint_dispatcher);
411 //Build the dispatch operations
412 DispatchRuntime db = endpoint_dispatcher.DispatchRuntime;
413 if (se.Contract.CallbackContractType != null) {
414 var ccd = ContractDescriptionGenerator.GetCallbackContract (se.Contract.CallbackContractType);
415 db.CallbackClientRuntime = ccd.CreateClientRuntime ();
416 db.CallbackClientRuntime.CallbackClientType = ccd.ContractType;
418 foreach (OperationDescription od in se.Contract.Operations)
419 if (!db.Operations.Contains (od.Name))
420 PopulateDispatchOperation (db, od);
422 return cd;
425 private MessageFilter GetContractFilter (ContractDescription contractDescription)
427 List<string> actions = new List<string> ();
428 foreach (OperationDescription od in contractDescription.Operations)
429 foreach (MessageDescription md in od.Messages)
430 if (md.Direction == MessageDirection.Input)
431 if (md.Action == "*")
432 return new MatchAllMessageFilter ();
433 else
434 actions.Add (md.Action);
436 return new ActionMessageFilter (actions.ToArray ());
439 private void AddBindingParameters (BindingParameterCollection commonParams, ServiceEndpoint endPoint) {
441 commonParams.Add (ChannelProtectionRequirements.CreateFromContract (endPoint.Contract));
442 foreach (IServiceBehavior b in Description.Behaviors)
443 b.AddBindingParameters (Description, this, Description.Endpoints, commonParams);
445 foreach (IContractBehavior b in endPoint.Contract.Behaviors)
446 b.AddBindingParameters (endPoint.Contract, endPoint, commonParams);
447 foreach (IEndpointBehavior b in endPoint.Behaviors)
448 b.AddBindingParameters (endPoint, commonParams);
449 foreach (OperationDescription operation in endPoint.Contract.Operations) {
450 foreach (IOperationBehavior b in operation.Behaviors)
451 b.AddBindingParameters (operation, commonParams);
455 void PopulateDispatchOperation (DispatchRuntime db, OperationDescription od) {
456 string reqA = null, resA = null;
457 foreach (MessageDescription m in od.Messages) {
458 if (m.Direction == MessageDirection.Input)
459 reqA = m.Action;
460 else
461 resA = m.Action;
463 DispatchOperation o =
464 od.IsOneWay ?
465 new DispatchOperation (db, od.Name, reqA) :
466 new DispatchOperation (db, od.Name, reqA, resA);
467 bool no_serialized_reply = od.IsOneWay;
468 foreach (MessageDescription md in od.Messages) {
469 if (md.Direction == MessageDirection.Input &&
470 md.Body.Parts.Count == 1 &&
471 md.Body.Parts [0].Type == typeof (Message))
472 o.DeserializeRequest = false;
473 if (md.Direction == MessageDirection.Output &&
474 md.Body.ReturnValue != null) {
475 if (md.Body.ReturnValue.Type == typeof (Message))
476 o.SerializeReply = false;
477 else if (md.Body.ReturnValue.Type == typeof (void))
478 no_serialized_reply = true;
482 // Setup Invoker
483 o.Invoker = new DefaultOperationInvoker (od);
485 // Setup Formater
486 o.Formatter = BaseMessagesFormatter.Create (od);
488 if (o.Action == "*" && (o.IsOneWay || o.ReplyAction == "*")) {
489 //Signature : Message (Message)
490 // : void (Message)
491 //FIXME: void (IChannel)
492 if (!o.DeserializeRequest && (!o.SerializeReply || no_serialized_reply)) // what is this double-ish check for?
493 db.UnhandledDispatchOperation = o;
496 db.Operations.Add (o);
499 [MonoTODO]
500 protected void LoadConfigurationSection (ServiceElement element)
502 ServicesSection services = ConfigUtil.ServicesSection;
505 void DoOpen (TimeSpan timeout)
507 foreach (var cd in ChannelDispatchers) {
508 cd.Open (timeout);
509 // This is likely hack.
510 if (cd is ChannelDispatcher)
511 ((ChannelDispatcher) cd).StartLoop ();
515 IChannelListener BuildListener (ServiceEndpoint se,
516 BindingParameterCollection pl)
518 Binding b = se.Binding;
519 if (b.CanBuildChannelListener<IReplySessionChannel> (pl))
520 return b.BuildChannelListener<IReplySessionChannel> (se.ListenUri, "", se.ListenUriMode, pl);
521 if (b.CanBuildChannelListener<IReplyChannel> (pl))
522 return b.BuildChannelListener<IReplyChannel> (se.ListenUri, "", se.ListenUriMode, pl);
523 if (b.CanBuildChannelListener<IInputSessionChannel> (pl))
524 return b.BuildChannelListener<IInputSessionChannel> (se.ListenUri, "", se.ListenUriMode, pl);
525 if (b.CanBuildChannelListener<IInputChannel> (pl))
526 return b.BuildChannelListener<IInputChannel> (se.ListenUri, "", se.ListenUriMode, pl);
528 if (b.CanBuildChannelListener<IDuplexChannel> (pl))
529 return b.BuildChannelListener<IDuplexChannel> (se.ListenUri, "", se.ListenUriMode, pl);
530 if (b.CanBuildChannelListener<IDuplexSessionChannel> (pl))
531 return b.BuildChannelListener<IDuplexSessionChannel> (se.ListenUri, "", se.ListenUriMode, pl);
532 throw new InvalidOperationException ("None of the listener channel types is supported");
535 [MonoTODO]
536 protected override sealed void OnAbort ()
540 Action<TimeSpan> close_delegate;
541 Action<TimeSpan> open_delegate;
543 protected override sealed IAsyncResult OnBeginClose (
544 TimeSpan timeout, AsyncCallback callback, object state)
546 if (close_delegate != null)
547 close_delegate = new Action<TimeSpan> (OnClose);
548 return close_delegate.BeginInvoke (timeout, callback, state);
551 protected override sealed IAsyncResult OnBeginOpen (
552 TimeSpan timeout, AsyncCallback callback, object state)
554 if (open_delegate == null)
555 open_delegate = new Action<TimeSpan> (OnOpen);
556 return open_delegate.BeginInvoke (timeout, callback, state);
559 protected override void OnClose (TimeSpan timeout)
561 DateTime start = DateTime.Now;
562 ReleasePerformanceCounters ();
563 List<ChannelDispatcherBase> l = new List<ChannelDispatcherBase> (ChannelDispatchers);
564 foreach (ChannelDispatcherBase e in l) {
565 try {
566 TimeSpan ts = timeout - (DateTime.Now - start);
567 if (ts < TimeSpan.Zero)
568 e.Abort ();
569 else
570 e.Close (ts);
571 } catch (Exception ex) {
572 Console.WriteLine ("ServiceHostBase failed to close the channel dispatcher:");
573 Console.WriteLine (ex);
578 protected override sealed void OnOpen (TimeSpan timeout)
580 InitializeRuntime ();
581 DoOpen (timeout);
584 protected override void OnEndClose (IAsyncResult result)
586 if (close_delegate == null)
587 throw new InvalidOperationException ("Async close operation has not started");
588 close_delegate.EndInvoke (result);
591 protected override sealed void OnEndOpen (IAsyncResult result)
593 if (open_delegate == null)
594 throw new InvalidOperationException ("Aync open operation has not started");
595 open_delegate.EndInvoke (result);
598 protected override void OnOpened ()
602 [MonoTODO]
603 protected void ReleasePerformanceCounters ()
607 void IDisposable.Dispose ()
609 Close ();
613 class SyncMethodInvoker : IOperationInvoker
615 readonly MethodInfo _methodInfo;
616 public SyncMethodInvoker (MethodInfo methodInfo) {
617 _methodInfo = methodInfo;
620 #region IOperationInvoker Members
622 public bool IsSynchronous {
623 get { return true; }
626 public object [] AllocateParameters () {
627 return new object [_methodInfo.GetParameters ().Length];
630 public object Invoke (object instance, object [] parameters)
632 return _methodInfo.Invoke (instance, parameters);
635 public IAsyncResult InvokeBegin (object instance, object [] inputs, AsyncCallback callback, object state) {
636 throw new NotSupportedException ();
639 public object InvokeEnd (object instance, out object [] outputs, IAsyncResult result) {
640 throw new NotSupportedException ();
643 #endregion
646 class AsyncMethodInvoker : IOperationInvoker
648 readonly MethodInfo _beginMethodInfo, _endMethodInfo;
649 public AsyncMethodInvoker (MethodInfo beginMethodInfo, MethodInfo endMethodInfo) {
650 _beginMethodInfo = beginMethodInfo;
651 _endMethodInfo = endMethodInfo;
654 #region IOperationInvoker Members
656 public bool IsSynchronous {
657 get { return false; }
660 public object [] AllocateParameters () {
661 return new object [_beginMethodInfo.GetParameters ().Length - 2 + _endMethodInfo.GetParameters().Length-1];
664 public object Invoke (object instance, object [] parameters) {
665 throw new NotImplementedException ("Can't invoke async method synchronously");
666 //BUGBUG: need to differentiate between input and output parameters.
667 IAsyncResult asyncResult = InvokeBegin(instance, parameters, delegate(IAsyncResult ignore) { }, null);
668 asyncResult.AsyncWaitHandle.WaitOne();
669 return InvokeEnd(instance, out parameters, asyncResult);
672 public IAsyncResult InvokeBegin (object instance, object [] inputs, AsyncCallback callback, object state) {
673 if (inputs.Length + 2 != _beginMethodInfo.GetParameters ().Length)
674 throw new ArgumentException ("Wrong number of input parameters");
675 object [] fullargs = new object [_beginMethodInfo.GetParameters ().Length];
676 Array.Copy (inputs, fullargs, inputs.Length);
677 fullargs [inputs.Length] = callback;
678 fullargs [inputs.Length + 1] = state;
679 return (IAsyncResult) _beginMethodInfo.Invoke (instance, fullargs);
682 public object InvokeEnd (object instance, out object [] outputs, IAsyncResult asyncResult) {
683 outputs = new object [_endMethodInfo.GetParameters ().Length - 1];
684 object [] fullargs = new object [_endMethodInfo.GetParameters ().Length];
685 fullargs [outputs.Length] = asyncResult;
686 object result = _endMethodInfo.Invoke (instance, fullargs);
687 Array.Copy (fullargs, outputs, outputs.Length);
688 return result;
691 #endregion