Self install Windows Service in .NET - c#

I have read this question. I have same issue, but I don't understand the answer from lubos hasko. How exactly can I do it? Can you someone post me full walkthrough?
When I run code below, something is installed, but in list of service, I could not find it.
I have this, but this not work:
using System;
using System.Collections.Generic;
using System.Configuration.Install;
using System.Linq;
using System.Reflection;
using System.ServiceProcess;
using System.Text;
using System.IO;
namespace ConsoleApplication1
{
public class Service1 : ServiceBase
{
public Service1()
{
File.AppendAllText("sss.txt", "ccccc");
}
protected override void OnStart(string[] args)
{
File.AppendAllText("sss.txt", "asdfasdf");
}
protected override void OnStop()
{
File.AppendAllText("sss.txt", "bbbbb");
}
static void Main(string[] args)
{
if (System.Environment.UserInteractive)
{
string parameter = string.Concat(args);
switch (parameter)
{
case "--install":
ManagedInstallerClass.InstallHelper(new string[] { Assembly.GetExecutingAssembly().Location });
break;
case "--uninstall":
ManagedInstallerClass.InstallHelper(new string[] { "/u", Assembly.GetExecutingAssembly().Location });
break;
}
}
else
{
ServiceBase.Run(new Service1());
}
Console.ReadKey();
}
}
}
I dont understad this either:
if (System.Environment.UserInteractive) ...

This is my complete solution, and it works. It is basically the same answer as in this question.
using System;
using System.Configuration.Install;
using System.Reflection;
using System.ServiceProcess;
using System.IO;
namespace ConsoleApplication1
{
class Program : ServiceBase
{
static void Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException += CurrentDomainUnhandledException;
if (System.Environment.UserInteractive)
{
string parameter = string.Concat(args);
switch (parameter)
{
case "--install":
ManagedInstallerClass.InstallHelper(new string[] { Assembly.GetExecutingAssembly().Location });
break;
case "--uninstall":
ManagedInstallerClass.InstallHelper(new string[] { "/u", Assembly.GetExecutingAssembly().Location });
break;
}
}
else
{
ServiceBase.Run(new Program());
}
}
private static void CurrentDomainUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
File.AppendAllText(#"C:\Temp\error.txt", ((Exception)e.ExceptionObject).Message + ((Exception)e.ExceptionObject).InnerException.Message);
}
public Program()
{
this.ServiceName = "My Service";
File.AppendAllText(#"C:\Temp\sss.txt", "aaa");
}
protected override void OnStart(string[] args)
{
base.OnStart(args);
File.AppendAllText(#"C:\Temp\sss.txt", "bbb");
}
protected override void OnStop()
{
base.OnStop();
File.AppendAllText(#"C:\Temp\sss.txt", "ccc");
}
}
}
and in same project create this class:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration.Install;
using System.Linq;
using System.ServiceProcess;
using System.Text;
namespace ConsoleApplication1
{
[RunInstaller(true)]
public class MyWindowsServiceInstaller : Installer
{
public MyWindowsServiceInstaller()
{
var processInstaller = new ServiceProcessInstaller();
var serviceInstaller = new ServiceInstaller();
//set the privileges
processInstaller.Account = ServiceAccount.LocalSystem;
serviceInstaller.DisplayName = "My Service";
serviceInstaller.StartType = ServiceStartMode.Automatic;
//must be the same as what was set in Program's constructor
serviceInstaller.ServiceName = "My Service";
this.Installers.Add(processInstaller);
this.Installers.Add(serviceInstaller);
}
}
}
Run this program with parameters --install/--uninstall as Administrator on Windows 7. Check the error log in temp. Check working log on the same path.

First of all, in your Service1 constructor set ServiceName property.
Excerpt from MSDN:
The minimum you need to implement in the constructor for a class inherited from ServiceBase is to set the ServiceName on your component. No other processing is specifically required in the constructor. You should handle most initialization in OnStart rather than in the constructor.
Second of all you need to pass arguments to your service when running it from command line. --install for install, --uninstall for uninstall - look at your switch statement it's doing it on input arguments.

System.Environment.UserInteractive property tells you that
whether a Windows process or a service like IIS that runs without a user interface.
If this property is false, do not display modal dialogs or message boxes because there is no graphical user interface for the user to interact with.
Source
Check this article as well.

Related

Windows service argument to run as console app

When I run my Windows Service with --console from a command prompt the console.txt file gets created so I know the ConsoleMode() method is being fired but nothing gets written to the console window. I know that Windows Services can't write to the console but shouldn't it just be running as a normal Console app since I bypass ServiceBase.Run?
using System;
using System.Collections.Generic;
using System.Configuration.Install;
using System.Linq;
using System.Reflection;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
namespace ShowCheckerService
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main(params string[] args)
{
#if DEBUG
Service1 myService = new Service1();
myService.OnDebug();
System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);
#else
if (Environment.UserInteractive)
{
string parameter = string.Concat(args);
switch (parameter)
{
case "--install":
ManagedInstallerClass.InstallHelper(new string[] { Assembly.GetExecutingAssembly().Location });
break;
case "--uninstall":
ManagedInstallerClass.InstallHelper(new string[] { "/u", Assembly.GetExecutingAssembly().Location });
break;
case "--console":
ConsoleMode();
break;
}
}
else
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new Service1()
};
ServiceBase.Run(ServicesToRun);
}
#endif
}
private static void ConsoleMode()
{
System.IO.File.Create(#"C:\ProgramData\ShowChecker\console.txt");
Console.WriteLine("asdf");
}
}
}
Had to change this in the properties:
A Windows service can actually Write A Console buffer, in fact i had once a console.read() and my service hung on that line look here https://stackoverflow.com/a/8793030/5601607

Windows service stops right after launching

I have following problem with the windows service I was writing:
When I start the service it stops immediately. When I was using a console app it wasn't crushing. I have no idea what's the cause of this problem.
Here's the code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.ServiceModel;
using System.ServiceProcess;
using System.Configuration;
using System.Configuration.Install;
using WindowsService;
namespace WS
{
[ServiceContract(Namespace = "http://WS")]
public interface INewsReader
{
}
public class NewsReaderService : INewsReader
{
public NewsReaderService()
{
var config = new Config();
var scheduled = new Schedule(config);
scheduled.ExecuteScheduledEvents();
while (true)
{
System.Threading.Thread.Sleep(1000);
int i = 0;
}
}
}
public class NewsReaderWindowsService : ServiceBase
{
public ServiceHost serviceHost = null;
public NewsReaderWindowsService()
{
ServiceName = "NewsReaderWindowsService";
}
public static void Main()
{
ServiceBase.Run(new NewsReaderWindowsService());
}
protected override void OnStart(string[] args)
{
var thread = new System.Threading.Thread(() =>
{
while (true)
{
int i = 0;
System.Threading.Thread.Sleep(1000);
}
});
thread.Start();
serviceHost = new ServiceHost(typeof(NewsReaderService));
serviceHost.Open();
}
protected override void OnStop()
{
}
}
[RunInstaller(true)]
public class ProjectInstaller : Installer
{
private ServiceProcessInstaller process;
private ServiceInstaller service;
public ProjectInstaller()
{
process = new ServiceProcessInstaller();
process.Account = ServiceAccount.LocalSystem;
service = new ServiceInstaller();
service.ServiceName = "NewsReaderWindowsService";
Installers.Add(process);
Installers.Add(service);
}
}
}
Well, first of all I think your OnStart method is written badly. I can't see the reason for creating a, basicly, empty thread. You should there only initialize service (If necessary), immediately start a new thread that will work for whole time and leave the OnStart method.
Second of all use try catch block, because in my opinion somewhere in there is exception and that's why your windows service stops.
Thirdly see this example WCF Hosting with Windows Service

Service is missing... Something.

Alright, so I'm creating a windows service that will start and stop my rendering executable based on whether or not a user is logged in. I have already determined that I want to run the exe from a service, and know it will run in a secure desktop with no gui. That is not a problem for me.
What is a problem is that my service, when I tell it to start, instantly starts. I think that means I'm missing some sort of loop or event handler or SOMETHING, but I have no clue what. This is the first service I've tried to program. As there seems to be no way to debug a service effectively, I was hoping to get some input from the brilliant minds here.
Any input would be very much appreciated! Here's the code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Management;
namespace SessionService
{
public partial class renderServ : ServiceBase
{
private readonly ManagementClass _wmiComputerSystem = new ManagementClass("Win32_ComputerSystem");
private Process process;
Boolean isRunning;
public renderServ()
{
InitializeComponent();
process = new Process();
process.StartInfo = new ProcessStartInfo(#"C:\Windows\notepad.exe");
isRunning = false;
}
public void OnUserLogin()
{
/*if (isRunning == true)
{
process.Kill();
process.WaitForExit();
}*/
isRunning = false;
}
public void OnUserLogoff()
{
if (isRunning == false)
{
process.Start();
process.PriorityClass = ProcessPriorityClass.Idle;
}
isRunning = true;
}
protected override void OnSessionChange(SessionChangeDescription changeDescription)
{
base.OnSessionChange(changeDescription);
switch (changeDescription.Reason)
{
case SessionChangeReason.SessionLogon:
{
OnUserLogin();
} break;
case SessionChangeReason.SessionLogoff:
{
OnUserLogoff();
} break;
case SessionChangeReason.SessionLock:
{
OnUserLogoff();
} break;
case SessionChangeReason.SessionUnlock:
{
OnUserLogin();
} break;
}
}
protected override void OnStart(string[] args)
{
CanHandleSessionChangeEvent = true;
System.Windows.Forms.Application.Run();
}
protected override void OnStop()
{
}
}
}

My service does not loop

It is my first program for service.
If i run this code as Console, LOOP works, but if I convert it to service, it does the operation initially, but does not LOOP.
Could you help me correct it?
tnx
using System;
using System.Net;
using KICBservice;
using System.Data;
using ConsoleApplication1.Classes;
using System.IO;
using System.ServiceProcess;
using System.Configuration.Install;
using System.ComponentModel;
namespace KICBService
{
[RunInstaller(true)]
public class MyWindowsServiceInstaller : Installer
{
public MyWindowsServiceInstaller()
{
var processInstaller = new ServiceProcessInstaller();
var serviceInstaller = new ServiceInstaller();
//set the privileges
processInstaller.Account = ServiceAccount.LocalSystem;
serviceInstaller.DisplayName = "KICB_Payment";
serviceInstaller.StartType = ServiceStartMode.Manual;
//must be the same as what was set in Program's constructor
serviceInstaller.ServiceName = "KICB_Payment";
this.Installers.Add(processInstaller);
this.Installers.Add(serviceInstaller);
}
}
class Program : ServiceBase
{
static void Main(string[] args)
{
ServiceBase.Run(new Program());
KICBservice.Service1SoapClient kicb = new KICBservice.Service1SoapClient();
kicb.ClientCredentials.Windows.ClientCredential = new NetworkCredential("register", "KICBregistr1");
kicb.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
while (true)
{
try
{
kicb.Open();
StreamWriter tw = File.AppendText("c:\\KICB.log");
NewPayment np = new NewPayment();
np = kicb.GetPayment("register", "KICBregistr1");
// Operation with Database
tw.WriteLine("----------------");
tw.WriteLine(DateTime.Now);
tw.Close();
kicb.Close();
System.Threading.Thread.Sleep(60000);
}
catch (Exception ex)
{
kicb.Abort();
}
}
}
public Program()
{
this.ServiceName = "KICB_Payment";
}
protected override void OnStart(string[] args)
{
base.OnStart(args);
//TODO: place your start code here
}
protected override void OnStop()
{
base.OnStop();
//TODO: clean up any variables and stop any threads
}
}
}
I am pasting full code of my program.
Where is that first code located?
Without that context, my best guess is that your OnStart() method fires, and then the service quits as soon the method ends because there's nothing left to do.
Also, I'm not a fan of the while (true) { Sleep(60000); // do work } pattern for services. Instead, you want to look for a function that actually blocks execution to keep your code going. Examples include TcpListener.AcceptTcpClient() and Thread.Join(). If you can't find something like that for the meat of your service, you may want to do something like set up a scheduled task instead.
You've placed the code outside of a function. What you have shown in the question should not even compile, and it certainly won't loop.
Note the //TODO: comment in the OnStart function definition:
protected override void OnStart(string[] args)
{
base.OnStart(args);
//TODO: place your start code here
}

Does WqlEventQuery contain a constructor with 1 argument?

I am trying to create a simple service in C# using VS2008 that creates a text file when the computer goes into sleep mode. My current code throws out the following error:
'SleepNotifierService.WqlEventQuery' does not contain a constructor that takes '1' arguments
Now I looked in the Object browser, and it looks like it does take in one argument. This is what the browser had to say:
public WqlEventQuery(string queryOrEventClassName)
Here is my code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Management;
using System.IO;
namespace SleepNotifierService
{
public class WqlEventQuery : EventQuery { }
public partial class Service1 : ServiceBase
{
ManagementEventWatcher _watcher;
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
WqlEventQuery query = new WqlEventQuery("Win32_PowerManagementEvent");
_watcher = new ManagementEventWatcher(query);
_watcher.EventArrived += new EventArrivedEventHandler(watcher_EventArrived);
_watcher.Start();
}
protected override void OnStop()
{
_watcher.Stop();
}
void watcher_EventArrived(object sender, EventArrivedEventArgs e)
{
try
{
int eventType = Convert.ToInt32(e.NewEvent.Properties["EventType"].Value);
switch (eventType)
{
case 4:
Sleep();
break;
case 7:
Resume();
break;
}
}
catch (Exception ex)
{
//Log(ex.Message);
}
}
public void Sleep()
{
StreamWriter SW;
SW = File.CreateText("c:\\MyTextFile.txt");
SW.WriteLine("Sleep mode initiated");
SW.Close();
}
public void Resume()
{
}
}
}
Am I interpreting that object browser wrong? I'm new to creating services and C#/.NET in general so it might be something trivial.
Appreciate any help,
Tomek
You're using wrong WqlEventQuery. There's one defined in System.Management and it indeed has a one-argument constructor, but there's also your custom WqlEventQuery class.
If you want to use .NET BCL's class, you'll have to fully qualify it:
var query = new System.Management.WqlEventQuery("Win32_PowerManagementEvent");
or even prefix it with global keyword:
var query = new global::System.Management.WqlEventQuery("Win32_PowerManagementEvent");

Categories

Resources