We are in the process of migrating an app from a Server 2008 set of servers to Server 2016, and since this app has ~75 private MSMQ queues, I wrote a very basic C# utility (just a console app) to get the list from our production server and recreate them on the new 2016 server via the following:
//connect to the specified server to pull all existings queues
var queues = MessageQueue.GetPrivateQueuesByMachine("[production server name]");
var acl = new AccessControlList();
acl.Add(new AccessControlEntry
{
EntryType = AccessControlEntryType.Allow,
GenericAccessRights = GenericAccessRights.All,
StandardAccessRights = StandardAccessRights.All,
Trustee = new Trustee("Everyone")
});
acl.Add(new AccessControlEntry
{
EntryType = AccessControlEntryType.Allow,
GenericAccessRights = GenericAccessRights.All,
StandardAccessRights = StandardAccessRights.All,
Trustee = new Trustee("Network Service")
});
foreach (var queue in queues)
{
var newQueue = MessageQueue.Create($".\\{queue.QueueName}", true);
newQueue.SetPermissions(acl);
newQueue.Label = queue.QueueName;
}
When I start running our web app on the new server and execute an action that places a message on the queue, it fails with System.Messaging.MessageQueueException: Access to Message Queuing system is denied, despite the Everyone ACL entry that is confirmed added to the queue.
The really strange part I'm running into though, is if I delete the queue in question and recreate it manually on the server with the same Everyone has full control permissions, the code works successfully. I've compared the properties of an auto-generated queue to a manually created one and everything is 100% identical, so it makes zero sense why this would occur.
Any suggestions? I'm at a loss, but trying not to have to create all of these queues manually if I can avoid it.
After a lot of back and forth testing, I reached out to Microsoft Support and one of their engineers has confirmed there's a bug of some kind on the .Net side with creating queues. We confirmed everything was identical, but the only time permissions worked was if the queue was created manually via the Computer Management snap-in. Creating it in code, regardless of permissions, caused it to not work correctly for multiple accounts.
Hopefully this helps anyone else trying to do this!
Related
I've been recently facing a strange issue with Hangfire and an ASP.Net app hosted on a single server (Virtual VM).
My code runs fine on the local machine, however, once the code is uploaded to the server it seems that I am getting the results of some older code that still exists somewhere.
Here is what the background job is supposed to do:
Query the database multiple times (some heavy queries are involved)
Write the results to an excel file
Send the file in an email.
What happens is that for the same request I am randomly getting 3 different outcomes. Either an empty excel file in the email with only the header data filled, an excel file that is partially filled, or a complete excel file.
I've disabled sending the email to debug but between one request and the other I am still getting emails (!)
I've restarted the IIS server, restarted the web app, deleted the directory of the web app and redeployed to no avail.
Hangfire is great but I am afraid that I have no choice but to look for an alternative if I do not overcome this problem (it's been a couple days of debugging). Is there a way to properly restart Hangfire?
Here is my setup for reference:
Global.asax.xs
Hangfire.GlobalConfiguration.Configuration
.SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
.UseSimpleAssemblyNameTypeSerializer()
.UseRecommendedSerializerSettings()
.UseSerilogLogProvider()
.UseSqlServerStorage("Server=localhost; Database = hangfire; Integrated Security = SSPI;", new SqlServerStorageOptions
{
CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
QueuePollInterval = TimeSpan.Zero,
UseRecommendedIsolationLevel = true,
DisableGlobalLocks = true
});
var options = new BackgroundJobServerOptions
{
WorkerCount = 1
};
yield return new BackgroundJobServer(options);
and this is how the job is called:
BackgroundJob.Enqueue(() => (new cReport()).GetReportInBackground(itemsToReport, tbStartDate.Text, tbEndDate.Text, ME));
Thanks!
I've been working to try and convert Microsoft's EWS Streaming Notification Example to a service
( MS source http://www.microsoft.com/en-us/download/details.aspx?id=27154).
I tested it as a console app. I then used a generic service template and got it to the point it would compile, install, and start. It stops after about 10 seconds with the ubiquitous "the service on local computer started and then stopped."
So I went back in and upgraded to C# 2013 express and used NLog to put a bunch of log trace commands to so I could see where it was when it exited.
The last place I can find it is in the example code, SynchronizationChanges function,
public static void SynchronizeChanges(FolderId folderId)
{
logger.Trace("Entering SynchronizeChanges");
bool moreChangesAvailable;
do
{
logger.Trace("Synchronizing changes...");
//Console.WriteLine("Synchronizing changes...");
// Get all changes since the last call. The synchronization cookie is stored in the
// _SynchronizationState field.
// Only the the ids are requested. Additional properties should be fetched via GetItem
//calls.
logger.Trace("Getting changes into var changes.");
var changes = _ExchangeService.SyncFolderItems(folderId, PropertySet.IdOnly, null, 512,
SyncFolderItemsScope.NormalItems,
_SynchronizationState);
// Update the synchronization cookie
logger.Trace("Updating _SynchronizationState");
the log file shows the trace message ""Getting changes into var changes." but not the "Updating _SynchronizationState" message.
so it never gets past var changes = _ExchangeService.SyncFolderItems
I cannot for the life figure out why its just exiting. There are many examples of EWS streaming notifications. I have 3 that compile and run just fine but nobody as far as I can tell has posted an example of it done as a service.
If you don't see the "Updating..." message it's likely the sync threw an exception. Wrap it in a try/catch.
OK, so now that I see the error, this looks like your garden-variety permissions problem. When you ran this as a console app, you likely presented the default credentials to Exchange, which were for your login ID. For a Windows service, if you're running the service with one of the built-in accounts (e.g. Local System), your default credentials will not have access to Exchange.
To rectify, either (1) run the service under the account you did the console app with, or (2) add those credentials to the Exchange Service object.
I would like to know how to programatically restart IIS 6.0 SMTP server.
The SMTP server I have setup crashes every now and then. I don't notice it for a couple days, but that is by far way too late to do anything about it.
I want to set up a scheduled task every 30 minutes or so to test if the SMTP server is running, and if its not, the Scheduled task with automatically start it back up.
I have found a way to check if the SMTP server is up and running, but I have not figured out how to restart the process if it crashes.
That way is posted here: Testing SMTP server is running via C#
Any help would be brilliant!
Thank you.
Im developing the Console application in C# to check if its running or not, so any code examples would be great too.
A ServiceController can help you, as it has start and stop methods. Look at the sample in the msdn page.
Another sample is taken from the ServiceControllerStatus Enumeration is nearly what you need (just replace the service name).
ServiceController sc = new ServiceController("Telnet");
Console.WriteLine("The Telnet service status is currently set to {0}",
sc.Status.ToString());
if ((sc.Status.Equals(ServiceControllerStatus.Stopped)) ||
(sc.Status.Equals(ServiceControllerStatus.StopPending)))
{
// Start the service if the current status is stopped.
Console.WriteLine("Starting the Telnet service...");
sc.Start();
}
else
{
// Stop the service if its status is not set to "Stopped".
Console.WriteLine("Stopping the Telnet service...");
sc.Stop();
}
// Refresh and display the current service status.
sc.Refresh();
Console.WriteLine("The Telnet service status is now set to {0}.",
sc.Status.ToString());
Maybe I'm missing something, or something changed, but when you install SMTP service on Windows 2012R2, there is no dedicated service for it. So, for recent version of Windows the advice above won't work.
Luckily there's a way to do it much easier. Powershell:
([ADSI]'IIS://LOCALHOST/SMTPSVC/1').Start() #to start
([ADSI]'IIS://LOCALHOST/SMTPSVC/1').Stop() #to ... you guess
The weirdest thing is that you control smtp service through AD, but it works.
And of course this should be run elevated.
If you have several virtual SMTP servers, you may need to identify your server by index or by some property (e.g. .ConnectionTimeout) first.
in c# you can write:
enum StatusVirtualServerSMTP
{
Started = 2,
Stopped = 4
}
DirectoryEntry dir = new DirectoryEntry("IIS://localhost/SMTPSVC/1");
if (Convert.ToInt32(dir.Properties["SERVERSTATE"].Value) == (int)StatusVirtualServerSMTP.Stopped)
{
dir.Properties["SERVERSTATE"].Value = (int)StatusVirtualServerSMTP.Started;
dir.CommitChanges();
}
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.
I've recently coded a .NET Console app using C#. It's purpose was to read the emails within a specific folder, parse them for specific values and save them to a database.
Our email system, at the time I originally coded this, was Exchange 2003. However, I was made aware we would soon be upgrading to Exchange 2010: ergo, I built the code to work in both environments.
Following the migration to Exchange 2010, however, the app has broken.
The app uses the EWS API for 2010 functionality. When it attempts to use the ExchangeService's FindFolders method to find the publicfoldersroot, it throws an exception. Here's the code:
ExchangeService service = new ExchangeService();
FindFoldersResults findRootFldrs;
service.UseDefaultCredentials = true;
service.AutodiscoverUrl("xxxxx#xxxx.xxx", delegate(string x) {
return true; });
FolderView fview = new FolderView(100);
fview.Traversal = FolderTraversal.Deep;
findRootFldrs = service.FindFolders(WellKnownFolderName.PublicFoldersRoot,
fview);
The exception: ErrorInvalidSchemaVersionForMailboxVersion, aka:
The mailbox that was requested doesn't support the specified RequestServerVersion
I've attempted:
Setting the exchangeservice to 2007 (throws an exception: "An internal server error occurred. The operation failed.")
Giving myself the highest level of permission to the Public Folder (no effect)
Manually setting my credentials (no effect)
I can view the public folders in Outlook 2007; the publicfoldersroot property is available in the intellisense; the code works on local folders (I can parse my inbox).
My current thinking is that it's a setting on the recent setup of Exchange 2010: unfortunately that isn't really my field. The exception tells me it's trying to use a previous version of Exchange. Setting it to 2007 simply causes the code to fail with an internal server error.
Old post, but this turned out to be the answer for me: http://technet.microsoft.com/en-us/library/bb629522.aspx
Essentially the account used to connect with EWS had a mailbox in a mailbox database whose default public folder server was still Exchange 2003. Any and all attempts to enumerate public folders over EWS failed. Swapping it out for a 2010 backend server cured it instantly.
Have you tried esb.RequestServerVersion.Version = ExchangeVersionType. Exchange2010 (or SP1)
Change this line:
ExchangeService service = new ExchangeService();
to something like this:
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2010);
or
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2010_SP1);
Depending on your version.