views:

806

answers:

2

I am trying to create a service that will log when users logon and logoff. I have no problem with capturing the logon, but for some reason i cannot catch the logoff event. this is the code i have used:

protected override void OnStart(string[] args)
    {
        //SystemEvents.SessionSwitch +=    new SessionSwitchEventHandler(SystemEvents_SessionSwitch);
        SystemEvents.SessionEnding += new SessionEndingEventHandler(SystemEvents_SessionEnding);
    }
    void SystemEvents_SessionEnding(object sender, SessionEndingEventArgs e)
    {
        this.EventLog.WriteEntry("We have cought logout event");
        sendData("off");
    }

I have tried using SessionEnding, SessionEnded and SessionSwitch but none of them seem to work. I have checked the service, it is up and running and the serive is running under Local System account and the "Allow service to interact with desktop" option is ticked.

Any suggestions?

Service class:

using System;

using System.Diagnostics; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; using System.ServiceProcess; using System.Security.Principal; using System.Management; using Microsoft.Win32; using System.Threading; using System.Windows.Forms; using System.ComponentModel;

public partial class Service1 : ServiceBase
{
    public Service1()
    {
        InitializeComponent();
    }
    protected override void OnStart(string[] args)
    {
        EventLog.WriteEntry("WinSendSED", "Starting WinSendSED");
        new Thread(RunMessagePump).Start();
    }
    void RunMessagePump()
    {
        EventLog.WriteEntry("WinSendSED.MessagePump", "Starting WinSendSED Message Pump");
        Application.Run(new HiddenForm());
    }
    protected override void OnStop()
    {
        //sendData("off");
        Application.Exit();
    }
}

Hidden Form:

    public partial class HiddenForm : Form
{
    public HiddenForm()
    {
        InitializeComponent();
    }
    private string makeNiceMAC(string s)
    {
        for (int i = 0; i < 5; i++)
        {
            s = s.Insert((3 * i) + 2, "-");
        }
        return s;
    }
    private string getUserName()
    {
        Process[] ps = Process.GetProcesses();
        foreach (Process p in ps)
        {
            if (p.ProcessName.Trim() == "explorer")
            {
                ObjectQuery sq = new ObjectQuery
                    ("Select * from Win32_Process Where ProcessID = '" + p.Id + "'");
                ManagementObjectSearcher searcher = new ManagementObjectSearcher(sq);
                foreach (ManagementObject oReturn in searcher.Get())
                {
                    string[] o = new String[2];
                    oReturn.InvokeMethod("GetOwner", (object[])o);
                    return o[0];
                }
            }
        }
        return "";
    }
    private string GetIPAddress()
    {
        IPAddress[] addr = Dns.GetHostEntry(Dns.GetHostName()).AddressList;
        for (int i = 0; i < addr.Length; i++)
        {
            if (addr[i].ToString().Contains("."))
                return addr[i].ToString();
        }
        return "127.0.0.1";
    }
    private string printData()
    {
        WindowsIdentity id = WindowsIdentity.GetCurrent();
        NetworkInterface[] nis = NetworkInterface.GetAllNetworkInterfaces();
        NetworkInterface ni = nis[0];
        foreach (NetworkInterface nii in nis)
        {
            if ((nii.OperationalStatus == OperationalStatus.Up)
                && (!nii.GetPhysicalAddress().ToString().EndsWith("000000E0"))
                && (nii.GetPhysicalAddress().ToString().Length > 5))
            {
                ni = nii;
            }
        }
        string line = "";
        line = line + "'" + makeNiceMAC(ni.GetPhysicalAddress().ToString()) + "', ";
        line = line + "'" + GetIPAddress() + "', ";
        line = line + "'" + getUserName() + "'";
        return line;
    }
    private void sendData(string cmd)
    {
        Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
        IPEndPoint ipAdd = new IPEndPoint(IPAddress.Parse(//"127.0.0.1"), 2345);
            "123.123.123.123"), 2345);
        try
        {
            s.Connect(ipAdd);
            String szData = cmd + printData();
            byte[] byData = System.Text.Encoding.ASCII.GetBytes(szData);
            s.Send(byData);
            s.Close();
        }
        catch (SocketException se)
        {
            s.Close();
        }
    }
    private void HiddenForm_Load(object sender, EventArgs e)
    {
        SystemEvents.SessionEnding += 
            new SessionEndingEventHandler(SystemEvents_SessionEnding);
    }

    void SystemEvents_SessionEnding(object sender, SessionEndingEventArgs e)
    {
        EventLog.WriteEntry("WinSendSED", "We have cought logout event");
        sendData("off");
    }

    private void HiddenForm_FormClosing(object sender, FormClosingEventArgs e)
    {
        SystemEvents.SessionEnding -= 
            new SessionEndingEventHandler(SystemEvents_SessionEnding);
    }
    private System.ComponentModel.IContainer components = null;

    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    private void InitializeComponent()
    {
        this.SuspendLayout();
        this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
        this.ClientSize = new System.Drawing.Size(0, 0);
        this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
        this.Name = "HiddenForm";
        this.Text = "HiddenForm";
        this.WindowState = System.Windows.Forms.FormWindowState.Minimized;
        this.Load += new System.EventHandler(this.HiddenForm_Load);
        this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.HiddenForm_FormClosing);
        this.ResumeLayout(false);

    }
}
+1  A: 

How about using the Group Policy? You can launch a process on the logoff event, if you use the group policy of the local machine.

Rihan Meij
thanks for your answer, my problem is i cannot catch the logoff event at all. i.e. my service is not notified when the logoff event happens.
DamonZ
I believe he meant calling a separate executable in the logoff event in group policy, which would indeed work.
tbs
i see. But is that the right solution if i want to install the service on about 3000 PCs?
DamonZ
A: 

You need to capture the SystemEvents.SessionEnded event in a hidden windows form. Check out these links for examples on what I mean:

Creating a hidden windows form in a service as a wrapper:

http://msdn.microsoft.com/en-us/library/microsoft.win32.systemevents.aspx

More info about the SystemEvents.SessionEnded event:

http://msdn.microsoft.com/en-us/library/microsoft.win32.systemevents.sessionended.aspx

tbs
I have read both those links and tried them. still isnt working :(
DamonZ
You've tried the hidden windows form? Can you show what you have so far?
tbs
I have edited the original post, as I mentioned i have tried this code with SessionEnded and SessionSwitch as well
DamonZ