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
)
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
.ArgCount
== 0)
75 methodFlags
|= MethodFlags
.NoArguments
;
77 if (AllTypesArePrimitive (call
.Args
))
78 methodFlags
|= MethodFlags
.PrimitiveArguments
;
80 if (infoArraySize
== 0)
81 methodFlags
|= MethodFlags
.ArgumentsInSimpleArray
;
83 methodFlags
|= MethodFlags
.ArgumentsInMultiArray
;
89 writer
.Write ((byte) (methodFlags
));
91 // FIXME: what are the following 3 bytes for?
92 writer
.Write ((byte) 0);
93 writer
.Write ((byte) 0);
94 writer
.Write ((byte) 0);
97 writer
.Write ((byte) BinaryTypeCode
.String
);
98 writer
.Write (call
.MethodName
);
101 writer
.Write ((byte) BinaryTypeCode
.String
);
102 writer
.Write (call
.TypeName
);
106 if ((methodFlags
& MethodFlags
.PrimitiveArguments
) > 0)
108 writer
.Write ((uint)call
.Args
.Length
);
109 for (int n
=0; n
<call
.ArgCount
; n
++)
111 object arg
= call
.GetArg(n
);
113 writer
.Write (BinaryCommon
.GetTypeCode (arg
.GetType()));
114 ObjectWriter
.WritePrimitiveValue (writer
, arg
);
117 writer
.Write ((byte)BinaryTypeCode
.Null
);
121 if ( infoArraySize
> 0)
123 object[] ainfo
= new object[infoArraySize
];
125 if ((methodFlags
& MethodFlags
.ArgumentsInMultiArray
) > 0) ainfo
[n
++] = call
.Args
;
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
);
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
)
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
;
158 if (resp
.Exception
!= null) {
159 returnTypeTag
= ReturnTypeTag
.Exception
| ReturnTypeTag
.Null
;
160 info
= new object[] {resp.Exception}
;
161 internalProperties
= MethodReturnDictionary
.InternalExceptionKeys
;
163 else if (resp
.ReturnValue
== null) {
164 returnTypeTag
= ReturnTypeTag
.Null
;
166 else if (IsMethodPrimitive(resp
.ReturnValue
.GetType())) {
167 returnTypeTag
= ReturnTypeTag
.PrimitiveType
;
170 returnTypeTag
= ReturnTypeTag
.ObjectType
;
176 MethodFlags contextFlag
;
177 MethodFlags formatFlag
;
179 if ((resp
.LogicalCallContext
!= null) && resp
.LogicalCallContext
.HasInfo
&& ((returnTypeTag
& ReturnTypeTag
.Exception
) == 0))
181 contextFlag
= MethodFlags
.IncludesLogicalCallContext
;
185 contextFlag
= MethodFlags
.ExcludeLogicalCallContext
;
187 if (resp
.Properties
.Count
> internalProperties
.Length
&& ((returnTypeTag
& ReturnTypeTag
.Exception
) == 0))
189 extraProperties
= GetExtraProperties (resp
.Properties
, internalProperties
);
193 if (resp
.OutArgCount
== 0)
194 formatFlag
= MethodFlags
.NoArguments
;
197 if (AllTypesArePrimitive (resp
.Args
))
198 formatFlag
= MethodFlags
.PrimitiveArguments
;
201 if (infoArrayLength
== 0)
202 formatFlag
= MethodFlags
.ArgumentsInSimpleArray
;
204 formatFlag
= MethodFlags
.ArgumentsInMultiArray
;
210 writer
.Write ((byte) (contextFlag
| formatFlag
));
211 writer
.Write ((byte) returnTypeTag
);
213 // FIXME: what are the following 2 bytes for?
214 writer
.Write ((byte) 0);
215 writer
.Write ((byte) 0);
219 if (returnTypeTag
== ReturnTypeTag
.PrimitiveType
)
221 writer
.Write (BinaryCommon
.GetTypeCode (resp
.ReturnValue
.GetType()));
222 ObjectWriter
.WritePrimitiveValue (writer
, resp
.ReturnValue
);
225 if (formatFlag
== MethodFlags
.PrimitiveArguments
)
227 writer
.Write ((uint)resp
.ArgCount
);
228 for (int n
=0; n
<resp
.ArgCount
; n
++)
230 object val
= resp
.GetArg(n
);
232 writer
.Write (BinaryCommon
.GetTypeCode (val
.GetType()));
233 ObjectWriter
.WritePrimitiveValue (writer
, val
);
236 writer
.Write ((byte)BinaryTypeCode
.Null
);
240 if (infoArrayLength
> 0)
242 object[] infoArray
= new object[infoArrayLength
];
245 if (formatFlag
== MethodFlags
.ArgumentsInMultiArray
)
246 infoArray
[n
++] = resp
.Args
;
248 if (returnTypeTag
== ReturnTypeTag
.ObjectType
)
249 infoArray
[n
++] = resp
.ReturnValue
;
251 if (contextFlag
== MethodFlags
.IncludesLogicalCallContext
)
252 infoArray
[n
++] = resp
.LogicalCallContext
;
254 if (extraProperties
!= null)
255 infoArray
[n
++] = extraProperties
;
259 else if ((formatFlag
& MethodFlags
.ArgumentsInSimpleArray
) > 0)
264 ObjectWriter objectWriter
= new ObjectWriter (surrogateSelector
, context
, assemblyFormat
);
265 objectWriter
.WriteObjectGraph (writer
, info
, headers
);
268 writer
.Write ((byte) BinaryElement
.End
);
271 public static object ReadMethodCall (BinaryReader reader
, bool hasHeaders
, HeaderHandler headerHandler
, BinaryFormatter formatter
)
273 BinaryElement elem
= (BinaryElement
)reader
.ReadByte(); // The element code
274 if (elem
!= BinaryElement
.MethodCall
) throw new SerializationException("Invalid format. Expected BinaryElement.MethodCall, found " + elem
);
276 MethodFlags flags
= (MethodFlags
) reader
.ReadByte();
278 // FIXME: find a meaning for those 3 bytes
283 if (((BinaryTypeCode
)reader
.ReadByte()) != BinaryTypeCode
.String
) throw new SerializationException ("Invalid format");
284 string methodName
= reader
.ReadString();
286 if (((BinaryTypeCode
)reader
.ReadByte()) != BinaryTypeCode
.String
) throw new SerializationException ("Invalid format");
287 string className
= reader
.ReadString();
289 bool hasContextInfo
= (flags
& MethodFlags
.IncludesLogicalCallContext
) > 0;
291 object[] arguments
= null;
292 object methodSignature
= null;
293 object callContext
= null;
294 object[] extraProperties
= null;
295 Header
[] headers
= null;
297 if ((flags
& MethodFlags
.PrimitiveArguments
) > 0)
299 uint count
= reader
.ReadUInt32();
300 arguments
= new object[count
];
301 for (int n
=0; n
<count
; n
++)
303 Type type
= BinaryCommon
.GetTypeFromCode (reader
.ReadByte());
304 arguments
[n
] = ObjectReader
.ReadPrimitiveTypeValue (reader
, type
);
308 if ((flags
& MethodFlags
.NeedsInfoArrayMask
) > 0)
310 ObjectReader objectReader
= new ObjectReader (formatter
);
313 objectReader
.ReadObjectGraph (reader
, hasHeaders
, out result
, out headers
);
314 object[] msgInfo
= (object[]) result
;
316 if ((flags
& MethodFlags
.ArgumentsInSimpleArray
) > 0) {
322 if ((flags
& MethodFlags
.ArgumentsInMultiArray
) > 0) {
323 if (msgInfo
.Length
> 1) arguments
= (object[]) msgInfo
[n
++];
324 else arguments
= new object[0];
327 if ((flags
& MethodFlags
.IncludesSignature
) > 0)
328 methodSignature
= msgInfo
[n
++];
330 if ((flags
& MethodFlags
.IncludesLogicalCallContext
) > 0)
331 callContext
= msgInfo
[n
++];
333 if (n
< msgInfo
.Length
)
334 extraProperties
= (object[]) msgInfo
[n
];
338 reader
.ReadByte (); // Reads the stream ender
341 if (arguments
== null) arguments
= new object[0];
344 if (headerHandler
!= null)
345 uri
= headerHandler(headers
) as string;
347 Header
[] methodInfo
= new Header
[6];
348 methodInfo
[0] = new Header("__MethodName", methodName
);
349 methodInfo
[1] = new Header("__MethodSignature", methodSignature
);
350 methodInfo
[2] = new Header("__TypeName", className
);
351 methodInfo
[3] = new Header("__Args", arguments
);
352 methodInfo
[4] = new Header("__CallContext", callContext
);
353 methodInfo
[5] = new Header("__Uri", uri
);
355 MethodCall call
= new MethodCall (methodInfo
);
357 if (extraProperties
!= null) {
358 foreach (DictionaryEntry entry
in extraProperties
)
359 call
.Properties
[(string)entry
.Key
] = entry
.Value
;
365 public static object ReadMethodResponse (BinaryReader reader
, bool hasHeaders
, HeaderHandler headerHandler
, IMethodCallMessage methodCallMessage
, BinaryFormatter formatter
)
367 BinaryElement elem
= (BinaryElement
)reader
.ReadByte(); // The element code
368 if (elem
!= BinaryElement
.MethodResponse
) throw new SerializationException("Invalid format. Expected BinaryElement.MethodResponse, found " + elem
);
370 MethodFlags flags
= (MethodFlags
) reader
.ReadByte ();
371 ReturnTypeTag typeTag
= (ReturnTypeTag
) reader
.ReadByte ();
372 bool hasContextInfo
= (flags
& MethodFlags
.IncludesLogicalCallContext
) > 0;
374 // FIXME: find a meaning for those 2 bytes
378 object returnValue
= null;
379 object[] outArgs
= null;
380 LogicalCallContext callContext
= null;
381 Exception exception
= null;
382 object[] extraProperties
= null;
383 Header
[] headers
= null;
385 if ((typeTag
& ReturnTypeTag
.PrimitiveType
) > 0)
387 Type type
= BinaryCommon
.GetTypeFromCode (reader
.ReadByte());
388 returnValue
= ObjectReader
.ReadPrimitiveTypeValue (reader
, type
);
391 if ((flags
& MethodFlags
.PrimitiveArguments
) > 0)
393 uint count
= reader
.ReadUInt32();
394 outArgs
= new object[count
];
395 for (int n
=0; n
<count
; n
++) {
396 Type type
= BinaryCommon
.GetTypeFromCode (reader
.ReadByte());
397 outArgs
[n
] = ObjectReader
.ReadPrimitiveTypeValue (reader
, type
);
401 if (hasContextInfo
|| (typeTag
& ReturnTypeTag
.ObjectType
) > 0 ||
402 (typeTag
& ReturnTypeTag
.Exception
) > 0 ||
403 (flags
& MethodFlags
.ArgumentsInSimpleArray
) > 0 ||
404 (flags
& MethodFlags
.ArgumentsInMultiArray
) > 0)
406 // There objects that need to be deserialized using an ObjectReader
408 ObjectReader objectReader
= new ObjectReader (formatter
);
410 objectReader
.ReadObjectGraph (reader
, hasHeaders
, out result
, out headers
);
411 object[] msgInfo
= (object[]) result
;
413 if ((typeTag
& ReturnTypeTag
.Exception
) > 0) {
414 exception
= (Exception
) msgInfo
[0];
416 else if ((flags
& MethodFlags
.NoArguments
) > 0 || (flags
& MethodFlags
.PrimitiveArguments
) > 0) {
418 if ((typeTag
& ReturnTypeTag
.ObjectType
) > 0) returnValue
= msgInfo
[n
++];
419 if (hasContextInfo
) callContext
= (LogicalCallContext
)msgInfo
[n
++];
420 if (n
< msgInfo
.Length
) extraProperties
= (object[]) msgInfo
[n
];
422 else if ((flags
& MethodFlags
.ArgumentsInSimpleArray
) > 0) {
427 outArgs
= (object[]) msgInfo
[n
++];
428 if ((typeTag
& ReturnTypeTag
.ObjectType
) > 0) returnValue
= msgInfo
[n
++];
429 if (hasContextInfo
) callContext
= (LogicalCallContext
)msgInfo
[n
++];
430 if (n
< msgInfo
.Length
) extraProperties
= (object[]) msgInfo
[n
];
434 reader
.ReadByte (); // Reads the stream ender
437 if (headerHandler
!= null)
438 headerHandler(headers
);
440 if (exception
!= null)
441 return new ReturnMessage (exception
, methodCallMessage
);
444 int argCount
= (outArgs
!=null) ? outArgs
.Length
: 0;
445 ReturnMessage result
= new ReturnMessage (returnValue
, outArgs
, argCount
, callContext
, methodCallMessage
);
447 if (extraProperties
!= null) {
448 foreach (DictionaryEntry entry
in extraProperties
)
449 result
.Properties
[(string)entry
.Key
] = entry
.Value
;
456 private static bool AllTypesArePrimitive(object[] objects
)
458 foreach (object ob
in objects
)
460 if (ob
!= null && !IsMethodPrimitive(ob
.GetType()))
466 // When serializing methods, string are considered primitive types
467 public static bool IsMethodPrimitive (Type type
)
469 return type
.IsPrimitive
|| type
== typeof(string) || type
== typeof (DateTime
) || type
== typeof (Decimal
);
472 static object[] GetExtraProperties (IDictionary properties
, string[] internalKeys
)
474 object[] extraProperties
= new object [properties
.Count
- internalKeys
.Length
];
477 IDictionaryEnumerator e
= properties
.GetEnumerator();
479 if (!IsInternalKey ((string) e
.Entry
.Key
, internalKeys
)) extraProperties
[n
++] = e
.Entry
;
481 return extraProperties
;
484 static bool IsInternalKey (string key
, string[] internalKeys
)
486 foreach (string ikey
in internalKeys
)
487 if (key
== ikey
) return true;