Chapter 7
Putting something under the hood
This is where code monkeys (such as myself) get to shine! Now that we’ve got a window, and controls for that window, we must begin to add code into the window procedure to do what the user would expect. Clicking on the “Exit” button of JotStuffDownPad probably means the user wants to, well, exit. So, let’s give the user what they want!
Processing messages from controls
What happens when you press a button? A control is a window unto itself, but where is its window procedure so we can intercept the “click” message? Well, as it turns out, it’s not really that complicated. Windows created with the style send their notification messages (such as a button saying “I’ve been clicked”) to their parent window! The message that holds notifications from controls is the message. So let’s add a new case into our window procedure:
switch(uMsg)
{
case WM_COMMAND:
// Control notifications are sent here
return 0;
case WM_CREATE:
if(!BuildControls(hwnd))
return -1;
return 0;
case WM_DESTROY:
// Exit the application when the window closes
PostQuitMessage(1);
return true;
}
We return zero to indicate that our window procedure, not , is processing the message.
Now that we know a control wants to notify us of an event, how can we find out what that event is? Well, looking in the MSDN entry for , it seems that the high-order word of the parameter to contains the specific notification message that was sent. Because the high-order word and the low-order word of both and are often used to pass in important information, Windows provides us with macros called and to access those values. Since we know where the notification message is, we can add a new statement to the case to collect the message that a button has been clicked (specifically, the message):
case WM_COMMAND:
// Control notifications are sent here
switch(HIWORD(wParam))
{
case BN_CLICKED:
break;
}
return 0;
We use a second statement here to make our lives easier in the future; we’re probably going to want to know about more than one event before we’re done!
Last point to make here, then we can start adding functionality until you’re blue in the face…you know an event fired via the message, you know that the event was a button click from the high word of . Well, unless you have only one button in your window, you have to know where it came from! Fortunatly, a handle to the control (the control’s HWND) is passed into the window procedure in the argument. Now, since we know everything about everything as it relates to button-clicking, let’s start making our four buttons do their jobs.
The “Exit” button
According to our design specs from chapter five (and common sense), the program should exit when the “Exit” button is clicked. How can we accomplish this? One way is to duplicate the call to that we already have set for the message. This is certainly a valid way of doing it, but not a very forward-thinking one. JotStuffDownPad is a text editor, and eventually you may want to ask if the user wants to save the text (a la the example laid out in chapter three). Should we then duplicate the question-asking code, as we’re thinking of duplicating the calls? No, that’s just silly; needless code repetition is bad. The code is already in the window procedure, ready to execute when a message is sent…so let’s send a message! We do this with the handy function , which performs the same action as the user clicking on the “X” button. Knowing what we know, and with that function in hand, let’s give life to the “Exit” button:
switch(HIWORD(wParam))
{
case BN_CLICKED:
if((HWND)lParam == exitButton)
DestroyWindow(hwnd);
break;
}
That’s all there is to it! Press F5 to compile and run the program again, and click the “Exit” button to see what happens. If the program stops (as it should), you’ve done your job well
The “New” button
We implemented the easiest button first. The second easiest would be the “New” button, and the only reason it’s any harder is because it introduces a new API call, the widely-used and powerful . When the user clicks the “New” button, according to the design specs, three things will happen:
- The text field must clear
- A new note, initially blank, will be added into the list of notes
- An entry will be put into the combo box to allow the user to select the new note.
All of these things (save for the addition to the list of notes) are accomplished by sending messages to the respective controls. Let’s watch…
First, we’ll clear the text field. This is done by setting the control’s text to “”. To accomplish this, we use the message. As you might notice from the prefix, this is a message that can go to any window (the comments in the code presented in chapter six mentioned some of the different prefixes for window styles; the same idea holds true for messages). Consequently, although we use this message to set the text of an edit control, we could just as easily use it to change the name of a window or change the text on a button. Now, how do we send the message to the control? Well, we use ! Add the following code into the case of the statement:
if((HWND)lParam == newButton)
{
// Clear the text field
SendMessage(editField,WM_SETTEXT,NULL,(LPARAM)"");
}
So, if the button that is clicked corresponds to , we send the message. The arguments to are, in order:
- The handle of the window that we’re sending the message to
- The message that is being sent
- wParam information
- lParam information
For each message, the wParam and lParam information is different, so consult MSDN if you aren’t sure. In the case of , the wParam argument isn’t used (hence the ) and the lParam argument is a pointer to the new text. It’s okay to simply put the literal string here instead of a pointer to the string, as we did in the above code.
One out of three tasks done…the next one is simple. Adding a string to the list is done with the function, which takes the text of the note to be added as an argument:
if((HWND)lParam == newButton)
{
// Clear the text field
SendMessage(editField,WM_SETTEXT,NULL,(LPARAM)"");
// Add a new string
AddNoteToList("");
}
Two down, one to go! We want to add a new item into the combo box, which means that we’ll be using the message. Like , the wParam argument is and the lParam argument is a pointer to the string to be added (although again, it’s alright to use a string literal). Here, though, we’re going to have to use the return value of . The function will return different things for different messages, but according to MSDN when is used to add a string into a combo box, it returns the index of the newly added string. Once we’ve added the string, we want to display the new item in the combo box, and for this we use the (SETCURrentSELection) message. takes the index of the item to display in its wParam argument. Weaving it all together, we get this:
if((HWND)lParam == newButton)
{
// Clear the text field
SendMessage(editField,WM_SETTEXT,NULL,(LPARAM)"");
// Add a new string
AddNoteToList("");
// Add a new item into the combo box
int newResult = SendMessage(comboNotes,CB_ADDSTRING,NULL,(LPARAM)"[new note]");
SendMessage(comboNotes,CB_SETCURSEL,(WPARAM)newResult,NULL);
}
Whew, you’re done implementing the “New” button! Press F5 to compile and run the program again, and click the “New” button to see what happens. If you end up with a few “[new note]” entries in the combo box, all is well with the program.
The “Delete” button
After implementing the “New” button, the “Delete” button will be almost the same thing, naturally with a couple twists to make life interesting. It’s pretty much the “New” process in reverse: find out the current selection in the combo box, remove that selection, remove the appropriate string from the list, and then clear the edit field. We’re going to use the and messages to remove the current item from the combo box, the function to remove the string from the stored list, and the message to clear the text. It all goes together thusly:
if((HWND)lParam == deleteButton)
{
// Get the index of the currently selected item
int deleteResult = SendMessage(comboNotes,CB_GETCURSEL,NULL,NULL);
// Delete that item from the combo box
SendMessage(comboNotes,CB_DELETESTRING,(WPARAM)deleteResult,NULL);
// Remove that item from the list
RemoveNoteFromList(deleteResult);
// Clear the text field
SendMessage(editField,WM_SETTEXT,NULL,(LPARAM)"");
}
Pressing F5 will compile and run JotStuffDownPad with a shiny new functionality. Add a few notes and then delete them. Take note of what happens in the following cases:
- Add two notes, then delete the current one. What does the combo box do?
- Close JotStuffDownPad, then press F5 to restart it. Delete a note without adding any. What happens?
The answer to these and similar questions will be answered forthwith, when we add functionality to the combo box. But first…
The “Save” button
The “Save” button probably provides the most interesting functionality of all four buttons. It’s responsible for the following:
- Retrieving the contents of the text field and putting it into the list of notes
- Updating the combo box entry for that note with some unique entry.
The first task isn’t difficult: Using the function, we fill a string with the text from the edit control (which, if you’ll recall, is also a window). needs as parameters a handle to the window, a buffer to place the text in, and the maximum size of the buffer so it doesn’t overflow. We find out the amount of text that the user has put in, and hence how large to make the buffer, by calling the function.
The second task, adding some identifing text into the combo box, also isn’t difficult from a programming standpoint; the currently selected item is removed from the combo box using the same technique as the “Delete” button, and then immediately replaced with the new text at the same index using . This does, however, run us into our first real design decision: What text will we insert into the combo box to identify a note? It would have to be uniquely identifiable to the user, because otherwise the hapless user may have to select several different notes before getting to the one he or she wants. Flipping through an actual pad of Post-It notes works that way, yes, but technology is supposed to improve life! Some methods of doing this could include putting the time that the note was saved into the combo box, or the full text of the note itself (the combo box would just cut off what didn’t display within its width). When all is said and done, experiment a little to see what you prefer.
For this project, we’ll be inserting the first 15 characters of a note. If the note is more than fifteen characters long, we’ll put an ellipsis “…” on the end of the shortened note for readability, which yields a maximum of 18 characters, into the combo box. This is nicely done for you in utilities.cpp using the function, which expects the index of the note, a buffer to return the short note in, and the length of the truncation (in our case, we’re truncating at fifteen characters). Let’s code!
if((HWND)lParam == saveButton)
{
// First, we'll put the text from the control into an appropriately
// sized buffer
int textLength = GetWindowTextLength(editField) + 1;
char* text = new char[textLength];
memset(text,0,textLength);
GetWindowText(editField,text,textLength);
// Get the index of the current note
int saveIndex = SendMessage(comboNotes,CB_GETCURSEL,NULL,NULL);
// Then call ModifyNote to update the note in the list
ModifyNote(saveIndex,text);
// Now we'll change the combo box. First we delete the current entry...
SendMessage(comboNotes,CB_DELETESTRING,(WPARAM)saveIndex,NULL);
// ...and insert a new entry at the same position with the short (20 character)
// version of the note
// We size the buffer for fifteen characters, three more for a possible ellipsis,
// and one for the null terminator
char* shortText = new char[15 + 3 + 1];
GetShortNote(saveIndex,shortText,15);
SendMessage(comboNotes,CB_INSERTSTRING,(WPARAM)saveIndex,(LPARAM)shortText);
// Put the current selection back to where it was before we began
SendMessage(comboNotes,CB_SETCURSEL,(WPARAM)saveIndex,NULL);
}
Press F5 to compile and run JotStuffDownPad with all of its buttons firmly in place and working.
Processing the combo box
There’s only one control left to breathe life into: the combo box. There’s still no way for a user to “flip” between notes, and it falls to you, the programmer, to deliver this important ability. In our design specs, it was decided that JotStuffDownPad would display a saved note when the user selects that note from the combo box.
To accomplish this, we need to know when the combo box selection changes. Never fear! Combo box controls send out the (ComboBoxNotification_SELectionCHANGEd) message when the user selects a different item. This is sent to the window procedure via the message, with in the high word of :
case WM_COMMAND:
// Control notifications are sent here
switch(HIWORD(wParam))
{
case BN_CLICKED:
// The user clicked a button
// ....
break;
case CBN_SELCHANGE:
// The user changed the selection of the combo box
}
return 0;
Now that the code has a place to go, what are we going to do? Simple…all we need to do is load the note that is at the same index as the newly selected item from the combo box. As before, we’re using the message to retrieve the index of the new selection. To pull a note out of the list, use the predefined functions and . Their usage should be made clear in the code. Finally, the message updates the edit control, and it all comes together like so:
case CBN_SELCHANGE: // The user changed the selection of the combo box // Get the new index int newIndex = SendMessage(comboNotes,CB_GETCURSEL,NULL,NULL); // Create a nicely sized buffer char* note = new char[GetNoteLength(newIndex)+1]; // Fill the nicely sized buffer, nicely GetNote(newIndex,note); // Update the text field SendMessage(editField,WM_SETTEXT,NULL,(LPARAM)note);
Press F5, and there it is…one fully functional Win32 GUI application! Toy around with it; it’s not perfect yet, and in particular pay attention to what JotStuffDownPad does when…
- …JotStuffDownPad is started, some text is entered into the field, and “Save” is clicked without clicking “New” first
- …JotStuffDownPad is started, several notes are added and saved, and then one is deleted. What does the combo box do? What happens if you save a note without touching the combo box again?
Fixin’ some “features”
You’re probably a programmer if you’re reading this tutorial…and as a programmer, unless you’re some crazy kind of perfect coder that doesn’t exist, there have been some bugs in your programs over time. JotStuffDownPad is no exception to this rule…although it works perfectly according to the design specs, there are some unexpected flaws that must be dealt with. Here are the two major ones I found:
- If JSDP is started, and “Save” is clicked without first adding a new note, a garbled entry is added into the combo box. The program crashes from an access violation if this garbled entry is selected by the user.

- Cause: There is no entry in the notes list to accomdate the note because one hasn’t been created via the “New” button
- Solution 1: Require that the “New” button be clicked *before* the “Save” button
- Solution 2: Make sure that a note always exists in the list from the start of the program
- If several notes are added and saved, then one is deleted, the combo box displays no entry. Saving another note without first clicking “New” or selecting another note in the combo box causes the program to crash
- Cause: The combo box displays no entry because the entry it was displaying just prior to clicking “Delete” has been removed. The reason for the program crash is because , which is sent to the combo box when “Save” is clicked, returns -1 if no entry is selected. There is no check for this in our code, and note # -1 is accessed. Naturally, note # -1 doesn’t exist, and the program crashes.
- Solution: Moving the combo box selection to a valid entry after the user clicks “Delete” would solve both problems. If no other note exists, automatically create a new one to make sure a note always exists to save to (a la Solution 2 for the above bug).
Now, to squash them. Let’s examine the first bug. Solution 1, while valid and programmatically possible, is a bit restrictive on the user. Requiring a user to take two steps when a solution could be managed in code isn’t very nice. Thus, Solution 2 is the winner. We can effect this solution by sending JSDP a message to simulate the “New” button being clicked right after is called:
case WM_CREATE: if(!BuildControls(hwnd)) return -1; InitializeNotesList(); // Simulate a button click SendMessage(hwnd,WM_COMMAND,MAKEWPARAM(0,BN_CLICKED),(LPARAM)newButton);
Looking at the MSDN documentation for , we see that it is expected that the HWND of the control generating the message is in lParam, and that the high-order word of wParam should be the specific notification method. The macro helps us create wParam properly, and the message is simulated flawlessly. Run JotStuffDownPad again, and bask in the glory that is a squashed bug!
Now to go at the second bug. The solution is to switch the combo box to a valid entry after the current entry is deleted. This gives us several options: We can switch to the previous note, we can switch to the next note in line, we can switch to the first note in the index, etc. This is a design decision that you can work out for yourself, in time. The simplest way to do it would be to just go to the first note in the list, but the user probably wouldn’t expect that; for this example, we’re going to go to the next note in the list. So, when the “Delete” button is clicked and the note is successfully removed, we must do the following things:
- If the deleted note was the only note in the list, we need to add a new note to maintain a non-empty list
- If the deleted note was the last note in the list, we obviously can’t switch to the next note, so we switch to the previous one
- If neither of the above cases are true, switch to the next note.
To effect the solution, here’s the new code for the “Delete” button:
if((HWND)lParam == deleteButton)
{
// Get the index of the currently selected item
int deleteResult = SendMessage(comboNotes,CB_GETCURSEL,NULL,NULL);
// Delete that item from the combo box
SendMessage(comboNotes,CB_DELETESTRING,(WPARAM)deleteResult,NULL);
// Remove that item from the list
RemoveNoteFromList(deleteResult);
// Clear the text field
SendMessage(editField,WM_SETTEXT,NULL,(LPARAM)"");
// Now we switch to the next note
int numberOfNotes = SendMessage(comboNotes,CB_GETCOUNT,NULL,NULL);
// Was it the only note in the list?
if(numberOfNotes == 0)
{
// Add a new one with a simulated click on "New"
SendMessage(hwnd,WM_COMMAND,MAKEWPARAM(0,BN_CLICKED),(LPARAM)newButton);
} else
// Was it the last note in the list?
if(deleteResult == numberOfNotes)
{
// Set the selection to the previous note
SendMessage(comboNotes,CB_SETCURSEL,(WPARAM)numberOfNotes - 1,NULL);
// Poke the combo box to load that note
SendMessage(hwnd,WM_COMMAND,MAKEWPARAM(0,CBN_SELCHANGE),NULL);
} else // Just set the selction to the next note
{
SendMessage(comboNotes,CB_SETCURSEL,(WPARAM)deleteResult,NULL);
SendMessage(hwnd,WM_COMMAND,MAKEWPARAM(0,CBN_SELCHANGE),NULL);
}
}
It isn’t an enormous amount of code…more than if we just switched to the first note in the list, but this method is a bit nicer to the user. Start up JotStuffDownPad, and try it out. It works, and the bugs are gone!
Summary
Whooooooowhee! This has been a long long chapter, and we did a *lot*. All of the controls now work together seamlessly, and we have a working application on our hands.
I’m hoping that this chapter has left you with a good idea of how important is. According to MSDN, there are one hundred and eleven messages alone! Knowing how to intercept messages in the window procedure, and knowing how to use to send messages to windows and controls, is one of the most crucial parts to understanding Win32 GUI programming. Armed with this knowledge, and the practical knowledge gained from prior programming experience and this tutorial, you can go far with the API! Handling messages isn’t the only thing to it though; in the next chapter, we’ll further fine-tune JotStuffDownPad and learn about the wonder of resource files…
