5 // Atsushi Enomoto <atsushi@ximian.com>
7 // Copyright (C) 2005-2006 Novell, Inc. http://www.novell.com
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
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)
79 if (relatieUri
.IsAbsoluteUri
)
83 return new Uri (baseUri
, relatieUri
);
86 public ChannelDispatcherCollection ChannelDispatchers
{
87 get { return channel_dispatchers; }
90 public ServiceAuthorizationBehavior Authorization
{
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; }
109 public IExtensionCollection
<ServiceHostBase
> Extensions
{
111 if (extensions
== null)
112 extensions
= new ExtensionCollection
<ServiceHostBase
> (this);
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
,
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
,
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
);
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
);
180 foreach (ContractDescription cd
in ImplementedContracts
.Values
) {
181 type
= cd
.ContractType
.Assembly
.GetType (typeName
);
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
))
211 Type type
= PopulateType (typeName
);
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
))
221 if (cd
.ContractType
== type
||
222 cd
.ContractType
.IsSubclassOf (type
) ||
223 type
.IsInterface
&& cd
.ContractType
.GetInterface (type
.FullName
) == type
)
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
)
252 ServiceEndpoint se
= new ServiceEndpoint (cd
, binding
, address
);
253 se
.ListenUri
= listenUri
.IsAbsoluteUri
? listenUri
: new Uri (address
.Uri
, listenUri
);
254 Description
.Endpoints
.Add (se
);
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) {
270 HostElement host
= service
.Host
;
271 foreach (BaseAddressElement baseAddress
in host
.BaseAddresses
) {
272 this.base_addresses
.Add (new Uri (baseAddress
.BaseAddress
));
276 foreach (ServiceEndpointElement endpoint
in service
.Endpoints
) {
277 // FIXME: consider BindingName as well
278 ServiceEndpoint se
= AddServiceEndpoint (
280 ConfigUtil
.CreateBinding (endpoint
.Binding
, endpoint
.BindingConfiguration
),
281 endpoint
.Address
.ToString ());
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 ();
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)
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
)
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
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
);
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 ();
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
)
463 DispatchOperation o
=
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;
483 o
.Invoker
= new DefaultOperationInvoker (od
);
486 o
.Formatter
= BaseMessagesFormatter
.Create (od
);
488 if (o
.Action
== "*" && (o
.IsOneWay
|| o
.ReplyAction
== "*")) {
489 //Signature : Message (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
);
500 protected void LoadConfigurationSection (ServiceElement element
)
502 ServicesSection services
= ConfigUtil
.ServicesSection
;
505 void DoOpen (TimeSpan timeout
)
507 foreach (var cd
in ChannelDispatchers
) {
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");
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
) {
566 TimeSpan ts
= timeout
- (DateTime
.Now
- start
);
567 if (ts
< TimeSpan
.Zero
)
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 ();
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 ()
603 protected void ReleasePerformanceCounters ()
607 void IDisposable
.Dispose ()
613 class SyncMethodInvoker : IOperationInvoker
615 readonly MethodInfo _methodInfo;
616 public SyncMethodInvoker (MethodInfo methodInfo) {
617 _methodInfo = methodInfo;
620 #region IOperationInvoker Members
622 public bool IsSynchronous {
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 ();
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);