Custom Disassembler

Tech-Archive recommends: Speed Up your PC by fixing your registry

From: Sam Loveridge (sloveridge_at_adelaidebank.com.au)
Date: 01/03/05

  • Next message: Bob Corcoran: "Re: choosing which map to use"
    Date: Tue, 4 Jan 2005 07:30:51 +1030
    
    

    Hi. I'm trying unsuccessfully to implement a custom disassembler. The
    inbound message, and desired message formats are as follows:

    Inbound:
    FieldName:Value
    FieldName:Value
    FieldName:Value

    Required Disassembled Format:
    <message>
      <fieldName>Value</fieldName>
      <fieldName>Value</fieldName>
      <fieldName>Value</fieldName>
    </message>

    When the inbound message is placed in the receive location it is picked up
    and then can be seen in HAT as suspended. When saving the message from HAT
    it can be seen in the original format, so I suspect the disassembler is
    either not functioning correctly, or not fired at all. Looking in the event
    viewer the following error can be seen:

    "The data at the root level is invalid. Line 1, position 1."

    I have read that this may be due to seekable streams and have tried various
    suggestions that I've read on various posts but with no success, so I've
    decided to post the original code to my disassembler below. Any help is
    greatly appreciated. The code I am using in my disassembler is as follows
    (apologies for the large post, please cut code below from replies to reduce
    bloated messages in thread):

    <code>

    Imports Microsoft.Biztalk.Component.Interop
    Imports Microsoft.Biztalk.Message.Interop
    Imports System
    Imports System.Collections
    Imports System.Diagnostics
    Imports System.IO
    Imports System.Text
    Imports System.Xml

    <ComponentCategory(CategoryTypes.CATID_PipelineComponent), _
    ComponentCategory(CategoryTypes.CATID_DisassemblingParser), _
    System.Runtime.InteropServices.Guid("B60F6801-C07E-45f7-A9C7-E8797671D710")>
    _
    Public Class MyDisassembler
      Implements IBaseComponent, _
                  IComponent, _
                  IComponentUI, _
                  IPersistPropertyBag, _
                  IDisassemblerComponent

      ' Constant pipeline component values:
      Private Const ComponentName As String = "MyDisassembler"
      Private Const ComponentDescription As String = "My Custom Receive Pipeline
    Disassembler."
      Private Const ComponentVersion As String = "1.0.0.0"

      ' Private Constant Declarations
      Private Const NameValueSeparator As String = ":"
      Private Const RootMessageElementName As String = "message"

      ' Private Variable Declarations
      Private _nameValues As Hashtable
      Private _originalMsgContext As IBaseMessageContext ' Original's message
    context.

      Private _outputFile As FileStream

      Public Sub New()

        ' Create a trace listener for the event log.
        Dim myTraceListener As New EventLogTraceListener("MyDisassemblerTrace")

        ' Add the event log trace listener to the collection.
        Trace.Listeners.Add(myTraceListener)

      End Sub

    #Region "Microsoft.Biztalk.Component.Interop.IBaseComponent Implementation"

      Public ReadOnly Property Description() As String _
      Implements Microsoft.Biztalk.Component.Interop.IBaseComponent.Description
        Get
          Return ComponentDescription
        End Get
      End Property

      Public ReadOnly Property Name() As String _
      Implements Microsoft.Biztalk.Component.Interop.IBaseComponent.Name
        Get
          Return ComponentName
        End Get
      End Property

      Public ReadOnly Property Version() As String _
      Implements Microsoft.Biztalk.Component.Interop.IBaseComponent.Version
        Get
          Return ComponentVersion
        End Get
      End Property

    #End Region

    #Region "Microsoft.Biztalk.Component.Interop.IComponent Implementation"

      Public Function Execute( _
                  ByVal pContext As
    Microsoft.Biztalk.Component.Interop.IPipelineContext, _
                  ByVal pInMsg As
    Microsoft.Biztalk.Message.Interop.IBaseMessage) As
    Microsoft.Biztalk.Message.Interop.IBaseMessage _
      Implements Microsoft.Biztalk.Component.Interop.IComponent.Execute
        '
      End Function

    #End Region

    #Region "Microsoft.Biztalk.Component.Interop.IComponentUI Implementation"

      Public ReadOnly Property Icon() As System.IntPtr _
      Implements Microsoft.Biztalk.Component.Interop.IComponentUI.Icon
        Get
          ' No icon associated with this pipeline component:
          Return IntPtr.Zero
        End Get
      End Property

      Public Function Validate(ByVal projectSystem As Object) As
    System.Collections.IEnumerator _
      Implements Microsoft.Biztalk.Component.Interop.IComponentUI.Validate

        Dim validationResult As System.Collections.Specialized.StringCollection
    = New System.Collections.Specialized.StringCollection
        Return DirectCast(validationResult.GetEnumerator(), IEnumerator)

      End Function

    #End Region

    #Region "Microsoft.Biztalk.Component.Interop.IPersistPropertyBag
    Implementation"

      Public Sub GetClassID(ByRef classID As System.Guid) _
      Implements
    Microsoft.Biztalk.Component.Interop.IPersistPropertyBag.GetClassID
        classID = Me.GetType().GUID
      End Sub

      Public Sub InitNew() _
      Implements Microsoft.Biztalk.Component.Interop.IPersistPropertyBag.InitNew
        '
      End Sub

      Public Sub Load(ByVal propertyBag As
    Microsoft.Biztalk.Component.Interop.IPropertyBag, ByVal errorLog As Integer)
    _
      Implements Microsoft.Biztalk.Component.Interop.IPersistPropertyBag.Load
        '
      End Sub

      Public Sub Save(ByVal propertyBag As
    Microsoft.Biztalk.Component.Interop.IPropertyBag, ByVal clearDirty As
    Boolean, ByVal saveAllProperties As Boolean) _
      Implements Microsoft.Biztalk.Component.Interop.IPersistPropertyBag.Save
        '
      End Sub

    #End Region

    #Region "Microsoft.Biztalk.Component.Interop.IDisassemblerComponent
    Implementation"

      Public Sub Disassemble(ByVal pContext As
    Microsoft.Biztalk.Component.Interop.IPipelineContext, ByVal pInMsg As
    Microsoft.Biztalk.Message.Interop.IBaseMessage) _
      Implements
    Microsoft.Biztalk.Component.Interop.IDisassemblerComponent.Disassemble

        Trace.WriteLine(String.Format("{0} : Entering Disassemble",
    DateTime.Now().ToLongTimeString()))

        Dim outputMessage As XmlDocument = Nothing

        ' Initialise the field name value collection
        _nameValues = New Hashtable

        Trace.WriteLine(String.Format("{0} : pInMsg.PartCount = {1}",
    DateTime.Now().ToLongTimeString(), pInMsg.PartCount))

        ' Consider this document only if it has parts
        If (pInMsg.PartCount >= 1) Then

          Dim bodyPart As IBaseMessagePart = pInMsg.BodyPart

          Trace.WriteLine(String.Format("{0} : bodyPart Is Nothing? {1}",
    DateTime.Now().ToLongTimeString(), (bodyPart Is Nothing).ToString()))

          ' Clone the message context so we can associate it with the new
    message later
          _originalMsgContext = PipelineUtil.CloneMessageContext(pInMsg.Context)

          ' Consider this only if this is not null
          If (Not bodyPart Is Nothing) Then

            Trace.WriteLine(String.Format("{0} : About to reference bodyPart
    Stream ...", DateTime.Now().ToLongTimeString()))

            Dim docStream As Stream = bodyPart.GetOriginalDataStream()
            Dim lineRead As String = Nothing
            Dim lineNum As Integer = 1

            ' We got the original data stream (not a copy) so rewind the stream
            docStream.Seek(0, SeekOrigin.Begin)

            Trace.WriteLine(String.Format("{0} : Creating StreamReader from
    bodyPart stream to read each line in input data",
    DateTime.Now().ToLongTimeString()))

            ' Use a stream reader to facilitate reading line by line
            Dim streamReader As New streamReader(docStream)

            ' Parse all tables and all values in betwen them
            lineRead = streamReader.ReadLine()
            While (Not lineRead Is Nothing)

              Trace.WriteLine(String.Format("{0} : lineRead = '{1}'",
    DateTime.Now().ToLongTimeString(), lineRead))

              ' TODO - Do we need to associate schema here?
              ' If so then we can determine which schema by HMT:150, HMV:001

              ' Make sure we don't process an empty line
              If lineRead.Trim().Length > 0 Then
                ' Process the field definition and value

                ' Make sure the name/value separator exists in line
                If (lineRead.IndexOf(NameValueSeparator) > 0) Then
                  Try
                    ' Read name & value details from line
                    Dim nameValue() As String =
    lineRead.Split(NameValueSeparator.ToCharArray())
                    Dim fieldName As String = nameValue(0).Trim().ToLower()
                    Dim value As String = nameValue(1).Trim()

                    Trace.WriteLine(String.Format("{0} : Adding data to
    collection with :: fieldName='{1}' and value='{2}'",
    DateTime.Now().ToLongTimeString(), fieldName, value))

                    ' Add the field to our collection
                    _nameValues.Add(fieldName, value)

                  Catch ex As Exception
                    ' Invalid line - ignore
                    ' (Invalid messages will be picked up in the validate
                    ' stage of the receive pipeline)
                  End Try
                End If
              End If

              ' Get the next line
              lineRead = streamReader.ReadLine()
              lineNum += 1
            End While
          End If
        End If

        Trace.WriteLine(String.Format("{0} : Exiting Disassemble",
    DateTime.Now().ToLongTimeString()))

      End Sub

      Public Function GetNext(ByVal pContext As
    Microsoft.Biztalk.Component.Interop.IPipelineContext) As
    Microsoft.Biztalk.Message.Interop.IBaseMessage _
      Implements
    Microsoft.Biztalk.Component.Interop.IDisassemblerComponent.GetNext

        Trace.WriteLine(String.Format("{0} : Entering GetNext",
    DateTime.Now().ToLongTimeString()))

        Dim newMsg As IBaseMessage = Nothing

        ' We have something to return
        If ((Not _nameValues Is Nothing) AndAlso (_nameValues.Count > 0)) Then
          ' Creates a new message
          newMsg = pContext.GetMessageFactory().CreateMessage()

          ' Prepare the message: We combine all tables into one XML document
          ' like the following:
          ' <message>
          ' <name>value</name>
          ' [...]
          ' </message>

          ' Get a new stream so we can prepare the XML and connect it to an
    XMLTextWriter
          Dim outputStream As New MemoryStream
          Dim xmlWriter As New XmlTextWriter(outputStream, Encoding.UTF8)

          ' Apply the appropriate formatting and start the XML document
          xmlWriter.Formatting = Formatting.Indented
          xmlWriter.WriteStartDocument(True)

          ' Write the root element
          xmlWriter.WriteStartElement(String.Empty, RootMessageElementName,
    String.Empty)

          ' Emit all name/value pairs
          For Each key As Object In _nameValues.Keys
            xmlWriter.WriteElementString(key.ToString(),
    _nameValues(key).ToString())
          Next

          ' Close the document
          xmlWriter.WriteEndElement()
          xmlWriter.WriteEndDocument()
          xmlWriter.Flush()

          ' Rewind the stream so it is ready for use by the messaging engine
          outputStream.Seek(0, SeekOrigin.Begin)

          ' Add one part to it. This part will be the "body"
          Dim newMsgPart As IBaseMessagePart =
    pContext.GetMessageFactory().CreateMessagePart()
          newMsgPart.Charset = "UTF-8"
          newMsgPart.ContentType = "text/xml"
          newMsgPart.Data = outputStream

          newMsg.AddPart("body", newMsgPart, True)

          ' Copy the original message context to the newly created message
          For iProp As Integer = 0 To
    (Convert.ToInt32(_originalMsgContext.CountProperties) - 1)
            Dim strName As String
            Dim strNSpace As String

            Dim val As Object = _originalMsgContext.ReadAt(iProp, strName,
    strNSpace)

            ' If the property has been promoted, respect the settings
            If (_originalMsgContext.IsPromoted(strName, strNSpace)) Then
              newMsg.Context.Promote(strName, strNSpace, val)
            Else
              newMsg.Context.Write(strName, strNSpace, val)
            End If
          Next

          ' There is a new stream so let's make it visible to the
    resourceTracker
          pContext.ResourceTracker.AddResource(outputStream)

          ' We returned this message so drop our internal table representation
          _nameValues = Nothing
        End If

        Trace.WriteLine(String.Format("{0} : newMsg Is Nothing? {1}",
    DateTime.Now().ToLongTimeString(), (newMsg Is Nothing).ToString()))
        Trace.WriteLine(String.Format("{0} : Exiting GetNext",
    DateTime.Now().ToLongTimeString()))

        Return newMsg

      End Function

    #End Region

    End Class

    </code>


  • Next message: Bob Corcoran: "Re: choosing which map to use"

    Relevant Pages

    • Re: Events raised by collections
      ... Private _deviceCollection As DeviceCollection ... StatusChangedHandler ... Public Sub AddDevice ... Public ReadOnly Property IsReadOnly() As Boolean Implements ...
      (microsoft.public.dotnet.languages.vb)
    • Re: Build Numbers in ASPX Pages
      ... Public Sub New ... Private m_assembly As Reflection.Assembly ... Public ReadOnly Property As Reflection.Assembly ... executing assembly (usually, the DLL). ...
      (microsoft.public.dotnet.framework.aspnet)
    • Re: Custom Windows Authentication Principal?
      ... > Private m_RolesAs String ... > Public ReadOnly Property UserName() As String ... > Public Sub WindowsAuthentication_OnAuthenticate(ByVal sender As Object, ...
      (microsoft.public.dotnet.framework.aspnet.security)
    • Re: GDI+ dll
      ... > Declare Auto Function QueryPerformanceFrequency Lib "Kernel32" (ByRef ... > Private myStartValue As Long = 0 ... > Public Sub Start ... > Public ReadOnly Property DurationInMilliSeconds() As Long ...
      (microsoft.public.de.german.entwickler.dotnet.vb)
    • extra properties exposed in WSDL to client
      ... Public Sub New ... Private pDistributorNumber As Integer ... Private pPricelist As String ... Public Property ArticleNumbers() As Integer ...
      (microsoft.public.dotnet.framework.webservices)