none
Reading MSMQ string messages in VB.Net RRS feed

  • Question

  • I have an venerable ActiveX Script that I have used very successfully in DTS to read messages from MSMQ and writes them to a text file. 

    I am having great diffilculty converting it to a VB.Net Task.

    Both scripts are shown below, the ActiveX Script works but the VB.Net yields the error "Data at the root level is invalid. Line 1, position 1."

    I suspect that the problem is that the message is just a plain string message and I don't understand which Formatter to use and how to use it OR how to bypass the whole Formatter feature.

    Any suggestions would be much appreciated.

    Active X Script

    Option Explicit
    Dim sSrceQueueName, sDestFilePathName, iMaxMsgCount, iMsgSize
    
    '*****Comment next line for DTS
    'Main()
    
    Function Main()
    
    	Const MQ_RECEIVE_ACCESS = 1
    	Const MQ_DENY_NONE = 0
    	Const ForWriting = 2
    	Const MQ_SINGLE_MESSAGE = 3
    	Dim q, qi, qm, fso, oTxt, lTxtLen, iMsgCount, lMsgSize, oArgs
    
    '+++++Comment the following 3 lines for VBS
    	sSrceQueueName = DTSGlobalVariables("SrceQueueName").Value
    	sDestFilePathName = DTSGlobalVariables("DestFilePathName").Value
    	iMaxMsgCount = CInt(DTSGlobalVariables("MaxMsgCount").Value)
    
    '*****Comment the following 4 lines for DTS
    	'Set oArgs = WScript.Arguments
    	'SrceQueueName = oArgs(0)
    	'sDestFilePathName = oArgs(1)
    	'iMaxMsgCount = oArgs(2)
    
    	Set q = CreateObject("MSMQ.MSMQQueue")
    	Set qi = CreateObject("MSMQ.MSMQQueueInfo")
    	Set qm = CreateObject("MSMQ.MSMQMessage")
    	Set fso = CreateObject("Scripting.FileSystemObject")
    
    	iMsgCount = 0
    	iMsgSize = 0
    
    	Set oTxt = fso.OpenTextFile(sDestFilePathName, ForWriting, True)
    
    	qi.FormatName = "Direct=OS:.\Private$\" & sSrceQueueName
    	Set q = qi.Open(MQ_RECEIVE_ACCESS, MQ_DENY_NONE)
    	Set qm = q.Receive(MQ_SINGLE_MESSAGE, False, True, 5, False)
    	While Not (qm Is Nothing) and (iMsgCount) < iMaxMsgCount
    		oTxt.WriteLine(qm.body)
    		iMsgSize = iMsgSize + qm.BodyLength
    		iMsgCount = iMsgCount + 1
    		If iMsgCount < iMaxMsgCount Then
    			Set qm = q.Receive(3, False, True, 5, False)
    		End If
    	Wend
    '+++++Comment the next 5 lines for VBS
    	oTxt.Close
    	Set fso = Nothing
    	q.Close
    	Set q = Nothing
    	Set qi = Nothing
    
    '+++++Comment next 5 lines for VBS
    	If iMsgSize > 0 Then
    		Main = DTSTaskExecResult_Success
    	Else
    		Main = DTSTaskExecResult_Failure
    	End If
    End Function

    VB.Net

        Public Sub Main()
            'Dim QueueName As String = Dts.Variables("QueueName").Value
            Dim QueueName As String = "FORMATNAME:DIRECT=OS:.\Private$\" & Dts.Variables("QueueName").Value
            Dim iMaxLoopCount As Integer = Dts.Variables("MaxMsgCount").Value
            Dim iLoopCount As Integer
            Dim q As MessageQueue
            Dim qm As Message
            Dim msgType(0) As Type
    
            iLoopCount = 0
            msgType(0) = GetType(String)
    
            q = New MessageQueue(QueueName, QueueAccessMode.Receive)
            qm = New Message
            Dim qenum As MessageEnumerator = q.GetMessageEnumerator2
    
            While (qenum.MoveNext) And (iLoopCount < iMaxLoopCount)
                qm = q.Receive(3)
                qm.Formatter = New XmlMessageFormatter(msgType)
                Try
                    MsgBox(CStr(qm.Body))
                Catch e As Exception
                    InputBox("ERROR", "ERROR", e.Message)
                End Try
                iLoopCount += 1
            End While
            Dts.TaskResult = ScriptResults.Success
        End Sub
    
    End Class


    R Campbell

    Thursday, August 6, 2015 6:38 AM

Answers

  • Follow the below link

    http://blogs.msdn.com/b/biztalknotes/archive/2013/12/30/how-to-send-and-receive-messages-in-msmq-using-c-vb.aspx


    Please Dont forget to mark as answer. It helps others to find relevant posts to the same question. Milan Das

    Thursday, August 6, 2015 7:23 AM
  • We I seem to have answered my own question, eventually. It seems that while the Message.Body() method was the right one to use for plain string messages in ActiveX scripting, this is no longer the case in VB.Net, because that now has serialization built in. They way to bypass that seems to be to use Message.BodyStream() and BinaryReader(). After a few dramas with BinaryReader() Encoding, it didn't take long to get things working. The good thing is that the script can pull a thousand messsages from queue, into a file in, less than a second. The rest should be straight forward DataFllow stuff.

    By the way, I can't seem to mark this as the answer, perhaps you can't do that for your own post :-).

        Public Sub Main()
            Dim QueueName As String = "FORMATNAME:DIRECT=OS:.\Private$\" & Dts.Variables("QueueName").Value
            Dim br As BinaryReader
            Dim sMessage As String
            Dim sFile As String
            Dim iMaxLoopCount As Integer = Dts.Variables("MaxMsgCount").Value
            Dim iLoopCount As Integer
            Dim q As MessageQueue
            Dim qm As Message
            sFile = Dts.Variables("FilePathName").Value
            Dim oFileStream As New StreamWriter(sFile)
            iLoopCount = 0
            q = New MessageQueue(QueueName)
            qm = New Message
            Dim qenum As MessageEnumerator = q.GetMessageEnumerator2
            While (qenum.MoveNext) And (iLoopCount <= iMaxLoopCount)
                qm = q.Receive()
                qm.Formatter = New ActiveXMessageFormatter()
                br = New BinaryReader(qm.BodyStream, encoding:=Unicode)
                sMessage = New String(br.ReadChars(CInt(qm.BodyStream.Length)))
                oFileStream.WriteLine(sMessage)
                iLoopCount += 1
            End While
            oFileStream.Close()
            Dts.TaskResult = ScriptResults.Success
        End Sub


    R Campbell


    Friday, August 7, 2015 12:52 AM
  • Because you post the question as a discussion, you cannot mark it as an answer. Since the issue is a question, I change its type from discussion to question.

    Katherine Xiong
    TechNet Community Support

    Thanks, I didn't notice that it was a only Discussion. Before marking it as Answered, I might as well include the script below which includes provision both XML and non-XML messages.

    Hopefully this will help others migrating a legacy Active x Script to .Net script, as I was. The confusing thing was that qm.Body.ToString() could only be used to access XML messages in .Net scripts and I seemed to have to use qm.BodyStream and BinaryReader for plain text messages. No amount of Google searching yielded a reault that actually stated this.

        Public Sub Main()
            Dim QueueName As String = "FORMATNAME:DIRECT=OS:.\Private$\" & Dts.Variables("QueueName").Value
            Dim br As BinaryReader
            Dim sMessage As String
            Dim sFile As String
            Dim iMaxLoopCount As Integer = Dts.Variables("MaxMsgCount").Value
            Dim iLoopCount As Integer
            Dim q As MessageQueue
            Dim qm As Message
            Dim isXML As Boolean
    
            sFile = Dts.Variables("FilePathName").Value
            Dim oFileStream As New StreamWriter(sFile)
    
            isXML = Dts.Variables("Is_XML_Msg").Value
            iLoopCount = 0
            q = New MessageQueue(QueueName)
            qm = New Message
            Dim qenum As MessageEnumerator = q.GetMessageEnumerator2
    
            While (qenum.MoveNext) And (iLoopCount <= iMaxLoopCount)
                qm = q.Receive()
                If isXML Then
                    qm.Formatter = New XmlMessageFormatter(New [String]() {"System.String,mscorlib"})
                    sMessage = qm.Body
                Else
                    qm.Formatter = New ActiveXMessageFormatter()
                    br = New BinaryReader(qm.BodyStream, encoding:=Unicode)
                    sMessage = New String(br.ReadChars(CInt(qm.BodyStream.Length)))
                End If
                oFileStream.WriteLine(sMessage)
                iLoopCount += 1
            End While
    
            oFileStream.Close()
            Dts.TaskResult = ScriptResults.Success
        End Sub


    R Campbell



    Monday, August 10, 2015 5:30 AM

All replies

  • Follow the below link

    http://blogs.msdn.com/b/biztalknotes/archive/2013/12/30/how-to-send-and-receive-messages-in-msmq-using-c-vb.aspx


    Please Dont forget to mark as answer. It helps others to find relevant posts to the same question. Milan Das

    Thursday, August 6, 2015 7:23 AM
  • The blog answers one quetiin for me, if the message has an xml wrapper it should show up in the properties. This is what the messge looks like.

    54 00 65 00 73 00 74 00 T.e.s.t.
    20 00 4D 00 65 00 73 00  .M.e.s.
    73 00 61 00 67 00 65 00 s.a.g.e.

    I am not sure why my example shows the hex codes and yours only shows text but I think it shows that there is no xml wrapper. It was generated by the SSIS MSMQ task by the way.

    I changed the code as shown below and now I get a different error Cannot deserialize the message passed as an argument. Cannot recognize the serialization format.

    I don't undrestand why there would a serialization error with plani text message using and ActiveXFormatter.

    Here is the changed code.

        Public Sub Main()
            'Dim QueueName As String = Dts.Variables("QueueName").Value
            Dim QueueName As String = "FORMATNAME:DIRECT=OS:.\Private$\" & Dts.Variables("QueueName").Value
            Dim iMaxLoopCount As Integer = Dts.Variables("MaxMsgCount").Value
            Dim iLoopCount As Integer
            Dim sMessage As String
            Dim q As MessageQueue
            Dim qm As Message
            Dim msgType(0) As Type
    
            iLoopCount = 0
            msgType(0) = GetType(String)
    
            q = New MessageQueue(QueueName, QueueAccessMode.Receive)
            qm = New Message
            Dim qenum As MessageEnumerator = q.GetMessageEnumerator2
    
            While (qenum.MoveNext) And (iLoopCount < iMaxLoopCount)
                qm = q.Receive(3)
                'qm.Formatter = New XmlMessageFormatter(msgType)
                qm.Formatter = New ActiveXMessageFormatter()
                Try
                    sMessage = qm.Body.ToString()
                    InputBox("Message", "Body", sMessage)
                Catch e As Exception
                    InputBox("ERROR", "ERROR", e.Message)
                End Try
                iLoopCount += 1
            End While
            Dts.TaskResult = ScriptResults.Success
        End Sub


    R Campbell

    Thursday, August 6, 2015 9:32 AM
  • We I seem to have answered my own question, eventually. It seems that while the Message.Body() method was the right one to use for plain string messages in ActiveX scripting, this is no longer the case in VB.Net, because that now has serialization built in. They way to bypass that seems to be to use Message.BodyStream() and BinaryReader(). After a few dramas with BinaryReader() Encoding, it didn't take long to get things working. The good thing is that the script can pull a thousand messsages from queue, into a file in, less than a second. The rest should be straight forward DataFllow stuff.

    By the way, I can't seem to mark this as the answer, perhaps you can't do that for your own post :-).

        Public Sub Main()
            Dim QueueName As String = "FORMATNAME:DIRECT=OS:.\Private$\" & Dts.Variables("QueueName").Value
            Dim br As BinaryReader
            Dim sMessage As String
            Dim sFile As String
            Dim iMaxLoopCount As Integer = Dts.Variables("MaxMsgCount").Value
            Dim iLoopCount As Integer
            Dim q As MessageQueue
            Dim qm As Message
            sFile = Dts.Variables("FilePathName").Value
            Dim oFileStream As New StreamWriter(sFile)
            iLoopCount = 0
            q = New MessageQueue(QueueName)
            qm = New Message
            Dim qenum As MessageEnumerator = q.GetMessageEnumerator2
            While (qenum.MoveNext) And (iLoopCount <= iMaxLoopCount)
                qm = q.Receive()
                qm.Formatter = New ActiveXMessageFormatter()
                br = New BinaryReader(qm.BodyStream, encoding:=Unicode)
                sMessage = New String(br.ReadChars(CInt(qm.BodyStream.Length)))
                oFileStream.WriteLine(sMessage)
                iLoopCount += 1
            End While
            oFileStream.Close()
            Dts.TaskResult = ScriptResults.Success
        End Sub


    R Campbell


    Friday, August 7, 2015 12:52 AM
  • By the way, I can't seem to mark this as the answer, perhaps you can't do that for your own post :-).


    Because you post the question as a discussion, you cannot mark it as an answer. Since the issue is a question, I change its type from discussion to question.

    Katherine Xiong
    TechNet Community Support

    • Marked as answer by Dick Campbell Monday, August 10, 2015 5:07 AM
    • Unmarked as answer by Dick Campbell Monday, August 10, 2015 5:07 AM
    Monday, August 10, 2015 3:17 AM
    Moderator
  • Because you post the question as a discussion, you cannot mark it as an answer. Since the issue is a question, I change its type from discussion to question.

    Katherine Xiong
    TechNet Community Support

    Thanks, I didn't notice that it was a only Discussion. Before marking it as Answered, I might as well include the script below which includes provision both XML and non-XML messages.

    Hopefully this will help others migrating a legacy Active x Script to .Net script, as I was. The confusing thing was that qm.Body.ToString() could only be used to access XML messages in .Net scripts and I seemed to have to use qm.BodyStream and BinaryReader for plain text messages. No amount of Google searching yielded a reault that actually stated this.

        Public Sub Main()
            Dim QueueName As String = "FORMATNAME:DIRECT=OS:.\Private$\" & Dts.Variables("QueueName").Value
            Dim br As BinaryReader
            Dim sMessage As String
            Dim sFile As String
            Dim iMaxLoopCount As Integer = Dts.Variables("MaxMsgCount").Value
            Dim iLoopCount As Integer
            Dim q As MessageQueue
            Dim qm As Message
            Dim isXML As Boolean
    
            sFile = Dts.Variables("FilePathName").Value
            Dim oFileStream As New StreamWriter(sFile)
    
            isXML = Dts.Variables("Is_XML_Msg").Value
            iLoopCount = 0
            q = New MessageQueue(QueueName)
            qm = New Message
            Dim qenum As MessageEnumerator = q.GetMessageEnumerator2
    
            While (qenum.MoveNext) And (iLoopCount <= iMaxLoopCount)
                qm = q.Receive()
                If isXML Then
                    qm.Formatter = New XmlMessageFormatter(New [String]() {"System.String,mscorlib"})
                    sMessage = qm.Body
                Else
                    qm.Formatter = New ActiveXMessageFormatter()
                    br = New BinaryReader(qm.BodyStream, encoding:=Unicode)
                    sMessage = New String(br.ReadChars(CInt(qm.BodyStream.Length)))
                End If
                oFileStream.WriteLine(sMessage)
                iLoopCount += 1
            End While
    
            oFileStream.Close()
            Dts.TaskResult = ScriptResults.Success
        End Sub


    R Campbell



    Monday, August 10, 2015 5:30 AM