How can I log LoggingChannel LogMessages to the StorageFile? - c#

I want to add logging of exceptions to my Windows Store App. Based on an idea from here, I've started off with this code in App.xaml.cs:
sealed partial class App : Application
{
private LoggingChannel channel;
private LoggingSession session;
/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
public App()
{
this.InitializeComponent();
this.Suspending += OnSuspending;
channel = new LoggingChannel("PlatypiChannel");
session = new LoggingSession("PlatypiSession");
session.AddLoggingChannel(channel, LoggingLevel.Error);
UnhandledException += Application_UnhandledException;
}
async private void Application_UnhandledException(object sender, UnhandledExceptionEventArgs ex)
{
ex.Handled = true;
String exceptionCornucopia = String.Format("Message0 == {0}; Message1 == {1}; HResult == {2}; Inner Ex == {3}; StackTrace == {4}", ex.Message, ex.Exception.Message, ex.Exception.HResult, ex.Exception.InnerException, ex.Exception.StackTrace);
channel.LogMessage(exceptionCornucopia, LoggingLevel.Error);
// not seeing how this saves the channel's logged messages...???
StorageFile logFile = await session.SaveToFileAsync(ApplicationData.Current.LocalFolder, "CrashLog");
}
As the comment indicates, it seems to me the last line simply saves a file named "CrashLog" to the LocalFolder. But how do the LogMessages get into that file? There is obviously a key piece missing here.

I know that this question has been open for a long time, but I just want to provide an answer for anyone else finding this.
The secret here is that the LogMessages are all written into the LoggingChannel, which itself has previously been registered with the LoggingSession:
session.AddLoggingChannel(channel, LoggingLevel.Error);
When the session is then saved to a file, it obviously knows about the associated channels and where to search for pending log messages.

Related

How to dispose of Runspace and PowerShell objects from a WPF project using C#

I receive the following error after running my WPF application for the fourth consecutive time:
Fail to create a runspace because you have exceeded the maximum number
of connections allowed : 3 for the policy party : MaxConcurrency.
Please close existing runspace and try again.
This error does not occur when opening the Runspace and PowerShell objects through a using (Runspace rs = RunspaceFactory.CreateInstance())... statement, as the resources seem to be properly disposed.
However, the way that this app is structured I would like to keep the Runspace/PowerShell object open between transactions because it takes a long time to create a remote session with Office 365 and I cannot keep users waiting to re-establish a remote session between every transaction.
When I keep these objects alive I then have to dispose of them manually as they are not contained within a using statement. I am using the following code:
namespace O365Wrapper
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
public App()
{
InitializeComponent();
}
[STAThread]
public static void Main()
{
App app = new App();
cOofSettingsView view = new cOofSettingsView();
app.Run(view);
Thread.GetDomain().UnhandledException += new UnhandledExceptionEventHandler(OnUnhandledException);
AppDomain.CurrentDomain.ProcessExit += new EventHandler(OnProcessExit);
}
public static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
var ex = (Exception) e.ExceptionObject;
MessageBox.Show(ex.Message);
Environment.Exit(System.Runtime.InteropServices.Marshal.GetHRForException(ex));
}
public static void OnProcessExit(object sender, EventArgs e)
{
cPSConnection.DisposePowerShellEnvironment();
}
}
}
And in cPSConnection:
private static void InitializePowerShellEnvironment()
{
if (_psrunspace == null || _psshell == null)
{
_psrunspace = RunspaceFactory.CreateRunspace();
_psrunspace.Open();
_psshell = PowerShell.Create();
_psshell.Runspace = _psrunspace;
CreatePowerShellSession();
}
}
public static void DisposePowerShellEnvironment()
{
if (_psshell != null)
{
_psshell.Dispose();
}
if (_psrunspace != null)
{
_psrunspace.Dispose();
}
_psshell = null;
_psrunspace = null;
}
According to msdn the Runspace.Dispose method calls Runspace.Close if it has not been closed already.
One last item to note: _psshell and _psrunspace are only instantiated one time there are no incidental duplicate items created that might be lingering - the cPSConnection class is using the Singleton pattern.
The code that I currently have in place is an accumulation of borrowed code from already researching this problem, and I am still having no luck. Not sure where else to look. Any help would be much appreciated. Thanks!

Debugging Program Keeps Sending Emails After Exit

I have the below code which I use as part of a program which reads some event logs.
After stepping through the code this morning to test something out, I keep getting error messages emailed to me:
EventLogHelper error occurred in QueryEvents
Exception: The RPC server is unavailable
User:
Client: D7-089
The process is not running on my machine, and I only stepped through the method in question once or twice. However The messages just keep coming. The space of time between each message seems to vary, and I have received at least 15 now.
How is this possible? I feel that if the program is sending me emails, it must be executing somewhere, but I cannot see it in the Processes tab in Task Manager, and I tried ending all Visual Studio related processes without any joy.
I restarted the PC on which VS was running, but I am still receiving the emails.
Main()
public static void Main()
{
var eh = new EventLogHelper();
var eventFired = eh.CheckEvents();
}
EventLogHelper.cs
public readonly string PcName;
private readonly int Timespan;
private readonly string Filter;
private static EventLogSession Session;
/// <summary>
/// ctor
/// </summary>
public EventLogHelper()
{
Timespan = 30000; // 30 seconds
PcName = "D7-089"; // This is usually "Environment.MachineName", but specified it here for testing
Filter = $"*[System[(EventID='5061' or EventID='5058') and TimeCreated[timediff(#SystemTime) <= {Timespan}]]]";
}
CheckEvents
/// <summary>
/// Checks the event logs for remote pc and returns true if any of the events we are interested in fired
/// </summary>
/// <returns></returns>
public bool CheckEvents()
{
var query = BuildQuery(PcName, Filter);
for (var i = 0; i < 60; i++)
{
var logs = QueryEvents(query);
var events = ReadLogs(logs);
if (events > 0)
{
return true;
}
System.Threading.Thread.Sleep(1000);
}
return false;
}
BuildQuery
/// <summary>
/// Builds an EventLogQuery for the given pcname and filter. This should be set up with a user who has admin rights
/// </summary>bh
private static EventLogQuery BuildQuery(string pcName, string filter)
{
try
{
using (var pw = GetPassword())
{
Session = new EventLogSession(
pcName,
"DOMAIN",
"USER",
pw,
SessionAuthentication.Default);
}
return new EventLogQuery("Security", PathType.LogName, filter)
{ Session = Session };
}
catch (Exception ex)
{
Email.Send($"EventLogHelper error occurred in BuildQuery \n\n Exception: {ex.Message} \n\n User: {Program.UserName} \n\n Client: {pcName}");
Environment.Exit(Environment.ExitCode);
return null;
}
}
QueryEvents
This is where the error is occurring. I stepped through this method 2 times at most and as I type this question I am still getting error emails through.
/// <summary>
/// Execute the given EventLogQuery
/// </summary>
private EventLogReader QueryEvents(EventLogQuery query)
{
try
{
return new EventLogReader(query);
}
catch (Exception ex)
{
Email.Send($"EventLogHelper error occurred in QueryEvents \n\n Exception: {ex.Message} \n\n User: {Program.UserName} \n\n Client: {PcName}");
Environment.Exit(Environment.ExitCode);
return null;
}
}
ReadLogs
/// <summary>
/// Read the given EventLogReader and return the amount of events that match the IDs we are looking for
/// </summary>
/// <param name="logReader"></param>
/// <returns></returns>
private int ReadLogs(EventLogReader logReader)
{
var count5058 = 0;
var count5061 = 0;
EventRecord entry;
try
{
while ((entry = logReader.ReadEvent()) != null)
{
if (entry.Id == 5058)
{
count5058++;
}
else
{
count5061++;
}
}
}
catch (Exception ex)
{
Email.Send($"EventLogHelper error occurred in ReadLogs \n\n Exception: {ex.Message} \n\n User: {Program.UserName} \n\n Client: {PcName}");
Environment.Exit(Environment.ExitCode);
}
return count5058 + count5061;
}
It turns out that this was being caused by a stupid mistake on my part.
This program has a Post-Build Event which is called with:
if $(ConfigurationName) == Release ("$(ProjectDir)PostBuildRelease.bat" "$(TargetDir)" #(VersionNumber) "$(TargetFileName)" "$(TargetName)")
So it only runs when VS build configuration is set to Release.
PostBuildRelease.bat simply copies the resuling assembly to the live location, ready for users to have copied to their desktops at logon.
Whilst testing my app, I foolishly edited the source code to query a specific PC, and then stepped through the code.
However, the build configuration was set to Release, So once the assembly was built ready to be debugged, it was automatically copied into the live executable location and therefore also copied to user's desktops at logon.
If the code is run with a hard-coded PcName where that PC is not the current machine, the event query appears to fail with the above error message.
So all of the emails I receiving were being sent out because the program was actually being executed on user PCs. However because PcName was hard-coded in, it always looked like it was coming from my instance of the program!
The lesson here is to always be aware of which build configuration is currently selected, especially if a Post-Build event is specified.

WCF+ duplex + Windows Form Application (ButtonClick) Trouble

I have WCF classes and now project to do. My teacher gave as to write app with streaming and duplex (I know that is impossible, but I found backdoor from this situation - I'm sending pics under 60KB).
My code worked well so far as I start wrote my GUI in Windows Form Application.
When I'm testing it via console - everything work well. But, when I want to use buttons in my GUI i have this exception:
An unhandled exception of type 'System.TimeoutException' occurred in
mscorlib.dll Additional information: This request operation sent to
net.tcp://localhost:7756/Przesylanie did not receive a reply within
the configured timeout (00:00:59.9740007). The time allotted to this
operation may have been a portion of a longer timeout. This may be
because the service is still processing the operation or because the
service was unable to send a reply message. Please consider
increasing the operation timeout (by casting the channel/proxy to
IContextChannel and setting the OperationTimeout property) and ensure
that the service is able to connect to the client.
Here bunch of code:
Service + IService (due to limitation of Stack I put it to one file):
public void WyslijstrumienNaSerwer()
{
IPrzesylanieCallback callback = OperationContext.Current.GetCallbackChannel<IPrzesylanieCallback>();
string sciezka = #"E:\5 semestr\Fras\Se płotek\Lab6\WcfServiceContractZadanie2\Host\bin\Pliki\" + "plik_odebrany.jpg";
string filePath = Path.Combine(System.Environment.SystemDirectory, sciezka);
Console.WriteLine("start");
callback.WyslijStrumien(filePath);
Console.WriteLine(filePath);
Console.WriteLine("meta");
}
namespace WcfServiceContractZadanie2
{
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IPrzesylanieCallback))]
public interface IPrzesylanie
{
[OperationContract]
void WyslijstrumienNaSerwer();
}
[ServiceContract]
public interface IPrzesylanieCallback
{
[OperationContract(IsOneWay = true)]
void WyslijStrumien(string filePath);
}
}
Client + callback + form + References.cs:
namespace Aplikacja
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
PrzesylanieClient klient = new PrzesylanieClient(new InstanceContext(new PrzesylanieCallback()), "NetTcpBinding_IPrzesylanie");
klient.WyslijstrumienNaSerwer();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
public class PrzesylanieCallback : Referencja1.IPrzesylanieCallback
{
public void WyslijStrumien(string filePath)
{
Form1 o1 = new Form1();
// Pobieranie obrazka od klienta
string sciezka = #"E:\5 semestr\Fras\Se płotek\Lab6\WcfServiceContractZadanie2\Aplikacja\bin\Pliki\" + o1.wybrany();
string filePathZrodla = Path.Combine(System.Environment.SystemDirectory, sciezka);
//Otwieranie obrazka
Stream strumien = null;
try
{
FileStream imageFile = File.OpenRead(filePathZrodla);
strumien = (Stream)imageFile;
}
catch(IOException ioe)
{
Console.WriteLine("Wyjatek otwierania pliku: {0}", ioe.ToString());
Console.ReadKey();
throw ioe;
}
// Zapisywanie obrazka
o1.SaveFile(strumien, filePath);
}
}
}
private void btnPrzeslijPlikNaSerwer_Click(object sender, EventArgs e)
{
PrzesylanieClient klient = new PrzesylanieClient(new InstanceContext(new PrzesylanieCallback()), "NetTcpBinding_IPrzesylanie");
klient.WyslijstrumienNaSerwer();
}
public void WyslijstrumienNaSerwer() {
base.Channel.WyslijstrumienNaSerwer();
}
I wrote methods SaveFile which works correctly.
As you see, I'm testing my code in the begining of Main function in Client and that works well.
But when I'm using the same code in Forms it does not work. Compiler is returnig me to References.cs and gives me exception I mentioned earlier.
Waiting for any respone!
My answer does not solve your exception issue; however, it might prevent you from getting a very bad grade. Streaming AND duplex are both supported in WCF.
Streaming:
https://msdn.microsoft.com/en-us/library/ms733742(v=vs.110).aspx
Duplex:
https://msdn.microsoft.com/en-us/library/ms731064(v=vs.110).aspx

Reasons for long-running WCF service dying?

I have a long-running WCF service, hosted in IIS, that handles printing without any user interaction. After about 2 hours of looping through 1000 print jobs, the WCF service just seems to die. The log file I track indicates that the last print job was sent to the printer, but it never returns success nor failure (again, according to the log file).
From the log file, it would normally say:
2015-12-17 19:00:23,673 [27] INFO Barn.WCF.SysPrsPrintServer - Sending Print Job... PrintDocumentId=168;PrintSectionId=742;CustomerId=112702;DeliveryAddressId=474984;DocumentName=/SystemProcesses/Reports/CertificateOfRegistry;PrinterLocation=HB-MFP1;PrinterPaperSource=Cassette 3
2015-12-17 19:00:32,626 [27] INFO Barn.WCF.SysPrsPrintServer - PdfPrintHandler.Print: Printer HB-MFP1 indicates the print job is complete.
But the last log file entry is:
2015-12-17 19:00:32,688 [27] INFO Barn.WCF.SysPrsPrintServer - Sending Print Job... PrintDocumentId=169;PrintSectionId=742;CustomerId=112702;DeliveryAddressId=474984;DocumentName=/SystemProcesses/Reports/CertificateOfRegistry;PrinterLocation=HB-MFP1;PrinterPaperSource=Cassette 3
Can you see how this might be possible that I get no message back at all? Is it possible the WCF service just died or IIS recycled the app pool or something like that? So without further ado, here is my class:
using System;
using System.Collections.Generic;
using System.Drawing.Printing;
using System.IO;
using System.Threading;
using log4net;
using Aspose.Pdf.Facades;
namespace Barn.API.Print.PrintHandlers
{
public class PdfPrintHandler
{
private const int LargePdfByteCount = 3000000;
private readonly ILog _log;
private readonly ManualResetEvent _resetEvent = new ManualResetEvent(false);
public PdfPrintHandler(ILog log)
{
_log = log;
var license = new Aspose.Pdf.License();
var pathLicense = AppDomain.CurrentDomain.SetupInformation.ApplicationBase + "bin\\Aspose.Pdf.lic";
license.SetLicense(pathLicense);
}
/// <summary>
/// Prints the specified stream.
/// </summary>
/// <param name="stream">The stream.</param>
/// <param name="printerSettings">The printer settings.</param>
/// <param name="pageSettings">The page settings.</param>
/// <param name="timeout">The timeout.</param>
/// <param name="errors"></param>
/// <returns>The status of the print job.</returns>
public PrintJobStatusEnum Print(Stream stream, PrinterSettings printerSettings, PageSettings pageSettings, PrintDocumentModel printJob, int timeout, out List<string> errors)
{
errors = new List<string>();
// Reset the wait handler to make sure the event is not signaled
_resetEvent.Reset();
// Set attributes for printing
var viewer = new PdfViewer
{
AutoResize = true,
AutoRotate = true,
PrintPageDialog = false,
PrintAsImage = false
};
// Add an event listener when print job sent to printer
viewer.EndPrint += ViewerOnEndPrint;
//Print document using printer and page settings
try
{
_log.InfoFormat("Sending Print Job... PrintDocumentId={0};PrintSectionId={1};CustomerId={2};DeliveryAddressId={3};DocumentName={4};PrinterLocation={5};PrinterPaperSource={6}",
printJob.PrintDocumentId,
printJob.PrintSectionId != null ? printJob.PrintSectionId.ToString() : "null",
printJob.CustomerId != null ? printJob.CustomerId.ToString() : "null",
printJob.DeliveryAddressId != null ? printJob.DeliveryAddressId.ToString() : "null",
printJob.DocumentName != null ? printJob.DocumentName : "null",
printJob.PrinterLocation != null ? printJob.PrinterLocation : "null",
pageSettings.PaperSource.SourceName != null ? pageSettings.PaperSource.SourceName : "null");
// Print
if (stream.Length <= LargePdfByteCount)
{
// Bind the stream then print
viewer.BindPdf(stream);
viewer.PrintDocumentWithSettings(pageSettings, printerSettings);
}
else
{
// Use a more efficient printing method with larger documents
viewer.PrintLargePdf(stream, pageSettings, printerSettings);
}
// Block until the event finishes or timeout reached
_resetEvent.WaitOne(TimeSpan.FromMinutes(timeout));
// Check the print status
if (viewer.PrintStatus != null)
{
// An exception was thrown
var ex = viewer.PrintStatus as Exception;
if (ex != null)
{
// Get exception message
_log.Error("PdfPrintHandler.Print: Print Error: " + ex.Message + ex.StackTrace, ex);
errors.Add(ex.Message);
}
return PrintJobStatusEnum.Error;
}
else
{
// No errors were found. Printing job has completed successfully
_log.InfoFormat("PdfPrintHandler.Print: Printer {0} indicates the print job is complete.", printerSettings.PrinterName);
return PrintJobStatusEnum.Printed;
}
}
catch (Exception e)
{
_log.Error("PdfPrintHandler.Print Exception: " + e.Message + e.StackTrace, e);
errors.Add(e.Message);
}
finally
{
viewer.Close();
}
return PrintJobStatusEnum.Error;
}
private void ViewerOnEndPrint(object sender, PrintEventArgs printEventArgs)
{
// Signal the event is finished
_resetEvent.Set();
}
}
}
WCF Service Interface:
using System.ServiceModel;
namespace Barn.WCF
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "ISysPrsPrintServer" in both code and config file together.
[ServiceContract]
public interface ISysPrsPrintServer
{
[OperationContract]
string DoWork(string data);
}
}
I would suggest checking the operation contracts of your print methods. I have seen WCF services fail out when the isOneWay attribute is not set or is set to false on a void method. try something like this
[OperationContract(IsOneWay=true)]
public void PrintDocumentWithSettings(PageSettings settings, PrinterSettings settings);
Here it is in the Windows event log:
A worker process with process id of '3236' serving application pool
'testws.mydomain.ca' was shutdown due to inactivity. Application Pool
timeout configuration was set to 20 minutes. A new worker process
will be started when needed.
So IIS seems to have thought that there was 20 minutes of inactivity because the WCF service is called asynchronously. The process is working away, but IIS decided to recycle the application pool in the middle of the process running.
I changed the settings in IIS on the application pool according to this:
https://serverfault.com/questions/333907/what-should-i-do-to-make-sure-that-iis-does-not-recycle-my-application

Running windows service to watch service running grow memory (leak)

I have checked all posts here, but can't find a solution for me so far.
I did setup a small service that should only watch if my other services I want to monitor runs, and if not, start it again and place a message in the application eventlog.
The service itself works great, well nothing special :), but when I start the service it use around 1.6MB of RAM, and every 10 seconds it grow like 60-70k which is way to much to live with it.
I tried dispose and clear all resources. Tried work with the System.Timers instead of the actual solution, but nothing really works as I want it, memory still grows.
No difference in debug or release version and I am using it on .Net 2, don't know if it make a difference to you 3,3.5 or 4.
Any hint?!
using System;
using System.IO;
using System.Diagnostics;
using System.ServiceProcess;
using System.Threading;
using System.Timers;
namespace Watchguard
{
class WindowsService : ServiceBase
{
Thread mWorker;
AutoResetEvent mStop = new AutoResetEvent(false);
/// <summary>
/// Public Constructor for WindowsService.
/// - Put all of your Initialization code here.
/// </summary>
public WindowsService()
{
this.ServiceName = "Informer Watchguard";
this.EventLog.Source = "Informer Watchguard";
this.EventLog.Log = "Application";
// These Flags set whether or not to handle that specific
// type of event. Set to true if you need it, false otherwise.
this.CanHandlePowerEvent = false;
this.CanHandleSessionChangeEvent = false;
this.CanPauseAndContinue = false;
this.CanShutdown = false;
this.CanStop = true;
if (!EventLog.SourceExists("Informer Watchguard"))
EventLog.CreateEventSource("Informer Watchguard", "Application");
}
/// <summary>
/// The Main Thread: This is where your Service is Run.
/// </summary>
static void Main()
{
ServiceBase.Run(new WindowsService());
}
/// <summary>
/// Dispose of objects that need it here.
/// </summary>
/// <param name="disposing">Whether or not disposing is going on.</param>
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
}
/// <summary>
/// OnStart: Put startup code here
/// - Start threads, get inital data, etc.
/// </summary>
/// <param name="args"></param>
protected override void OnStart(string[] args)
{
base.OnStart(args);
MyLogEvent("Init");
mWorker = new Thread(WatchServices);
mWorker.Start();
}
/// <summary>
/// OnStop: Put your stop code here
/// - Stop threads, set final data, etc.
/// </summary>
protected override void OnStop()
{
mStop.Set();
mWorker.Join();
base.OnStop();
}
/// <summary>
/// OnSessionChange(): To handle a change event from a Terminal Server session.
/// Useful if you need to determine when a user logs in remotely or logs off,
/// or when someone logs into the console.
/// </summary>
/// <param name="changeDescription"></param>
protected override void OnSessionChange(SessionChangeDescription changeDescription)
{
base.OnSessionChange(changeDescription);
}
private void WatchServices()
{
string scName = "";
ServiceController[] scServices;
scServices = ServiceController.GetServices();
for (; ; )
{
// Run this code once every 10 seconds or stop right away if the service is stopped
if (mStop.WaitOne(10000)) return;
// Do work...
foreach (ServiceController scTemp in scServices)
{
scName = scTemp.ServiceName.ToString().ToLower();
if (scName == "InformerWatchguard") scName = ""; // don't do it for yourself
if (scName.Length > 8) scName = scName.Substring(0, 8);
if (scName == "informer")
{
ServiceController sc = new ServiceController(scTemp.ServiceName.ToString());
if (sc.Status == ServiceControllerStatus.Stopped)
{
sc.Start();
MyLogEvent("Found service " + scTemp.ServiceName.ToString() + " which has status: " + sc.Status + "\nRestarting Service...");
}
sc.Dispose();
sc = null;
}
}
}
}
private static void MyLogEvent(String Message)
{
// Create an eEventLog instance and assign its source.
EventLog myLog = new EventLog();
myLog.Source = "Informer Watchguard";
// Write an informational entry to the event log.
myLog.WriteEntry(Message);
}
}
}
Your code may throw an exceptions inside loop, but these exception are not catched. So, change the code as follows to catch exceptions:
if (scName == "informer")
{
try {
using(ServiceController sc = new ServiceController(scTemp.ServiceName.ToString())) {
if (sc.Status == ServiceControllerStatus.Stopped)
{
sc.Start();
MyLogEvent("Found service " + scTemp.ServiceName.ToString() + " which has status: " + sc.Status + "\nRestarting Service...");
}
}
} catch {
// Write debug log here
}
}
You can remove outer try/catch after investigating, leaving using statement to make sure Dispose called even if exception thrown inside.
At a minimum, you need to do this in your logging code since EventLog needs to be Dispose()d. Seems like this resource could be reused rather than new-ed on every call. You could also consider using in your main loop for the ServiceController objects, to make your code more exception-safe.
private static void MyLogEvent(String Message)
{
// Create an eEventLog instance and assign its source.
using (EventLog myLog = new EventLog())
{
myLog.Source = "Informer Watchguard";
// Write an informational entry to the event log.
myLog.WriteEntry(Message);
}
}
This should be moved into the loop, since you don't want to keep a reference to old service handles for the life of your service:
ServiceController[] scServices = ServiceController.GetServices();
You also want to dispose of your reference to EventLog and to the ServiceController instances. As Artem points out, watch out for exceptions that are preventing you from doing this.
Since memory is going up every 10 seconds, it has to be something in your loop.
If memory goes up whether or not you write to the EventLog, then that is not the main problem.
Does memory used ever come down? Ie does the garbage collector kick in after awhile? You could test the GC's effect by doing a GC.Collect() before going back to sleep (though I'd be careful of using it in production).
I am not sure I understand the problem exactly. Is the service you are going to be monitoring always the same. It would appear from your code that the answer is yes, and if that is the case then you can simply create the ServiceController class instance passing the name of the service to the constructor.
In your thread routine you want to continue looping until a stop is issued, and the WaitOne method call returns a Boolean, so a while loop seems to be appropriate. Within the while loop you can call the Refresh method on the ServiceController class instance to get the current state of the service.
The event logging should simple require a call one of the static method EventLog.WriteEntry methods, at minimum passing your message and the source 'Informer Watchguard'
The ServiceController instance can be disposed when you exit from the loop in the thread routine
All this would mean you are creating fewer objects that need to be disposed, and therefore less likely that some resource leak will exist.
Thanks to all suggestions.
Finally the service is stable now with some modifications.
#Steve: I watch many services all beginning with the same name "Informer ..." but I don't know exactly full names, that's why I go this way.

Categories