Feel like a geek and get yourself Ema Personal Wiki for Android and Windows

16 June 2009

MSMQ continued

Created a simple server app, which creates the queue if instructed, and sends a message down the line. Also created a client app, which listenes to a queue and displays the string message. This works if used on the same machine. But how will it work if i deploy it to another machine?

Step 1: try to make the client app work with the queue name FormatName:Direct=TCP:<ip>\private$\<queuename>

Client app fails on the local machine when I use the ip address that is assigned this computer, but the client app succeeds if I use the loopback address 127.0.0.1

Step 2: copy the client app to a virtual machine that has access to the host machine.

Step 3: start the client, pointing it to the queue on the host machine.
Ouch!!
InvalidOperationException occurred: Message Queuing has not been installed on this computer.

I was silently hoping that Message Queuing would not have to be installed on client machines. In our situation we may have hundreds of client computers that will need this installation. Would there be another way of deploying MSMQ?

Step 4: read about deployment in the FAQ.
Some quotes:
"I want to configure a central queue on a Message Queuing server and have multiple remote clients read from it. Is this a good design? [..leave out some shades of gray...] No."

"The supporting server should be running the same (or later) Message Queuing version as the dependent client"

"Message Queuing does not scale well with multi-CPU computers"

And lots of cryptic error message with their explanations. If a solution requires a 133 page Word document for FAQ alone... what will it be like to work with MSMQ in the field? Looks like a horror scenario to me. Imagine getting this message from a client: "Unsupported option at CQ2QMsgF::QMsg, at (or near) line 2323". Or: "Message Queuing may function in an unpredictable fashion". How about this prose: "This error [MQ_ERROR_INSUFFICIENT_RESOURCES] can be returned by several APIs. It has several (unrelated) reasons"

The document does not answer my question so far. But this discussion seems to do. It involves a nicely formatted batch file which also acts as a configuration file for the installation that it invokes. The file should be named MSMQsetup.bat.
;@ECHO OFF 
;sysocmgr.exe /i:sysoc.inf /u:MSMQsetup.bat
;GOTO Finished
[Components]
msmq_Core = ON
msmq_LocalStorage = ON
msmq_ADIntegrated = ON
msmq_TriggersService = ON
msmq_HTTPSupport = OFF
msmq_RoutingSupport = OFF
msmq_MQDSService = OFF
;:Finished


Step 5: try out the magic batch file on my VM
Ha! it works: installation completed successfully.

Step 6: start my client app again
MessageQueueException occurred: The queue does not exist or you do not have sufficient permissions to perform the operation.
Bad UX from developer point of view: this error message does not give you very much information about how to solve this. But OK, that's what we get paid for: to solve problems that we or other developers create. So:

Step 7: Try out if settings the appropriate permissions on the queue solves the issue.
There are 2 ways to do this: 1) programmatically, 2) by starting compmgmt.msc > services > messsage queuing > private queues > right click on appropriate queue > properties and set permissions.
Setting the permissions programmatically would involve something like this:
  createdQueueu.SetPermissions(
new MessageQueueAccessControlEntry(
new Trustee("Iedereen"),
MessageQueueAccessRights.FullControl));

The "Iedereen" string is Dutch for "Everyone", which obviously is translated to the language of the OS. Very handy. I could not find a (programmatic) way to find out how the "Everyone" group is called on the local machine. But anyway, you would want to configure that and don't want to put that hard-coded in you app.

Step 8: Start the client app again on the VM
Still not working. Set the ANONYMOUS_LOGON permissions to Full Control. Still not working. Could be something with the VM / Host communication. So created the queue on the VM, tried to connect from host. Not helping.

OK I don't have a clue anymore about what could be the problem.

Try something else.
Not
FormatName:Direct=TCP:<ip>\private$\<queuename>
But
FormatName:Direct=OS:<hostname>\private$\<queuename>

Step 9: Start "server" app on host machine, "client" app on VM...
Eureka, it's working now.
Whatever.
Remove permissions for ANONYMOUS_LOGON:
Aha! A different error message:
MessageQueueException occurred: Access to Message Queuing system is denied.

So the problem with the TCP is yet unresolved, but I do have a working remote queue installation now.


By the way. The server app:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Messaging;

namespace ServerApp
{
class Program
{
static void Main(string[] args)
{
if (args.Length < 1)
{
Console.WriteLine("Usage: ServerApp <queue> [create]");
Console.WriteLine(" Will send a message \"Hallo!\" down the line.");
Console.WriteLine(" Specifying the \"create\" command will create the queue.");
return;
}

var queueName = args[0];

try
{
using (var q = (args.Length == 2 ? MessageQueue.Create(queueName) : new MessageQueue(queueName)))
{
q.Formatter = new XmlMessageFormatter(new Type[] { typeof(String) });
q.Send("Hallo!");
Console.WriteLine("Message sent");
}
}
catch (Exception e)
{
Console.WriteLine(e.GetType().Name + " occurred. " + e.Message);
}
}
}
}]]>



The client app:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Messaging;

namespace ClientApp
{
class Program
{
static void Main(string[] args)
{
if (args.Length < 1)
{
Console.WriteLine("usage: ClientApp <queuename>");
Console.WriteLine(" reads 1 message from the given queue");
return;
}

var queueName = args[0];

try
{
using (MessageQueue q = new MessageQueue(queueName))
{
q.Formatter = new XmlMessageFormatter(new Type[] { typeof(String) });
Console.WriteLine("Waiting for message...");
Console.WriteLine("Received message: " + (string)q.Receive().Body);
}
}
catch (Exception e)
{
Console.WriteLine(e.GetType().Name + " occurred: " + e.Message);
}
}
}
}

5 comments:

John Breakwell said...

Hi

[[you have a stray ampersand in your queue name: "FormatName:Direct=TCP:< ip >\private$amp;\< queuename >"]]

"Client app fails on the local machine when I use the ip address that is assigned this computer, but the client app succeeds if I use the loopback address 127.0.0.1"

This is probably a reverse lookup issue.
MSMQ will take the IP address and determine what the network name is for the destination.
Have a look in the HOSTS file on the machine to see if this IP address is mapped to any aliases.
MSMQ may be determining that the reverse lookup does not match the machine's real name and so discarding the message.
There's a registry value (IgnoreOSNameValidation) that you may need to set here.

"If a solution requires a 133 page Word document for FAQ alone... what will it be like to work with MSMQ in the field? Looks like a horror scenario to me."

The FAQ was written several years ago by one of the original MSMQ developers and covers the wide range of functionality available at the time. The size of the FAQ is, I think, more a reflection of the writer's zealous dedication to the product than problems with MSMQ. The document is a mix of troubleshooting, best practices, training and how-to - I wish other Microsoft products had an FAQ this good :-)
Sadly, I don't think it will be brought up to date.


"Remove permissions for ANONYMOUS_LOGON: Aha! A different error message: MessageQueueException occurred: Access to Message Queuing system is denied."

Yes, "Everyone" only applies to accounts that the local machine knows about. Two workgroup machines cannot resolve each other's local accounts as they do not share a security database so you always need "anonymous logon" for messages sent between the two.

Cheers
John Breakwell (MSFT)
http://blogs.msdn.com/johnbreakwell

Jan Willem said...

The ip-address is not mapped to any aliases in the hosts file. I added a new entry in the hosts file with the ip-address and the hostname that would probably be the right name, but to no avail.

I also tried adding the value you mentioned, according to this article, it should be
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSMQ\Parameters\IgnoreOSNameValidation]
@=dword:00000001

Unfortunately, no success.

It does actually find the computer, because if I specify a non-existent IP address, i get a different error.
MessageQueueException occurred: Remote computer is not available.

John Breakwell said...

After re-reading your post I see I misinterpreted what was going on - the errors are related to the client app receiving messages. My registry recommendation was for problems when sending. Sorry about the confusion.

So, if I've read the Step 8 notes properly [[you have two Step 8s]], the client app on the VM can read from a queue on the Host as long as you use OS:Hostname instead of TCP:IPAddress. Otherwise you get an exception.

This still sounds like a reverse lookup issue although I can't reproduce the problem on my own machine with invalid entries in my HOSTS file. What about the possibility of multiple entries in DNS for the IP address?

Cheers
John Breakwell (MSFT)
http://blogs.msdn.com/johnbreakwell

Jan Willem said...

John,

I am not sure how I can find if there is something wrong with the DNS settings.
Both the host machine and the VirtualPC get an IP address from the DHCP server which is my home router: i am working at home at the moment and not connected to the corporate network.

The host PC is in the corporate domain, the VirtualPC isn't.

Did you try to reproduce the problem using a VirtualPC? Is there a logfile somewhere where I can find what's going on?

John Breakwell said...

Hi,

Yes, I've also tried with VirtualPC. I've yet to come across an issue with MSMQ on virtual machines.

MSMQ does have logging you can enable. Unfortunately the output is a binary file which can only be decoded by Microsoft PSS. You would need to raise a support case to follow that troubleshooting approach.

Cheers
John Breakwell (MSFT)