"Object reference not set to an instance of an object” (NullReference Exception) was intermittently reported when executing a customized receiving pipeline when de-batch an incoming message

Problem:

========

“Object reference not set to an instance of an object” was intermittently reported when executing a customized receiving pipeline when de-batch an incoming message.

Analysis:

========

1.       The error was caused by the following first chance Access Violation.

 

(2fac.347c): Access violation - code c0000005 (first/second chance not available)

eax=1e0a7a40 ebx=1e09d858 ecx=00000000 edx=00078272 esi=1e09c998 edi=100d455c

eip=08e16ee5 esp=05daf880 ebp=05daf8c8 iopl=0         nv up ei ng nz na pe cy

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010287

08e16ee5 3909            cmp     dword ptr [ecx],ecx  ds:002b:00000000=????????

Microsoft_BizTalk_Pipeline_Components!Microsoft.BizTalk.Component.XmlDasmReader.get_MessageContext()

Microsoft_BizTalk_Pipeline_Components!Microsoft.BizTalk.Component.XmlDasmComp.GetNext2(Microsoft.BizTalk.Component.Interop.IPipelineContext)

Microsoft_BizTalk_Pipeline_Components!Microsoft.BizTalk.Component.XmlDasmComp.GetNext(Microsoft.BizTalk.Component.Interop.IPipelineContext)

Microsoft_BizTalk_Messaging!Microsoft.BizTalk.Internal.ComponentWrapper.GetNext(Microsoft.BizTalk.Component.Interop.IPipelineContext)

Microsoft_BizTalk_Pipeline!Microsoft.BizTalk.PipelineOM.DisassemblerComponent.GetNext(Microsoft.BizTalk.Component.Interop.IPipelineContext)

Microsoft_BizTalk_Pipeline!Microsoft.BizTalk.PipelineOM.DisassemblingParserStage.GetNext(Microsoft.BizTalk.PipelineOM.IBTMPipelineContext)

Microsoft_BizTalk_Pipeline!Microsoft.BizTalk.PipelineOM.ReceivePipeline.GetNext()

 

2.       The AV occurred because the field m_npStk of the object XmlDasmReader is NULL.

0:038> !u @eip

Normal JIT generated code

Microsoft.BizTalk.Component.XmlDasmReader.get_MessageContext()

Begin 08e16ee0, size 12

08e16ee0 8bc1            mov     eax,ecx

08e16ee2 8b4820          mov     ecx,dword ptr [eax+20h]

>>> 08e16ee5 3909            cmp     dword ptr [ecx],ecx            // Access Violation here since ecx is zero, ecx is coming from eax+20

08e16ee7 e844dbffff      call    Microsoft_BizTalk_Pipeline_Components!Microsoft.BizTalk.Component.NodeProcessorStack.get_Current()

08e16eec 8bd0            mov     edx,eax

08e16eee 8b4208          mov     eax,dword ptr [edx+8]

08e16ef1 c3              ret

 

0:038> !do @eax

Name: Microsoft.BizTalk.Component.XmlDasmReader

….

7933291c  40001e2       1c ...ections.ArrayList  0 instance 00000000 m_envelopes

080c923c  40001e3       20 ...odeProcessorStack  0 instance 00000000 m_npStk          // eax+20 is the field m_npStk(NodeProcessorStack) of the object Microsoft.BizTalk.Component.XmlDasmReader

793308ec  40001e4       24        System.String  0 instance 1e0aafd0 m_docType

7933291c  40001e5       28 ...ections.ArrayList  0 instance 00000000 propertyPromotionList

....

3.       The field m_npStk is set to NULL because the  XmlDasmReader.Close() method is called by the object method ReadOnlySeekableStream.Finalize() when it is Disposed by GC.

microsoft_biztalk_pipeline_components!Microsoft.BizTalk.Component.XmlDasmReader.Close()

microsoft_biztalk_streaming!Microsoft.BizTalk.Streaming.XmlTranslatorStream.Close()

microsoft_biztalk_streaming!Microsoft.BizTalk.Streaming.ReadOnlySeekableStream.Cleanup()

microsoft_biztalk_streaming!Microsoft.BizTalk.Streaming.ReadOnlySeekableStream.Finalize()

mscorwks!FastCallFinalize

mscorwks!MethodTable::CallFinalizer

mscorwks!SVR::CallFinalizer

mscorwks!SVR::DoOneFinalization

mscorwks!SVR::FinalizeAllObjects

mscorwks!SVR::GCHeap::FinalizerThreadWorker

 

4.       The object ReadOnlySeekableStream was disposed above was created by the  customized pipeline component MessageValidator used in the pipeline.

microsoft_biztalk_streaming!Microsoft.BizTalk.Streaming.ReadOnlySeekableStream..ctor(System.IO.Stream)

xxxx_xxxx_xxxx_xxxx!xxxx.xxxx.xxxx.xxxx.xxxxx (System.IO.Stream)

xxxx_xxxx_xxxx_xxxx!xxxx.xxxx.xxxx.xxxx.MessageValidator.Execute(Microsoft.BizTalk.Component.Interop.IPipelineContext, Microsoft.BizTalk.Message.Interop.IBaseMessage)

microsoft_biztalk_messaging!Microsoft.BizTalk.Internal.ComponentWrapper.Execute(Microsoft.BizTalk.Component.Interop.IPipelineContext, Microsoft.BizTalk.Message.Interop.IBaseMessage)

microsoft_biztalk_pipeline!Microsoft.BizTalk.PipelineOM.SimpleComponent.Execute(Microsoft.BizTalk.Component.Interop.IPipelineContext, Microsoft.BizTalk.Message.Interop.IBaseMessage)

microsoft_biztalk_pipeline!Microsoft.BizTalk.PipelineOM.SimpleStage.Execute(Microsoft.BizTalk.PipelineOM.IBTMPipelineContext, Microsoft.BizTalk.Message.Interop.IBaseMessage, Int32)

114af168 1c7f38c6 microsoft_biztalk_pipeline!Microsoft.BizTalk.PipelineOM.Pipeline.Execute(Int32, Int32, Microsoft.BizTalk.Message.Interop.IBaseMessage)

….

 

5.       The problem can be reproduced using a simple pipeline component  as the below (at Validate stage) + the default XML receiving component(at Disassemble stage) when  de-batch an incoming XML message.

….

        public IBaseMessage Execute(IPipelineContext pc, IBaseMessage inmsg)

        {

            IBaseMessagePart bodyPart = inmsg.BodyPart;

            if (bodyPart!=null)

            {

                                Stream originalStrm                        = bodyPart.GetOriginalDataStream();

                                Stream strm = null;

                                if (originalStrm != null)

                                {

                                                        strm             = new ReadOnlySeekableStream(originalStrm);

                                                        bodyPart.Data    = strm;

// pc.ResourceTracker.AddResource( strm ); // comments this line, the problem can be reproduced easily, the problem will be fixed by remaining the line.

                                    }

            }

            return inmsg;

        }

….

 

 

Solution:

==========

This is the coding  flaw in the developed pipeline component(MessageValidator in this case), please refer the description from the below document. (it is very good article for Streaming Pipeline component development).

https://download.microsoft.com/download/0/7/3/073B05EF-E629-4425-9851-295B98CDE699/DevelopingAStreamingPipelineComponent.docx

……

Before returning the message back to the pipeline in line 18, the StreamWriter object that was created is added to the context’s ResourceTracker (line 15). This ensures the .NET Garbage Collector does not dispose of this object and the stream it is using. If it did, the underlying stream would have been closed and disposed of also ll (the behaviour of the StreamWriter Dispose() method) and would not be available for other component’s or indeed BizTalk Server itself. Equally it also means that BizTalk Server will ensure disposing of this object once the processing of the message is complete.

….

To add the ResourceTracker.AddResource call in the pipeline component and let the BizTalk engine managing the lifetime of the new created Stream object for you.