wsw
wsw

Reputation: 861

WCF cannot Serialization for Object (System.Byte[*] Type)

All

Basically my WCF Service needs to connect to a DCOM Server and obtain a struct containing a "VARIANT" field and then pass it to my WCF Service client.

This is my WCF Data Contract for this Struct obtained from native DCOM Service

[DataContract]
[ComVisible(true)]
[StructLayout(LayoutKind.Sequential)]
public struct tagProcReadAns
{
    [DataMember]
    [MarshalAs(UnmanagedType.Struct)]
    public object vItemValue;           //VARIANT: marshalled as object in C# 
    [DataMember]
    public ushort wQuality;
    [DataMember]
    public Int32 Error;}            

since i expected this data contract can be registered as COM object and marshal back to C++ code again, that's why i have the [ComVisible] and [MarshalAs] part

Anyway, the vItemValue is the VARIANT object i obtained successfully from DCOM Server, (i have verified the content). However, when i encountered exceptions when i tried to pass it over the WCF.

I expected that the data contained in vItemValue VARIANT is of "byte array type" since i saw that the VARIANT.vt value is "VTUI1|VT_ARRAY", so i modify the DataContract as following

[DataContract]
[KnownType(TypeOf(byte[])]  //new 
[ComVisible(true)]
[StructLayout(LayoutKind.Sequential)]
public struct tagProcReadAns
{
    [DataMember]
    [MarshalAs(UnmanagedType.Struct)]
    public object vItemValue;           //VARIANT: marshalled as object in C# 
    [DataMember]
    public ushort wQuality;
    [DataMember]
    public Int32 Error;} 

However, when i run the code again, i still ecountered exception, by adding the tracing, i have the following error messages

There was an error while trying to serialize parameter http://tempuri.org/:aryAns. The InnerException message was 'Type 'System.Byte[*]' with data contract name 'ArrayOfunsignedByte:http://schemas.microsoft.com/2003/10/Serialization/Arrays' is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.'.  Please see InnerException for more details.

It seems that the data type is not byte[] but byte[*] ?? Is there anything wrong i have done? and as the exception has suggested, i can use DataContractResolver to somehow solve this problem, can you suggest any solutions?

thank you

Upvotes: 2

Views: 2990

Answers (2)

wsw
wsw

Reputation: 861

I figure out how to avoid the serialization issue mentioned above..

Basically what i did before is directly assign vItemValue i obtained from C++ code and then pass it over WCF service for serialization.

Follow @Marc Gravell suggestion, i have figure out that the data contract should be defined as following as VARIANT with VT_ARRAY|VT_* type will by default marshalled to Managed code as Object with type System.Array (of byte) instead of System.Byte[] etc.

    [DataContract]
[KnownType(typeof(string))]    
[KnownType(typeof(byte))]
[KnownType(typeof(uint))]
[KnownType(typeof(UInt16))]
[KnownType(typeof(UInt64))]
[KnownType(typeof(ulong))]
[KnownType(typeof(System.Array))]  //here !!!!! for System.Byte[*]
[KnownType(typeof(byte[]))]
[ComVisible(true)]
[StructLayout(LayoutKind.Sequential)]
public struct tagProcReadAns
{
    [DataMember]
    [MarshalAs(UnmanagedType.Struct)]
    public object vItemValue;           //VARIANT type has to be carefully marshalled 
    [DataMember]
    public ushort wQuality;
    [DataMember]
    public Int32 Error;         
}

In addtion, when passing the VARIANT object obtained from C++ code and pass over WCF boundary, i did a

System.Array result = System.Array.CreateInstance(typeof(byte), ((System.Array)ans[i].vItemValue).Length);
((System.Array)ans[i].vItemValue).CopyTo(result, 0);

instead of directly assign result to the ans[i].vItemValue

by doing this, i am able to pass the object accross the WCF boundary...

thanks Marc for the suggestions!

Upvotes: 1

Marc Gravell
Marc Gravell

Reputation: 1062790

byte[*] (or more specifically any T[*]) is a 1-dimensional array that is not explicitly a vector (a vector is a special category of array, zero-based, 1-dimensional). Referencing the type for such is pretty hard, since it doesn't directly exist in C# as a language (you need to use Array to talk to a non-vector 1-dimensional array).

My advice: introduce a dedicated DTO here. Trying to use an existing object model for serialization is often a good starting point, but when it gets tricky: use a DTO. I would add the following, and map to this manually before going anywhere near WCF:

[DataContract]
public class TagAnswer // or whatever this is
{
    [DataMember]
    public byte[] Data {get;set;}
    [DataMember]
    public ushort Quality {get;set;}
    [DataMember]
    public Int32 ErrorCode {get;set;}
}

Your COM/struct is fine when talking to COM, but has no place whatsoever on a service boundary.

Upvotes: 0

Related Questions