Hi all i have created a service based application which communicates with a pipe
the service application works until the windows application stops which contains the
pipe server code.
Try the following code. You will have to merge this process in the code with whatever code you already have for your service. Replace the "PipeServiceName.exe" to whatever the name of the process is called. Also, this code checks every 5 seconds. You can change this by changing the 5000 number.
Without knowing more about how the "pipe" and the service interact with each other, its hard to put together a workflow.
private readonly ManualResetEvent _shutdownEvent = new ManualResetEvent(false);
private Thread _thread;
public MyService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
_thread = new Thread(MonitorThread)
{
IsBackground = true
}
}
protected override void OnStop()
{
_shutdownEvent.Set();
if (!_thread.Join(5000))
{
_thread.Abort();
}
}
private void MonitorThread()
{
while (!_shutdownEvent.WaitOne(5000))
{
Process[] pname = Process.GetProcessesByName("PipeServiceName.exe");
if (pname.Count == 0)
{
// Process has stopped. ReLaunch
RelaunchProcess();
}
}
}
private void RelaunchProcess()
{
Process p = new Process();
p.StartInfo.FileName = "PipeServiceName.exe";
p.StartInfo.Arguments = ""; // Add Arguments if you need them
p.Start();
}
Related
I'm developing a windows service using Topshelf library...
I want to start cmd and map a folder from my network and run an console application from it.
This is my whole class:
class ClientService
{
private Scheduler ServiceScheduler { get; set; }
private Process MyApp{ get; set; }
private Config ConfigFile { get; set; }
public void Start()
{
ServiceScheduler = new Scheduler(6000) { Enabled = true };
ServiceScheduler.Elapsed += Scheduler_Elapsed;
ConfigFile =Config.get();
ConfigMyApp();
}
private void ConfigMyApp() => ConfigMyApp= new Process
{
StartInfo =
{
CreateNoWindow = false,
ErrorDialog = false,
UseShellExecute = false,
RedirectStandardInput = true,
FileName = "CMD.exe",
}
};
private void Scheduler_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if (Network.CurrentHourMin == ConfigFile.StartTime)
{
MyApp.Start();
using (var sw = MyApp.StandardInput)
{
if (sw.BaseStream.CanWrite)
{
//Map My Folder Network
sw.WriteLine("pushd \\\\MyNetwork\\General\\");
//Run MyApp with Args
sw.WriteLine(MyApp -run -args -others);
}
}
}
else if (Network.CurrentHourMin == ConfigFile.EndTime)
{
//////////
How To stop running MyApp.exe
//////////
}
}
public void Stop()
{
}
}
The problem is I can't close MyApp.exe that previously ran from cmd.exe and doing MyApp.Close won't stop running the application and doesn't any affect at all.
Even Calling Stop method from Topshelf service (or stopping the service) doesn't stop running MyApp
What is the best practice for Exit or kill an Console application in this scenario?
Don't create processes unless you have to. Here's some code to map a network drive:
public void mapDrive(string driveLetter, string networkPath, bool isPersistent)
{
private WshNetwork _networkShell = new WshNetwork();
object persistent = isPersistent;
_networkShell.MapNetworkDrive(driveLetter, networkPath, ref persistent) ;
}
You'll need a reference to Windows Script Host Object Model.
You'll have better control over the exception handling if you keep it in process.
I've been building out a service that processes files using a Queue<string> object to manage the items.
public partial class BasicQueueService : ServiceBase
{
private readonly EventWaitHandle completeHandle =
new EventWaitHandle(false, EventResetMode.ManualReset, "ThreadCompleters");
public BasicQueueService()
{
QueueManager = new Queue<string>();
}
public bool Stopping { get; set; }
private Queue<string> QueueManager { get; }
protected override void OnStart(string[] args)
{
Stopping = false;
ProcessFiles();
}
protected override void OnStop()
{
Stopping = true;
}
private void ProcessFiles()
{
while (!Stopping)
{
var count = QueueManager.Count;
for (var i = 0; i < count; i++)
{
//Check the Stopping Variable again.
if (Stopping) break;
var fileName = QueueManager.Dequeue();
if (string.IsNullOrWhiteSpace(fileName) || !File.Exists(fileName))
continue;
Console.WriteLine($"Processing {fileName}");
Task.Run(() =>
{
DoWork(fileName);
})
.ContinueWith(ThreadComplete);
}
if (Stopping) continue;
Console.WriteLine("Waiting for thread to finish, or 1 minute.");
completeHandle.WaitOne(new TimeSpan(0, 0, 15));
completeHandle.Reset();
}
}
partial void DoWork(string fileName);
private void ThreadComplete(Task task)
{
completeHandle.Set();
}
public void AddToQueue(string file)
{
//Called by FileWatcher/Manual classes, not included for brevity.
lock (QueueManager)
{
if (QueueManager.Contains(file)) return;
QueueManager.Enqueue(file);
}
}
}
Whilst researching how to limit the number of threads on this (I've tried a manual class with an incrementing int, but there's an issue where it doesn't decrement properly in my code), I came across TPL DataFlow, which seems like its a better fit for what I'm trying to achieve - specifically, it allows me to let the framework handle threading/queueing, etc.
This is now my service:
public partial class BasicDataFlowService : ServiceBase
{
private readonly ActionBlock<string> workerBlock;
public BasicDataFlowService()
{
workerBlock = new ActionBlock<string>(file => DoWork(file), new ExecutionDataflowBlockOptions()
{
MaxDegreeOfParallelism = 32
});
}
public bool Stopping { get; set; }
protected override void OnStart(string[] args)
{
Stopping = false;
}
protected override void OnStop()
{
Stopping = true;
}
partial void DoWork(string fileName);
private void AddToDataFlow(string file)
{
workerBlock.Post(file);
}
}
This works well. However, I want to ensure that a file is only ever added to the TPL DataFlow once. With the Queue, I can check that using .Contains(). Is there a mechanism that I can use for TPL DataFlow?
Your solution with Queue works only if file goes into your service twice in a small period of time. If it came again in, say, few hours, queue will not contain it, as you Dequeue it from there.
If this solution is expected, then you may use a MemoryCache to store file paths being already handled, like this:
using System.Runtime.Caching;
private static object _lock = new object();
private void AddToDataFlow(string file)
{
lock (_lock)
{
if (MemoryCache.Default.Contains(file))
{
return;
}
// no matter what to put into the cache
MemoryCache.Default[file] = true;
// we can now exit the lock
}
workerBlock.Post(file);
}
However, if your application must run for a long time (which service is intended to do), you'll eventually run out of memory. In that case you probably need to store your file paths in database or something, so even after restarting the service your code will restore the state.
You can check it inside of DoWork.
You have to save in Hash already works items and check current filename doesn't exist in hash.
Currently have a program that opens another program. I need to perform an action should that program close. I am pretty new to C# so could use some help getting me pointed in the correct direction.
EDIT: So I am able to get the current form to open the external program. This form then needs to close and another form needs to have a function to perform an action should the loader.exe close. This is what I have in the first form. How do I code the other form to know if this program has ended.
public static class Program
{
public static bool OpenManager { get; set; }
public static int DeskNumber { get; set; }
/// The main entry point for the application.
[STAThread]
static void Main(string[] arguments)
{
/// Do not move this!
OpenManager = false;
MYvariable = 0;
/// ----------------
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Loader());
if (OpenManager)
{
if (MYvariable == 1)
{
System.Diagnostics.Process startProgram = System.Diagnostics.Process.Start("loader.exe", "-r 52");
startProgram.EnableRaisingEvents = true;
Application.Run(new Manager());
Assuming you use the Process class to start the other program, you can subscribe to the Exited event of the Process instance:
Process myProcess = Process.Start("myprogram.exe", "arguments");
myProcess.EnableRaisingEvents = true;
myProcess.Exited += (sender, e) =>
{
// Do what you want to do when the process exited.
}
Or, to do it more explicit, declare an explicit event handler that is called when the process finishes:
public void OnProcessExited(object sender, EventArgs e)
{
// Do what you want to do when the process exited.
}
public void StartProcess()
{
Process myProcess = Process.Start("myprogram.exe", "arguments");
myProcess.EnableRaisingEvents = true;
myProcess.Exited += OnProcessExited;
}
if you are using the Process class within c# to open and keep a track on the opened program you can handle its exited event like so:
App.Exited += AppOnExited;
private static void AppOnExited(object sender, EventArgs eventArgs)
{
// Do your action here
}
App being the name of your process object.
dont forget you may need to remove the handler when your done like so:
App.Exited -= AppOnExited;
If the application returns an int code you can use:
Process P = Process.Start("App.exe");
P.WaitForExit();
int code = P.ExitCode;
An application that has return:
public static int Main(string[] args)
{
return (int);
}
Will set P.ExitCode to the return value when it closes.
This is my main:
static class Program
{
static void Main()
{
//Debugger.Launch();
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new Service1()
};
ServiceBase.Run(ServicesToRun);
}
}
And this is my Service1() code:
public partial class Service1 : ServiceBase
{
public Service1()
{
Thread messageThread = new Thread(new ThreadStart(Messaggi.Check));
messageThread.Start();
bool checkGruppoIndirizzi = true;
for (; ; )
{
SediOperative.Check();
Autisti.Check();
AutistiVeicoli.Check();
StatiVega.Check();
int res = Ordini.Check();
if (res == 0) AssegnazioniVega.Check();
Thread.Sleep(10000);
}
}
protected override void OnStart(string[] args)
{
}
protected override void OnStop()
{
}
}
First thing is I don't know if launching two threads in that way is a good thing to do, but the real problem is the program run fine inside Visual Studio but after installation (I've created a setup project using InstallShield) I try to start my service from the windows service panel and I get:
Error 1053: The service did not respond to the start or control request in a timely fashion
The problem you have is that your service will be started sucessfully after the susyem has called the Start method and it has sucessfully returned. Given that you have an infinite loop in the constructor, the system is saying to itself something like "Can't even create the this let alone call start. I'm giving up.'
Your code should be refactored along these lines:
public partial class Service1 : ServiceBase
{
public Service1()
{
}
private Thread messageThread;
private Thread otherThread;
private bool stopNow;
protected override void OnStart(string[] args)
{
this.stopNow = false;
this.messageThread = new Thread(new ThreadStart(Messaggi.Check));
this.messageThread.Start();
this.otherThread = new Thread(new ThreadStart(this.StartOtherThread));
this.otherThread.Start();
}
private void StartOtherThread()
{
bool checkGruppoIndirizzi = true;
while (this.stopNow == false)
{
SediOperative.Check();
Autisti.Check();
AutistiVeicoli.Check();
StatiVega.Check();
int res = Ordini.Check();
if (res == 0) AssegnazioniVega.Check();
for (int 1 = 0; i < 10; i++)
{
if (this.stopNow)
{
break;
}
Thread.Sleep(1000);
}
}
}
}
protected override void OnStop()
{
this.stopNow = true;
this.messageThread.Join(1000);
this.otherThread.Join(1000);
}
}
And yes, starting stuff on Threads is exactly the way to do it! You'll have to have some way of stopping them in the Stop() method. (The code above is air code so don't trust it.) for the 'otherThread' I've got it checking a bool and exiting when the bool is set. the thread.Join is just a tidy-up which isn't strictly necessary, but is good housekeeping I think.
Cheers -
At the moment I'm developing a series of Windows services in C#, that use a timer (from System.Timers) to poll the machine they are running on and report stats to a remote listener (remote machine hosts a WCF data service).
The setup is as follows: I have a class that wraps System.Timers.Timer with additional functionality, accepting a generic event handler to be fired when the timer elapses and log information:
public class GenericPoller
{
private EventLog _log;
private string _componentName;
private System.Timers.Timer _timer;
public GenericPoller
(
double interval,
System.Timers.ElapsedEventHandler handler,
EventLog log,
string componentName
)
{
_componentName = componentName;
_log = log;
_timer = new System.Timers.Timer();
_timer.Interval = interval;
_timer.AutoReset = true;
_timer.Enabled = false;
_timer.Elapsed += handler;
}
public void StartPolling()
{
try
{
_timer.Enabled = true;
_log.WriteEntry
(
"Timer started for component '" + _componentName
+ "', with " + _timer.Interval + "ms interval."
);
}
catch
{
_log.WriteEntry("Failed to start timer for component '" + _componentName + "'.");
}
}
public void StopPolling()
{
try
{
_timer.Enabled = false;
_log.WriteEntry("Timer stopped for component '" + _componentName + "'.");
}
catch
{
_log.WriteEntry("Failed to stop timer for component '" + _componentName + "'.");
}
}
}
Any one of the services I'm implementing creates a GenericPoller in their constructor, and in the OnStart method I invoke StartPolling via a separate thread to consume as little time as possible inside this method. The service class looks roughly like this:
public partial class CPUMonitor : ServiceBase
{
private GenericPoller _poller;
Thread _thread;
public CPUMonitor()
{
InitializeComponent();
_poller = new GenericPoller
(
HardwareMonitorCommon.Instance.DefaultPollingInterval,
PollCPU,
EventLog,
"CPUMonitor"
);
}
protected override void OnStart(string[] args)
{
_thread = new Thread(new ThreadStart(_poller.StartPolling));
_thread.Start();
}
protected override void OnStop()
{
_poller.StopPolling();
_thread.Join();
}
private void PollCPU(object sender, System.Timers.ElapsedEventArgs e)
{
Code to aqcuire CPU stats and report to WCF data service...
}
}
I install the services with the installutil.exe command.
The services will sometimes fail to start within the allotted time (error 1053), but there doesn't seem to be any pattern to this problem. Failure to start is about 4 attempts out of 10.
I simply can't understand why this happens. The System/Application event log doesn't report any errors or exceptions from the service processes, and I can't figure out why a timeout would occur if all the "heavy lifting" is taking place in a separate thread.
I'm in no way a pro/expert at writing multithreaded code, so I fully expect to be doing something wrong...I just can't see what it is...
EDIT: the Main method remains in the automatically generated Program.cs file, and I only modified it to add the components that will be run:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new CPUMonitor(),
new MemoryMonitor()
};
ServiceBase.Run(ServicesToRun);
}
}