Home > .NET, Vs2012 > Quick & Dirty Windows Service

Quick & Dirty Windows Service

2013/11/21

The problem was a pc for my use in a customer intranet, behind a proxy, no webmail: and i was needing to read my emails.

So i need someone that put on the pc (no, is not possible to leave it working..), then from a VPN i can use the pc via remote desktop.

First problem: via VPN i can’t use the BIOS name.

Second problem: the ping, perhaps a trouble caused from the proxy, gives an erratic IP from all others pc on site.

The only possibility to have the right IP is by login locally and run ipconfig…

But to give away my user and password to the first available clerk is not beautiful.

After a while i thinked that an Windows Service automatically started without the need to make login, was the only possibility.

In Visual Studio 2012 File->New Project->Templates->Visual C#->Windows->Windows Service, using MyIpReminderSvc as name, .NET 4.5

The first interesting thing is that by default is created an Service1.cs file which has a Designer.

I will study in future what means that i can drag&drop an textbox, for example…a Windows Service is by definition something that works without user interface and does not run directly, if you try an F5 this is the result:


A Windows service needs an mechanism that permits his installation, and this is done by right clic the Service1.cs designer and choosing

“Add Installer”.

This action creates another class, by default is named ProjectInstaller.cs; pressing F7 we can see the code behind:

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration.Install;
using System.Linq;
using System.Threading.Tasks;

namespace MyIpReminderSvc
{
    [RunInstaller(true)]
    public partial class ProjectInstaller : System.Configuration.Install.Installer
    {
        public ProjectInstaller()
        {
            InitializeComponent();
        }
    }
}

Below “InitializeComponent();” we can add these lines :

this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
this.serviceProcessInstaller1.Password = null;
this.serviceProcessInstaller1.Username = null;
// 
this.serviceInstaller1.Description = "Get current IP and send it to an email account";
this.serviceInstaller1.DisplayName = "My pc IP service";
this.serviceInstaller1.ServiceName = "MyPcIpService";
this.serviceInstaller1.StartType = System.ServiceProcess.ServiceStartMode.Automatic;

Here we specify that by default the service is running as LocalSystem user, that tipically has no user and password.

Don’t use special chars in the ServiceName (for example “ä”) or you will be in troubles trying to manage the service with batch files (using NET START, NET STOP and so on).

In this step we have defined the administrative items of our service, now opening the Service1.cs we can define the core activity or the service: pressing F7 we see the code automatically created (i don’t write the “using” part):

namespace MyIpReminderSvc
{
    public partial class Service1 : ServiceBase
    {
        public Service1()
        {
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {
        }

        protected override void OnStop()
        {
        }
    }
}

The code is obvious, in OnStart we can write what happens when our service is starting , and obviously OnStop is called when the service is stopping.

In the class constructor we can define other administrative properties.

So the code could be, in this case where i need to know the pc IP, something as:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Mail;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;

namespace MyIpReminderSvc
{
    public partial class Service1 : ServiceBase
    {
        public Service1()
        {
            InitializeComponent();
            this.ServiceName = "MyNetworkIPSendmail"; // short name used to identify the service to the system
            this.EventLog.Log = "Application";
            //
            this.CanHandlePowerEvent = true; //for example the system is running on battery, caused from a power shortage
            this.CanHandleSessionChangeEvent = true; //change events from terminal session
            this.CanPauseAndContinue = true;
            this.CanShutdown = true; //service notified when the system is shutting down
            this.CanStop = true;
        }

        protected override void OnStart(string[] args)
        {
            MailMessage objHtmlMsg;
            SmtpClient objClient;
            //
            String strHostName = string.Empty;
            // First get the host name of local machine.
            strHostName = Dns.GetHostName();
            // Then using host name, get the IP address list..
            IPHostEntry ipEntry = Dns.GetHostEntry(strHostName);
            IPAddress[] addr = ipEntry.AddressList;
            //
            for (int i = 0; i < addr.Length; i++)
            {
                strHostName += String.Format("IP Address {0}: {1}<br/>", i, addr[i].ToString());
            }
            // in this case a "jolly" intranet user name (ok, the domain name is a fake) without password
            objHtmlMsg = new MailMessage("dummyuser@acme.com", "youraccount@yourdomain.com", "Today IP from my pc", strHostName);
            objClient = new SmtpClient("10.87.11.9"); // ip or netbios name of the mail server
            objHtmlMsg.IsBodyHtml = true;
            objClient.Credentials = CredentialCache.DefaultNetworkCredentials;
            objClient.Send(objHtmlMsg);
        }

        protected override void OnStop()
        {
        }
    }
}

In the Build menu of Visual Studio 2012 we can compile our solution, and now the problem is how to insert our service in the Windows Services list.

First, we must copy the bin\Debug or bin\Release (depending how the solution was built) folder or our solution in another folder: is a bad practice to publish directly the solution folder.

In the copied folder, for our solution, we have


In the same folder create 2 bat files: install.bat that contains a call to InstallUtil (the framework must be the same of the solution, of course)

@ECHO OFF
echo Installing Windows Service...
C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\InstallUtil /i MyIpReminderSvc.exe
pause

And uninstall.bat

@ECHO OFF
echo Uninstalling Windows Service...
C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\InstallUtil /u MyIpReminderSvc.exe
pause

In practice the only difference is the /u switch.

In the Windows Control Panel in Administrative Tools->Services now we can see


Double clicking it we can read the settings defined in ProjectInstaller.cs

Now rebooting the pc we should receive the mail with a body similar to (verify your spam folder,also…)

sthm0627IP Address 0: fe85::a8a1:62c6:8094:r930%17
IP Address 1: fe87::t57c:e392:c8d3:8248%16
IP Address 2: 192.168.137.7
IP Address 3: 10.87.13.161

The last is my true PC IP on the local network.

You end up with a unuseful running service in this case: but we can provide an initial batch, when the user is logging, with a NET STOP.

Naturally is better to make some code test; for example the service logic can be inserted in another class where we can do Unit Testing or call the class from a Console program.

Well, but if i run into a dreaded problem that occurs only on the true running service, and for example i publish the service not on my own pc but another server ? how to debug ?

You can provide log4net logging for example, but if it is needed a true debug we can insert a function as this in Service1.cs code

[Conditional("DEBUG")]
static void DebugMode()
{
    if (!Debugger.IsAttached)
        Debugger.Launch();
    Debugger.Break();
}

we can insert the call to DebugMode() anywhere in our Service1.cs code; then when the service is launched pops out this window:


And we can debug our code: obviously must be installed something that can act as a .NET debugger (in primis Visual Studio, even an Express version)


The code must be compiled in Debug mode and must be provided the .pdb file.
A service can be deleted from an administrative command prompt with sc delete .

Advertisements
Categories: .NET, Vs2012
%d bloggers like this: