Windows service Started and stopped automatically, exception handling issue - c#

I have developed a 32 bit service, I am running it in Windows 7 Home Premium x64.
Problem is when I start it, windows gives me the following message
The WLConsumer service on Local Computer started and then stopped. Some services stop
automatically if they are not in use by other services or programs.
I found the following message in the event log
Service cannot be started. System.ArgumentException: Log WLConsumer has already been registered as a source on the local computer.
at System.Diagnostics.EventLogInternal.CreateEventSource(EventSourceCreationData sourceData)
at System.Diagnostics.EventLogInternal.VerifyAndCreateSource(String sourceName, String currentMachineName)
at System.Diagnostics.EventLogInternal.WriteEntry(String message, EventLogEntryType type, Int32 eventID, Int16 category, Byte[] rawData)
at System.Diagnostics.EventLog.WriteEntry(String message, EventLogEntryType type)
at WeblogicConsumerService.WeblogicConsumer.winEventlogMe(String logTxt, String logSrc, Char entryType) in C:\Program Files (x86)\CSI\WeblogicConsumerService\WeblogicConsumer.cs:line 136
at WeblogicConsumerService.WeblogicConsumer.OnStart(String[] args) in C:\Program Files (x86)\CSI\WeblogicConsumerService\WeblogicConsumer.cs:line 63
at System.ServiceProcess.ServiceBase.ServiceQueuedMainCallback(Object state)
This is my codeblock in the OnStart() method
protected override void OnStart(string[] args)
{
#region WEBLOGIC CREDENTIALS
try
{
//Weblogic URL
this.url = Registry.LocalMachine.OpenSubKey(#"Software\CSI_WL").GetValue("URL").ToString();
//Queue name
this.qName = Registry.LocalMachine.OpenSubKey(#"Software\CSI_WL").GetValue("Queue").ToString();
//Weblogic login name
this.user = Registry.LocalMachine.OpenSubKey(#"Software\CSI_WL").GetValue("User").ToString();
//Weblogic password
this.pwd = Registry.LocalMachine.OpenSubKey(#"Software\CSI_WL").GetValue("Pwd").ToString();
//Weblogic Connection Factory
this.cfName = Registry.LocalMachine.OpenSubKey(#"Software\CSI_WL").GetValue("ConnectionFactory").ToString();
//root folder
this.rFolder = Registry.LocalMachine.OpenSubKey(#"Software\CSI_WL").GetValue("root").ToString();
}
catch (Exception e)
{
winEventlogMe(e.Message, "WLRegistryKeys", 'e');
}
#endregion
winEventlogMe("Successful start", "SeriviceStartup", 'i');
synchro.Enabled = true;
}
winEventLogMe is the method I am calling for logging.
public static void winEventlogMe(string logTxt, string logSrc, char entryType)
{
#region Log
//Log to event log
EventLog theEvent = new EventLog("WLConsumer");
theEvent.Source = logSrc;
if (entryType == 'e')
theEvent.WriteEntry(logTxt, EventLogEntryType.Error);
else if (entryType == 'i')
theEvent.WriteEntry(logTxt, EventLogEntryType.Information);
else if (entryType == 'w')
theEvent.WriteEntry(logTxt, EventLogEntryType.Warning);
else
theEvent.WriteEntry(logTxt, EventLogEntryType.Error);*/
#endregion
}
When I comment out the calls to winEventLogMe() method in the OnStart() method, the service starts without an error. So obviously something is wrong with the winEventLogMe() method.
Can someone please help me figure out whats the problem as I am totally clueless on how to solve this issue now.
thanx in advance :)
#nick_w I have edited my code as you suggested, the service started succesfully. But on Stopping it I got the following message:
Failed to stop service. System.ArgumentException: The source 'WLConsumer2012' is not registered in log 'ServiceStop'. (It is registered in log 'SeriviceStartup'.) " The Source and Log properties must be matched, or you may set Log to the empty string, and it will automatically be matched to the Source property.
at System.Diagnostics.EventLogInternal.VerifyAndCreateSource(String sourceName, String currentMachineName)
at System.Diagnostics.EventLogInternal.WriteEntry(String message, EventLogEntryType type, Int32 eventID, Int16 category, Byte[] rawData)
at System.Diagnostics.EventLog.WriteEntry(String message, EventLogEntryType type)
at WeblogicConsumerService.WeblogicConsumer.winEventlogMe(String logTxt, String logSrc, Char entryType) in C:\Program Files (x86)\CSI\WeblogicConsumerService\WeblogicConsumer.cs:line 139
at WeblogicConsumerService.WeblogicConsumer.OnStop() in C:\Program Files (x86)\CSI\WeblogicConsumerService\WeblogicConsumer.cs:line 70
at System.ServiceProcess.ServiceBase.DeferredStop()
here is the OnStop() method
protected override void OnStop()
{
winEventlogMe("Successful stop", "ServiceStop", 'i');
}
These event logs are starting to confuse me a lot. I have done the same method of logging in other services and never encountered such problems. How can I be getting these errors in this service yet its not much different from all the others I have done :(

I think this is your problem:
EventLog theEvent = new EventLog("WLConsumer");
Judging by the exception, I am thinking that WLConsumer is the name of the event source. What this means is that you might be better off with this:
EventLog theEvent = new EventLog(logSrc);
theEvent.Source = "WLConsumer";
This is just using the parameters the other way around.
If I do a little decompilation, there is a check like this:
if (!EventLogInternal.SourceExists(logName, machineName, true))
In your case I would think this check is returning true, meaning that it is trying to create a log named WLConsumer but failing because WLConsumer has been registered as an event source.
Edit:
When I have used the event log in the past, I wrote everything to the same combination of source and log. In your case you seem to be using a different combination of source and log each time you write an entry.
From MSDN (emphasis mine):
If you write to an event log, you must specify or create an event Source. You must have administrative rights on the computer to create a new event source. The Source registers your application with the event log as a valid source of entries. You can only use the Source to write to one log at a time. The Source can be any random string, but the name must be distinct from other sources on the computer. It is common for the source to be the name of the application or another identifying string. An attempt to create a duplicated Source value throws an exception. However, a single event log can be associated with multiple sources.
What I would suggest is this:
Use WLConsumer (or WLConsumer2012) as your source, and either
Define your own log, 'WLConsumerServiceEventLog` or something; or
Leave the log blank. They go into the Application log in this case.
Regardless, standard practice seems to be to do something like this prior to running your service for the first time, such as in an installer (copied straight from above link):
// Create the source, if it does not already exist.
if(!EventLog.SourceExists("MySource"))
{
//An event log source should not be created and immediately used.
//There is a latency time to enable the source, it should be created
//prior to executing the application that uses the source.
//Execute this sample a second time to use the new source.
EventLog.CreateEventSource("MySource", "MyNewLog");
Console.WriteLine("CreatedEventSource");
Console.WriteLine("Exiting, execute the application a second time to use the source.");
// The source is created. Exit the application to allow it to be registered.
return;
}
Note the point in the comments re latency. The logs are not necessarily created immediately, so it pays to code with this in mind. You could also use the EventLogInstaller to create the log. This may be the easier option if you are using an installer to deploy your service.

It is critical not to overload the on start method and in order to prevent service start failures, normally the onstart method launches the main code as a separate thread

Related

Foreach is stopped when exception is caught in try catch block located within the loop in ASP.NET Webapi

In my API I'm calling external SOAP API method several times. In order to achieve this I use foreach loop and inside it I put try catch block to handle exception and continue the loop. Everything works fine on my machine but when I deploy the API to another server running IIS it seems to stop calling the external API's method when exception is thrown as if the try catch was suddenly moved outside of the loop. Is it possible that it might have something to do with IIS configuration?
I've already tried putting this method inside another one and then putting that method inside try catch block but it didn't help.
public class Loader
{
private static SoapClient client;
private static string AddItems(Order order)
{
foreach(item in order.items)
{
try
{
client.SoapMethod(item);
}
catch(Exception e)
{
// Log error and go to next iteration
Log.LogError(e.Message);
}
}
}
}
Log class uses Log4Net to put error message in a text file:
public class Log
{
private static readonly ILog log = LogManager.GetLogger("API");
public static void LogError(string message)
{
log.Error(message);
}
}
In case the external SOAP API's method throws exception it should be skipped and it's supposed to go to the next iteration of the foreach loop to call this method with another data.
EDIT: I deployed my API to another server and it works there without breaking the loop so it seems that there's something wrong with that particular machine.
Thanks to the comment of #Silvermind I found out what the problem was. Logs were also sent to Windows EventLog which threw IOException every time any application tried to save logs in Application source (from which a custom view was created). I googled it and it appears to be a known issue in all Windows systems from Windows 7 SP1 and Windows Server 2008 SP2 as described on this site. The same error can be seen while filtering the Application view in Event Viewer.

Dynamics CRM - Custom Workflow - reading input parameter stops plugin from running?

I have a custom workflow activity which accepts an input parameter called "InputTest". This is just a string.
[Input("InputTest")]
[RequiredArgument]
public InArgument<string> TargetEntity { get; set; }
In my custom workflow activity I want to read it, so I do the following:
string targetEntityValue = TargetEntity.Get(executionContext);
As soon as I add that line my workflow activity will no longer execute. When I run it the status will show "Succeeded" but nothing in the workflow will have run, not even the trace at the beginning of the workflow to say the workflow has been entered. There is nothing in the Diag Logs. When I run the SQL Profiler there are only a few statements added to the AsyncBase table showing that the workflow has run and instantly finished.
If I remove the above line the workflow runs ok. I am wondering what I am doing wrong here? Why would reading an input parameter cause CRM not to do anything in the workflow?
It is almost like somehow the code isnt entering its main method.
[Input("Entity")]
[RequiredArgument]
public InArgument<string> TargetEntity { get; set; }
protected override void Execute(CodeActivityContext executionContext)
{
// Create the tracing service
ITracingService tracingService = executionContext.GetExtension<ITracingService>();
if (tracingService == null)
{
throw new InvalidPluginExecutionException("Failed to retrieve tracing service.");
}
tracingService.Trace("Entered TestWorkflow.Execute(), Activity Instance Id: {0}, Workflow Instance Id: {1}",
executionContext.ActivityInstanceId,
executionContext.WorkflowInstanceId);
string targetEntityValue = TargetEntity.Get<string>(executionContext);
}
Ok well this was very strange. In the end I got desperate so I created a new project with a new assembly name, added the code back in and deployed and now it is working fine.
So.. I think either:
This is my first custom workflow plugin so perhaps I set up the original project wrong somehow and for some reason it was not entering its main method.
There is some kind of issue with the deployment of the original plugin on the CRM server.
Quite why CRM would run a plugin and return status of "Success" without running the main method I am not sure.
if you want to read a Custom Workflow Activity parameter, the right syntax is:
string targetEntityValue = TargetEntity.Get<string>(executionContext);

Can the same application be both publisher/subscriber with NServiceBus?

I am new at messaging architectures, so I might be going at this the wrong way. But I wanted to introduce NServiceBus slowly in my team by solving a tiny problem.
Appointments in Agenda's have states. Two users could be looking at the same appointment in the same agenda, in the same application. They start this application via a Remote session on a central server. So if user 1 updates the state of the appointment, I'd like user 2 to see the new state 'real time'.
To simulate this or make a proof of concept if you will, I made a new console application. Via NuGet I got both NServiceBus and NServiceBus.Host, because as I understood from the documentation I need both. And I know in production code it is not recommended to put everything in the same assembly, but the publisher and subscriber will most likely end up in the same assembly though...
In class Program method Main I wrote the following code:
BusConfiguration configuration = new BusConfiguration();
configuration.UsePersistence<InMemoryPersistence>();
configuration.UseSerialization<XmlSerializer>();
configuration.UseTransport<MsmqTransport>();
configuration.TimeToWaitBeforeTriggeringCriticalErrorOnTimeoutOutages(new TimeSpan(1, 0, 0));
ConventionsBuilder conventions = configuration.Conventions();
conventions.DefiningEventsAs(t => t.Namespace != null
&& t.Namespace.Contains("Events"));
using (IStartableBus bus = Bus.Create(configuration))
{
bus.Start();
Console.WriteLine("Press key");
Console.ReadKey();
bus.Publish<Events.AppointmentStateChanged>(a =>
{
a.AppointmentID = 1;
a.NewState = "New state";
});
Console.WriteLine("Event published.");
Console.ReadKey();
}
In class EndPointConfig method Customize I added:
configuration.UsePersistence<InMemoryPersistence>();
configuration.UseSerialization<XmlSerializer>();
configuration.UseTransport<MsmqTransport>();
ConventionsBuilder conventions = configuration.Conventions();
conventions.DefiningEventsAs(t => t.Namespace != null
&& t.Namespace.Contains("Events"));
AppointmentStateChanged is a simple class in the Events folder like so:
public class AppointmentStateChanged: IEvent {
public int AppointmentID { get; set; }
public string NewState { get; set; }
}
AppointmentStateChangedHandler is the event handler:
public class AppointmentStateChangedHandler : IHandleMessages<Events.AppointmentStateChanged> {
public void Handle(Events.AppointmentStateChanged message) {
Console.WriteLine("AppointmentID: {0}, changed to state: {1}",
message.AppointmentID,
message.NewState);
}
}
If I start up one console app everything works fine. I see the handler handle the event. But if I try to start up a second console app it crashes with: System.Messaging.MessageQueueException (Timeout for the requested operation has expired). So i must be doing something wrong and makes me second guess that I don't understand something on a higher level. Could anyone point me in the right direction please?
Update
Everthing is in the namespace AgendaUpdates, except for the event class which is in the AgendaUpdates.Events namespace.
Update 2
Steps taken:
Copied AgendaUpdates solution (to AgendaUpdates2 folder)
In the copy I changed MessageEndpointMappings in App.Config the EndPoint attribute to "AgendaUpdates2"
I got MSMQ exception: "the queue does not exist or you do not have sufficient permissions to perform the operation"
In the copy I added this line of code to EndPointConfig: configuration.EndpointName("AgendaUpdates2");
I got MSMQ exception: "the queue does not exist or you do not have sufficient permissions to perform the operation"
In the copy I added this line of code to the Main methodin the Program class:
configuration.EndpointName("AgendaUpdates2");
Got original exception again after pressing key.
--> I tested it by starting 2 visual studio's with the original and the copied solution. And then start both console apps in the IDE.
I'm not exactly sure why you are getting that specific exception, but I can explain why what you are trying to do fails. The problem is not having publisher and subscriber in the same application (this is possible and can be useful); the problem is that you are running two instances of the same application on the same machine.
NServiceBus relies on queuing technology (MSMQ in your case), and for everything to work properly each application needs to have its own unique queue. When you fire up two identical instances, both are trying to share the same queue.
There are a few things you can tinker with to get your scenario to work and to better understand how the queuing works:
Change the EndPointName of your second instance
Run the second instance on a separate machine
Separate the publisher and subscriber into separate processes
Regardless of which way you go, you will need to adjust your MessageEndpointMappings (on the consumer/subscriber) to reflect where the host/publisher queue lives (the "owner" of the message type):
http://docs.particular.net/nservicebus/messaging/message-owner#configuring-endpoint-mapping
Edit based on your updates
I know this is a test setup/proof of concept, but it's still useful to think of these two deployments (of the same code) as publisher/host and subscriber/client. So let's call the original the host and the copy the client. I assume you don't want to have each subscribe to the other (at least for this basic test).
Also, make sure you are running both IDEs as Administrator on your machine. I'm not sure if this is interfering or not.
In the copy I changed MessageEndpointMappings in App.Config the EndPoint attribute to "AgendaUpdates2" I got MSMQ exception: "the queue does not exist or you do not have sufficient permissions to perform the operation"
Since the copy is the client, you want to point its mapping to the host. So this should be "AgendaUpdates" (omit the "2").
In the copy I added this line of code to EndPointConfig: configuration.EndpointName("AgendaUpdates2"); I got MSMQ exception: "the queue does not exist or you do not have sufficient permissions to perform the operation"
In the copy I added this line of code to the Main methodin the Program class: configuration.EndpointName("AgendaUpdates2"); Got original exception again after pressing key
I did not originally notice this, but you don't need to configure the endpoint twice. I believe your EndPointConfig is not getting called, as it is only used when hosting via the NSB host executable. You can likely just delete this class.
This otherwise sounds reasonable, but remember that your copy should not be publishing if its the subscriber, so don't press any keys after it starts (only press keys in the original).
If you want to publisher to also be the receiver of the message, you want to specify this in configuration.
This is clearly explained in this article, where the solution to your problem is completely at the end of the article.

Using EventLog in ClickOnce application

I've got a library that I use across multiple ClickOnce applications. In the event of an error in this library I would like to write the error to the windows EventLog.
I found a KB article on how but it seems that this requires administrator permissions to search the for the source. Specifically it chokes when trying to search the Security event log.
Is there anyway to work around this and write to the event log in a ClickOnce application? I saw one person trying to write to a known source, but they didn't seem to be able to find a source that was consistently available.
EDIT:
Based on answers here I create an program that's included with my application that I can run on the first run to set up the event source that can get admin privileges. However once the source is created it seems I still cannot write to it.
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
if (!EventLog.SourceExists("ATE"))
{
EventLog.CreateEventSource("ATE", "Application");
}
}
Is properly creates a source (which is equivalent to the registry edit provided by Yannick Blondeau). When I write to the source in my non-elevated application I receive an new error, but it still doesn't work. The new error is:
Cannot open log for source 'ATE'. You may not have write access.
EDIT 2:
I've now been trying to get it to work through registry edits on the CustomSD key. I tried adding (A;;0x7;;;AU) to give authenticated users full access but it didn't seem to have any effect.
Unfortunately, the event source requires administrative priveledges to be created. However, you don't need admin rights to write to the event log, or read from it.
There are two ways around this.
You add the new event source when you install the application as an administrator.
You create a simple app that you run as an admin to configure your application. This could even be included in the installer.
If you don't have or want an installer, the load the app onto the computer as an admin and run the program once. Your app startup should configure the event source if it isn't already there anyway, right? (Okay, that's three ways.)
This code snippet is from microsoft: http://msdn.microsoft.com/en-us/library/system.diagnostics.eventlog.aspx and is designed to be run as an admin user.
using System;
using System.Diagnostics;
using System.Threading;
class MySample
{
public static void Main()
{
// Create the source, if it does not already exist.
if (!EventLog.SourceExists("MySource"))
{
//An event log source should not be created and immediately used.
//There is a latency time to enable the source, it should be created
//prior to executing the application that uses the source.
//Execute this sample a second time to use the new source.
EventLog.CreateEventSource("MySource", "MyNewLog");
Console.WriteLine("CreatedEventSource");
Console.WriteLine("Exiting, execute the application a second time to use the source.");
// The source is created. Exit the application to allow it to be registered.
return;
}
// Create an EventLog instance and assign its source.
EventLog myLog = new EventLog();
myLog.Source = "MySource";
// Write an informational entry to the event log.
myLog.WriteEntry("Writing to event log.");
}
}
I know it may not be exactly what you were after, but I reckon it's the only way to do this.
EDIT 1: Added some more code
public class EventLogger
{
private const string logName = "Application";
private static string appName = "";
private static bool sourceExists = false;
public static string AppName
{
get { return appName; }
set { appName = value; }
}
public static void Init(string appName)
{
AppName = appName;
sourceExists = EventLog.SourceExists(AppName);
if (!sourceExists)
{
EventLog.CreateEventSource(AppName, logName);
sourceExists = true;
}
}
private static void Write(string entry, EventLogEntryType logType, int eventID)
{
if (sourceExists)
{
EventLog.WriteEntry(AppName, entry, logType, eventID);
}
}
public static void Warning(string entry) { Write(entry, EventLogEntryType.Warning, 200); }
public static void Warning(string entry, int eventID) { Write(entry, EventLogEntryType.Warning, eventID); }
public static void Error(string entry) { Write(entry, EventLogEntryType.Error, 300); }
public static void Error(string entry, int eventID) { Write(entry, EventLogEntryType.Error, eventID); }
public static void Info(string entry) { Write(entry, EventLogEntryType.Information, 100); }
public static void Info(string entry, int eventID) { Write(entry, EventLogEntryType.Information, eventID); }
}
This is the way that I have implemented my EventLogger class which is in use in a production application.
If you could post your code we can do a comparison.
One thing that occurs to me is that when I create my source, I use the application name, but stick with the Application logfile. Are you also attempting to create a new logfile. If so check that it is created in the event viewer.
EDIT 2: Impersonate User with a user token value of zero
This is a bit of a stumper.
Try this code, wrapped around the event writing code.
System.Security.Principal.WindowsImpersonationContext wic = System.Security.Principal.WindowsIdentity.Impersonate(IntPtr.Zero);
// your code to write to event log or any to do something which needs elevated permission--
wic.Undo();
I haven't tried this, simply because my code is working, it comes from here: http://sharenotes.wordpress.com/2008/03/18/cannot-open-log-for-source-you-may-not-have-write-access/
An alternative approach to this is to download the ClickOnce setup.exe file, right click and run as administrator. Not very satisfactory but seems to work.
In the ClickOnce documentation, it is said that if the user running a ClickOnce application is not an administrator, the application will fail writing to the event log.
Full quote:
After a ClickOnce deployment, the application will create an event
source if it does not already exist when the application attempts to
write an event to the Event log. If the user is not an administrator,
the attempt fails and the application will not log any events. In this
case, you can create the event source manually.
To create the event source manually, you will have to add an entry in the registry like this one during your deployment process:
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Application\YourApp]
"EventMessageFile"="%SystemRoot%\\Microsoft.NET\\Framework\\v2.0.50727\\EventLogMessages.dll"
This approach will limit the elevation need at the deployment phase.

getting an exception when refreshing the configuration in memory on change to external config file

I have a windows service which reads the config settings from an external file which is located at a different path than the path to the executable for the windows service. the windows service uses a FileSystemWatcher to monitor the changes to the external config file and when it the config file is changed, it should refresh the settings in memory by reading the updated settings from the config file. but this is where I am getting an exception "ConfigurationErrorsException" and the message is "An error occurred creating the configuration section handler for appSettings: The process cannot access the file 'M:\somefolder\WindowsService1.Config' because it is being used by another process." and the inner exception is actually "IOException" with same message. here is the code. I am not sure what is wrong with the code. Please help.
protected void watcher_Changed(object sender, FileSystemEventArgs e)
{
ConfigurationManager.RefreshSection(ConfigSectionName);
WriteToEventLog(ConfigKeyCheck);
if (FileChanged != null)
FileChanged(this, EventArgs.Empty);
}
private void WriteToEventLog(string key)
{
if (EventLog.SourceExists(ServiceEventSource))
{
EventLog.WriteEntry(ServiceEventSource,
string.Format("key:{0}, value:{1}", key, ConfigurationManager.AppSettings[key]));
}
}
You should expect that IO exceptions occur if trying to re-read the configuration section upon every detected change. For example, the file could be locked (as in your case), or write could be only partially completed. You should just put the code in a try/catch block, catch IOExceptions (and maybe more), and retry the refresh later, perhaps after a timer elapses.
changed the code as per Jacob's suggestion and it works now (I tried catching only IOException, but it didn't work). if exception occurs more than 3 times, then the exception is swallowed, but any subsequent reads from config file will throw unhandled exception forcing the service to stop. hopefully that situation won't occur.
private void WriteToEventLog(string key)
{
try
{
if (EventLog.SourceExists(ServiceEventSource))
{
EventLog.WriteEntry(ServiceEventSource,
string.Format("key:{0}, value:{1}", key, ConfigurationManager.AppSettings[key]));
_configReadCount = 0;
}
}
catch (Exception)
{
System.Threading.Thread.Sleep(TimeSpan.FromMinutes(1)); //sleep for a minute and try again
_configReadCount++;
if (_configReadCount <= 3) //try 3 times
WriteToEventLog(ConfigKeyCheck);
}
}

Categories

Resources