C# - Run a command on shutdown

kbjradmin

New Member
Messages
512
Reaction score
2
Points
0
I am working on writing a program that will start automatically when the system boots, times how long the computer is running, and store that information in a file when the computer is shutdown. I know how to do all of this except the last part. Is there some way to make my program do something ( like store info in a file ) when the computer shutdown? I don't even know where to start on this, so if someone could explain the basic idea of how to do this, or point me to a tutorial or explaination about this, it would be greatly appreciated. Thanks in advance.
 

saif7463

New Member
Messages
30
Reaction score
0
Points
0
I don't know C#, but I'm sure you could constantly store timedata in a textfile. When the computer is shutdown, the updating will cease, thus the textfile will contain the shutdown time.
 

kbjradmin

New Member
Messages
512
Reaction score
2
Points
0
i guess that idea would work, but what kind of resources would that require? would the computer be able to do that and run other programs without slowing or lagging? i guess i'll start working on that and see how it works. thanks.
 

misson

Community Paragon
Community Support
Messages
2,572
Reaction score
72
Points
48
If the app is running all the time, the application exits at system shutdown, so store the runtime just before the main application function ends. Depending on how you're handling system notifications, you can also either handle the SessionEnding event or WM_ENDSESSION message.
 

kbjradmin

New Member
Messages
512
Reaction score
2
Points
0
misson, i'm still somewhat new to C#. i have a basic understanding of event handling, but don't know how to handle system notifications. i tried to see if i could figure out how to handle the SessionEnding event, but couldn't get it to work. this is what i have:
Code:
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Windows;

namespace UseMonitor
{

    public class DataContainer
    {
        public static string filesource = @"C:\Documents and Settings\HP_Administrator\Desktop\UseMonitor\UseMonitor\bin\Debug\";
        public static string filename = filesource + "system_use.log";
    }

    public partial class UseMonitor : ServiceBase
    {

        public UseMonitor()
        {
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {
            // check that the log file exists
            if (!File.Exists(DataContainer.filename))
                CreateLog(DataContainer.filename);
            // update the log file
            UpdateLog(DataContainer.filename, false);
        }

        protected override void OnStop()
        {
            UpdateLog(DataContainer.filename, "[S]");
        }

        public delegate void SessionEndingEventHandler(object sender, EventArgs e);

        public event SessionEndingEventHandler SessionEnding
        {
            add
            {
                UpdateLog(DataContainer.filename, true);
            }
            remove
            {
            }
        }

        /// <summary>
        /// Creates the log file if it does not already exist
        /// </summary>
        /// <param name="file"></param>
        public static void CreateLog(string filename)
        {
            StreamWriter log;
            log = File.CreateText(filename);
            log.WriteLine("This Log File Was Generated By UseMonitor.");
            log.WriteLine("Each entry in this log contains a boot date and time, shutdown/log off date and time, and a total time the session was active.");
            log.WriteLine("");
            log.WriteLine("Legend:");
            log.WriteLine("[S] - The UseMonitor service was stopped prematurly and does not have an acurate session end time.");
            log.WriteLine("");
            log.Close();
        }

        /// <summary>
        /// Update the log file
        /// Runs on start-up and session end
        /// </summary>
        /// <param name="stop">
        /// False if the function is running at start-up, True if running at session end
        /// </param>
        public static void UpdateLog(string filename, bool stop)
        {
            // get data needed
            string[] time = GetTime();
            int line = GetLogLength(filename);

            // append data to the file
            StreamWriter SW;
            SW = File.AppendText(filename);
            if (stop)
            {
                SW.WriteLine(" " + time[0] + " " + time[1] + time[2]);
            }
            else
            {
                SW.Write(time[0] + " " + time[1] + time[2]);
            }
            SW.Close();
        }

        /// <summary>
        /// Update the log file
        /// Runs on start-up and session end
        /// </summary>
        /// <param name="stop">
        /// False if the function is running at start-up, True if running at session end
        /// </param>
        /// <param name="comment">
        /// Any comments about the nature of the service stop
        /// </param>
        public static void UpdateLog(string filename, string comment)
        {
            // get data needed
            string[] time = GetTime();
            int line = GetLogLength(filename);

            // append data to the file
            StreamWriter SW;
            SW = File.AppendText(filename);
            SW.WriteLine(" " + time[0] + " " + time[1] + time[2] + " " + comment);
            SW.Close();
        }

        /// <summary>
        /// Counts the number of line in a file
        /// Note: Not used in the recent version
        /// </summary>
        /// <param name="filename"></param>
        /// <returns>The number of lines in filename</returns>
        public static int GetLogLength(string filename)
        {
            int count = 0;
            using (var reader = File.OpenText(filename))
            {
                while (reader.ReadLine() != null)
                {
                    count++;
                }
            }
            return count;
        }

        /// <summary>
        /// Gets the current date and time and isolates the seperate parts in an array.
        /// </summary>
        /// <returns>The date and time in an array</returns>
        public static string[] GetTime()
        {
            return DateTime.Now.ToString().Split(new Char[] { ' ' });
        }

    }

}
am i even close, or am i just way off?
 
Last edited:

descalzo

Grim Squeaker
Community Support
Messages
9,373
Reaction score
326
Points
83
I could probably write it for you in perl.

What you do in perl is register a SIGNAL handler, since the system sends the running program a signal when the system is shutting down. Do they have signal handlers in C#?

Then open the file and write the starting time.

Then call sleep() in a loop. Does C# have a sleep function?

When the terminate signal is sent to the program, it writes the current time, closes the file and exits.
 

misson

Community Paragon
Community Support
Messages
2,572
Reaction score
72
Points
48
Code:
namespace UseMonitor {
[...]
    public partial class UseMonitor : ServiceBase {
[...]
        public delegate void SessionEndingEventHandler(object sender, EventArgs e);

        public event SessionEndingEventHandler SessionEnding {
            add {
                UpdateLog(DataContainer.filename, true);
            }
            remove {}
        }
[...]
    }
}
am i even close, or am i just way off?
Not way off, but not exactly close. What you've done is defined an event on your UseMonitor class, so it can fire its own SessionEnding events. Take a look at "How do I... Catch system level events in C#?" and the following example:

Code:
using Microsoft.Win32;
namespace UseMonitor {
[...]
    public partial class UseMonitor : ServiceBase {
        static UseMonitor () {
            // register event handler
            SystemEvents.SessionEnding += new SessionEndingEventHandler(OnSessionEnding);
        }

        // define event handler
        private static void OnSessionEnding(object sender, SessionEndingEventArgs e) {
            UpdateLog(DataContainer.filename, true);
        }
        [...]
    }
}
 

kbjradmin

New Member
Messages
512
Reaction score
2
Points
0
thank you, misson, for a very thorough answer as usual.


Edit:
ok, i tried doing what you said, but i still can't get it to work. what am i doing wrong? here is what i have now:
Code:
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Windows;
using Microsoft.Win32;

namespace UseMonitor
{

    /// <summary>
    /// Used to contain variables needed by multiple methods
    /// </summary>
    public class DataContainer
    {
        public static string filesource = @"C:\Documents and Settings\HP_Administrator\Desktop\UseMonitor\UseMonitor\bin\Debug\";
        public static string filename = filesource + "system_use.log";
        public static DateTime startTime;
        public static DateTime endTime;
    }

    public partial class UseMonitor : ServiceBase
    {

        public UseMonitor()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Occurs on service start
        /// </summary>
        /// <param name="args"></param>
        protected override void OnStart(string[] args)
        {
            SystemEvents.SessionEnding += new SessionEndingEventHandler(OnSessionEnding);
            // check that the log file exists
            if (!File.Exists(DataContainer.filename))
                CreateLog(DataContainer.filename);
            // update the log file
            UpdateLog(DataContainer.filename, false);
        }

        /// <summary>
        /// Occurs on service stop
        /// </summary>
        protected override void OnStop()
        {
            UpdateLog(DataContainer.filename, "[S]");
        }

        /// <summary>
        /// Occurs on session end (shutdown / logoff)
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void OnSessionEnding(object sender, SessionEndingEventArgs e)
        {
            UpdateLog(DataContainer.filename, true);
        }

        /// <summary>
        /// Creates the log file if it does not already exist
        /// </summary>
        /// <param name="file"></param>
        public static void CreateLog(string filename)
        {
            StreamWriter log;
            log = File.CreateText(filename);
            log.WriteLine("This Log File Was Generated By UseMonitor.");
            log.WriteLine("Each entry in this log contains a boot date and time, shutdown/log off date and time, and a total time the session was active.");
            log.WriteLine("");
            log.WriteLine("Legend:");
            log.WriteLine("[S] - The UseMonitor service was stopped prematurly and does not have an acurate session end time.");
            log.WriteLine("");
            log.Close();
        }

        /// <summary>
        /// Update the log file
        /// Runs on start-up and session end
        /// </summary>
        /// <param name="stop">
        /// False if the function is running at start-up, True if running at session end
        /// </param>
        public static void UpdateLog(string filename, bool stop)
        {
            // get data needed
            string[] time = GetTime();
            int line = GetLogLength(filename);

            // append data to the file
            StreamWriter SW;
            SW = File.AppendText(filename);
            if (stop)
            {
                DataContainer.endTime = DateTime.Parse(time[0] + " " + time[1] + " " + time[2]);
                TimeSpan totalTime = DataContainer.endTime.Subtract(DataContainer.startTime);
                SW.WriteLine("     " + time[0] + " " + time[1] + time[2] + "     " + WriteTime(totalTime));
            }
            else
            {
                DataContainer.startTime = DateTime.Parse(time[0] + " " + time[1] + " " + time[2]);
                SW.Write(time[0] + " " + time[1] + time[2]);
            }
            SW.Close();
        }

        /// <summary>
        /// Update the log file
        /// Runs on start-up and session end
        /// </summary>
        /// <param name="stop">
        /// False if the function is running at start-up, True if running at session end
        /// </param>
        /// <param name="comment">
        /// Any comments about the nature of the service stop
        /// </param>
        public static void UpdateLog(string filename, string comment)
        {
            // get data needed
            string[] time = GetTime();
            int line = GetLogLength(filename);

            // append data to the file
            StreamWriter SW;
            SW = File.AppendText(filename);
            DataContainer.endTime = DateTime.Parse(time[0] + " " + time[1] + " " + time[2]);
            TimeSpan totalTime = DataContainer.endTime.Subtract(DataContainer.startTime);
            SW.WriteLine("     " + time[0] + " " + time[1] + time[2] +  "     " + WriteTime(totalTime) + "     " + comment);
            SW.Close();
        }

        /// <summary>
        /// Counts the number of line in a file
        /// Note: Not used in the recent version
        /// </summary>
        /// <param name="filename"></param>
        /// <returns>The number of lines in filename</returns>
        public static int GetLogLength(string filename)
        {
            int count = 0;
            using (var reader = File.OpenText(filename))
            {
                while (reader.ReadLine() != null)
                {
                    count++;
                }
            }
            return count;
        }

        /// <summary>
        /// Gets the current date and time and isolates the seperate parts in an array.
        /// </summary>
        /// <returns>The date and time in an array</returns>
        public static string[] GetTime()
        {
            return DateTime.Now.ToString().Split(new Char[] { ' ' });
        }

        /// <summary>
        /// Converts a TimeSpan into a string in the format d:hh:mm:ss
        /// </summary>
        /// <param name="time"></param>
        /// <returns></returns>
        public static string WriteTime(TimeSpan time)
        {
            return time.Days + ":" + time.Hours + ":" + time.Minutes + ":" + time.Seconds;
        }

    }

}
 
Last edited:

kbjradmin

New Member
Messages
512
Reaction score
2
Points
0
ok, i've gone from one problem to three...
first, there is the problem i first posted about (see previous post for details).
second, i have a part of my program that is using the current username. when i handcode the username in, it works fine; but when i use Environment.UserName, it doesn't work, and i can't figure out why.
third, is there anyway to get a list of all of the users on the computer?

here is my current code:
Code:
using Microsoft.Win32;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Management;
using System.Security.AccessControl;
using System.ServiceProcess;
using System.Text;
using System.Windows;
using System.Windows.Forms;

namespace UseMonitor
{

    /// <summary>
    /// Used to contain variables needed by multiple methods
    /// </summary>
    public class DataContainer
    {
        public static string filesource = Environment.GetEnvironmentVariable("userprofile") + @"\Desktop\UseMonitor\UseMonitor\bin\Debug\";
        public static string filename = filesource + "system_use.log";
        public static DateTime startTime;
        public static DateTime endTime;
        public static string systemName = Environment.MachineName; // YOUR-4DACD0EA75
        public static string userName = Environment.UserName; // HP_Administrator
        public static string[] userList = { "HP_Administrator" };
    }

    public partial class UseMonitor : ServiceBase
    {

        public UseMonitor()
        {
            SystemEvents.SessionEnding += new SessionEndingEventHandler(OnSessionEnding);
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {
            // check that the log file exists
            if (!File.Exists(DataContainer.filename))
                CreateLog();
            // update the log file
            UpdateLog(false);
        }

        protected override void OnStop()
        {
            UpdateLog("[S]");
            SystemEvents.SessionEnding -= new SessionEndingEventHandler(OnSessionEnding);
        }

        void OnSessionEnding(object sender, SessionEndingEventArgs e)
        {
            UpdateLog(true);
        }

        public static void CreateLog()
        {
            StreamWriter log;
            log = File.CreateText(DataContainer.filename);
            log.WriteLine("This Log File Was Generated By UseMonitor.");
            log.WriteLine("Each entry in this log contains a boot date and time, shutdown/log off date and time, and a total time the session was active.");
            log.WriteLine("");
            log.WriteLine("Legend:");
            log.WriteLine("[S] - The UseMonitor service was stopped prematurly and does not have an acurate session end time.");
            log.WriteLine(""); log.WriteLine("");
            log.WriteLine("======================================================");
            log.WriteLine(""); log.WriteLine("");
            log.Close();

            // lock the file for all users on the system
            foreach (string user in DataContainer.userList)
            {
                LockFile(DataContainer.filename, DataContainer.systemName + "\\" + user, true);
            }
        }

        public static void UpdateLog(bool stop)
        {
            // get data needed
            string[] time = GetTime();
            int line = GetLogLength(DataContainer.filename);

            // unlock the log file
            LockFile(DataContainer.filename, DataContainer.systemName + "\\" + DataContainer.userName, false);

            // append data to the file
            StreamWriter SW;
            SW = File.AppendText(DataContainer.filename);
            if (stop)
            {
                DataContainer.endTime = DateTime.Parse(time[0] + " " + time[1] + " " + time[2]);
                TimeSpan totalTime = DataContainer.endTime.Subtract(DataContainer.startTime);
                SW.WriteLine("     " + time[0] + " " + time[1] + time[2] + "     " + WriteTime(totalTime));
            }
            else
            {
                DataContainer.startTime = DateTime.Parse(time[0] + " " + time[1] + " " + time[2]);
                SW.Write(time[0] + " " + time[1] + time[2]);
            }
            SW.Close();

            // re-lock the log file
            LockFile(DataContainer.filename, DataContainer.systemName + "\\" + DataContainer.userName, true);
        }

        public static void UpdateLog(string comment)
        {
            // get data needed
            string[] time = GetTime();
            int line = GetLogLength(DataContainer.filename);

            // unlock the log file
            LockFile(DataContainer.filename, DataContainer.systemName + "\\" + DataContainer.userName, false);

            // append data to the file
            StreamWriter SW;
            SW = File.AppendText(DataContainer.filename);
            DataContainer.endTime = DateTime.Parse(time[0] + " " + time[1] + " " + time[2]);
            TimeSpan totalTime = DataContainer.endTime.Subtract(DataContainer.startTime);
            SW.WriteLine("     " + time[0] + " " + time[1] + time[2] +  "     " + WriteTime(totalTime) + "     " + comment);
            SW.Close();

            // re-lock the log file
            LockFile(DataContainer.filename, DataContainer.systemName + "\\" + DataContainer.userName, true);
        }

        public static int GetLogLength(string filename)
        {
            int count = 0;
            using (var reader = File.OpenText(filename))
            {
                while (reader.ReadLine() != null)
                {
                    count++;
                }
            }
            return count;
        }

        public static string[] GetTime()
        {
            return DateTime.Now.ToString().Split(new Char[] { ' ' });
        }

        public static string WriteTime(TimeSpan time)
        {
            return time.Days + ":" + time.Hours + ":" + time.Minutes + ":" + time.Seconds;
        }

        public static void AddFileSecurity(string fileName, string account, FileSystemRights rights, AccessControlType controlType)
        {

            // Get a FileSecurity object that represents the
            // current security settings.
            System.Security.AccessControl.FileSecurity fSecurity = File.GetAccessControl(fileName);

            // Add the FileSystemAccessRule to the security settings.
            fSecurity.AddAccessRule(new FileSystemAccessRule(account, rights, controlType));

            // Set the new access settings.
            File.SetAccessControl(fileName, fSecurity);

        }

        public static void RemoveFileSecurity(string fileName, string account, FileSystemRights rights, AccessControlType controlType)
        {

            // Get a FileSecurity object that represents the
            // current security settings.
            System.Security.AccessControl.FileSecurity fSecurity = File.GetAccessControl(fileName);

            // Add the FileSystemAccessRule to the security settings.
            fSecurity.RemoveAccessRule(new FileSystemAccessRule(account, rights, controlType));

            // Set the new access settings.
            File.SetAccessControl(fileName, fSecurity);

        }

        public static void LockFile(string filename, string account, bool lockfile)
        {
            if (lockfile)
            {
                AddFileSecurity(filename, account, FileSystemRights.Write, AccessControlType.Deny);
                AddFileSecurity(filename, account, FileSystemRights.Delete, AccessControlType.Deny);
                AddFileSecurity(filename, account, FileSystemRights.ChangePermissions, AccessControlType.Deny);
            }
            else
            {
                RemoveFileSecurity(filename, account, FileSystemRights.Write, AccessControlType.Deny);
                RemoveFileSecurity(filename, account, FileSystemRights.Delete, AccessControlType.Deny);
                RemoveFileSecurity(filename, account, FileSystemRights.ChangePermissions, AccessControlType.Deny);
            }
        }

    }

}
thanks in advance for any help.
 

misson

Community Paragon
Community Support
Messages
2,572
Reaction score
72
Points
48
ok, i've gone from one problem to three...
first, there is the problem i first posted about (see previous post for details).

From the MSDN documentation for SessionEnding
This event is only raised if the message pump is running. In a Windows service, unless a hidden form is used or the message pump has been started manually, this event will not be raised. For a code example that shows how to handle system events by using a hidden form in a Windows service, see the SystemEvents class.


second, i have a part of my program that is using the current username. when i handcode the username in, it works fine; but when i use Environment.UserName, it doesn't work, and i can't figure out why.
Is the handcoded user name the same as Environment.UserName? One potential problem is you're adding a deny permission for FileSystemRights.ChangePermissions, which means non-admins can't then remove all the deny permissions.

Note that you don't need separate calls to AddFileSecurity/RemoveFileSecurity because you can combine permissions by or-ing them:
Code:
 AddFileSecurity(filename, account, 
                        FileSystemRights.Write | FileSystemRights.Delete | FileSystemRights.ChangePermissions, 
                        AccessControlType.Deny);
Also, having separate LockFile and UnlockFile methods rather than a single LockFile method is cleaner.

If you're using file permissions for exclusive access, the better way is to use file locks, via the FileShare argument to the FileStream constructor.
Code:
        public static void UpdateLog(string comment) {
            // get data needed
            string[] time = GetTime();
            int line = GetLogLength(DataContainer.filename);

            // append data to the file
            FileStream fs = new FileStream(DataContainer.filename, FileMode.Append, FileAccess.Write, FileShare.Read);
            StreamWriter log = new StreamWriter(fs);
            ...
            log.Close(); // also closes fs
        }
FileShare.Read allows other processes to open the log file with read access but not write access. Use FileShare.None for exclusive access.

third, is there anyway to get a list of all of the users on the computer?
Should be moot at this point, but here's one way.
 

kbjradmin

New Member
Messages
512
Reaction score
2
Points
0
thanks again, misson. you always give great answers. :]



edit:
ok, i know this must be getting old, but i have some more problems now...

first problem:
my log file looked kind of wierd because the columns weren't even, so i wrote a method to add zeros where the values were only one digit. but it won't seem to work.

what is wrong with my method?

second problem:

for some reason, my program has trouble with being stopped on the command line. as far as i can tell, it isn't causing a problem in the functioning of the program, but it still a problem.
this is what happens when i try to stop the service:

netStop.png


my code:
Code:
using Microsoft.Win32;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.DirectoryServices;
using System.IO;
using System.Linq;
using System.Management;
using System.Security.AccessControl;
using System.ServiceProcess;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Forms;

namespace UseMonitor
{
    public class DataContainer
    {
        public static string filesource = @"C:\Documents and Settings\HP_Administrator\Desktop\UseMonitor\UseMonitor\bin\Debug\";
        public static string filename = filesource + "system_use.log";
        public static DateTime startTime;
        public static DateTime endTime;
        public static string[] date;
        public static string[] time;
        public static string systemName = Environment.MachineName; // "YOUR-4DACD0EA75"
        public static string userName = Environment.UserName; // "HP_Administrator"
        public static string[] userList = { userName };
    }

    public partial class UseMonitor : ServiceBase
    {

        public UseMonitor()
        {
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {
            // start the message pump
            new Thread(RunMessagePump).Start();
            // check that the log file exists
            if (!File.Exists(DataContainer.filename))
                CreateLog();
            // update the log file
            UpdateLog(false);
        }

        protected override void OnStop()
        {
            UpdateLog("[S]");
            Application.Exit();
        }

        void RunMessagePump()
        {
            Application.Run(new HiddenForm());
        }

        public static void CreateLog()
        {
            FileStream fs = new FileStream(DataContainer.filename, FileMode.Append, FileAccess.Write, FileShare.Read);
            StreamWriter log = new StreamWriter(fs);
            log.WriteLine("This Log File Was Generated By UseMonitor.");
            log.WriteLine("Each entry in this log contains a boot date and time, shutdown/log off date and time, and a total time the session was active.");
            log.WriteLine("");
            log.WriteLine("Legend:");
            log.WriteLine("[S] - The UseMonitor service was stopped prematurly and does not have an acurate session end time.");
            log.WriteLine(""); log.WriteLine("");
            log.WriteLine("======================================================");
            log.WriteLine(""); log.WriteLine("");
            log.Close();
        }

        public static void UpdateLog(bool stop)
        {
            // get data needed
            string[] time = GetTime();
            int line = GetLogLength(DataContainer.filename);

            // unlock the log file
            // UnlockFile(DataContainer.filename, DataContainer.systemName + "\\" + DataContainer.userName);

            // append data to the file
            FileStream fs = new FileStream(DataContainer.filename, FileMode.Append, FileAccess.Write, FileShare.Read);
            StreamWriter log = new StreamWriter(fs);
            if (stop)
            {
                DataContainer.endTime = DateTime.Parse(time[0] + " " + time[1] + " " + time[2]);
                TimeSpan totalTime = DataContainer.endTime.Subtract(DataContainer.startTime);
                log.WriteLine("     " + time[0] + " " + time[1] + time[2] + "     " + WriteTimeSpan(totalTime));
            }
            else
            {
                DataContainer.startTime = DateTime.Parse(time[0] + " " + time[1] + " " + time[2]);
                log.Write(time[0] + " " + time[1] + time[2]);
            }
            log.Close();
        }

        public static void UpdateLog(string comment)
        {
            // get data needed
            string[] time = GetTime();
            int line = GetLogLength(DataContainer.filename);

            // append data to the file
            FileStream fs = new FileStream(DataContainer.filename, FileMode.Append, FileAccess.Write, FileShare.Read);
            StreamWriter log = new StreamWriter(fs);
            log = File.AppendText(DataContainer.filename);
            DataContainer.endTime = DateTime.Parse(time[0] + " " + time[1] + " " + time[2]);
            TimeSpan totalTime = DataContainer.endTime.Subtract(DataContainer.startTime);
            log.WriteLine("     " + time[0] + " " + time[1] + time[2] +  "     " + WriteTimeSpan(totalTime) + "     " + comment);
            log.Close();
        }

        public static string[] GetTime()
        {
            // get the date and time
            string[] now = DateTime.Now.ToString().Split(new char[] { ' ' });

            // split up the date and time into parts
            DataContainer.date = now[0].Split(new char[] { '/' });
            DataContainer.time = now[1].Split(new char[] { ':' });
            string ampm = now[3];

            // check each value in date for less than 10
            if (Int32.Parse(DataContainer.date[0]) < 10)
                DataContainer.date[0] = "0" + DataContainer.date[0];
            if (Int32.Parse(DataContainer.date[1]) < 10)
                DataContainer.date[1] = "0" + DataContainer.date[1];
            if (Int32.Parse(DataContainer.date[2]) < 10)
                DataContainer.date[2] = "0" + DataContainer.date[2];

            // check each value in time for less than 10
            if (Int32.Parse(DataContainer.time[0]) < 10)
                DataContainer.time[0] = "0" + DataContainer.time[0];
            if (Int32.Parse(DataContainer.time[1]) < 10)
                DataContainer.time[1] = "0" + DataContainer.time[1];
            if (Int32.Parse(DataContainer.time[2]) < 10)
                DataContainer.time[2] = "0" + DataContainer.time[2];

            // reconstruct the date and time
            string[] date = {
                                DataContainer.date[0] + DataContainer.date[1] + DataContainer.date[2],
                                DataContainer.time[0] + DataContainer.time[1] + DataContainer.time[2],
                                ampm
                            };

            return date;
        }

        public static string WriteTimeSpan(TimeSpan time)
        {
            return time.Days + ":" + time.Hours + ":" + time.Minutes + ":" + time.Seconds;
        }

        public static void AddFileSecurity(string fileName, string account, FileSystemRights rights, AccessControlType controlType)
        {

            // Get a FileSecurity object that represents the
            // current security settings.
            System.Security.AccessControl.FileSecurity fSecurity = File.GetAccessControl(fileName);

            // Add the FileSystemAccessRule to the security settings.
            fSecurity.AddAccessRule(new FileSystemAccessRule(account, rights, controlType));

            // Set the new access settings.
            File.SetAccessControl(fileName, fSecurity);

        }

        public static void RemoveFileSecurity(string fileName, string account, FileSystemRights rights, AccessControlType controlType)
        {

            // Get a FileSecurity object that represents the
            // current security settings.
            System.Security.AccessControl.FileSecurity fSecurity = File.GetAccessControl(fileName);

            // Add the FileSystemAccessRule to the security settings.
            fSecurity.RemoveAccessRule(new FileSystemAccessRule(account, rights, controlType));

            // Set the new access settings.
            File.SetAccessControl(fileName, fSecurity);

        }

        public static void LockFile(string filename, string account)
        {
            AddFileSecurity(filename, account,
                FileSystemRights.Write | FileSystemRights.Delete,
                AccessControlType.Deny);
        }

        public static void UnlockFile(string filename, string account)
        {
            RemoveFileSecurity(filename, account,
                FileSystemRights.Write | FileSystemRights.Delete,
                AccessControlType.Deny);
        }

    }

    public partial class HiddenForm : Form
    {
        ...
    }

    partial class HiddenForm
    {
        ...
    }
}
 
Last edited:

misson

Community Paragon
Community Support
Messages
2,572
Reaction score
72
Points
48
Since you're now using threads, you'll want to use synchronization. The UpdateLog methods are critical sections that will need locking. Also, you'll have to handle the possibility that opening the log file will fail when another process already has a write lock (synchronization should prevent this from happening with other threads in the same process).

edit:
ok, i know this must be getting old, but i have some more problems now...
As long as you're making progress, it doesn't get old.

first problem:
my log file looked kind of wierd because the columns weren't even, so i wrote a method to add zeros where the values were only one digit. but it won't seem to work.
The code seems to be doing unnecessary work, converting from a DateTime to a string array and back to a DateTime and concatenating the elements of the array to form a string... Also, there's no need to write your own date formatting function when .Net already supports format strings. Work with DateTime objects and convert to strings only when you need strings (in this case, when you write them to the log).

If you want a specific format, call DateTime.ToString(string format), e.g.
Code:
DataContainer.endTime.toString("yyyy/MM/dd hh:mm:ss tt")

Even better, use format strings with StreamWriter.WriteLine:
Code:
    public static void UpdateLog(string comment)
        {
            // get data needed
            int line = GetLogLength(DataContainer.filename);

            // append data to the file
            FileStream fs = new FileStream(DataContainer.filename, FileMode.Append, FileAccess.Write, FileShare.Read);
            StreamWriter log = new StreamWriter(fs);
            /* note: you don't need File.AppendText() because you're creating a FileStream
              and wrapping it in a StreamWriter
            */

            DataContainer.endTime = DateTime.Now;
            TimeSpan totalTime = DataContainer.endTime.Subtract(DataContainer.startTime);

            log.WriteLine("     {0:yyyy/MM/dd hh:mm:ss tt}     {1: hh:mm:ss}     {2}", 
                                      DataContainer.endTime,            totalTime,            comment)
            log.Close();
        }

second problem:

for some reason, my program has trouble with being stopped on the command line. as far as i can tell, it isn't causing a problem in the functioning of the program, but it still a problem.
Can you start it on the command line? What's the code to start the service? Anything like:
Code:
namespace UseMonitor {
    class Program {
        static UseMonitor useMe;
        static void Main(string[] args) {
            useMe = new UseMonitor();
            System.ServiceProcess.ServiceBase.Run(useMe);
        }
    }
}

You might as well clear out the unnecessary fields and methods (e.g. UseMonitor.AddFileSecurity, DataContainer.userList); they're just clutter at this point.

You shouldn't start the RunMessagePump thread until log file setup has completed.
Code:
protected override void OnStart(string[] args)
        {
            // check that the log file exists
            if (!File.Exists(DataContainer.filename))
                CreateLog();
            // update the log file
            UpdateLog(false);
            // start the message pump
            new Thread(RunMessagePump).Start();
        }
With that change, you can also get rid of the long FileStream constructor in CreateLog and use the simpler FileStream.Create() (which is how it used to be written).
 

kbjradmin

New Member
Messages
512
Reaction score
2
Points
0
ok, i've gotten the string formatting with the dates and times to work and i figured out why the service was having trouble stopping ( it was because i needed to remove log = File.AppendText(...); ). but i can't quite figure out the syncronization thing. i'm going to keep working on it to see if i can figure it out, and if i can't i'll repost here.
here's my code right now if you're curious:
Code:
using Microsoft.Win32;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.DirectoryServices;
using System.IO;
using System.Linq;
using System.Management;
using System.Security.AccessControl;
using System.ServiceProcess;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Forms;


/*
 * TODO:
 * 1. SessionEnding Event
 * 2. Environment.UserName
 */


namespace UseMonitor
{
    
    /// <summary>
    /// Used to contain variables needed by multiple methods
    /// </summary>
    public class DataContainer
    {
        public static string filesource = @"C:\Documents and Settings\HP_Administrator\Desktop\UseMonitor\UseMonitor\bin\Debug\";
        public static string filename = filesource + "system_use.log";
        public static DateTime startTime;
        public static DateTime endTime;
        public static string systemName = Environment.MachineName; // "YOUR-4DACD0EA75"
        public static string userName = Environment.UserName; // "HP_Administrator"
        public static AutoResetEvent autoReset;
    }

    /// <summary>
    /// The Service
    /// </summary>
    public partial class UseMonitor : ServiceBase
    {

        static private System.Object lockThis = new System.Object();

        public UseMonitor()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Occurs on service start
        /// </summary>
        /// <param name="args"></param>
        protected override void OnStart(string[] args)
        {
            // check that the log file exists
            if (!File.Exists(DataContainer.filename))
                CreateLog();

            // update the log file
            UpdateLogStart();

            // start the message pump
            Thread pump = new Thread(RunMessagePump);
            pump.Start();
        }

        /// <summary>
        /// Occurs on service stop
        /// </summary>
        protected override void OnStop()
        {
            UpdateLogEnd("[S]");
            Application.Exit();
        }

        void RunMessagePump()
        {
            Application.Run(new HiddenForm());
            DataContainer.autoReset.WaitOne();
            UpdateLogEnd(null);
        }

        /// <summary>
        /// Creates the log file if it does not already exist
        /// </summary>
        /// <param name="file"></param>
        public static void CreateLog()
        {
            FileStream fs = File.Create(DataContainer.filename);
            StreamWriter log = new StreamWriter(fs);
            log.WriteLine("This Log File Was Generated By UseMonitor.");
            log.WriteLine("Each entry in this log contains a boot date and time, shutdown/log off date and time, and a total time the session was active.");
            log.WriteLine("");
            log.WriteLine("Legend:");
            log.WriteLine("[S] - The UseMonitor service was stopped prematurly and does not have an acurate session end time.");
            log.WriteLine(""); log.WriteLine("");
            log.WriteLine("======================================================");
            log.WriteLine("");
            log.Close();
        }

        /// <summary>
        /// Update the log file
        /// Runs on start-up and session end
        /// </summary>
        public static void UpdateLogStart()
    {
            // get data needed
            string[] time = GetTime();

            // append data to the file
            FileStream fs = new FileStream(DataContainer.filename, FileMode.Append, FileAccess.Write, FileShare.Read);
            StreamWriter log = new StreamWriter(fs);
            DataContainer.startTime = DateTime.Parse(time[0] + " " + time[1] + " " + time[2]);
            log.WriteLine();
            log.Write(time[0] + " " + time[1] + time[2]);
            log.Close();
        }

        /// <summary>
        /// Update the log file
        /// Runs on start-up and session end
        /// </summary>
        /// <param name="comment">
        /// Any comments about the nature of the service stop
        /// Null if no comments
        /// </param>
        public static void UpdateLogEnd(string comment)
        {
            lock (lockThis)
            {
                // get data needed
                string[] time = GetTime();

                // if there is a comment, prepend it with five (5) spaces
                if (comment != null)
                    comment = "     " + comment;

                // append data to the file
                FileStream fs = new FileStream(DataContainer.filename, FileMode.Append, FileAccess.Write, FileShare.Read);
                StreamWriter log = new StreamWriter(fs);
                DataContainer.endTime = DateTime.Parse(time[0] + " " + time[1] + " " + time[2]);
                TimeSpan totalTime = DataContainer.endTime.Subtract(DataContainer.startTime);
                log.Write("     " + time[0] + " " + time[1] + time[2] + "     " + WriteTimeSpan(totalTime) + comment);
                log.Close();
            }
        }

        /// <summary>
        /// Gets the current date and time and isolates the seperate parts in an array.
        /// </summary>
        /// <returns>The date and time in an array</returns>
        public static string[] GetTime()
        {
            // get the date and time
            DateTime dt = DateTime.Now; 
            string now = string.Format("{0:MM/dd/yyyy hh:mm:ss tt}", dt);
            return now.Split(new char[] { ' ' });
        }

        /// <summary>
        /// Converts a TimeSpan into a string in the format d:hh:mm:ss
        /// </summary>
        /// <param name="time"></param>
        /// <returns></returns>
        public static string WriteTimeSpan(TimeSpan time)
        {
            return string.Format("{0:D2}", time.Days) + ":" + string.Format("{0:hh:mm:ss}", time);
        }

    }

    /// <summary>
    /// Message Pump
    /// </summary>
    public partial class HiddenForm : Form
    {
        public HiddenForm()
        {
            InitializeComponent();
        }

        private void HiddenForm_Load(object sender, EventArgs e)
        {
            SystemEvents.SessionEnding += new SessionEndingEventHandler(SystemEvents_SessionEnding);
        }

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

        private void SystemEvents_SessionEnding(object sender, EventArgs e)
        {
            DataContainer.autoReset.Set();
        }
    }

    /// <summary>
    /// Message Pump Form
    /// </summary>
    partial class HiddenForm
    {

        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);

        }

    }

}
Edit:
ok, i just can't seem to figure out the message pump/thread/syncronization thing.
my code is posted above. from what i have, what should be my next step?
 
Last edited:

misson

Community Paragon
Community Support
Messages
2,572
Reaction score
72
Points
48
ok, i've gotten the string formatting with the dates and times to work
The code you posted still converts a DateTime to a string array and back again, which is still inefficient.

Code:
[...]
        void RunMessagePump()
        {
            Application.Run(new HiddenForm());
            DataContainer.autoReset.WaitOne();
            UpdateLogEnd(null);
        }
Application.Run runs a Form's message loop in the same thread, so Application.Run doesn't return until the message loop is done. The use of a AutoResetEvent is unnecessary.

Code:
[...]
        private void InitializeComponent()
        {
            this.SuspendLayout();
            [...]
            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);

        }
This is where the problem actually lies. Form.Load "occurs before a form is displayed for the first time" (according to MSDN). Since you're creating a hidden form (it's the minimized state, in particular), the form isn't displayed and the load event never fires. There's a similar caveat about Form.Dispose:
Dispose will be called automatically if the form is shown using the Show method. If another method such as ShowDialog is used, or the form is never shown at all, you must call Dispose yourself within your application.
Instead of the Load and FormClosing events, try the constructor & destructor to register even handlers.

Code:
[...]
        private System.ComponentModel.IContainer components = null;

        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }
You're not using components anywhere, so you can ditch both it and Dispose(bool).
 

kbjradmin

New Member
Messages
512
Reaction score
2
Points
0
i tried changing the HiddenForm class to set/remove event handlers with the constructor and destructor, but it still doesn't work. also, i changed it to format the DateTime as it writes to the log file instead of changing to a string, and then back to a DateTime.
here is my code:
Code:
using Microsoft.Win32;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration.Install;
using System.Data;
using System.Diagnostics;
using System.DirectoryServices;
using System.IO;
using System.Linq;
using System.Management;
using System.Security.AccessControl;
using System.ServiceProcess;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Forms;


/*
 * TODO:
 * 1. SessionEnding Event
 * 2. Environment.UserName
 * 3. File Locks
 */


namespace UseMonitor
{

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    static class Program
    {
        static void Main()
        {
            UseMonitor service = new UseMonitor();
            ServiceBase.Run(service);
        }
    }
    


    /// <summary>
    /// Used to contain variables needed by multiple methods
    /// </summary>
    public class DataContainer
    {
        public static string filesource = @"C:\Documents and Settings\HP_Administrator\Desktop\UseMonitor\UseMonitor\bin\Debug\";
        public static string filename = filesource + "system_use.log";
        public static DateTime startTime;
        public static DateTime endTime;
        public static string systemName = Environment.MachineName; // "YOUR-4DACD0EA75"
        public static string userName = Environment.UserName; // "HP_Administrator"
    }



    /// <summary>
    /// The main service class
    /// </summary>
    public partial class UseMonitor : ServiceBase
    {

        static private System.Object lockThis = new System.Object();

        public UseMonitor()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Occurs on service start
        /// </summary>
        /// <param name="args"></param>
        protected override void OnStart(string[] args)
        {
            // check that the log file exists
            if (!File.Exists(DataContainer.filename))
                CreateLog();

            // update the log file
            UpdateLogStart();

            // start the message pump
            Thread pump = new Thread(RunMessagePump);
            pump.Start();
        }

        /// <summary>
        /// Occurs on service stop
        /// </summary>
        protected override void OnStop()
        {
            UpdateLogEnd("[S]");
            Application.Exit();
        }

        void RunMessagePump()
        {
            Application.Run(new HiddenForm());
        }

        /// <summary>
        /// Creates the log file if it does not already exist
        /// </summary>
        /// <param name="file"></param>
        public static void CreateLog()
        {
            FileStream fs = File.Create(DataContainer.filename);
            StreamWriter log = new StreamWriter(fs);
            log.WriteLine("This Log File Was Generated By UseMonitor.");
            log.WriteLine("Each entry in this log contains a boot date and time, shutdown/log off date and time, and a total time the session was active.");
            log.WriteLine("");
            log.WriteLine("Legend:");
            log.WriteLine("[S] - The UseMonitor service was stopped prematurly and does not have an acurate session end time.");
            log.WriteLine(""); log.WriteLine("");
            log.WriteLine("======================================================");
            log.WriteLine("");
            log.Close();
        }

        /// <summary>
        /// Update the log file
        /// Runs on start-up and session end
        /// </summary>
        public static void UpdateLogStart()
        {
            // get data needed
            DataContainer.startTime = DateTime.Now;

            // append data to the file
            FileStream fs = new FileStream(DataContainer.filename, FileMode.Append, FileAccess.Write, FileShare.Read);
            StreamWriter log = new StreamWriter(fs);
            log.WriteLine();
            log.Write(string.Format("{0:MM/dd/yyyy hh:mm:sstt}", DataContainer.startTime));
            log.Close();
        }

        /// <summary>
        /// Update the log file
        /// Runs on start-up and session end
        /// </summary>
        /// <param name="comment">
        /// Any comments about the nature of the service stop
        /// Null if no comments
        /// </param>
        public static void UpdateLogEnd(string comment)
        {
            lock (lockThis)
            {
                // get data needed
                DataContainer.endTime = DateTime.Now;
                TimeSpan totalTime = DataContainer.endTime.Subtract(DataContainer.startTime);

                // if there is a comment, prepend it with five (5) spaces
                if (comment != null)
                    comment = "     " + comment;

                // append data to the file
                FileStream fs = new FileStream(DataContainer.filename, FileMode.Append, FileAccess.Write, FileShare.Read);
                StreamWriter log = new StreamWriter(fs);
                log.Write("     " + string.Format("{0:MM/dd/yyyy hh:mm:sstt}", DataContainer.endTime) + "     " + WriteTimeSpan(totalTime) + comment);
                log.Close();
            }
        }

        /// <summary>
        /// Converts a TimeSpan into a string in the format d:hh:mm:ss
        /// </summary>
        /// <param name="time"></param>
        /// <returns></returns>
        public static string WriteTimeSpan(TimeSpan time)
        {
            return string.Format("{0:D2}", time.Days) + ":" + string.Format("{0:hh:mm:ss}", time);
        }

    }

    partial class UseMonitor
    {
        /// <summary> 
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        private void InitializeComponent()
        {
            components = new System.ComponentModel.Container();
            this.ServiceName = "UseMonitor";
        }
    }



    /// <summary>
    /// Message pump to get the SessionEnding event
    /// </summary>
    public partial class HiddenForm : Form
    {
        public HiddenForm()
        {
            InitializeComponent();
            SystemEvents.SessionEnding += new SessionEndingEventHandler(SystemEvents_SessionEnding);
        }

        ~HiddenForm()
        {
            SystemEvents.SessionEnding -= new SessionEndingEventHandler(SystemEvents_SessionEnding);
        }

        private void SystemEvents_SessionEnding(object sender, EventArgs e)
        {
            UseMonitor.UpdateLogEnd(null);
        }
    }

    partial class HiddenForm
    {

        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.ResumeLayout(false);
        }

    }



    /// <summary>
    /// Installer Class
    /// </summary>
    [RunInstaller(true)]
    public partial class Install : Installer
    {
        public Install()
        {
            ServiceProcessInstaller UseMonitorProcessInstaller = new ServiceProcessInstaller();
            ServiceInstaller UseMonitorInstaller = new ServiceInstaller();

            UseMonitorProcessInstaller.Account = ServiceAccount.LocalSystem;
            UseMonitorProcessInstaller.Username = null;
            UseMonitorProcessInstaller.Password = null;

            UseMonitorInstaller.ServiceName = "UseMonitor";
            UseMonitorInstaller.Description = "Monitors the use of the current system.";
            UseMonitorInstaller.StartType = ServiceStartMode.Automatic;
            UseMonitorInstaller.DisplayName = "UseMonitor";

            this.Installers.Add(UseMonitorProcessInstaller);
            this.Installers.Add(UseMonitorInstaller);
        }
    }

}
 

misson

Community Paragon
Community Support
Messages
2,572
Reaction score
72
Points
48
Code:
namespace UseMonitor {
    public class DataContainer {
        public static string filesource = @"C:\Documents and Settings\HP_Administrator\Desktop\UseMonitor\UseMonitor\bin\Debug\";
Make sure whatever user the service runs as has write access to this folder. You might want to change it to
Code:
        public static string filesource = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + @"\UseMonitor\";

As for the rest, the main thing is to follow the advise in the SystemEvents.SessionEnding documentation and override WndProc for the hidden form:
Code:
    partial class HiddenForm {
        private static int WM_QUERYENDSESSION = 0x11;

        protected override void WndProc(ref System.Windows.Forms.Message m) {
            if (m.Msg == WM_QUERYENDSESSION) {
                UseMonitor.UpdateLogEnd(null);
            }

            // If this is WM_QUERYENDSESSION, the closing event should be
            // raised in the base WndProc.
            base.WndProc(ref m);
        } //WndProc 
    }
}
Ther above will log a shutdown even if the shutdown is cancelled. There's no sure-fire way of preventing this that I can think of without introducing other bugs. The approach in the SystemEvents.SessionEnding doc is another way, but will log a spurious session end if a session end is cancelled and the service is stopped without ending the session.

If there's something you want the HiddenForm to do (such as logging the session end, rather than doing it in WndProc) when the form is destroyed, you'll need to keep a reference to the HiddenForm when you create it in RunMessagePump and call its Close method from the main service thread. Otherwise the thread will be aborted.
Code:
    public partial class UseMonitor : ServiceBase {
        private HiddenForm msgPump;
        void RunMessagePump() {
            msgPump = new HiddenForm();
            System.Windows.Forms.Application.Run(msgLoop);
        }
        protected override void OnStop() {
            msgPump.Close();
            System.Windows.Forms.Application.Exit();
        }
    }
Then add the code to HiddenForm.Dispose() or a FormClosing handler (seems ~HiddenForm() won't work). Dispose() is simpler because if you use a FormClosing handler, you'll need to register it. Note that unlike the Load event, FormClosing will fire on an undisplayed form.
 

kbjradmin

New Member
Messages
512
Reaction score
2
Points
0
i still can't get it to work. this is what i've got. what am i doing wrong?
Code:
using Microsoft.Win32;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration.Install;
using System.Data;
using System.Diagnostics;
using System.DirectoryServices;
using System.IO;
using System.Linq;
using System.Management;
using System.Security.AccessControl;
using System.ServiceProcess;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Forms;


/*
 * TODO:
 * 1. SessionEnding Event
 * 2. Environment.UserName
 * 3. File Locks
 */


namespace UseMonitor
{

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    static class Program
    {
        static void Main()
        {
            UseMonitor service = new UseMonitor();
            ServiceBase.Run(service);
        }
    }
    


    /// <summary>
    /// Used to contain variables needed by multiple methods
    /// </summary>
    public class DataContainer
    {
        public static string filesource = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + @"\UseMonitor\";
        public static string filename = filesource + "system_use.log";
        public static DateTime startTime;
        public static DateTime endTime;
        public static string systemName = Environment.MachineName; // "YOUR-4DACD0EA75"
        public static string userName = Environment.UserName; // "HP_Administrator"
    }



    /// <summary>
    /// The main service class
    /// </summary>
    public partial class UseMonitor : ServiceBase
    {

        static private System.Object lockThis = new System.Object();
        private HiddenForm msgPump;

        public UseMonitor()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Occurs on service start
        /// </summary>
        /// <param name="args"></param>
        protected override void OnStart(string[] args)
        {
            // check that the appdata directory exists
            if (!Directory.Exists(DataContainer.filesource))
                Directory.CreateDirectory(DataContainer.filesource);

            // check that the log file exists
            if (!File.Exists(DataContainer.filename))
                CreateLog();

            // update the log file
            UpdateLogStart();

            // start the message pump
            Thread pump = new Thread(RunMessagePump);
            pump.Start();
        }

        /// <summary>
        /// Occurs on service stop
        /// </summary>
        protected override void OnStop()
        {
            msgPump.Close();
            Application.Exit();
            UpdateLogEnd("[S]");
        }

        void RunMessagePump()
        {
            msgPump = new HiddenForm();
            Application.Run(msgPump);
        }

        /// <summary>
        /// Creates the log file if it does not already exist
        /// </summary>
        /// <param name="file"></param>
        public static void CreateLog()
        {
            FileStream fs = File.Create(DataContainer.filename);
            StreamWriter log = new StreamWriter(fs);
            log.WriteLine("This Log File Was Generated By UseMonitor.");
            log.WriteLine("Each entry in this log contains a boot date and time, shutdown/log off date and time, and a total time the session was active.");
            log.WriteLine("");
            log.WriteLine("Legend:");
            log.WriteLine("[S] - The UseMonitor service was stopped prematurly and does not have an acurate session end time.");
            log.WriteLine(""); log.WriteLine("");
            log.WriteLine("======================================================");
            log.WriteLine("");
            log.Close();
        }

        /// <summary>
        /// Update the log file
        /// Runs on start-up and session end
        /// </summary>
        public static void UpdateLogStart()
        {
            // get data needed
            DataContainer.startTime = DateTime.Now;

            // append data to the file
            FileStream fs = new FileStream(DataContainer.filename, FileMode.Append, FileAccess.Write, FileShare.Read);
            StreamWriter log = new StreamWriter(fs);
            log.WriteLine();
            log.Write(string.Format("{0:MM/dd/yyyy hh:mm:sstt}", DataContainer.startTime));
            log.Close();
        }

        /// <summary>
        /// Update the log file
        /// Runs on start-up and session end
        /// </summary>
        /// <param name="comment">
        /// Any comments about the nature of the service stop
        /// Null if no comments
        /// </param>
        public static void UpdateLogEnd(string comment)
        {
            lock (lockThis)
            {
                // get data needed
                DataContainer.endTime = DateTime.Now;
                TimeSpan totalTime = DataContainer.endTime.Subtract(DataContainer.startTime);

                // if there is a comment, prepend it with five (5) spaces
                if (comment != null)
                    comment = "     " + comment;

                // append data to the file
                FileStream fs = new FileStream(DataContainer.filename, FileMode.Append, FileAccess.Write, FileShare.Read);
                StreamWriter log = new StreamWriter(fs);
                log.Write("     " + string.Format("{0:MM/dd/yyyy hh:mm:sstt}", DataContainer.endTime) + "     " + WriteTimeSpan(totalTime) + comment);
                log.Close();
            }
        }

        /// <summary>
        /// Converts a TimeSpan into a string in the format d:hh:mm:ss
        /// </summary>
        /// <param name="time"></param>
        /// <returns></returns>
        public static string WriteTimeSpan(TimeSpan time)
        {
            return string.Format("{0:D2}", time.Days) + ":" + string.Format("{0:hh:mm:ss}", time);
        }

    }

    partial class UseMonitor
    {
        /// <summary> 
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        private void InitializeComponent()
        {
            components = new System.ComponentModel.Container();
            this.ServiceName = "UseMonitor";
        }
    }



    /// <summary>
    /// Message pump to get the SessionEnding event
    /// </summary>
    public partial class HiddenForm : Form
    {
        public HiddenForm()
        {
            InitializeComponent();
        }

        private void Form_Closing(object sender, CancelEventArgs e)
        {
            UseMonitor.UpdateLogEnd(null);
        }
    }

    partial class HiddenForm
    {

        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.ResumeLayout(false);
            this.Closing += new CancelEventHandler(this.Form_Closing);
        }

        private static int WM_QUERYENDSESSION = 0x11;

        protected override void WndProc(ref System.Windows.Forms.Message m)
        {
            if (m.Msg == WM_QUERYENDSESSION)
            {
                UseMonitor.UpdateLogEnd(null);
            }

            // If this is WM_QUERYENDSESSION, the closing event should be
            // raised in the base WndProc.
            base.WndProc(ref m);
        } // WndProc

    }



    /// <summary>
    /// Installer Class
    /// </summary>
    [RunInstaller(true)]
    public partial class Install : Installer
    {
        public Install()
        {
            ServiceProcessInstaller UseMonitorProcessInstaller = new ServiceProcessInstaller();
            ServiceInstaller UseMonitorInstaller = new ServiceInstaller();

            UseMonitorProcessInstaller.Account = ServiceAccount.LocalSystem;
            UseMonitorProcessInstaller.Username = null;
            UseMonitorProcessInstaller.Password = null;

            UseMonitorInstaller.ServiceName = "UseMonitor";
            UseMonitorInstaller.Description = "Monitors the use of the current system.";
            UseMonitorInstaller.StartType = ServiceStartMode.Automatic;
            UseMonitorInstaller.DisplayName = "UseMonitor";

            this.Installers.Add(UseMonitorProcessInstaller);
            this.Installers.Add(UseMonitorInstaller);
        }
    }

}
 

misson

Community Paragon
Community Support
Messages
2,572
Reaction score
72
Points
48
i still can't get it to work. this is what i've got. what am i doing wrong?
You need to be more informative. What isn't working? Can you install the service? Does the service create the log file? Is anything logged?

The name of the Install class will cause problems as the base class has an Install method and methods with the same name as the class are constructors. Rename the Install class to (e.g.) UseMonitorInstaller.
 

kbjradmin

New Member
Messages
512
Reaction score
2
Points
0
the service install and runs fine. it stores information to the log file fine. i still can't get the SessionEnding part to work.
 

misson

Community Paragon
Community Support
Messages
2,572
Reaction score
72
Points
48
Sorry for the delay, the real world was calling.

At some point, I had working code and hoped to post that, but it was lost when I was playing around with the code a few posts back. Currently, even the sample on MSDN isn't working for me.

An alternative is to search the System EventLog for events with source EventLog. Look for event ID 6009 to mark a system start, and event ID 6006 for system shutdown.
 
Top