Web Security, A Lot More Complicated Than It Seems

I recently finished writing Security for Web Developers. During the months that I worked on the book, I became aware of a serious problem in the reporting, handling, and supposed fixes for the problem of web security—everyone seems intent on making things fast and easy. Depending on the source, you also see a fair amount of finger pointing at play. Sources put the blame on just one or two entities in most cases. Unfortunately, the picture is far more complex than simply applying a bandage to one or two potential security problem sources. I started understanding the problem when I wrote HTML5 Programming with JavaScript for Dummies and CSS3 for Dummies, but it wasn’t until I wrote this book that I began to understand the true enormity of the problem. It isn’t just one or two or three sources—it’s all the sources combined. In this latest book I explore a lot of different sources of security problems and provide advice on how to overcome these issues to some extent.

  • Users
  • Application Developers
  • Third Party Library, API, and Microservice Providers
  • Administrators and Other IT Staff
  • Product Distributors
  • Data Service Providers

Many other groups appear in the book as well. The more I dug, the more I found that just fixing one problem or educating one group wouldn’t solve anything. Hackers look for easy ways to gain access to applications and the current system provides them with plenty of opportunities. The current strategy of responding to just one potential threat will continue to fail simply because the hacker will move on to another threat. Unless an organization is willing to take a holistic approach to security, hackers will continue to enjoy overwhelming success without a whole lot of work. In writing Security for Web Developers, I attempted to provide a broader view of the security picture so that development teams that include all of the stakeholders involved in an application effort can finally work together to resolve the security issues in their individual areas of expertise (including users who are susceptible to too many kinds of attack to mention).

A reader recently asked me whether the strategies in my book will prevent attacks, which is a loaded question and one that is hard to answer. My view of security is that a determined hacker will always gain entrance to your system, so you must remain vigilant at all times. If someone wants your data, they’ll gain access, but if you’re equally vigilant, you can keep the damage to a minimum. For that matter, you might be able to prevent any real damage. However, you need to realize that no security measure you take is going to succeed all the time.

What my book does is help make your system less appealing. In other words, if a hacker is just looking for a system to invade and not specifically your system, then making your system less appealing will see the hacker move to other systems. Like anyone else, a hacker seeks to minimize effort and maximize gain. Making your system less appealing by employing a holistic security approach will increase the effort the hacker must employ and make it less likely that the hacker will continue probing.

Unless you really want to see your organization’s name join the victim list in the trade press, you really do need to employ security across an organization, which means vetting software fully, educating users, having appropriate policies in place, reviewing software before placing it in production, and so on. Using just one or two measures simply won’t work. Let me know if you have questions regarding my upcoming book at John@JohnMuellerBooks.com.

 

Exploring the TypingBuddy Application (Part 9)

In the Exploring the TypingBuddy Application (Part 8) post you discovered how the TypingBuddy form loads and closes. However, after the initial configuration, you might never see that form again because TypingBuddy loads itself in the Notification Area. The first thing to notice is that the Notification Area contains an icon for this application named ThisNotifyIcon. You can assign a default behavior to this icon by creating an event handler for the DoubleClick event named NotifyIcon_DoubleClick().  All that this event handler does is display the form as shown in the following code.

private void NotifyIcon_DoubleClick(object sender, EventArgs e)
{
   // Display the form.
   ThisFrmMain.Show();
   WindowState = FormWindowState.Normal;
}

When the user right clicks on the TypingBuddy icon in the notification, the application displays a menu containing options for opening the window, pausing the countdown, and exiting the application. Of course, you could easily add any other functionality required by your personal needs, including the ability to configure the application.

TypingBuddy0901

In order to create this menu, you must select the NotificationMenu component (added as part of the Exploring the TypingBuddy Application (Part 2) post) and click on the right pointing arrow that appears in the upper right corner of the control. You’ll see an Edit Menu link on a menu like this one shown here.

TypingBuddy0902

Clicking Edit Menu adds the context menu to the design area. You can then add the menu entries to it as shown here.

TypingBuddy0903

Each of these menu entries requires some configuration as shown in the following table.

Menu Property/Event Value/Event Handler
mnuOpen Text &Open Window
  Click NotifyIcon_DoubleClick()
mnuPause Text &Pause
  Click btnPause_Click()
mnuExit Text E&xit
  Click mnuExit_Click()

You’ve already seen the code for mnuOpen, which is the same code that executes when the user double clicks the icon. Here is the code you use for mnuPause and mnuExit.

bool IsPaused;   // Is the timer paused?
 
private void btnPause_Click(object sender, System.EventArgs e)
{
   if (IsPaused)
   {
      // Start the timer.
      IsPaused = false;
      TypeTimer.Start();
 
      // Display status information.
      btnPause.Text = "&Pause";
      mnuPause.Text = "&Pause";
   }
   else
   {
      // Stop the timer.
      IsPaused = true;
      TypeTimer.Stop();
 
      // Display status information.
      btnPause.Text = "&Restart";
      mnuPause.Text = "&Restart";
      ThisNotifyIcon.Text = "Timer is Paused";
   }
}
 
private void mnuExit_Click(object sender, System.EventArgs e)
{
   // Obtain the current settings.
   Settings.DisplayMessageIcon = chkShowIcon.Checked;
   Settings.PlaySound = chkPlaySound.Checked;
   Settings.RestInterval = (Int32)RestingValue.Value;
   Settings.TypingInterval = (Int32)TypingInterval.Value;
 
   // Save the settings.
   TBMessages.SaveSettings(Settings);
 
   // Exit the application.
   Close();
}

The btnPause_Click() event handler is actually shared with the Pause button on frmMain, so you’ll see it mentioned again. Because there are two distinctly different ways to pause the application, the code requires a semaphore (a shared variable) to maintain the state information. When the application is paused, selecting Restart will change the IsPaused variable, restart the timer, and change the captions for both the Pause button and the NotificationMenu menu to Pause. The reverse happens when the application is running—IsPaused is set to true, the timer is stopped, and the captions are changed to Restart.

When the user clicks Exit on the NotificationMenu, the application calls the mnuExit_Click() event handler. This event handler changes the Settings properties to match those of frmMain, and then saves the settings to disk. It then closes frmMain, which ends the application.

That’s almost it for the Notification Area code. There is one added feature for ThisNotifyIcon. It displays the remaining time when you hover the mouse over it. This feature is covered in next week’s post, which discusses the Timer_Tick() event handler. This event handler is quite complex because it has to consider issues like which message to display, which can vary depending on whether the user has chosen to use dates with the messages. In the meantime, let me know whether you have any questions about this part of the application at John@JohnMuellerBooks.com. You can find the next post in this series at Exploring the TypingBuddy Application (Part 10).

 

Exploring the TypingBuddy Application (Part 8) Update

There is a small mistake in the first listing in Exploring the TypingBuddy Application (Part 8). I didn’t include a definition for two of the required global variables because they appear later in my personal code listing. As a result, the code won’t currently compile without error. You actually need three global variable definitions in place of the one shown for Settings. So, the global variable list in that first listing should look like this:

// Holds the main form.
static frmMain ThisFrmMain;
 
// Current typing time.
Int32 TimeLeft;
 
// Create a variable to hold the messages.
private TBMessages Settings;

Thank you to everyone who took time to write me about this error.  Let me know if you have any additional questions or observations at John@JohnMuellerBooks.com.

 

Exploring the TypingBuddy Application (Part 8)

In the previous post in this series, Exploring the TypingBuddy Application (Part 7), you saw the last underlying component of the message management part of this application. It’s time to start looking at frmMain—the part of the application that pulls everything together. From a design perspective, this part of the application has these parts:

 

  • Application loading and closing
  • Notification icon management
  • Timer management and event handling
  • User interface event handling


The posts that follow will discuss these elements one at a time. So, today, you’ll learn about how the application loads and what happens when you close it. When the application loads, it also has the load any settings that the user may have configured for it as shown here.

// Create a variable to hold the messages.
private TBMessages Settings;
 
private void frmMain_Load(object sender, System.EventArgs e)
{
   // Attempp to load the settings.
   Settings = TBMessages.LoadSettings();
 
   // See if the user has worked with this program before.
   if (Settings == null)
   {
      // If not, display a welcome message and the configuration
      // screen.
      MessageBox.Show("Welcome to TypingBuddy!\r\n" +
         "You need to set the initial typing and rest times by " +
         "entering a typing and resting interval and clicking " +
         "Save Settings. It's also a good idea to create custom " +
         "messages that will tell you specific things to do " +
         "while resting.",
         "Welcome Message",
         MessageBoxButtons.OK,
         MessageBoxIcon.Information);
      ThisFrmMain.Show();
      WindowState = FormWindowState.Normal;
 
      // Initialize Settings.
      Settings = new TBMessages();
   }
   else
   {
      // Configure the form.
      chkShowIcon.Checked = Settings.DisplayMessageIcon;
      chkPlaySound.Checked = Settings.PlaySound;
      RestingValue.Value = Convert.ToDecimal(Settings.RestInterval);
      TypingInterval.Value = Convert.ToDecimal(Settings.TypingInterval);
      TimeLeft = (Int32)TypingInterval.Value * 60;
 
      // Start the timer.
      TypeTimer.Start();
 
      // Hide the form.
      ThisFrmMain.Hide();
   }
}

The settings are stored into a TBMessages object, Settings, by calling TBMessages.LoadSettings(). When Settings is null the application either hasn’t been run before or the user has deleted the old settings. In this case, the application begins by telling the user that he/she has to provide some configuration settings. It then displays frmMain with the default configuration in place and saves a default set of settings to Settings. The user can make any required changes and close the form (a task described in an upcoming post).

When there are settings to use, the application configures itself according to those settings. It then starts the Timer object, TypeTimer. Notice that it immediately hides frmMain from view. The user sees the application in the Notification Area, but never gets any other indication that the application is running. This is how applications of this sort normally run. You could add a splash screen, if desired, to provide a visual cue to the user that the application is running. If you do add a splash screen, you need to provide a configuration setting for it. Some users will prefer not to see the splash screen.

The application also needs to perform a number of tasks when it ends. Typically, the user will click Quit on frmMain to accomplish this task or choose Exit on the Notification Area context menu (an option discussed in a later post). In both cases, there is one important task to perform—saving the settings to disk. The following code shows how this task is performed when clicking Quit on frmMain.

private void btnQuit_Click(object sender, System.EventArgs e)
{
   DialogResult RetValue;   // Users selection.
 
   // Display an exit message.
   RetValue = MessageBox.Show("Are you sure you want to exit?" +
      "\r\n(Program will minimize if you select No.)",
      "Exit Application",
      MessageBoxButtons.YesNo,
      MessageBoxIcon.Question,
      MessageBoxDefaultButton.Button2);
 
   if (RetValue == DialogResult.Yes)
   {
      // Obtain the current settings.
      Settings.DisplayMessageIcon = chkShowIcon.Checked;
      Settings.PlaySound = chkPlaySound.Checked;
      Settings.RestInterval = (Int32)RestingValue.Value;
      Settings.TypingInterval = (Int32)TypingInterval.Value;
 
      // Save the settings.
      TBMessages.SaveSettings(Settings);
 
      // Exit the application.
      Close();
   }
   else
   {
      // Minimize the form.
      WindowState = FormWindowState.Minimized;
      ThisFrmMain.Hide();
   }
}

The code begins by asking the user whether he/she is sure about ending the application. In some cases, a user will click a button by accident. Adding these sorts of reminders too often in an application is annoying. However, this is one time when a reminder is helpful because the application is ended when the user clicks Quit. What many users will want to click instead is Minimize Window.

When the user clicks Yes in response to the message. The application saves its settings and closes the form, which ends the application. However, when the user clicks No instead, the application merely minimizes the window. It then hides the window from view.

Well, that’s it for loading the application settings and ending the application. Next week we’ll discuss how the Notification Area context menu works. In the meantime, let me know whether you have any questions about the application at John@JohnMuellerBooks.com. Make sure you check out the update for this post at Exploring the TypingBuddy Application (Part 8) Update. You can see the next post in this series at Exploring the TypingBuddy Application (Part 9).

 

Exploring the TypingBuddy Application (Part 7)

In the previous post, Exploring the TypingBuddy Application (Part 6), you learned how the user can create custom messages for TypingBuddy. These messages make it possible for TypingBuddy to suggest activities that interest the user. The form used for this activity, frmAddEdit, can display a single message at a time for either adding or editing a message. The focus of this week’s post, frmMessages, makes it possible to manage the list of configured messages, as well as add new ones. It provides access to the functionality required to add, edit, and delete messages as shown here.

TypingBuddy0701

The setup required to create this form appears in the Exploring the TypingBuddy Application (Part 3) post. Before this form can do anything else, it must have a copy of the current message list. It uses this list to populate the Messages list box when the form loads. Here’s the code needed to perform this task:

// Create a copy of the database.
TBMessages Settings;
 
private void frmMessages_Load(object sender, EventArgs e)
{
   // Initialize the settings.
   Settings = TBMessages.LoadSettings();
 
   // Determine if there are any messages to load.
   if (Settings.MessageList != null)
 
      // If there are messages to process, check them one at a time.
      foreach (TBMessage ThisMessage in Settings.MessageList)
 
         // Display just the title in the listbox.
         lstMessages.Items.Add(ThisMessage.Title);
}

The example begins by creating a TBMessages object, Settings, to hold the list of messages along with the other settings used by the application. In frmMessages_Load(), the application instantiates this object by calling TBMessages.LoadSettings(). When Settings contains one or more TBMessage entries, the application loads them into lstMessages so that the user can see them immediately after the form loads.

The form contains four buttons. The code for Close is very simple—all it does is pass back a DialogResult value of Cancel. The Add button is a little more complex as shown here:

private void btnAdd_Click(object sender, EventArgs e)
{
   // Create the required form.
   frmAddEdit AddMessage = new frmAddEdit();
 
   // Provide the required resources.
   if (Settings.MessageList != null)
      AddMessage.MessageList = Settings.MessageList;
 
   // Display the form.
   if (AddMessage.ShowDialog(this) == DialogResult.OK)
   {
      // Retrieve the new settings.
      Settings.MessageList = AddMessage.MessageList;
 
      // Save the settings.
      TBMessages.SaveSettings(Settings);
   }
 
   // Clear the message list.
   lstMessages.Items.Clear();
 
   // Determine if there are any messages to load.
   if (Settings.MessageList != null)
 
      // If there are messages to process, check them one at a time.
      foreach (TBMessage ThisMessage in Settings.MessageList)
 
         // Display just the title in the listbox.
         lstMessages.Items.Add(ThisMessage.Title);
}

The code begins by creating a frmAddEdit object, AddMessage. It then passes the current list of messages to AddMessage and displays the form on screen where the user will provide values for a new message. When the return value is DialogResult.OK, the code retrieves the updated list of messages and places them in Settings.MessageList. The code then saves the new list of messages to disk.

At this point, the code must update the user’s view of the messages. It does this by calling lstMessages.Items.Clear() first to clear any existing messages. When Settings.MessageList contains messages (it may not because the user may have chosen to click Cancel instead of adding a message), the code adds each of the message titles to lstMessages.

The Edit button is the most complex of the four buttons. It not only must choose which message to edit, but it must also configure the frmAddEdit object to display a message editing dialog box, rather than a message adding dialog box. Of course, this event handler will also need to perform all of the tasks performed when adding a new message. Here’s the code you use to perform these tasks.

private void btnEdit_Click(object sender, EventArgs e)
{
   // Create the required form.
   frmAddEdit AddMessage = new frmAddEdit();
 
   // Configure the form.
   AddMessage.Text = "Edit Existing Message";
   AddMessage.btnAdd.Text = "&Edit";
   AddMessage.btnAdd.AccessibleDescription =
      "Edits an existing item in the list.";
   AddMessage.toolTip1.SetToolTip(AddMessage.btnAdd,
      "Edits an existing item in the list.");
 
   // Set up for an edit.
   AddMessage.IsEdit = true;
 
   // Make sure there are records to edit.
   if (Settings.MessageList != null)
   {
      // Provide the required resources.
      AddMessage.MessageList = Settings.MessageList;
 
      // Set the record number.
      AddMessage.RecordNumber = lstMessages.SelectedIndex;
   }
   else
   {
      // Display a message and exit when there are no records to edit.
      MessageBox.Show(
         "There are no messages to edit!",
         "Editing Error",
         MessageBoxButtons.OK,
         MessageBoxIcon.Error);
      return;
   }
 
   // Make sure the user has selected an item to edit.
   if (lstMessages.SelectedIndex == -1)
   {
      // Display a message and exit when there are no records to edit.
      MessageBox.Show(
         "You must select a message to edit!",
         "Selection Error",
         MessageBoxButtons.OK,
         MessageBoxIcon.Error);
      return;
   }
 
   // Display the form.
   if (AddMessage.ShowDialog(this) == DialogResult.OK)
   {
      // Retrieve the new settings.
      Settings.MessageList = AddMessage.MessageList;
 
      // Save the settings.
      TBMessages.SaveSettings(Settings);
   }
 
   // Clear the message list.
   lstMessages.Items.Clear();
 
   // Determine if there are any messages to load.
   if (Settings.MessageList != null)
 
      // If there are messages to process, check them one at a time.
      foreach (TBMessage ThisMessage in Settings.MessageList)
 
         // Display just the title in the listbox.
         lstMessages.Items.Add(ThisMessage.Title);
}

This part of the example begins by creating a frmAddEdit object, AddMessage (I simply recycled the name from the previous event handler). The first two changes configure the form so that the user sees the right messages. However, because we’re also focusing on accessibility in this example, the application must also change the settings that affect accessibility. For example, this changes would modify what a user hears through a screen reader. When making your application accessibility friendly, you need to keep these sorts of changes in mind.

Because the application is editing an existing message, rather than editing an existing message, it must also set AddMessage.IsEdit to true. As mentioned in the previous posting, this setting affects how the form reacts to user input and also controls initial configuration.

At this point, the application must pass the list of messages along to AddMessage, just as it did when adding a new message. However, notice the error trapping code that is added in this case. For example, you can add a message when the message list is null, but you can edit a message that doesn’t exist. Likewise, a user must actually select a message in the list in order to edit it—you can’t simply assume which message the user intended to edit.

Only after the conditions are right can the application display the editing dialog box. When the user makes changes and clicks Edit, the application updates the message list and updates the list of messages, just as it does when adding a new message.

The Delete button is unique in that it does change the message list, but it doesn’t rely on frmAddEdit to do it. All that really needs to happen is that the application removes the required record from the message database as shown here:

private void btnDelete_Click(object sender, EventArgs e)
{
   // Make sure the user has selected an item to edit.
   if (lstMessages.SelectedIndex == -1)
   {
      // Display a message and exit when
      // the user hasn't selected a record.
      MessageBox.Show(
         "You must select a message to edit!",
         "Selection Error",
         MessageBoxButtons.OK,
         MessageBoxIcon.Error);
      return;
   }
 
   // Create a temporary message database.
   TBMessage[] Temp = new TBMessage[Settings.MessageList.Length - 1];
 
   // Create a counter for the temporary database.
   Int32 Count = 0;
 
   // Check each of the records for the defined message and delete
   // the selected message by not copying it to the message list.
   foreach (TBMessage ThisMessage in Settings.MessageList)
      if (ThisMessage.Title != (String)lstMessages.SelectedItem)
      {
         // Copy the record.
         Temp[Count] = ThisMessage;
 
         // Update the counter.
         Count++;
      }
 
   // Update the settings.
   Settings.MessageList = Temp;
    
   // Save the new message list.
   TBMessages.SaveSettings(Settings);
 
   // Update the message list.
   lstMessages.Items.RemoveAt(lstMessages.SelectedIndex);
}

The main requirement that the application must adhere to in this case is that the user must select a record to delete. Again, the application can’t assume anything about what the user really intended to do. Good design practices dictate that you make this check and then ask the user to make a selection when there isn’t any selection.

There are many different methods you can use to delete a record from MessageList. The example chooses to use a simple approach that works quite fast. It simply creates a temporary list of messages, Temp, of type TBMessage[] that contains one less record than the original array. It then copies all of the original records to the temporary array, except for the one record that matches the one that the user has selected. After it completes the copying process, the application saves the message list and updates lstMessages so the user sees that the entry is deleted.

We’ve moved up to frmMain. This particular form will require a little more work than you’ve done so far because it’s the focal point of everything that TypingBuddy does. Next week we’ll start a series of posts about frmMain. In the meantime, let me know if you have any questions at John@JohnMuellerBooks.com. You can see the next post in this series at Exploring the TypingBuddy Application (Part 8).

 

Exploring the TypingBuddy Application (Part 6)

The previous post, Exploring the TypingBuddy Application (Part 5), describes the technique for displaying messages of various types. A user can choose to use the default message or provide any of a number of randomly selected custom messages. This post assumes that the user has chosen the custom message route. Of course, that means having a database of messages to choose from. The act of adding and editing messages requires a special form, one that contains all of the fields required to perform these two tasks. The TypingBuddy application relies on frmAddEdit to perform this task. You saw this form described in the Exploring the TypingBuddy Application (Part 3) post. As described by its name, this is a dual use form that allows for either adding or editing message records to the XML file that contains the user’s data for this application.

A developer has a number of techniques available to create forms used for multiple purposes. In this case, the caller exercises control over public members of frmAddEdit to modify its behavior. This approach is actually the easiest way to do things. In order to make configuration easier, frmAddEdit defines three special properties:

// Create a resources property that contains the message database.
public TBMessage[] MessageList { get; set; }
 
// Make it possible to edit a particular record.
public Int32 RecordNumber { get; set; }
 
// Check whether this is an add or an edit.
public Boolean IsEdit { get; set; }

These properties contain the message database, the selected record within that database, and a simple Boolean value that determines whether the form is in adding or editing mode. Because this form is created with flexibility in mind, you could probably add other properties to better control precisely how the form appears. However, you don’t require the level of flexibility described for frmMessage, so the use of multiple constructors (as in that case) is unnecessary.

The task of configuring the form falls to the frmAddEdit_Load() event handler. There are actually two levels of configuration required—the appearance of the user interface and the data shown within the form. The appearance is handled by the caller at the time the form is created, before it gets displayed. The data is configured as shown here:

private void frmAddEdit_Load(object sender, EventArgs e)
{
   // Fill the related sound list with the correct values.
   cbSound.Items.AddRange(
      Enum.GetNames(typeof(TBMessages.SystemSoundList)));
   cbSound.SelectedIndex = 0;
 
   // When this is an edit, update the form with the correct
   // data.
   if (IsEdit)
   {
      txtTitle.Text = MessageList[RecordNumber].Title;
      chkShowTitle.Checked = MessageList[RecordNumber].ShowTitle;
      txtMessageText.Text = MessageList[RecordNumber].MessageText;
      cbSound.SelectedItem = MessageList[RecordNumber].Sound.ToString();
      chkDateSpecific.Checked = MessageList[RecordNumber].UseDates;
 
      // Don't provide date values unless the message uses dates.
      if (MessageList[RecordNumber].UseDates)
      {
         dtStartDate.Value = MessageList[RecordNumber].StartDate;
         dtEndDate.Value = MessageList[RecordNumber].EndDate;
      }
      else
      {
         dtStartDate.Enabled = false;
         dtEndDate.Enabled = false;
      }
   }
}

When the form is in adding mode, the frmAddEdit_Load() event handler only has to add the list of available sounds to cbSound, and then select one of the items on the list. Because the form is already configured for adding mode, you don’t need to do anything to the user interface.

In editing mode, the frmAddEdit_Load() event handler must perform some additional tasks. It begins by configuring all of the common message data on the form. Then, if the UseDates property is checked, the event handler must also configure the starting and ending date values.

Handling the use of dates is an important consideration. You don’t want to set any dates unless the user says to use dates. Consequently, the application also requires the chkDateSpecific_CheckedChanged() event handler shown here.

private void chkDateSpecific_CheckedChanged(object sender, EventArgs e)
{
   // Determine the checked state.
   if (chkDateSpecific.Checked)
   {
      // Enable the dates when using date information.
      dtStartDate.Enabled = true;
      dtEndDate.Enabled = true;
   }
   else
   {
      // Otherwise, disable the dates.
      dtStartDate.Enabled = false;
      dtEndDate.Enabled = false;
   }
}

This event handler enables or disables the date fields as needed. Notice that the date fields are always enabled and disabled as a pair. The user must set both a starting and ending date in order for the date system to work properly.

The most complex piece of code for this part of the example appears in the btnAdd_Click() event handler. This code must incorporate a number of safety check to ensure the user doesn’t enter an invalid record into the database. In addition, the code must ensure that dates are only recorded when the user wants to employ dates as a filtering mechanism as shown here.

private void btnAdd_Click(object sender, EventArgs e)
{
   // Verify that the fields are correct.
   if ((txtTitle.TextLength == 0) || (txtMessageText.TextLength == 0))
   {
      // Display a message and exit when the user
      // hasn't provided the right information.
      MessageBox.Show(
         "You must provide values for both the Message Title " +
         "and Message Text fields!",
         "Entry Error",
         MessageBoxButtons.OK,
         MessageBoxIcon.Error);
      return;
   }
 
   // Create a temporary array.
   TBMessage[] Temp;
 
   // Determine the sort of change taking place.
   if (IsEdit)
   {
      // Initialize the temporary array.
      Temp = MessageList;
 
      // Add information to the new element.
      Temp[RecordNumber].Title = txtTitle.Text;
      Temp[RecordNumber].ShowTitle = chkShowTitle.Checked;
      Temp[RecordNumber].MessageText = txtMessageText.Text;
      Temp[RecordNumber].Sound =
         (TBMessages.SystemSoundList)Enum.Parse(
            typeof(TBMessages.SystemSoundList),
            cbSound.SelectedItem.ToString());
      Temp[RecordNumber].UseDates = chkDateSpecific.Checked;
 
      // Add dates only when required.
      if (chkDateSpecific.Checked)
      {
         Temp[RecordNumber].StartDate = dtStartDate.Value;
         Temp[RecordNumber].EndDate = dtEndDate.Value;
      }
 
      // Update the permanent message list.
      MessageList = Temp;
   }
   else
   {
      // Initialize the temporary array.
      if (MessageList != null)
      {
         // Create a temporary array that's one longer than the
         // existing array.
         Temp = new TBMessage[MessageList.Length + 1];
 
         // Copy the messages from the existing array to the
         // temporary array.
         for (Int32 Record = 0; Record < MessageList.Length; Record++)
            Temp[Record] = MessageList[Record];
 
         // Add a new record to the end of the temporary array.
         Temp[Temp.Length - 1] = new TBMessage();
      }
      else
      {
         Temp = new TBMessage[1];
         Temp[Temp.Length - 1] = new TBMessage();
      }
 
      // Add information to the new element.
      Temp[Temp.Length - 1].Title = txtTitle.Text;
      Temp[Temp.Length - 1].ShowTitle = chkShowTitle.Checked;
      Temp[Temp.Length - 1].MessageText = txtMessageText.Text;
      Temp[Temp.Length - 1].Sound =
         (TBMessages.SystemSoundList)Enum.Parse(
            typeof(TBMessages.SystemSoundList),
            cbSound.SelectedItem.ToString());
      Temp[Temp.Length - 1].UseDates = chkDateSpecific.Checked;
 
      // Add dates only when required.
      if (chkDateSpecific.Checked)
      {
         Temp[Temp.Length - 1].StartDate = dtStartDate.Value;
         Temp[Temp.Length - 1].EndDate = dtEndDate.Value;
      }
 
      // Update the permanent message list.
      MessageList = Temp;
   }
}

As a minimum, a message must include a title and some message text. If the user doesn’t include any other information, the application will either rely on defaults or not provide that information as part of the output. Consequently, txtTitle.Text and txtMessageText.Text are the only two required entries. If the user doesn’t provide these two entries, the application displays an error message and exits.

When the required input is correct, the code moves on to creating a temporary array to hold the updated data. This is a safety feature to ensure that the entire transaction takes place before being made permanent. If the application should suddenly fail between steps, the initial array will still hold valid information.

In editing mode, the btnAdd_Click() event handler begins by copying all of the records from MessageList (the permanent array) to Temp (the temporary array). It then changes each of the required values in Temp. Notice how the code handles dates. Again, it only saves dates when the user wants to use dates as a filtering mechanism. The final step is to make the change permanent by copying Temp to MessageList.

In adding mode, the application can’t assume there are any existing records. As a result, the first check is to determine whether this is a new list of messages or an addition to an existing list. When it’s an addition, the code simply adds a new entry to Temp and copies the required records from MessageList to Temp. However, when this is a new list, the code simply creates a single entry array in Temp. At this point, the process is much like editing an entry. The new values are added to the empty array element and then Temp is copied to MessageList.

Next week we’ll move one level up in the form hierarchy and examine frmMessages. In the meantime, let me know if you have any questions about this part of the example at John@JohnMuellerBooks.com. You can see the next part of this series at Exploring the TypingBuddy Application (Part 7).

 

Exploring the TypingBuddy Application (Part 5)

At this point, you have classes you can use to create and store messages in the user’s folder on the current system as described in Exploring the TypingBuddy Application (Part 4). These messages appear whenever the typing time is over in TypingBuddy. Of course, you need some way to display the messages. That’s the responsibility of frmMessage described in Exploring the TypingBuddy Application (Part 2).

One of the requirements for frmMessage is flexibility. The developer has a number of requirements for formatting the message box to convey information in the way the user wants to see it. In order to meet this requirement, frmMessage includes a number of constructors as shown here.

// Contains the current resting time.
private Int32 RemainingTime;
 
public frmMessage(Int32 RestTime)
{
   // Perform the default task.
   InitializeComponent();
 
   // Configure the resting timer.
   RemainingTime = RestTime;
 
   // Configure the Continue button.
   btnContinue.Enabled = false;
   btnContinue.Text = RemainingTime.ToString() + " Secs";
 
   // Start the timer.
   RestTimer.Start();
}
 
public frmMessage(Int32 RestTime, Boolean ShowIcon)
{
   // Perform the default task.
   InitializeComponent();
 
   // Configure the resting timer.
   RemainingTime = RestTime;
 
   // Configure the Continue button.
   btnContinue.Enabled = false;
   btnContinue.Text = RemainingTime.ToString() + " Secs";
 
   // Configure the window.
   this.ControlBox = ShowIcon;
 
   // Start the timer.
   RestTimer.Start();
}
 
public frmMessage(Int32 RestTime, String Message)
{
   // Perform the default task.
   InitializeComponent();
 
   // Configure the resting timer.
   RemainingTime = RestTime;
 
   // Configure the Continue button.
   btnContinue.Enabled = false;
   btnContinue.Text = RemainingTime.ToString() + " Secs";
 
   // Add a message.
   txtMessage.Text = Message;
 
   // Start the timer.
   RestTimer.Start();
}
 
public frmMessage(Int32 RestTime, String Title, String Message)
{
   // Perform the default task.
   InitializeComponent();
 
   // Configure the resting timer.
   RemainingTime = RestTime;
 
   // Configure the Continue button.
   btnContinue.Enabled = false;
   btnContinue.Text = RemainingTime.ToString() + " Secs";
 
   // Set the window titlebar.
   this.Text = Title;
 
   // Add a message.
   txtMessage.Text = Message;
 
   // Start the timer.
   RestTimer.Start();
}
 
public frmMessage(Int32 RestTime, String Message, Boolean ShowIcon)
{
   // Perform the default task.
   InitializeComponent();
 
   // Configure the resting timer.
   RemainingTime = RestTime;
 
   // Configure the Continue button.
   btnContinue.Enabled = false;
   btnContinue.Text = RemainingTime.ToString() + " Secs";
 
   // Configure the window.
   this.ControlBox = ShowIcon;
 
   // Add a message.
   txtMessage.Text = Message;
 
   // Start the timer.
   RestTimer.Start();
}
 
public frmMessage(Int32 RestTime, String Title,
   String Message, Boolean ShowIcon)
{
   // Perform the default task.
   InitializeComponent();
 
   // Configure the resting timer.
   RemainingTime = RestTime;
 
   // Configure the Continue button.
   btnContinue.Enabled = false;
   btnContinue.Text = RemainingTime.ToString() + " Secs";
 
   // Configure the window.
   this.Text = Title;
   this.ControlBox = ShowIcon;
 
   // Add a message.
   txtMessage.Text = Message;
 
   // Start the timer.
   RestTimer.Start();
}

All of the constructors require RestTime as input. This is the amount of time that the user must do something other than working at the computer (based on whatever messages the user has configured or using the default message). The code uses this input to initialize RemainingTime, which contains the amount of time remaining to rest. When working with the simplest constructor, the code sets btnContinue.Enabled to false so that the user must wait to clear the message box. It also sets btnContinue.Text to the initial resting time value. The code then starts the timer by calling RestTimer.Start().

Beyond this simple setup, the developer can also choose to provide additional information. For example, supplying the Boolean value, ShowIcon, determines whether frmMessage displays an icon when displayed. The String value, Message, contains a custom message that the user has defined. Otherwise, frmMessage displays a default message to the user. The developer can also choose to provide a custom title for the message box by supplying the String value, Title. The constructors can accept various combinations of these three inputs, or provide all of them.

Once the timer is started, it counts down the time for the rest period. The Timer control, RestTime performs this task for you. The Tick() event handler code is as shown here.

private void RestTimer_Tick(object sender, EventArgs e)
{
   // Check the remaining time.
   if (RemainingTime == 0)
   {
      // Stop the timer.
      RestTimer.Stop();
 
      // Configure the Continue button.
      btnContinue.Text = "&Continue";
      btnContinue.Enabled = true;
   }
   else
   {
      // Count down another second.
      RemainingTime--;
 
      // Display the update.
      btnContinue.Text = RemainingTime.ToString() + " Secs";
   }
}

During the countdown phase, the value of RemainingTime is decremented. The code then displays the new number of seconds in btnContinue.Text so the user can see it on screen. When RemainingTime reaches 0, the code calls RestTimer.Stop() to stop the timer. It then changes btnContinue.Text to “&Continue” (with the C underlined) and btnContinue.Enable to true so that the user can click the Continue button.

The code for the Continue and Quit Click() event handlers simply closes the form. The reaction differs based on the button the user clicks, but this is handled by frmMain.

Next we’ll you’ll see the code used to add and edit messages. This information is handled by frmAddEdit. In the meantime, let me know if you have questions about the TypingBuddy application code you’ve seen today at John@JohnMuellerBooks.com. You can see the next post in this series at Exploring the TypingBuddy Application (Part 6).

 

Exploring the TypingBuddy Application (Part 4)

The previous post in this series, Exploring the TypingBuddy Application (Part 3), completed the design elements of TypingBuddy. It’s time to begin looking at the application code. Of course, one of the more important issues is storing the user’s configuration information. The first version of TypingBuddy relied on the registry to store this information. However, as TypingBuddy evolved and using the registry has become more difficult due to Windows security restrictions, it’s a lot more practical to store the settings in a separate file. Using a separate file has these advantages:

 

  • No need to request a privilege elevation to write the data.
  • The user can easily create a backup of the application settings.
  • Moving the settings from one system to another is easy.
  • Errors in the file are easy to fix using any XML editor (or even Notepad for that matter).
  • Roaming users can have their settings moved automatically to whatever machine they’re using.


Because of the environment that both Vista and Windows 7 provide, using a configuration file is the only solution I use for applications today. Unless you have a significant reason to use the registry, it’s always better to use a configuration file as described in this post.

Before you do anything else, you need to add two classes to the application. The following procedure will get you started.

 

  1. Right click the TypingBuddy project entry in Solution Explorer and choose Add | Class from the context menu. You’ll see the Add New Item – TypingBuddy dialog box shown here.
    TypingBuddy0401
  2. Type TBMessage in the Name field and click Add. Visual Studio adds the TBMessage class to your application.
  3. Perform steps 1 and 2, replacing TBMessage with TBMessages. You’ll end up with a new TBMessage and TBMessages class in your project.


One of the first features you need to consider is that the user is able to assign a sound to the activities that TypingBuddy displays when it’s time to rest. In order to make working with the sounds easier, this version of the application relies on the following enumeration found in the TBMessages class.

// Defines the set of system sounds available for a message.
public enum SystemSoundList
{
   None,
   Asterisk,
   Beep,
   Exclamation,
   Hand,
   Question
}

All that this enumeration does is make it easy to provide a list of common sounds for your application. The first place you see these sounds is in the TBMessage class code shown here.

[Serializable()]
public class TBMessage
{
   // Create automatically implemented properties
   // for each of the message arguments.
   public String Title { get; set; }
   public Boolean ShowTitle { get; set; }
   public String MessageText { get; set; }
   public TBMessages.SystemSoundList Sound { get; set; }
   public Boolean UseDates { get; set; }
   public DateTime StartDate { get; set; }
   public DateTime EndDate { get; set; }
}

One of the more important features to notice is that the class uses the [Serializable()] attribute, which makes it possible to store the data found in this class in an XML file with relative ease. No matter how complex the data becomes, you can use the [Serializable()] attribute to make it easy to turn it into an XML data file. The rest of the class is a series of strongly typed properties. Normally, I’d recommend adding error trapping to the get() method for each property, but in this case, there really isn’t much a caller could do to create data that has easily detected errors. About the only thing you could do is to monitor the lengths of the two String properties, Title and MessageText. However, given the way these properties are used, it would be hard to create an injection script scenario that would present some sort of risk to the application or its data.

The TBMessage class defines the content of a single message.  However, a user is likely to create a wealth of messages. The purpose of the TBMessages class is to manage a group of TBMessage class entries. The naming of the class implies as much. A plural form of a singular class usually denotes some sort of collection or array. Here’s the code for the TBMessages class.

// Define the configuration properties.
public Int32 TypingInterval { get; set; }
public Int32 RestInterval { get; set; }
public Boolean DisplayMessageIcon { get; set; }
public Boolean PlaySound { get; set; }
 
// Define an array of messages.
public TBMessage[] MessageList { get; set; }
 
// Create a method for saving the settings.
public static void SaveSettings(TBMessages Settings)
{
   // Define a path to the user settings.
   String UserPath =
      Environment.GetFolderPath(
         Environment.SpecialFolder.ApplicationData) + @"\TypingBuddy";
 
   // Create the path if necessary.
   if (!Directory.Exists(UserPath))
      Directory.CreateDirectory(UserPath);
 
   // Create an XML serializer.
   XmlSerializer DataWriter = new XmlSerializer(typeof(TBMessages));
 
   // Define a data stream.
   StreamWriter Output = new StreamWriter(UserPath + @"\AppData.Config");
 
   // Perform the data write.
   DataWriter.Serialize(Output, Settings);
 
   // Close the stream.
   Output.Close();
}
 
// Create a method for loading the settings.
public static TBMessages LoadSettings()
{
   // Define a path to the user settings.
   String UserPath =
      Environment.GetFolderPath(
         Environment.SpecialFolder.ApplicationData) + @"\TypingBuddy";
 
   // Check for the file.
   if (!File.Exists(UserPath + @"\AppData.Config"))
      return null;
 
   // Create an XML serializer.
   XmlSerializer DataReader = new XmlSerializer(typeof(TBMessages));
 
   // Define a data stream.
   StreamReader Input = new StreamReader(UserPath + @"\AppData.Config");
 
   // Load the settings.
   TBMessages Settings = new TBMessages();
   Settings = (TBMessages)DataReader.Deserialize(Input);
   Input.Close();
 
   // Return the settings to the caller.
   return Settings;
}

The code begins with a list of properties. These properties differ from those found in the TBMessage class in that they affect the application as a whole. In other words, you only need one set of these values in the XML file, not a set for every message that the user wants to see. The properties include the typing time, resting time, whether the messages should include an icon, and whether the user wants to hear a sound play when the message is displayed.

Both of the methods in this class, SaveSettings() and LoadSettings() are static, which means that the developer can call them without creating an instance of the class. This feature is important because the interface code will use these methods to save and load the XML file to and from disk.

The SaveSettings() code requires that that caller supply an object of type TBMessages (which includes both the application level settings and all of the message settings) containing the settings to save to disk. The code begins by obtaining the location of the user’s roaming application data folder. On my machine, that’s the C:\Users\John\AppData\Roaming folder. It then adds the \TypingBuddy subfolder, which contains the settings for the application. If this folder doesn’t exist, the application creates it. The application then creates an XmlSerializer of type TBMessages named DataReader to handle the data file content. Writing is performed using a StreamWriter object, Output, that points to the AppData.Config file in the TypingBuddy folder. The application then serializes the object data in Settings to the AppData.Config file and closes the file. It’s absolutely essential that you always call Close() to close the data file or the file can become corrupted. Here’s some typical output information.

TypingBuddy0402

As you can see, this XML file reflects the sort of data you’d expect. It begins with the application level settings and then contains a list of <TBMessage> entries containing the individual messages.

The LoadSettings() method works similar to the SaveSettings() method, except in reverse. In this case, you’re loading settings from disk into a TBMessages object and passing that object back to the caller. The code begins in the same way, by defining the location of the data file on disk. Notice that that code contains error trapping that returns null when the file doesn’t exist. In place of a StreamWriter, the application uses a StreamReader, Input, to read the data from disk. The code relies on the XmlSerializer, DataReader, to deserialize the data and place it into a TBMessages object, Settings. As before, make absolutely certain to call Close() to close the file when you’re finished using it.

That’s it for the data file-specific code. Next week we’ll begin using the data to configure the application. In the meantime, please let me know if you have any questions about this example at John@JohnMuellerBooks.com. You can see the next post in this series at Exploring the TypingBuddy Application (Part 5).