Question

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?

Was it helpful?

Solution

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
}

OTHER TIPS

Or you can use

 message.Formatter =
     new System.Messaging.XmlMessageFormatter(new Type[1] { typeof(string) });

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();
}
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" });

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;

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!

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();

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();
}

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top