My program is successfully using .NET's MessageQueue class to read from an MSMQ. The queue is user configurable, and is sometimes on the local machine, and sometimes on a remote machine. The user can specify the remote machine either by name or IP address, and the queue name (and I'm only using "Private" queues).
I want to display to the user how many messages remain in the Queue, but haven't found a way to do this. The MessageQueue class does not seem to have a Count (or similar) property to give this to me easily.
I've been able to use the PerformanceCounter and PerformanceCounterCategory classes to get the count - but this only seems to work for me on the local machine (although I'm not completely sure I'm using these classes correctly).
My question is how to read the Count (number of messages) from an MSMQ on a remote machine?
I use the following method for message counting (works for both local and remote queues),
var machineName = "mymachine01";
var formatName = "FormatName:DIRECT=OS:mymachine01\private$\ftpreceived":
try
{
var msmqManagement = new MSMQ.MSMQManagement();
msmqManagement.Init(machineName, null, formatName );
return (uint)msmqManagement.MessageCount;
}
catch (COMException ex)
{
// If queue is not active or does not exist.
if (ex.ErrorCode == -1072824316)
{
return 0;
}
throw;
}
Note: It returns 0 in the case the queue does not exist or is not active as the MSMQ Managment API considers this the same error.
Note: If the machine name value is null it will look at the queue on the local machine.
Note: If the machinename variable is different from the machinename part of the formatname, it will return a count of messages in the "Outgoing" message queue with the given format name on the machine specified by machinename.
Active means it has 1 or more messages in it, or it has had a message in it within the last N (Not sure how big N is :)) seconds, after that time the queue is considered inactive.
The most reliable solution for getting the count of messages in a local queue is to use the MSMQ APIs using P/Invoke. There's a nice article here: Counting the number of messages in a Message Queue in .NET.
I don't know if it works with remote queues, but I wouldn't rely on it. Generally, the only thing you should do with a remote queue is to send a message. Trying to read messages or properties from a remote queue should be avoided, if possible. "Send remote and read local" will always give you the best performance and avoid all kinds of problems (e.g., what if the remote machine isn't available?)
I am using WMI to get this information. The following is an example of the code that I am using to accomplish this.
var query = String.Format("select MessagesinQueue from Win32_PerfRawdata_MSMQ_MSMQQueue where name ='{0}'", path.Replace("\\", "\\\\"));
var selectQuery = new SelectQuery(query);
using (var searcher = new ManagementObjectSearcher(selectQuery))
using (var results = searcher.Get())
{
foreach (var result in results)
{
var messages = result["MessagesinQueue"].ToString();
return long.Parse(messages);
}
}
This may be a solution for you: http://jopinblog.wordpress.com/2008/03/12/counting-messages-in-an-msmq-messagequeue-from-c/
This might be overkill, but you could possibly put a WCF service on the same machine hosting your queue, so you'd be able to retrieve the count using the performance counters in the WCF service and expose that functionality through a method of that service.
I would like to say several things here.
Before I get to my own answer, I would like to comment John Opncar's solution (in Dr. Wily's Apprentice answer). His code does work on remote machines. I used his code in our project at work to watch the queues on a remote cluster server and it works very well.
So, if get "RemoteMachineNotAvailable" errors, please check your configurations. Are all machines in the same network or - not to forget - are the queue's security permissions on the remote machine set up to allow others to read them?
Do the users/ accounts have sufficient rights to read from other systems?
For example, we had to allow everyone on the clustered queues.
As far as I know the PerformanceCounters do indeed have problems to read message properties on remote machines. I tried to use them for queues in a Windows Server cluster environment, but was never able to get it working. I did some internet research at that time, but unfortunatelly I do not recall if this is due to security reasons or simply a bug. :-(
Now to the actual answer. If you do not like the Cursor-Method as described by John Opincar you could also use the MessageQueue.GetAllMessages() or MessageQueue.GetMessageEnumerator methods.
I never tried GetMessageEnumerator, but I can say that I would not recommend GetAllMessages().
We suffered from heavy performance issues when using it every second on a system with several queues that contain several thousands of messages.
The method takes a snapshot of all messages in the queue, which can cause heavy loads in memory and network.
The cursor-methodis still somewhat slow. But, at least in our production environment, it feels more snappy than with the GetAllMessages() solution.
If counting the messages in your scenario does not need to be as often as one second and you have less messages to count than we do, then working with GetAllMessages() or GetMessageEnumerator() might be a possible solution for you.
Finally, it always comes down to your own individual needs.
Related
I am trying to use
Process[] processes = Process.GetProcessesByName(processName, workerRoleIPAddress);
From my controller to check if a certain process is running in my worker role. What is the correct IPAddress to use for the worker role? Using the IPAddress that the Input Endpoitns use doesn't seem to work.
Is there a better way to do this?
You are not forced to use the second parameter. There are two method signatures:
http://msdn.microsoft.com/en-us/library/System.Diagnostics.Process.GetProcessesByName(v=vs.71).aspx
public static Process[] GetProcessesByName(string);
public static Process[] GetProcessesByName(string, string);
I suggest you try to get clear with the first method signature. After, try to use the IP address.
I would try 127.0.0.1 first. This is the shortest way to try. Maybe even 'localhost' would be working.
Then, I don't know this precise in detail, but to ask a process list on a distant computer, this method will need some service running on the distant computer. So there might be firewall ports need to be opened opened or a certain demon need to be running on the other computer. Asking a list of processes on a distant computer will quickly become a security issue.
I eventually solved the problem by using an Azure Storage table.
I queried the worker role locally using GetProcesssByName() and stored the result in an Azure Storage table. I subsequently accessed this table from the web role to read the result.
Care must be taken to delete the older entries from the storage table so we don't assume that older(and currently non-existent) processes are still running.
My machine is in Domain D1 and there are public MSMQs in a remote server in domain D2. I am connected through vpn to D2, i.e I can RDP the machine in D2 and access the MSMQ.
What I want is to access (Know the message count) of the MSMQ without RDPing the system. So I build an application for this. I used Impersonation to impersonate the user of D2(i.e used credentials of D2)but the problem is I am not able to access the "Public" MSMQ ( used Messagequeue.GetPublicQueue() ) and exceptions are thrown with message "A workgroup installation computer does not support the operation." but when I used MessageQueue.GetPrivateQueue() it returned a collection of private queue.
I tried using MSMQManager for messageCount
Path = #"Direct:OS:machine\publicqueue";
FormatName=null;
new MSMQManager.inIt(machineName, path , FormatName);
This also throws an exception either the queue is not present or not open. but I can check that queue is working fine.
Are you comfortable doing a tiny bit of programming? If not, are you comfortable using PowerShell?
Either way - I would check out this post as it seems to contain the answers you are looking for.
Good luck, hope this helps
Your problem might be that you are working remotely.
The method GetPublicQueuesByMachine() is indeed not available over remote access.
You can see this in a feature matrix in the MSDN documentation: MessageQueue.GetPublicQueuesByMachine:
The following table shows whether this method is available in various Workgroup modes.
Workgroup mode Available
-------------- ---------
Local computer No
Local computer and direct format name No
Remote computer No
Remote computer and direct format name No
Also check the access privileges of your queues.
If I am wrong in the previous suggestion, it might be as simple as experimenting with the access rights for specific users in the network.
MSDN article Public and private queues states:
Default security access for public queues gives everyone permission to
send messages to a public queue. Specific permissions must be
granted for read access.
As for the actual message counting, John Opincar wrote a nice article about counting messages here: Counting Messages in an MSMQ MessageQueue from C#
I'm trying to setup msmq communication between two services on two separate machines within the same domain.
The machines are a Primary - Secondary situation.
I need to be able to Send and Purge from the Primary, and Receive from the Secondary
I can't seem to find a setup that allows for this. I had one setup where the queues existed on the Secondary and the Primary could Send and the Secondary Receive BUT I could not purge from the Primary as I need to. I believe this was due to the queues being private and remote.
So I tried to flip the situation. I put the queues on the Primary, but then I couldn't Send to the private local queue, so I made them public, and now I cant get the secondary to find the remote public queues.
if (!MessageQueue.Exists(queueName))
{
//log they don't exist and exit
throw new Exception("One or more of the required queues do not exist");
}
syncQ = new MessageQueue(queueName){Formatter = new XmlMessageFormatter(new Type[] {typeof (String)})};
if (machineState == MachineState.Primary)
{
syncQ.SetPermissions(ASPConfiguration.SyncUser, MessageQueueAccessRights.FullControl, AccessControlEntryType.Allow);
syncQ.Purge();
}
Primary = ".\nw"
Secondary = "FormatName:Direct=OS:CACTEST-WS-D\nw"
It doesn't really matter if the queues are private or public to me just as long as I can do what I need.
Thanks for the Help.
In answer to your queries:
I had one setup where the queues existed on the Secondary and the
Primary could Send and the Secondary Receive BUT I could not purge
from the Primary as I need to.
The initial set up you had, with the queue on secondary and primary sending to secondary is the correct configuration. Unsure why you are unable to purge secondary queues from primary. This is probably just a simple case of queue permissions. Try granting full control to the service account running on primary.
So I tried to flip the situation. I put the queues on the Primary, but
then I couldn't Send to the private local queue
Again, unsure why this is. What errors do you receive? You can enable MSMQ tracing which will give you any transmission errors on both machines. Anyhow, this is not the correct configuration. It's always best to send remote - receive local.
Primary = ".\nw"
It's always better to use the fully formatted msmq address in all situations to avoid addressing problems.
It doesn't really matter if the queues are private or public to me
Well it really really does matter. Public queues are for enterprise-scale configurations with high volume, clustering and/or routing requirements, and anyhow you have to register them with active directory for them to even work. If you just want to send stuff from place to place ALWAYS use private queues.
My advice is to revert back to you initial setup and concentrate on getting the remote purge working.
I am trying to get a count of MSMQ messages from a private queue on a local Win7 Enterprise machine. I can get the count from a remote machine without problem and the code works on Win2008 Server R2. I am using C# VS 2008.
Code sample:
int result = MQMgmtGetInfo(cname, "queue=DIRECT=OS:" + qname, ref props);
where cname is the machine name with private queue, MyLocalComputer, and
qname is the path reference to the private queue, MyLocalComputer\\private$\\QueueName
When I call this code from the Win7 computer containing the private queue I get a -1072824319 return value. There are messages in the queue.
If I call this code from a WinXP computer to get counts on the Win7 queue, I get correct counts. I can also call the code from a WinXP computer to get counts from a local private queue on the same WinXP machine and get correct counts.
Is there something different about accessing local private queues on Win7? Since I can access the queue remotely I wouldn't think it is a permissions issue, but I may be wrong.
Sounds like you are using the MSMQ 3.0 API. Windows 7 uses MSMQ 4.0.
In the .NET references window look for System.Messaging (version 4.0.0.0) and add to your project.
Take a look in this link: http://www.codeproject.com/KB/cs/mgpmyqueue.aspx
[]'s
Renan
-1072824319 = 0xC00E0001 which is a generic MSMQ error.
Try using a full stop instead of the machine name.
That will tell MSMQ to use "local host".
If that works then there may be a problem with the machine resolving it's own name.
Cheers
John Breakwell
I'm working on a Mono application that will run on Linux, Mac, and Windows, and need the ability for apps (on a single os) to send simple string messages to each other.
Specifically, I want a Single Instance Application. If a second instance is attempted to be started, it will instead send a message to the single instance already running.
DBus is out, as I don't want to have that be an additional requirement.
Socket communication seems to be hard, as windows seems to not allow permission to connect.
Memory Mapped Files seems not to be supported in Mono.
Named Pipes appears not to be supported in Mono.
IPC seems not to be supported on Mono.
So, is there a simple method to send string messages on a single machine to a server app that works on each os, without requiring permissions, or additional dependencies?
On my ubuntu (10.10 mono version: 2.6.7) I've tried using WCF for interprocess communication with BasicHttpBinding, NetTcpBinding and NetNamedPipeBinding. First 2 worked fine, for NetNamedPipeBinding I got an error:
Channel type IDuplexSessionChannel is
not supported
when calling ChannelFactory.CreateChannel() method.
I've also tried using Remoting (which is a legacy technology since WCF came out) with IpcChannel; example from this msdn page started and worked without problems on my machine.
I suppose you shouldn't have problems using WCF or Remoting on Windows either, not sure about Mac though, don't have any of those around to test. Let me know if you need any code examples.
hope this helps, regards
I wrote about this on the mono-dev mailing list. Several general-purpose inter-process messaging systems were considered, including DBus, System.Threading.Mutex class, WCF, Remoting, Named Pipes... The conclusions were basically mono doesn't support Mutex class (works for inter-thread, not for inter-process) and there's nothing platform agnostic available.
I have only been able to imagine three possible solutions. All have their drawbacks. Maybe there's a better solution available, or maybe just better solutions for specific purposes, or maybe there exist some cross-platform 3rd party libraries you could include in your app (I don't know.) But these are the best solutions I've been able to find so far:
Open or create a file in a known location, with exclusive lock. (FileShare.None). Each application tries to open the file, do its work, and close the file. If failing to open, Thread.Sleep(1) and try again. This is kind of ghetto, but it works cross-platform to provide inter-process mutex.
Sockets. First application listens on localhost, some high numbered port. Second application attempts to listen on that port, fails to open (because some other process already has it) so second process sends a message to the first process, which is already listening on that port.
If you have access to a transactional database, or message passing system (sqs, rabbitmq, etc) use it.
Of course, you could detect which platform you're on, and then use whatever works on that platform.
Solved my problem with two techniques: a named mutex (so that the app can be run on the same machine by different users), and a watcher on a message file. The file is opened and written to for communication. Here is a basic solution, written in IronPython 2.6:
(mutex, locked) = System.Threading.Mutex(True, "MyApp/%s" % System.Environment.UserName, None)
if locked:
watcher = System.IO.FileSystemWatcher()
watcher.Path = path_to_user_dir
watcher.Filter = "messages"
watcher.NotifyFilter = System.IO.NotifyFilters.LastWrite
watcher.Changed += handleMessages
watcher.EnableRaisingEvents = True
else:
messages = os.path.join(path_to_user_dir, "messages")
fp = file(messages, "a")
fp.write(command)
fp.close()
sys.exit(0)
For your simple reason for needing IPC, I'd look for another solution.
This code is confirmed to work on Linux and Windows. Should work on Mac as well:
public static IList Processes()
{
IList<Process> processes = new List<Process>();
foreach (System.Diagnostics.Process process in System.Diagnostics.Process.GetProcesses())
{
Process p = new Process();
p.Pid = process.Id;
p.Name = process.ProcessName;
processes.Add(p);
}
return processes;
}
Just iterate through the list and look for your own ProcessName.
To send a message to your application, just use MyProcess.StandardInput to write to the applications standard input. This only works assuming your application is a GUI application though.
If you have problems with that, then you could maybe use a specialized "lock" file. Using the FileSystemWatcher class you can check when it changes. This way the second instance could write a message in the file and then the first instance notice that it changes and can read in the contents of the file to get a message.