Photon does not show me any logs when connecting - c#

I'm doing this tutorial and just wrote the first code block. The following is in the tutorial:
At this point, you can save the Launch Scene and hit Play. You should see in the Unity Console a good dozens of logs. Look specifically for "Connected to masterserver." log which indicated that indeed we are now connected and ready to join a room for example.
But I'm not getting any logs when running this line PhotonNetwork.ConnectUsingSettings(_gameVersion);.
public class Launcher : MonoBehaviour
{
#region Public Variables
#endregion
#region Private Variables
/// <summary>
/// Client version number, this number seperates users
/// </summary>
string _gameVersion = "1";
#endregion
#region Monobehavior callbacks
/// <summary>
/// Eary init method of monobehavior
/// </summary>
void Awake()
{
// #NotImportant
// Force Full LogLevel
PhotonNetwork.logLevel = PhotonLogLevel.Full;
PhotonNetwork.networkingPeer.DebugOut = ExitGames.Client.Photon.DebugLevel.ALL; // <---------- added this later but still no logs.
// #Critical
// we don't join the lobby. There is no need to join a lobby to get the list of rooms.
PhotonNetwork.autoJoinLobby = false;
// #Critical
// this makes sure we can use PhotonNetwork.LoadLevel() on the master client and all clients in the same room sync their level automatically
PhotonNetwork.automaticallySyncScene = true;
}
// Use this for initialization
void Start()
{
Connect();
}
/// <summary>
/// Start the connection process.
/// - If already connected, we attempt joining a random room
/// - if not yet connected, Connect this application instance to Photon Cloud Network
/// </summary>
public void Connect()
{
// we check if we are connected or not, we join if we are , else we initiate the connection to the server.
if (PhotonNetwork.connected)
{
// #Critical we need at this point to attempt joining a Random Room. If it fails, we'll get notified in OnPhotonRandomJoinFailed() and we'll create one.
PhotonNetwork.JoinRandomRoom();
}
else
{
// #Critical, we must first and foremost connect to Photon Online Server.
PhotonNetwork.ConnectUsingSettings(_gameVersion);
// <----------------------------- This is reached!
}
}
// Update is called once per frame
void Update()
{
}
#endregion
}
How can I enable full logging?

yes, the latest version of PUN now features logging settings in the Photon Settings themselves. So set it there. The updated tutorial and code should be up very soon on the website.

Related

Detect dead connections with SignalR

SignalR version: SignalR 2.4.1
.Net Framework version: 4.8 (I am not using .Net Core)
SignalR transport: websockets
I am developing a background service for SignalR (PresenceMonitor) where I need to detect whether a connection with specific clientid is alive or not.
I am using the following code for Presence Monitor to start with what I want to achieve:
using System;
using System.Data.Entity.SqlServer;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Microsoft.AspNet.SignalR.Transports;
namespace UserPresence
{
/// <summary>
/// This class keeps track of connections that the <see cref="UserTrackingHub"/>
/// has seen. It uses a time based system to verify if connections are *actually* still online.
/// Using this class combined with the connection events SignalR raises will ensure
/// that your database will always be in sync with what SignalR is seeing.
/// </summary>
public class PresenceMonitor
{
private readonly ITransportHeartbeat _heartbeat;
private Timer _timer;
// How often we plan to check if the connections in our store are valid
private readonly TimeSpan _presenceCheckInterval = TimeSpan.FromSeconds(10);
// How many periods need pass without an update to consider a connection invalid
private const int periodsBeforeConsideringZombie = 3;
// The number of seconds that have to pass to consider a connection invalid.
private readonly int _zombieThreshold;
public PresenceMonitor(ITransportHeartbeat heartbeat)
{
_heartbeat = heartbeat;
_zombieThreshold = (int)_presenceCheckInterval.TotalSeconds * periodsBeforeConsideringZombie;
}
public void StartMonitoring()
{
if (_timer == null)
{
_timer = new Timer(_ =>
{
try
{
Check();
}
catch (Exception ex)
{
// Don't throw on background threads, it'll kill the entire process
Trace.TraceError(ex.Message);
}
},
null,
TimeSpan.Zero,
_presenceCheckInterval);
}
}
private void Check()
{
using (var db = new UserContext())
{
// Get all connections on this node and update the activity
foreach (var trackedConnection in _heartbeat.GetConnections())
{
if (!trackedConnection.IsAlive)
{
continue;
}
Connection connection = db.Connections.Find(trackedConnection.ConnectionId);
// Update the client's last activity
if (connection != null)
{
connection.LastActivity = DateTimeOffset.UtcNow;
}
else
{
// We have a connection that isn't tracked in our DB!
// This should *NEVER* happen
// Debugger.Launch();
}
}
// Now check all db connections to see if there's any zombies
// Remove all connections that haven't been updated based on our threshold
var zombies = db.Connections.Where(c =>
SqlFunctions.DateDiff("ss", c.LastActivity, DateTimeOffset.UtcNow) >= _zombieThreshold);
// We're doing ToList() since there's no MARS support on azure
foreach (var connection in zombies.ToList())
{
db.Connections.Remove(connection);
}
db.SaveChanges();
}
}
}
}
The issue I am facing is here:
// Get all connections on this node and update the activity
foreach (var trackedConnection in _heartbeat.GetConnections())
{
Scanning all the connections when there are large number of connections is deeply affecting the performance of my application and is giving lot of CPU spikes.
In my database, I already have the mapping for connection ids per user. Based on that I already a have field in my cache per user whether that user has any connection in db or not. Those mappings are are already cached. I would scan each of those mappings and would check whether the connection (connection id) for that specific user is is alive or not. I tried looking for ITransportHeartbeat Interface for the same but unfortunately, that interface gives us just these four methods:
//
// Summary:
// Manages tracking the state of connections.
public interface ITransportHeartbeat
{
//
// Summary:
// Adds a new connection to the list of tracked connections.
//
// Parameters:
// connection:
// The connection to be added.
//
// Returns:
// The connection it replaced, if any.
ITrackingConnection AddOrUpdateConnection(ITrackingConnection connection);
//
// Summary:
// Gets a list of connections being tracked.
//
// Returns:
// A list of connections.
IList<ITrackingConnection> GetConnections();
//
// Summary:
// Marks an existing connection as active.
//
// Parameters:
// connection:
// The connection to mark.
void MarkConnection(ITrackingConnection connection);
//
// Summary:
// Removes a connection from the list of tracked connections.
//
// Parameters:
// connection:
// The connection to remove.
void RemoveConnection(ITrackingConnection connection);
}
Ther is no method where I can get the state of connection by connectionid. Is there any way where I can get a specific connection information without scannig all the connetcions. I am aware of the traditional way to get that which could be using this: _heartbeat.GetConnections().Select(b => b.ConnectionId). But that code also will scan all the connections.
I am aware of OnDisconnected event also which we could use on a Hub itself but the OnDisconnected even doesn't guarantee to fire always (browser can close, internet shut down, site restart).
Is there any code which I could hook in my Hub itself to detect the ping done by the Heartbeat API? I could store the last pings per connection (kind of denormalize the way of detecting last ping) and can detect whether that connection is dead or not?
SignalR for .Net Core has something like that:
var heartbeat = Context.Features.Get<IConnectionHeartbeatFeature>();
heartbeat.OnHeartBeat(MyAction,
but I am looking for a similar feature like that in SignalR for .NET Framework.

Where can I find the SSIS script task PreExecute method?

In SSIS (using VS 2013 with latest SSDT) I'm returning a SQL result set to the package and iterating through it with a Foreach ADO Enumerator. In the loop I'd like to have a control flow Script Task call a WCF service.
I have read and understand the tutorial found here but, as referenced here, this tutorial is using the data flow Script Component so I can't use its PreExecute() method.
How do I override the app.config setting programmatically to avoid the problem stated in the tutorial?
using a WCF client, the normal method of configuring the WCF client from the application configuration file doesn't work well.
Edited after answer:
I ended up structuring my code like this.
public ChannelFactory<IMyService> ChannelFactory;
public IMyService Client;
public void PreExecute()
{
//create the binding
var binding = new BasicHttpBinding
{
Security =
{
Mode = BasicHttpSecurityMode.Message,
Transport = {ClientCredentialType = HttpClientCredentialType.Windows}
}
};
//configure the binding
Uri myUri = new Uri(Dts.Variables["myUri"].Value.ToString());
var endpointAddress = new EndpointAddress(myUri);
ChannelFactory = new ChannelFactory<IMyService>(binding, endpointAddress);
//create the channel
Client = ChannelFactory.CreateChannel();
}
public void PostExecute()
{
//close the channel
IClientChannel channel = (IClientChannel)Client;
channel.Close();
//close the ChannelFactory
ChannelFactory.Close();
}
/// <summary>
/// This method is called when this script task executes in the control flow.
/// Before returning from this method, set the value of Dts.TaskResult to indicate success or failure.
/// To open Help, press F1.
/// </summary>
public void Main()
{
PreExecute();
//TODO: code
PostExecute();
Dts.TaskResult = (int)ScriptResults.Success;
}
A ScriptTask does not have a PreExecute method. You'll have to do all the instantiation and binding stuff per iteration of your loop. It's similar to what's happening in the script component example in that the setup happens once and then all the rows stream out. If you were looping over your data flow, it'd have to redo the preexecute methods per loop.
Based on the comments at the end of the article, it sounds like the code controls the configuration and there's no need to modify the app.config. WCF stuff isn't my strong suit so I can't comment on that.

What is the most efficient way to make an application handle hundreds of simultaneous file open requests from Windows Explorer in a single instance?

When I select a bunch of files in Explorer and right click open them (or press enter) I want all the files to be passed to a single instance of my application. I've used named pipes before to pass arguments from secondary instances to an existing global instance, but it seems like doing this for hundreds of program instances simultaneously (not to mention actually loading the application hundreds of times) is far from optimal. Is there a way to get explorer to concatenate the arguments on its own?
edit: I found a copy of the Paint.net 3.36 source code and it uses a memory mapped file to communicate between instances. That seems even more bloated than named pipes though (although it's not as likely to open hundreds of images for editing).
You can do it with shell extension.
Check out Creating Shortcut Menu Handlers
and
Create Namespace Extensions for Windows Explorer with the .NET Framework
I couldn't figure out the shell extension so I went with the named pipes. It seems to perform reasonably well, especially since there seems to be a limit in Windows 7 of how many files you can open with multi-select (although this will solve that: open-more-than-15-files-at-once-on-windows-7)
It took me forever to figure out how to get the timeout behavior to work (had to use a manual signal instead of AsyncWaitHandle). Hopefully this will save somebody some time:
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// The Original Code is the IDPicker project.
//
// The Initial Developer of the Original Code is Matt Chambers.
//
// Copyright 2011 Vanderbilt University
//
// Contributor(s):
//
using System;
using System.IO;
using System.IO.Pipes;
using System.Text;
using System.Threading;
using System.ComponentModel;
using System.Collections.Generic;
/// <summary>
/// A handler for an application to consolidate arguments from multiple instances
/// (within a Timeout period) into a single instance.
/// </summary>
public sealed class SingleInstanceHandler
{
/// <summary>
/// Occurs when the Timeout period has elapsed: the single instance is launched with the consolidated argument list.
/// </summary>
public event EventHandler<SingleInstanceEventArgs> Launching;
/// <summary>
/// Time to wait in milliseconds for additional instances to add their arguments to the first instance's.
/// </summary>
public int Timeout { get; set; }
/// <summary>
/// Constructs a handler for an application to consolidate arguments from multiple instances
/// (within a Timeout period) into a single instance.
/// </summary>
/// <param name="uniqueID">A unique string for the application.</param>
public SingleInstanceHandler (string uniqueID)
{
var rng = new Random(uniqueID.GetHashCode());
byte[] ipcMutexGuidBytes = new byte[16];
byte[] ipcNamedPipeGuidBytes = new byte[16];
rng.NextBytes(ipcMutexGuidBytes);
rng.NextBytes(ipcNamedPipeGuidBytes);
ipcMutexGuid = new Guid(ipcMutexGuidBytes).ToString().Trim('{', '}');
ipcNamedPipeGuid = new Guid(ipcNamedPipeGuidBytes).ToString().Trim('{', '}');
Timeout = 500;
}
/// <summary>
/// Launch a new instance using 'args' or consolidate 'args' into a recent instance.
/// </summary>
public void Connect (string[] args)
{
if (Launching == null)
return; // nothing to do
// create global named mutex
using (ipcMutex = new Mutex(false, ipcMutexGuid))
{
// if the global mutex is not locked, wait for args from additional instances
if (ipcMutex.WaitOne(0))
waitForAdditionalInstances(args);
else
sendArgsToExistingInstance(args);
}
}
private void waitForAdditionalInstances (string[] args)
{
var accumulatedArgs = new List<string>(args);
while (true)
{
var signal = new ManualResetEvent(false);
using (var pipeServer = new NamedPipeServerStream(ipcNamedPipeGuid, PipeDirection.In, -1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous))
{
pipeServer.BeginWaitForConnection(x =>
{
// if timed out, stop waiting for a connection
if (signal.WaitOne(0))
{
signal.Close();
return;
}
pipeServer.EndWaitForConnection(x);
signal.Set();
}, null);
// no client connected to the pipe within the Timeout period
if (!signal.WaitOne(Timeout, true))
{
signal.Set();
break;
}
using (var sr = new StreamReader(pipeServer))
{
int length = Convert.ToInt32(sr.ReadLine());
for (int i = 0; i < length; ++i)
accumulatedArgs.Add(sr.ReadLine());
}
}
// new args have been added to accumulatedArgs, continue loop to listen for another client
}
Launching(this, new SingleInstanceEventArgs(accumulatedArgs.ToArray()));
}
private void sendArgsToExistingInstance (string[] args)
{
var pipeClient = new NamedPipeClientStream(".", ipcNamedPipeGuid, PipeDirection.Out);
// try to connect to the pipe server for the Timeout period
try
{
var sb = new StringBuilder();
sb.AppendLine(args.Length.ToString());
foreach (string arg in args)
sb.AppendLine(arg);
byte[] buffer = Encoding.ASCII.GetBytes(sb.ToString());
pipeClient.Connect(Timeout);
// can this ever happen? if it does, don't handle it like a timeout exception
if (!pipeClient.IsConnected)
throw new Exception("did not throw exception");
pipeClient.Write(buffer, 0, buffer.Length);
}
catch (Exception e)
{
if (!e.Message.ToLower().Contains("time"))
throw;
// no server was running; launch a new instance
Launching(this, new SingleInstanceEventArgs(args));
}
}
private string ipcMutexGuid;
private string ipcNamedPipeGuid;
private Mutex ipcMutex;
}
/// <summary>
/// Stores the consolidated argument list from one or more instances of an application.
/// </summary>
public sealed class SingleInstanceEventArgs : EventArgs
{
public SingleInstanceEventArgs (string[] args) { Args = args; }
/// <summary>
/// The consolidated argument list from one or more instances of an application.
/// </summary>
public string[] Args { get; private set; }
}
How to use it:
static void Main (string[] args)
{
var singleInstanceHandler = new SingleInstanceHandler(Application.ExecutablePath) { Timeout = 200 };
singleInstanceHandler.Launching += (sender, e) =>
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm(e.Args));
};
singleInstanceHandler.Connect(args);
}

Check for internet connection constantly

How can I check for an internet connection constantly in my application and respond if the connection is not available?
Currently I am using:
while(true) {
if(HasConnection()) {
//doSomething..
}
//stop app by 1sec
}
but it seems rather inelegant.
The accepted answer to this question on superuser describes the way the Windows determines if it has network access. You could use a similar method, but I would spawn a separate thread when your application starts that is responsible for doing the check. Have the separate thread perform the check in whatever manner you feel is the best and raise an event if the connection status changes.
You're looking for the NetworkAvailabilityChanged event.
To check for internet connectivity, you can ping a reliable website, such as Google.com.
Note that it is not possible to be notified of every change in internet connectivity (such as an ISP outage).
Use the following code:
public static class LocalSystemConnection
{
[DllImport("wininet.dll", SetLastError=true, CallingConvention = CallingConvention.ThisCall)]
extern static bool InternetGetConnectedState(out ConnectionStates lpdwFlags, long dwReserved);
/// <summary>
/// Retrieves the connected state of the local system.
/// </summary>
/// <param name="connectionStates">A <see cref="ConnectionStates"/> value that receives the connection description.</param>
/// <returns>
/// A return value of true indicates that either the modem connection is active, or a LAN connection is active and a proxy is properly configured for the LAN.
/// A return value of false indicates that neither the modem nor the LAN is connected.
/// If false is returned, the <see cref="ConnectionStates.Configured"/> flag may be set to indicate that autodial is configured to "always dial" but is not currently active.
/// If autodial is not configured, the function returns false.
/// </returns>
public static bool IsConnectedToInternet(out ConnectionStates connectionStates)
{
connectionStates = ConnectionStates.Unknown;
return InternetGetConnectedState(out connectionStates, 0);
}
/// <summary>
/// Retrieves the connected state of the local system.
/// </summary>
/// <returns>
/// A return value of true indicates that either the modem connection is active, or a LAN connection is active and a proxy is properly configured for the LAN.
/// A return value of false indicates that neither the modem nor the LAN is connected.
/// If false is returned, the <see cref="ConnectionStates.Configured"/> flag may be set to indicate that autodial is configured to "always dial" but is not currently active.
/// If autodial is not configured, the function returns false.
/// </returns>
public static bool IsConnectedToInternet()
{
ConnectionStates state = ConnectionStates.Unknown;
return IsConnectedToInternet(out state);
}
}
[Flags]
public enum ConnectionStates
{
/// <summary>
/// Unknown state.
/// </summary>
Unknown = 0,
/// <summary>
/// Local system uses a modem to connect to the Internet.
/// </summary>
Modem = 0x1,
/// <summary>
/// Local system uses a local area network to connect to the Internet.
/// </summary>
LAN = 0x2,
/// <summary>
/// Local system uses a proxy server to connect to the Internet.
/// </summary>
Proxy = 0x4,
/// <summary>
/// Local system has RAS (Remote Access Services) installed.
/// </summary>
RasInstalled = 0x10,
/// <summary>
/// Local system is in offline mode.
/// </summary>
Offline = 0x20,
/// <summary>
/// Local system has a valid connection to the Internet, but it might or might not be currently connected.
/// </summary>
Configured = 0x40,
}
if you only need to know if at least one connection is available you can try this:
InternetGetConnectedStateEx()
http://msdn.microsoft.com/en-us/library/aa384705%28v=vs.85%29.aspx
if you want to check continuously then use timer
private Timer timer1;
public void InitTimer()
{
timer1 = new Timer();
timer1.Tick += new EventHandler(timerEvent);
timer1.Interval = 2000; // in miliseconds
timer1.Start();
}
private void timerEvent(object sender, EventArgs e)
{
DoSomeThingWithInternet();
}
private void DoSomeThingWithInternet()
{
if (isConnected())
{
// inform user that "you're connected to internet"
}
else
{
// inform user that "you're not connected to internet"
}
}
public static bool isConnected()
{
try
{
using (var client = new WebClient())
using (client.OpenRead("http://clients3.google.com/generate_204"))
{
return true;
}
}
catch
{
return false;
}
}
I know this is an old question but this works great for me.
System.Net.NetworkInformation.NetworkChange.NetworkAvailabilityChanged += NetworkChange_NetworkAvailabilityChanged;
private async void NetworkChange_NetworkAvailabilityChanged(object sender, System.Net.NetworkInformation.NetworkAvailabilityEventArgs e)
{
//code to execute...
}
I subscribe to the event to a listener and it constantly checks for connection. this you can add a If statement such as:
if (System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable())
{
//Send Ping...
}
else
{
//other code....
}
How will you know if you have an Internet Connection? Is it enough that you can route packets to a nearby router? Maybe the machine has only a single NIC, a single gateway, and perhaps that Gateway's connection goes down but the machine can still route to the gateway and local network?
Maybe the machine has a single NIC and a dozen gateways; maybe they come and go all the time, but one of them is always up?
What if the machine has multiple NICs, but only a single gateway? Perhaps it can route to some subset of the Internet, but still has an excellent connection to a local network not connected to the Internet?
What if the machine has muliple NICs, multiple gateways, but for administrative policy reasons, still only portions of the Internet are routeble?
Do you really only care if clients have connectivity to your servers?
What kind of latency between packets is acceptable? (30ms is good, 300ms is pushing the limits of human endurance, 3000ms is intolerable long time, 960000ms is what would be required for a connection to a solar probe.) What kind of packet loss is acceptable?
What are you really trying to measure?
This would be a start but as sarnold has mentioned there are a lot of things you need to consider
You can test internet connectivity by pinging to some website like:
public bool IsConnectedToInternet
{
try
{
using (System.Net.NetworkInformation.Ping ping = new System.Net.NetworkInformation.Ping())
{
string address = #"http://www.google.com";// System.Net.NetworkInformation.PingReply pingReplay = ping.Send(address);//you can specify timeout.
if (pingReplay.Status == System.Net.NetworkInformation.IPStatus.Success)
{
return true;
}
}
}
catch
{
#if DEBUG
System.Diagnostics.Debugger.Break();
#endif//DEBUG
}
return false;
}
This code will life-saving for you.. it not only checks real internet connection but also handle the exception with indication on the console window...
after every 2 seconds
using System;
using System.Net;
using System.Threading;
using System.Net.Http;
bool check() //Checking for Internet Connection
{
while (true)
{
try
{ var i = new Ping().Send("www.google.com").Status;
if (i == IPStatus.Success)
{ Console.WriteLine("connected");
return true;
}
else { return false; }
}
catch (Exception)
{
Console.WriteLine("Not Connected");
Thread.Sleep(2000);
continue;
}
}
};
check();
using the NetworkChange.NetworkAvailabilityChanged is the most misleading answer. It check the network availablity change not the internet connection change.
we can monitor the internet connection using Windows NLM API.
using System;
using System.Runtime.InteropServices.ComTypes;
using NETWORKLIST;
namespace Components.Network.Helpers
{
public class InternetConnectionChecker : INetworkListManagerEvents, IDisposable
{
private int _cookie;
private IConnectionPoint _connectionPoint;
private readonly INetworkListManager _networkListManager;
public InternetConnectionChecker()
{
_networkListManager = new NetworkListManager();
}
public bool IsConnected()
{
return _networkListManager.IsConnectedToInternet;
}
public void StartMonitoringConnection()
{
try
{
var container = _networkListManager as IConnectionPointContainer;
if (container == null)
throw new Exception("connection container is null");
var riid = typeof(INetworkListManagerEvents).GUID;
container.FindConnectionPoint(ref riid, out _connectionPoint);
_connectionPoint.Advise(this, out _cookie);
}
catch (Exception e)
{
}
}
public void ConnectivityChanged(NLM_CONNECTIVITY newConnectivity)
{
if (_networkListManager.IsConnectedToInternet)
{
// do something based on internet connectivity
}
}
public void Dispose()
{
_connectionPoint.Unadvise(_cookie);
}
}
}

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