detect shutdown in window service

Tl;dr

In your service set

CanShutdown = true;

then override

protected override void OnShutdown()
{
    //Your code here

    //Don't forget to call ServiceBase OnShutdown()
    base.OnShutdown();
}

Now the extended answer

I know I'm bringing this up from the dead but I found it helpful and hope to add a little to the topic. I'm implementing a WCF duplex library hosted in a Windows Service and came across this thread because I needed to detect, from within the windows service, when a user logs off or shuts down the computer. I'm using .Net Framework 4.6.1 on Windows 7 and Windows 10. Like previously suggested for shutdown what worked for me was overriding ServiceBase.OnShutdown() like so:

protected override void OnShutdown()
{
    //Your code here

    //Don't forget to call ServiceBase OnShutdown()
    base.OnShutdown();
}

Remember to add the following to your service's constructor to allow the shutdown event to be caught:

CanShutdown = true;

Then to capture when a user logs off, locks the screen, switches user, etc. you can just override the OnSessionChange method like so:

protected override void OnSessionChange(SessionChangeDescription changeDescription)
{
    if (changeDescription.Reason == SessionChangeReason.SessionLogoff)
    {
        //Your code here...

        //I called a static method in my WCF inbound interface class to do stuff...
    }

    //Don't forget to call ServiceBase OnSessionChange()
    base.OnSessionChange(changeDescription);
}

And of course remember to add the following to your service's constructor to allow catching of session change events:

CanHandleSessionChangeEvent = true;

For a shutdown, override the OnShutdown method:

protected override void OnShutdown()
{
    //your code here
    base.OnShutdown();
}

For a logoff:

First, add an event handler to Microsoft.Win32.SystemEvents.SessionEnded in the Service Constructor:

public MyService()
{
    InitializeComponent;
    Microsoft.Win32.SystemEvents.SessionEnded += new Microsoft.Win32.SessionEndedEventHandler(SystemEvents_SessionEnded);
}

Then add the handler:

void SystemEvents_SessionEnded(object sender, Microsoft.Win32.SessionEndedEventArgs e)
{
    //your code here
}

This should catch any ended session, including the console itself (the one running the services).