2 // System.Runtime.Remoting.MessageFormatter.cs
4 // Author: Lluis Sanchez Gual (lluis@ideary.com)
6 // (C) 2003, Lluis Sanchez Gual
10 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System
.Reflection
;
35 using System
.Collections
;
36 using System
.Runtime
.Remoting
;
37 using System
.Runtime
.Serialization
;
38 using System
.Runtime
.Remoting
.Messaging
;
40 namespace System
.Runtime
.Serialization
.Formatters
.Binary
42 internal class MessageFormatter
44 public static void WriteMethodCall (BinaryWriter writer
, object obj
, Header
[] headers
, ISurrogateSelector surrogateSelector
, StreamingContext context
, FormatterAssemblyStyle assemblyFormat
, FormatterTypeStyle typeFormat
)
46 IMethodCallMessage call
= (IMethodCallMessage
)obj
;
47 writer
.Write ((byte) BinaryElement
.MethodCall
);
49 MethodFlags methodFlags
;
50 int infoArraySize
= 0;
52 object[] extraProperties
= null;
54 if (call
.LogicalCallContext
!= null && call
.LogicalCallContext
.HasInfo
)
56 methodFlags
= MethodFlags
.IncludesLogicalCallContext
;
60 methodFlags
= MethodFlags
.ExcludeLogicalCallContext
;
62 if (RemotingServices
.IsMethodOverloaded (call
))
65 methodFlags
|= MethodFlags
.IncludesSignature
;
68 if (call
.Properties
.Count
> MethodCallDictionary
.InternalKeys
.Length
)
70 extraProperties
= GetExtraProperties (call
.Properties
, MethodCallDictionary
.InternalKeys
);
74 if (call
.MethodBase
.IsGenericMethod
) {
76 methodFlags
|= MethodFlags
.GenericArguments
;
78 if (call
.ArgCount
== 0)
79 methodFlags
|= MethodFlags
.NoArguments
;
81 if (AllTypesArePrimitive (call
.Args
))
82 methodFlags
|= MethodFlags
.PrimitiveArguments
;
84 if (infoArraySize
== 0)
85 methodFlags
|= MethodFlags
.ArgumentsInSimpleArray
;
87 methodFlags
|= MethodFlags
.ArgumentsInMultiArray
;
93 writer
.Write ((int) methodFlags
);
96 writer
.Write ((byte) BinaryTypeCode
.String
);
97 writer
.Write (call
.MethodName
);
100 writer
.Write ((byte) BinaryTypeCode
.String
);
101 writer
.Write (call
.TypeName
);
105 if ((methodFlags
& MethodFlags
.PrimitiveArguments
) > 0)
107 writer
.Write ((uint)call
.Args
.Length
);
108 for (int n
=0; n
<call
.ArgCount
; n
++)
110 object arg
= call
.GetArg(n
);
112 writer
.Write (BinaryCommon
.GetTypeCode (arg
.GetType()));
113 ObjectWriter
.WritePrimitiveValue (writer
, arg
);
116 writer
.Write ((byte)BinaryTypeCode
.Null
);
120 if ( infoArraySize
> 0)
122 object[] ainfo
= new object[infoArraySize
];
124 if ((methodFlags
& MethodFlags
.ArgumentsInMultiArray
) > 0) ainfo
[n
++] = call
.Args
;
125 if ((methodFlags
& MethodFlags
.GenericArguments
) > 0) ainfo
[n
++] = call
.MethodBase
.GetGenericArguments ();
126 if ((methodFlags
& MethodFlags
.IncludesSignature
) > 0) ainfo
[n
++] = call
.MethodSignature
;
127 if ((methodFlags
& MethodFlags
.IncludesLogicalCallContext
) > 0) ainfo
[n
++] = call
.LogicalCallContext
;
128 if (extraProperties
!= null) ainfo
[n
++] = extraProperties
;
131 else if ((methodFlags
& MethodFlags
.ArgumentsInSimpleArray
) > 0)
136 ObjectWriter objectWriter
= new ObjectWriter (surrogateSelector
, context
, assemblyFormat
, typeFormat
);
137 objectWriter
.WriteObjectGraph (writer
, info
, headers
);
140 writer
.Write ((byte) BinaryElement
.End
);
143 public static void WriteMethodResponse (BinaryWriter writer
, object obj
, Header
[] headers
, ISurrogateSelector surrogateSelector
, StreamingContext context
, FormatterAssemblyStyle assemblyFormat
, FormatterTypeStyle typeFormat
)
145 IMethodReturnMessage resp
= (IMethodReturnMessage
)obj
;
146 writer
.Write ((byte) BinaryElement
.MethodResponse
);
148 string[] internalProperties
= MethodReturnDictionary
.InternalReturnKeys
;
150 int infoArrayLength
= 0;
152 object[] extraProperties
= null;
154 // Type of return value
156 ReturnTypeTag returnTypeTag
;
157 MethodFlags contextFlag
= MethodFlags
.ExcludeLogicalCallContext
;
159 if (resp
.Exception
!= null) {
160 returnTypeTag
= ReturnTypeTag
.Exception
| ReturnTypeTag
.Null
;
161 internalProperties
= MethodReturnDictionary
.InternalExceptionKeys
;
164 else if (resp
.ReturnValue
== null) {
165 returnTypeTag
= ReturnTypeTag
.Null
;
167 else if (IsMethodPrimitive(resp
.ReturnValue
.GetType())) {
168 returnTypeTag
= ReturnTypeTag
.PrimitiveType
;
171 returnTypeTag
= ReturnTypeTag
.ObjectType
;
177 MethodFlags formatFlag
;
179 if ((resp
.LogicalCallContext
!= null) && resp
.LogicalCallContext
.HasInfo
)
181 contextFlag
= MethodFlags
.IncludesLogicalCallContext
;
185 if (resp
.Properties
.Count
> internalProperties
.Length
&& ((returnTypeTag
& ReturnTypeTag
.Exception
) == 0))
187 extraProperties
= GetExtraProperties (resp
.Properties
, internalProperties
);
191 if (resp
.OutArgCount
== 0)
192 formatFlag
= MethodFlags
.NoArguments
;
195 if (AllTypesArePrimitive (resp
.Args
))
196 formatFlag
= MethodFlags
.PrimitiveArguments
;
199 if (infoArrayLength
== 0)
200 formatFlag
= MethodFlags
.ArgumentsInSimpleArray
;
202 formatFlag
= MethodFlags
.ArgumentsInMultiArray
;
208 writer
.Write ((byte) (contextFlag
| formatFlag
));
209 writer
.Write ((byte) returnTypeTag
);
211 // FIXME: what are the following 2 bytes for?
212 writer
.Write ((byte) 0);
213 writer
.Write ((byte) 0);
217 if (returnTypeTag
== ReturnTypeTag
.PrimitiveType
)
219 writer
.Write (BinaryCommon
.GetTypeCode (resp
.ReturnValue
.GetType()));
220 ObjectWriter
.WritePrimitiveValue (writer
, resp
.ReturnValue
);
223 if (formatFlag
== MethodFlags
.PrimitiveArguments
)
225 writer
.Write ((uint)resp
.ArgCount
);
226 for (int n
=0; n
<resp
.ArgCount
; n
++)
228 object val
= resp
.GetArg(n
);
230 writer
.Write (BinaryCommon
.GetTypeCode (val
.GetType()));
231 ObjectWriter
.WritePrimitiveValue (writer
, val
);
234 writer
.Write ((byte)BinaryTypeCode
.Null
);
238 if (infoArrayLength
> 0)
240 object[] infoArray
= new object[infoArrayLength
];
243 if ((returnTypeTag
& ReturnTypeTag
.Exception
) != 0)
244 infoArray
[n
++] = resp
.Exception
;
246 if (formatFlag
== MethodFlags
.ArgumentsInMultiArray
)
247 infoArray
[n
++] = resp
.Args
;
249 if (returnTypeTag
== ReturnTypeTag
.ObjectType
)
250 infoArray
[n
++] = resp
.ReturnValue
;
252 if (contextFlag
== MethodFlags
.IncludesLogicalCallContext
)
253 infoArray
[n
++] = resp
.LogicalCallContext
;
255 if (extraProperties
!= null)
256 infoArray
[n
++] = extraProperties
;
260 else if ((formatFlag
& MethodFlags
.ArgumentsInSimpleArray
) > 0)
265 ObjectWriter objectWriter
= new ObjectWriter (surrogateSelector
, context
, assemblyFormat
, typeFormat
);
266 objectWriter
.WriteObjectGraph (writer
, info
, headers
);
269 writer
.Write ((byte) BinaryElement
.End
);
272 public static object ReadMethodCall (BinaryReader reader
, bool hasHeaders
, HeaderHandler headerHandler
, BinaryFormatter formatter
)
274 BinaryElement elem
= (BinaryElement
)reader
.ReadByte(); // The element code
275 return ReadMethodCall (elem
, reader
, hasHeaders
, headerHandler
, formatter
);
278 public static object ReadMethodCall (BinaryElement elem
, BinaryReader reader
, bool hasHeaders
, HeaderHandler headerHandler
, BinaryFormatter formatter
)
280 if (elem
!= BinaryElement
.MethodCall
) throw new SerializationException("Invalid format. Expected BinaryElement.MethodCall, found " + elem
);
282 MethodFlags flags
= (MethodFlags
) reader
.ReadInt32();
284 if (((BinaryTypeCode
)reader
.ReadByte()) != BinaryTypeCode
.String
) throw new SerializationException ("Invalid format");
285 string methodName
= reader
.ReadString();
287 if (((BinaryTypeCode
)reader
.ReadByte()) != BinaryTypeCode
.String
) throw new SerializationException ("Invalid format");
288 string className
= reader
.ReadString();
290 //bool hasContextInfo = (flags & MethodFlags.IncludesLogicalCallContext) > 0;
292 object[] arguments
= null;
293 object methodSignature
= null;
294 object callContext
= null;
295 object[] extraProperties
= null;
296 Header
[] headers
= null;
297 Type
[] genericArguments
= null;
299 if ((flags
& MethodFlags
.PrimitiveArguments
) > 0)
301 uint count
= reader
.ReadUInt32();
302 arguments
= new object[count
];
303 for (int n
=0; n
<count
; n
++)
305 Type type
= BinaryCommon
.GetTypeFromCode (reader
.ReadByte());
306 arguments
[n
] = ObjectReader
.ReadPrimitiveTypeValue (reader
, type
);
310 if ((flags
& MethodFlags
.NeedsInfoArrayMask
) > 0)
312 ObjectReader objectReader
= new ObjectReader (formatter
);
315 objectReader
.ReadObjectGraph (reader
, hasHeaders
, out result
, out headers
);
316 object[] msgInfo
= (object[]) result
;
318 if ((flags
& MethodFlags
.ArgumentsInSimpleArray
) > 0) {
324 if ((flags
& MethodFlags
.ArgumentsInMultiArray
) > 0) {
325 if (msgInfo
.Length
> 1) arguments
= (object[]) msgInfo
[n
++];
326 else arguments
= new object[0];
329 if ((flags
& MethodFlags
.GenericArguments
) > 0)
330 genericArguments
= (Type
[]) msgInfo
[n
++];
332 if ((flags
& MethodFlags
.IncludesSignature
) > 0)
333 methodSignature
= msgInfo
[n
++];
335 if ((flags
& MethodFlags
.IncludesLogicalCallContext
) > 0)
336 callContext
= msgInfo
[n
++];
338 if (n
< msgInfo
.Length
)
339 extraProperties
= (object[]) msgInfo
[n
];
343 reader
.ReadByte (); // Reads the stream ender
346 if (arguments
== null) arguments
= new object[0];
349 if (headerHandler
!= null)
350 uri
= headerHandler(headers
) as string;
352 Header
[] methodInfo
= new Header
[7];
353 methodInfo
[0] = new Header("__MethodName", methodName
);
354 methodInfo
[1] = new Header("__MethodSignature", methodSignature
);
355 methodInfo
[2] = new Header("__TypeName", className
);
356 methodInfo
[3] = new Header("__Args", arguments
);
357 methodInfo
[4] = new Header("__CallContext", callContext
);
358 methodInfo
[5] = new Header("__Uri", uri
);
359 methodInfo
[6] = new Header("__GenericArguments", genericArguments
);
361 MethodCall call
= new MethodCall (methodInfo
);
363 if (extraProperties
!= null) {
364 foreach (DictionaryEntry entry
in extraProperties
)
365 call
.Properties
[(string)entry
.Key
] = entry
.Value
;
371 public static object ReadMethodResponse (BinaryReader reader
, bool hasHeaders
, HeaderHandler headerHandler
, IMethodCallMessage methodCallMessage
, BinaryFormatter formatter
)
373 BinaryElement elem
= (BinaryElement
) reader
.ReadByte ();
374 return ReadMethodResponse (elem
, reader
, hasHeaders
, headerHandler
, methodCallMessage
, formatter
);
377 public static object ReadMethodResponse (BinaryElement elem
, BinaryReader reader
, bool hasHeaders
, HeaderHandler headerHandler
, IMethodCallMessage methodCallMessage
, BinaryFormatter formatter
)
379 if (elem
!= BinaryElement
.MethodResponse
) throw new SerializationException("Invalid format. Expected BinaryElement.MethodResponse, found " + elem
);
381 MethodFlags flags
= (MethodFlags
) reader
.ReadByte ();
382 ReturnTypeTag typeTag
= (ReturnTypeTag
) reader
.ReadByte ();
383 bool hasContextInfo
= (flags
& MethodFlags
.IncludesLogicalCallContext
) > 0;
385 // FIXME: find a meaning for those 2 bytes
389 object returnValue
= null;
390 object[] outArgs
= null;
391 LogicalCallContext callContext
= null;
392 Exception exception
= null;
393 object[] extraProperties
= null;
394 Header
[] headers
= null;
396 if ((typeTag
& ReturnTypeTag
.PrimitiveType
) > 0)
398 Type type
= BinaryCommon
.GetTypeFromCode (reader
.ReadByte());
399 returnValue
= ObjectReader
.ReadPrimitiveTypeValue (reader
, type
);
402 if ((flags
& MethodFlags
.PrimitiveArguments
) > 0)
404 uint count
= reader
.ReadUInt32();
405 outArgs
= new object[count
];
406 for (int n
=0; n
<count
; n
++) {
407 Type type
= BinaryCommon
.GetTypeFromCode (reader
.ReadByte());
408 outArgs
[n
] = ObjectReader
.ReadPrimitiveTypeValue (reader
, type
);
412 if (hasContextInfo
|| (typeTag
& ReturnTypeTag
.ObjectType
) > 0 ||
413 (typeTag
& ReturnTypeTag
.Exception
) > 0 ||
414 (flags
& MethodFlags
.ArgumentsInSimpleArray
) > 0 ||
415 (flags
& MethodFlags
.ArgumentsInMultiArray
) > 0)
417 // There objects that need to be deserialized using an ObjectReader
419 ObjectReader objectReader
= new ObjectReader (formatter
);
421 objectReader
.ReadObjectGraph (reader
, hasHeaders
, out result
, out headers
);
422 object[] msgInfo
= (object[]) result
;
424 if ((typeTag
& ReturnTypeTag
.Exception
) > 0) {
425 exception
= (Exception
) msgInfo
[0];
426 if (hasContextInfo
) callContext
= (LogicalCallContext
)msgInfo
[1];
428 else if ((flags
& MethodFlags
.NoArguments
) > 0 || (flags
& MethodFlags
.PrimitiveArguments
) > 0) {
430 if ((typeTag
& ReturnTypeTag
.ObjectType
) > 0) returnValue
= msgInfo
[n
++];
431 if (hasContextInfo
) callContext
= (LogicalCallContext
)msgInfo
[n
++];
432 if (n
< msgInfo
.Length
) extraProperties
= (object[]) msgInfo
[n
];
434 else if ((flags
& MethodFlags
.ArgumentsInSimpleArray
) > 0) {
439 outArgs
= (object[]) msgInfo
[n
++];
440 if ((typeTag
& ReturnTypeTag
.ObjectType
) > 0) returnValue
= msgInfo
[n
++];
441 if (hasContextInfo
) callContext
= (LogicalCallContext
)msgInfo
[n
++];
442 if (n
< msgInfo
.Length
) extraProperties
= (object[]) msgInfo
[n
];
446 reader
.ReadByte (); // Reads the stream ender
449 if (headerHandler
!= null)
450 headerHandler(headers
);
452 if (exception
!= null)
453 return new ReturnMessage (exception
, methodCallMessage
);
456 int argCount
= (outArgs
!=null) ? outArgs
.Length
: 0;
457 ReturnMessage result
= new ReturnMessage (returnValue
, outArgs
, argCount
, callContext
, methodCallMessage
);
459 if (extraProperties
!= null) {
460 foreach (DictionaryEntry entry
in extraProperties
)
461 result
.Properties
[(string)entry
.Key
] = entry
.Value
;
468 private static bool AllTypesArePrimitive(object[] objects
)
470 foreach (object ob
in objects
)
472 if (ob
!= null && !IsMethodPrimitive(ob
.GetType()))
478 // When serializing methods, string are considered primitive types
479 public static bool IsMethodPrimitive (Type type
)
481 return type
.IsPrimitive
|| type
== typeof(string) || type
== typeof (DateTime
) || type
== typeof (Decimal
);
484 static object[] GetExtraProperties (IDictionary properties
, string[] internalKeys
)
486 object[] extraProperties
= new object [properties
.Count
- internalKeys
.Length
];
489 IDictionaryEnumerator e
= properties
.GetEnumerator();
491 if (!IsInternalKey ((string) e
.Entry
.Key
, internalKeys
)) extraProperties
[n
++] = e
.Entry
;
493 return extraProperties
;
496 static bool IsInternalKey (string key
, string[] internalKeys
)
498 foreach (string ikey
in internalKeys
)
499 if (key
== ikey
) return true;