I recently got a question from a customer on how one can add custom headers to NETCF app. There is a lot of documentation on how to do this using OperationContextScope and IDispatchMessageInspector on the desktop side, but neither is supported on CF 3.5 itself.

The trick on NETCF is to use the Message class, and the Message.Headers.Add() method to add new custom headers to the outgoing message.

Below is an outline of the steps that you could use in conjunction with NetcfSvcUtil.exe that generates a proxy to talk to the service.

  1. Point netcfsvcutil.exe to the service address (http://localhost/service1.svc), and generate the proxy classes – CFClientBase.cs and Service1.cs
  2. Inside CFClientBase.cs, you see an implementation of the Request/Response handling and a CFContractSerializer implementation (CFContractSerializer inherits from XmlObjectSerializer, which is needed to serialize any data using WCF).
  3. You need to make 2 modifications now:
    • Change the CFContractSerializer (the serializer generated by NETCFSvcUtil), to move functionality from WriteObject to WriteObjectContent. The generated one throws an exception when WriteObjectContent was called.

          public override void WriteObject(System.Xml.XmlDictionaryWriter writer, object graph)
           {
               if (this.info.IsWrapped)
               {
                   this.serializer.Serialize(writer, graph);
               }
               else
               {
                   this.WriteObjectContent(writer, graph);
               }
           }

           public override void WriteObjectContent(System.Xml.XmlDictionaryWriter writer, object graph)
           {
                   System.IO.MemoryStream ms = new System.IO.MemoryStream();
                   System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings();
                   settings.OmitXmlDeclaration = true;
                   System.Xml.XmlWriter innerWriter = System.Xml.XmlDictionaryWriter.Create(ms, settings);
                   this.serializer.Serialize(innerWriter, graph);
                   innerWriter.Close();
                   ms.Position = 0;
                   System.Xml.XmlReader innerReader = System.Xml.XmlDictionaryReader.Create(ms);
                   innerReader.Read();
                   writer.WriteAttributes(innerReader, false);
                   if ((innerReader.IsEmptyElement == false))
                   {
                       innerReader.Read();
                       for (
                       ; ((innerReader.NodeType == System.Xml.XmlNodeType.EndElement)
                                   == false);
                       )
                       {
                           writer.WriteNode(innerReader, false);
                       }
                   }
                   innerReader.Close();
               }

    • In the Invoke<> function that invokes the method call functionality, added a new CFContractSerializerInfo for the type of “custom header” class (here I took a simple string, you can have a class instead), and create a new header from it.

              protected TRESPONSE Invoke<TREQUEST, TRESPONSE>(CFInvokeInfo info, TREQUEST request)

             {                …

               CFContractSerializerInfo customHeaderSerializerInfo = new CFContractSerializerInfo();
               customHeaderSerializerInfo .MessageContractType = typeof( string );
              customHeaderSerializerInfo .IsWrapped = info.RequestIsWrapped;
              customHeaderSerializerInfo .ExtraTypes = info.ExtraTypes;
              customHeaderSerializerInfo .UseEncoded = info.UseEncoded;
              msg.Headers.Add( System.ServiceModel.Channels.MessageHeader.CreateHeader( "myHeader", "
http://tempuri.org", "myData", this.GetContractSerializer( customHeaderSerializerInfo ) ) );

               return this.getResult<TRESPONSE>(this.getReply(msg), info);

              }