Click here to Skip to main content
15,881,381 members
Articles / Desktop Programming / Win32

Monitoring desktop windows from a Windows service

Rate me:
Please Sign up or sign in to vote.
4.72/5 (17 votes)
2 Jan 2008CPOL4 min read 85.1K   7.2K   115  
Capture and save desktop windows from a Windows service.
/*
 * Please leave this Copyright notice in your code if you use it
 * Written by Decebal Mihailescu [http://www.codeproject.com/script/articles/list_articles.asp?userid=634640]
 */
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
using System.Text;
using Microsoft.Win32;
using System.Threading;
using System.Reflection;
using System.Configuration;
using ScreenMonitorLib;
using System.Security.Principal;
using System.Runtime.InteropServices;
using WndList = System.Collections.Generic.List<System.IntPtr>;

namespace ScreenmonitorService
{
    enum SystemCategory : short
    {
        None = 0,
        Devices,
        Disk,
        Printers,
        Services,
        Shell,
        SystemEvent,
        Network

    }
    enum RunAs : uint
    {
        Service = 0,
        Process
    }
    public partial class ScreenMonitor : ServiceBase
    {
        static ManualResetEvent _desktopUnLocked = new ManualResetEvent(true);
        static ManualResetEvent _terminate= new ManualResetEvent(false);
        static ImpersonateInteractiveUser _imptst = null;

        public Thread workerThread = null;
        public static readonly TimeSpan _interval;
        static private bool _blocked = false;
        static ScreenMonitor()
        {

            AppSettingsReader reader = new AppSettingsReader();
            int interval = (int)reader.GetValue("interval", typeof(int));
            _interval = new TimeSpan(interval / 3600, (interval / 60) % 60, interval % 60);
        }
        public ScreenMonitor()
        {
            InitializeComponent();
        }


        protected override void OnStart(string[] args)
        {


            IntPtr handle = this.ServiceHandle;

            if ((workerThread == null) ||
                ((workerThread.ThreadState &
                 (System.Threading.ThreadState.Unstarted | System.Threading.ThreadState.Stopped)) != 0))
            {

                workerThread = new Thread(new ParameterizedThreadStart(DoWork));
                workerThread.Start(this);
            }
            if (workerThread != null)
            {

            }
        }


        public void DoWork(object data)
        {
            
            try
            {

                StartNewDesktopSession();
                string folder = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\";
                ScreenMonitorLib.SnapShot snp = new ScreenMonitorLib.SnapShot(folder, _interval, this.ServiceHandle);


                bool bexit = false;


                do
                {
                    bexit = _terminate.WaitOne(_interval, false);
                    _desktopUnLocked.WaitOne();
                    if(_blocked)
                    {
                        _blocked = false;
                       // EventLog.WriteEntry("Screen Monitor", string.Format("_desktopUnLocked signaled at {0:T}", DateTime.Now.TimeOfDay), EventLogEntryType.Information, 1, 1);
                        continue;
                    }

                    WndList lst = snp.GetDesktopWindows(_imptst.HDesktop);
                    snp.SaveAllSnapShots(lst);
                }
                while (!bexit);
                
            }
            catch (ApplicationException ex)
            {
                EventLog.WriteEntry("Screen Monitor", string.Format("ApplicationException: {0}\n at {1}", ex.Message, ex.InnerException.TargetSite), EventLogEntryType.Error, 1, 1);
            }
            catch (Exception ex)
            {

                EventLog.WriteEntry("Screen Monitor", string.Format("exception in thread at: {0}:{1}", ex.TargetSite.Name, ex.Message), EventLogEntryType.Error, 1, 1);
            }
            finally
            {
                if (_imptst != null)
                    _imptst.Dispose();
            }
        }

        /// <summary>
        /// OnStop: Put your stop code here
        /// - Stop threads, set final data, etc.
        /// </summary>
        protected override void OnStop()
        {
            try
            {
                _terminate.Set();
                _desktopUnLocked.Set();
                workerThread.Join(_interval);
                base.OnStop();
            }
            catch (Exception ex)
            {

                EventLog.WriteEntry("Screen Monitor", "exception in OnStop:" + ex.Message, EventLogEntryType.Error, 1, 1);
            }
        }

        /// <summary>
        /// OnPause: Put your pause code here
        /// - Pause working threads, etc.
        /// </summary>
        protected override void OnPause()
        {
            base.OnPause();
        }

        /// <summary>
        /// OnContinue: Put your continue code here
        /// - Un-pause working threads, etc.
        /// </summary>
        protected override void OnContinue()
        {
            base.OnContinue();
        }

        /// <summary>
        /// OnShutdown(): Called when the System is shutting down
        /// - Put code here when you need special handling
        ///   of code that deals with a system shutdown, such
        ///   as saving special data before shutdown.
        /// </summary>
        protected override void OnShutdown()
        {
            base.OnShutdown();
        }

        /// <summary>
        /// OnCustomCommand(): If you need to send a command to your
        ///   service without the need for Remoting or Sockets, use
        ///   this method to do custom methods.
        /// </summary>
        /// <param name="command">Arbitrary Integer between 128 & 256</param>
        protected override void OnCustomCommand(int command)
        {
            //  A custom command can be sent to a service by using this method:
            //#  int command = 128; //Some Arbitrary number between 128 & 256
            //#  ServiceController sc = new ServiceController("NameOfService");
            //#  sc.ExecuteCommand(command);

            base.OnCustomCommand(command);
        }

        /// <summary>
        /// OnPowerEvent(): Useful for detecting power status changes,
        ///   such as going into Suspend mode or Low Battery for laptops.
        /// </summary>
        /// <param name="powerStatus">The Power Broadcase Status (BatteryLow, Suspend, etc.)</param>
        protected override bool OnPowerEvent(PowerBroadcastStatus powerStatus)
        {
            return base.OnPowerEvent(powerStatus);
        }

        /// <summary>
        /// OnSessionChange(): To handle a change event from a Terminal Server session.
        ///   Useful if you need to determine when a user logs in remotely or logs off,
        ///   or when someone logs into the console.
        /// </summary>
        /// <param name="changeDescription"></param>
        protected override void OnSessionChange(SessionChangeDescription changeDescription)
        {
            switch (changeDescription.Reason)
            {
                case SessionChangeReason.SessionUnlock:
                case SessionChangeReason.SessionLogon:
                    {
                        StartNewDesktopSession();
                        _desktopUnLocked.Set();
                    }
                    break;
                case SessionChangeReason.SessionLock:
                case SessionChangeReason.SessionLogoff:
                    _blocked = true;
                    _desktopUnLocked.Reset(); 
                    break;
                default:
                    break;
            }
            
            //EventLog.WriteEntry("Screen Monitor", string.Format("OnSessionChange reason: {0} session id: {1} at {2:T}", 
            //    changeDescription.Reason, changeDescription.SessionId ,DateTime.Now.TimeOfDay), EventLogEntryType.SuccessAudit, 1, 1);
            base.OnSessionChange(changeDescription);
        }

        private static void StartNewDesktopSession()
        {
            Process[] plst = Process.GetProcessesByName("explorer");
            while (plst.Length == 0)
            {
                Thread.Sleep(Convert.ToInt32(_interval.TotalMilliseconds));
                plst = Process.GetProcessesByName("explorer");
            }
            if(_imptst != null)
                _imptst.Dispose();
            _imptst = new ImpersonateInteractiveUser(plst[0], false);
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
Decebal Mihailescu is a software engineer with interest in .Net, C# and C++.

Comments and Discussions