28

I'm writing messages to a Message Queue in C# as follows:

queue.Send(new Message("message"));

I'm trying to read the messages as follows:

Messages messages = queue.GetAllMessages();
foreach(Message m in messages)
{
  String message = m.Body;
  //do something with string
}

However I'm getting an error message which says: "Cannot find a formatter capable of reading this message."

What am I doing wrong?

Mr Lister
  • 45,515
  • 15
  • 108
  • 150
macleojw
  • 4,113
  • 10
  • 43
  • 63
  • Don't use Microsoft Message Queue (MSMQ). Just don't. It is both deprecated, and bottom of the pile in terms of anything useful, performant or even remotely well designed. – Contango Nov 29 '19 at 12:29
  • @Contango advices to good alternatives? thx – usr4217 Jul 01 '22 at 14:35
  • @usr4217 Really depends on your use case. Heard good things about Kafka. Avoid RabbitMQ. It's aim was to be reliable in an enterprise setting, but in practice, it's has design flaws. These flaws mean that it constantly requires expert attention to pull it our of the various holes it's dug for itself, and no amount of expert programming can work around its limitations. – Contango Jul 04 '22 at 15:20

9 Answers9

37

I solved the problem by adding a formatter to each message. Adding a formatter to the queue didn't work.

Messages messages = queue.GetAllMessages();
foreach(Message m in messages)
{
  m.Formatter = new XmlMessageFormatter(new String[] { "System.String,mscorlib" });
  String message = m.Body;

  //do something with string
}
Ben Collins
  • 20,538
  • 18
  • 127
  • 187
macleojw
  • 4,113
  • 10
  • 43
  • 63
25

Or you can use

 message.Formatter =
     new System.Messaging.XmlMessageFormatter(new Type[1] { typeof(string) });
prime23
  • 3,362
  • 2
  • 36
  • 52
6

you could try reading the bodystream of the message instead of the body, like this:

StreamReader sr = new StreamReader(m.BodyStream);    
string messageBody = "";    
while (sr.Peek() >= 0) 
{
    messageBody += sr.ReadLine();
}
abatishchev
  • 98,240
  • 88
  • 296
  • 433
felbus
  • 2,639
  • 2
  • 24
  • 30
  • 1
    The `StreamReader` class has a method `ReadToEnd` that will perform better than looping and building up a series of `string` objects. – Jonathan Gilbert Oct 22 '14 at 20:31
4
Message recoverableMessage = new Message();
recoverableMessage.Body = "Sample Recoverable Message";

recoverableMessage.Formatter = new XmlMessageFormatter(new String[] {"System.String,mscorlib" });

MessageQueue myQueue = new MessageQueue(@".\private$\teste");

Queue must be set Formatter too.

myQueue.Formatter = new XmlMessageFormatter(new String[] { "System.String,mscorlib" });
Mr Milk
  • 144
  • 9
3

Everyone here has done a fantastic job at providing solutions, and having just finished battling this problem myself I wanted to throw my own 2c in and show the solution I came up with that works very well.

Firstly when the queue is created I make sure I open up the permissions like so (I'm not concerned about queue security in the context of our application... this is a calculated decision):

queue.SetPermissions("Everyone", MessageQueueAccessRights.FullControl, AccessControlEntryType.Set);

Without that line I would receive all sorts of inaccessible errors and couldn't even browse the queue from the computer management screen. Incidentally if that happens to you and you're wondering how to kill the queue that you don't have access to just:

  1. Stop the service "Message Queueing"
  2. Goto "C:\Windows\System32\msmq\storage\lqs"
  3. Open each file in notepad and look for your queue name (it will most likely be the file that was most recently modified)
  4. Delete that file and restart the Messaging service

Create a base class for your queue message items and mark it [Serializable]. On application load cache a list of all your message types using something like this:

var types = typeof(QueueItemBase).Assembly
            .GetTypes()
            .Where(t => typeof(QueueItemBase).IsAssignableFrom(t) && t.IsAbstract == false)
            .ToArray();
...
// Create and cache a message formatter instance
_messageFormatter = new XmlMessageFormatter(types);

Now you're ready to start receiving messages. My first instinct was to poll for messages, but the api doesn't really like working that way. So I create a background thread and call the blocking method Receive on the queue which will return once a message is available. From there decoding the message is as simple as:

var message = queue.Receive();
if (message == null)
    continue;

// Tell the message about our formatter containing all our message types before we 
// try and deserialise
message.Formatter = _messageFormatter;

var item = message.Body as QueueItemBase;

And that should be all you need to get nicely implemented, typesafe MSMQ integration!

Paul Carroll
  • 1,523
  • 13
  • 15
2

This worked for me to read a private queue from a remote machine:

MessageQueue queue = new MessageQueue(@"FormatName:Direct=OS:MACHINENAME\private$\MyQueueName", QueueAccessMode.Peek);

Message msg = queue.Peek();
StreamReader sr = new StreamReader(msg.BodyStream);
string messageBody = sr.ReadToEnd();

Update 2019-11-29

Don't use Microsoft Message Queue (MSMQ). Just don't. It is both deprecated, and bottom of the pile in terms of anything useful, performant or even remotely well designed.

Contango
  • 76,540
  • 58
  • 260
  • 305
  • I used `msg.BodyType` and this led, via this https://stackoverflow.com/q/18134862/11163122 and this https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms703268(v=vs.85)?redirectedfrom=MSDN, for me to realize this worked when I my type was "string data in unicode" – Intrastellar Explorer Oct 28 '19 at 21:55
2

It seems that the serialization is only done when accessing the Body property of the Message class. As long as you access the Body property after you set on the message the right Formatter it works fine.

If you prefer not to create a Formatter for each message you can set the Formatter on the queue and for each message (before accessing the Body property) set the Formatter property from the Formatter of the queue.

_queue.Send(new Message() { Formatter = _queue.Formatter, Body = myData } );

var msg = _qeueu.Receive();
msg.Formatter = _queue.Formatter;
var myObject = (MyClass) msg.Body;
selalerer
  • 3,766
  • 2
  • 23
  • 33
1

Adding formatter solved my issue:

 public void ReceiveAsync<T>(MqReceived<T> mqReceived)
    {
        try
        {
            receiveEventHandler = (source, args) =>
            {
                var queue = (MessageQueue)source;
                using (Message msg = queue.EndPeek(args.AsyncResult))
                {
                    XmlMessageFormatter formatter = new XmlMessageFormatter(new Type[] { typeof(T) });
                    msg.Formatter = formatter;
                    queue.ReceiveById(msg.Id);
                    T tMsg = (T)msg.Body;
                    mqReceived(tMsg);

                }
                queue.BeginPeek();
            };

            messageQueu.PeekCompleted += receiveEventHandler;
            messageQueu.BeginPeek();

        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
        }
    }

You can see sample code and msmq library on github: https://github.com/beyazc/MsmqInt

cahit beyaz
  • 4,829
  • 1
  • 30
  • 25
0

this works very fine:

static readonly XmlMessageFormatter f = new XmlMessageFormatter(new Type[] { typeof(String) });

private void Client()
{
    var messageQueue = new MessageQueue(@".\Private$\SomeTestName");

    foreach (Message message in messageQueue.GetAllMessages())
    {
        message.Formatter = f;
        Console.WriteLine(message.Body);
    }
    messageQueue.Purge();
}
Sith2021
  • 3,245
  • 30
  • 22