"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).
……
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.