Windows Development Using Visual Studio 2008

Visual Studio

Visual Studio is an environment for developing applications in Windows. It has a number of tools, such as an editor, compilers, linkers, a debugger, and a project manager. It also has several Wizards—tools designed for rapid development. The Wizard you will first encounter is the Application Wizard. It generates code for an Application Framework. The idea is that we use the Application Wizard to design a skeleton application that is later completed with more application-specific code. There is no real magic about wizards, all they do is generate the skeleton code. We could write the code ourselves, but it is a rather tedious job. Moreover, an application can be run in either debug or release mode. In debug mode, additional information is added in order to allow debugging; in release mode, all such information is omitted in order to make the execution as fast as possible. The code of this article is developed with Visual Studio 2008.

The Windows 32 bits Application Programming Interface (Win32 API) is a huge C function library. It contains a couple of thousand functions for managing the Windows system. With the help of Win32 API it is possible to totally control the Windows operating system. However, as the library is written in C, it could be a rather tedious job to develop a large application, even though it is quite possible. That is the main reason for the existence of the Microsoft Foundation Classes (MFC). It is a large C++ class library containing many classes encapsulating the functionality of Win32 API. It does also hold some generic classes to handle lists, maps, and arrays. MFC combines the power of Win32 API with the advantages of C++. However, on some occasions MFC is not enough. When that happens, we can simply call an appropriable Win32 API function, even though the application is written in C++ and uses MFC.

Most of the classes of MFC belong to a class hierarchy with CObject at the top. On some occasions, we have to let our classes inherit CObject in order to achieve some special functionality. The baseclass Figure in the Draw and Tetris applications inherits CObject in order to read or write objects of unknown classes. The methods UpdateAllViews and OnUpdate communicate by sending pointers to CObject objects. The Windows main class is CWnd.

In this environment, there is no function main. Actually, there is a main, but it is embedded in the framework. We do not write our own main function, and there is not one generated by the Application Wizard. Instead, there is the object theApp, which is an instance of the application class. The application is launched by its constructor.

When the first version of MFC was released, there was no standard logical type in C++. Therefore, the type BOOL with the values TRUE and FALSE was introduced. After that, the type bool was introduced to C++. We must use BOOL when dealing with MFC method calls, and we could use bool otherwise. However, in order to keep things simple, let us use BOOL everywhere.

In the same way, there is a MFC class CString that we must use when calling MFC methods. We could use the C++ built-in class string otherwise. However, let us use CString everywhere. The two classes are more or less equivalent.

There are two types for storing a character, char and wchar_t. In earlier version of Windows, you were supposed to use char for handling text, and in more modern versions you use wchar_t. In order to make our application independent of which version it is run on, there are two macros TCHAR and TEXT. TCHAR is the character type that replaces char and wchar_t. TEXT is intended to encapsulate character and string constants.

TCHAR *pBuffer;
stScore.Format(TEXT("Score: %d."), iScore);

There is also the MFC type BYTE which holds a value of the size of one byte, and UINT which is shorthand for unsigned integer. Finally, all generated framework classes have a capital C at the beginning of the name. The classes we write ourselves do not.

The Document/View model

The applications in this article are based on the Document/View model. Its main idea is to have two classes with different responsibilities. Let us say we name the application Demo, the Application Wizard will name the document class CDemoDoc and the view class will be named CDemoView. The view class has two responsibilities: to accept input from the user by the keyboard or the mouse, and to repaint the client area (partly or completely) at the request of the document class or the system. The document's responsibility is mainly to manage and modify the application data.

The model comes in two forms: Single Document Interface (SDI) and Multiple Document Interface (MDI). When the application starts, a document object and a view object are created, and connected to each other. In the SDI, it will continue that way. In the MDI form, the users can then add or remove as many views they want to. There is always exactly one document object, but there may be one or more view objects, or no one at all.

The objects are connected to each other by pointers. The document object has a list of pointers to the associated view objects. Each view object has a fieldm_pDocument that points at the document object. When a change in the document's data has occurred, the document instructs all of its views to repaint their client area by calling the method UpdateAllViews in order to reflect the change.

Windows Development Using Visual Studio 2008

The message system

Windows is built on messages. When the users press one of the mouse buttons or a key, when they resize a window, or when they select a menu item, a message is generated and sent to the current appropriate class.

The messages are routed by a message map. The map is generated by the Application Wizard. It can be modified manually or with the Properties Window View (the Messages or Events button).

The message map is declared in the file class' header file as follows:


The message map is implemented in the class' implementation file as follows:

BEGIN_MESSAGE_MAP(this_class, base_class)
// Message handlers.

Each message has it own handle, and is connected to a method of a specific form that catches the message. There are different handlers for different types of messages. There are around 200 messages in Windows. Here follows a table with the most common ones. Note that we do not have to catch every message. We just catch those we are interested in, the rest will be handled by the framework.






When the window is created, but not yet showed.



When the window has been resized.



When the window has been moved.




When the window receives input focus.




When the window loses input focus.




When the user scrolls the vertical bar.




When the user scrolls the horizontal bar.











When the user presses the left, middle, or right mouse button.




When the user moves the mouse, there are flags available to decide whether the buttons are pressed.












When the user releases the left, middle, or right button.



When the user inputs a writable character of the keyboard.




When the user presses a key of the keyboard.




When the user releases a key of the keyboard.



When the client area of the window needs to be repainted, partly or completely.



When the user clicks at the close button in the upper right corner of the window.




When the window is to be closed.





When the user selects a menu item, a toolbar button, or a accelerator key connected to the identifier.






On idle time, when the system is not busy with any other task, this message is sent in order to enable/disable or to check menu items and toolbar buttons.

When a user selects a menu item, a command message is sent to the application. Thanks to MFC, the message can be routed to virtually any class in the application. However, in the applications of this article, all menu messages are routed to the document class. It is possible to connect an accelerator key or a toolbar button to the same message, simply by giving it the same identity number.

Moreover, when the system is in idle mode (not busy with any other task) thecommand update message is sent to the application. This gives us an opportunity to check or disable some of the menu items. For instance, the Save item in the File menu should be grayed (disabled) when the document has not been modified and does not have to be saved. Say that we have a program where the users can paint in one of three colors. The current color should be marked by a radio box.

The message map and its methods can be written manually or be generated with the Resource View (the View menu in Visual Studio) which can help us generate the method prototype, its skeleton definition, and its entry in the message map.

The Resource is a system of graphical objects that are linked to the application. When the framework is created by the Application Wizard, the standard menu bar and toolbar are included. We can add our own menus and buttons in Resource Editor, a graphical tool of Visual Studio.

The coordinate system

In Windows, there are device (physical) and logical coordinates. There are several logical coordinate mapping systems in Windows. The simplest one is the text system; it simply maps one physical unit to the size of a pixel, which means that graphical figures will have different size monitors with different sizes or resolutions. This system is used in the Ring and Tetris applications.

The metric system maps one physical unit to a tenth of a millimeter (low metric) or a hundredth of a millimeter (high metric). There is also the British system that maps one physical unit to a hundredth of an inch (low English) or a thousandth of an inch (high English). The British system is not used in this article.

The position of a mouse click is always given in device units. When a part of the client area is invalidated (marked for repainting), the coordinates are also given in device units, and when we create or locate the caret, we use device coordinates. Except for these events, we translate the positions into logical units of our choice. We do not have to write translation routines ourselves, there are device context methods LPtoDP (Logical Point to Device Point) and DPtoLP (Device Point to Logical Point) in the next section that do the job for us. The setting of the logical unit system is done in OnInitialUpdate and OnPrepareDC in the view classes.

In the Ring and Tetris Applications, we just ignore the coordinates system and use pixels. In the Draw application, the view class is a subclass of the MFC class CScrollView. It has a method SetScrollSizes that takes the logical coordinate system and the total size of the client area (in logical units). Then the mapping between the device and logical system is done automatically and the scroll bars are set to appropriate values when the view is created and each time its size is changed.

void SetScrollSizes(int nMapMode, CSize sizeTotal, 
const CSize& sizePage = sizeDefault,
const CSize& sizeLine = sizeDefault);

In the Calc and Word Applications, however, we set the mapping between the device and logical system manually by overriding the OnPrepareDC method. It calls the method SetMapMode which sets the logical horizontal and vertical units to be equal. This ensures that circles will be kept round. The MFC device context method GetDeviceCaps returns the size of the screen in pixels and millimeters. Those values are used in the call to SetWindowExt and SetViewportExt, so that the logical unit is one hundredth of a millimeter also in those applications. The SetWindowOrg method sets the origin of the view's client area in relation to the current positions of the scroll bars, which implies that we can draw figures and text without regarding the current positions of the scroll bars.

int SetMapMode(int iMapMode);
int GetDeviceCaps(int iIndex) const;
CSize SetWindowExt(CSize szScreen);
CSize SetViewportExt(CSize szScreen);
CPoint SetWindowOrg(CPoint ptorigin);

The device context

The device context can be thought of as a toolbox, equipped with pens and brushes, as well as a canvas on which we can draw lines, paint figures, and write text. It also contains methods for converting between device and logical units. Finally, it can be regarded as a connection between our program and the screen or printer.

In Windows, a window usually has a frame with an icon at the top left corner, buttons to resize the window at the top right corner and, possibly a menu bar, a toolbar, and a status bar. the white area inside the frame is called the client area. With the help of a device context, we can paint the client area.

When the view class is created with the Application Wizard, the method OnDraw is included. It takes a parameter pCD that is a pointer to a device context. The device context class CDC is a very central part of a Windows application. However, CDC is an abstract class, a device context object is instantiated from the subclass ClientDC. In order to draw lines or paint areas we need a pen and a brush.

CPen(int iPenStyle, int iWidth, COLORREF crColor);
CBrush(COLORREF crColor);

The pen style can be solid, dashed, or dotted. If the width of the line is set to zero, the line will be drawn as thin as possible (one pixel) on the output device (screen or printer). We also need to select the pen and brush for the device context, and when we have used them, we reset the drawing system by returning the previous ones.

void GraphicalClass::Draw(CDC* pDC) const
CPen pen(PS_SOLID, 0, BLACK);
CBrush brush(WHITE);
CPen* pOldPen = pDC->SelectObject(&pen);
CBrush* pOldBrush = pDC->SelectObject(&brush);
// Painting the client area.

For drawing and painting, there are a number of methods to call.

BOOL MoveTo(int x, int y);
BOOL LineTo(int x, int y);
BOOL Rectangle(int x1, int y1, int x2, int y2);
BOOL Ellipse(int x1, int y1, int x2, int y2);

When we write text, we do not have to select a pen or a brush. Instead, we set the text and background colors directly by calling the methods below. They return the previous set text and background color, respectively. We do not have to put back the previous colors.

COLORREF SetTextColor(COLORREF crColor);

DrawText does the actual writing of the text. Besides the text, it takes the rectangle where the text will be written inside. It also takes a number of flags to align the text in the rectangle. Possible flags for horizontal alignment are DT_LEFT, DT_CENTER, and DT_RIGHT, possible flags for vertical alignment are DT_TOP, DT_VCENTER, and DT_BOTTOM. If there is not enough room for the text in the given rectangle, the text is wrapped. That can be avoided with the DT_SINGLE_LINE flag. TextOut is a simpler version of DrawText. It takes a position and text that are by default written with the position at its top left corner. It can be combined with a preceding call to SetTextAlign that sets the horizontal and vertical alignment of the written text.

int DrawText(const CString& stText, LPRECT pRect, UINT uFormat);
BOOL TextOut(int xPos, int yPos, const CString& stText);
UINT SetTextAlign(UINT uFlags);

SetTextJustification is a rather special method that is used in the Calc application to write the values of cells. It ensures that the following calls to DrawText insert extra long spaces between the words in order to display the text in justified horizontal alignment. It takes the total width of the spaces and the number of spaces as parameters. After the writing of the text, we should reset the alignment with another call to SetTextJustification to prevent succeeding calls to DrawText to write with justified alignment. The horizontal alignment is irrelevant when the call to DrawText has been preceded by a call to SetTextJustification.

int SetTextJustification(int iTotalSpaceWidth, int iNumberOfSpaces);

However, before we write text, we have to select a font. In the applications of this article, the font is represented by the Font class. Note that the font size is stored in typographical points (1/72 inch) and needs to be translated into logical units.

void GraphicalClass::Draw(CDC* pDC) const
Font font("Times New Roman", 12);
CFont cFont;
CFont* pPrevFont = pDC->SelectObject(&cFont);
pDC->DrawText("Hello, World!", CRect(0, 0, 1000, 1000),

GetTextMetrics fills a structure with information about the average dimension of a text written in a specific font. The measured values are given in logical units.

typedef struct tagTEXTMETRIC
LONG tmHeight; // Height of the text.
LONG tmAscent; // Ascent line of the text.
LONG tmAveCharWidth; // Average width of the text, roughly
// equal to the width of the character
// 'z'.
LONG tmDescent;
LONG tmInternalLeading;
LONG tmExternalLeading;
LONG tmMaxCharWidth;
LONG tmWeight;
LONG tmOverhang;
LONG tmDigitizedAspectX;
LONG tmDigitizedAspectY;
TCHAR tmFirstChar;
TCHAR tmLastChar;
TCHAR tmDefaultChar;
TCHAR tmBreakChar;
BYTE tmItalic;
BYTE tmUnderlined;
BYTE tmStruckOut;
BYTE tmPitchAndFamily;
BYTE tmCharSet;
BOOL GetTextMetrics(TEXTMETRIC* pTextMetrics) const;

Of the fields above, we will only use the first three ones in the applications of this article. This method will be called when we actually do not have any text to write (an empty cell or an empty paragraph), but when we, nevertheless, need information about the width, height, and ascent line of text in a particular font.

When we have a text to write, we can use GetTextExtent instead. It takes a text and returns the size of it in the selected font in logical units.

CSize GetTextExtent(const CString& stText) const;

When the user clicks the mouse on a certain position, the position is given in device coordinates that need to be translated into logical coordinates. When a part of the client area is to be marked for repainting (invalidated), the area should be given in device coordinates. DPtoLP (Device Coordinates to Logical Coordinates) and LPtoDP (Logical Coordinates to Device Coordinates) translates MFC class objects CSize, and CRect, and (one or more) CPoint objects between logical and device coordinates.

void DPtoLP(CSize* pSize) const;
void DPtoLP(CRect* pRect) const;
void DPtoLP(CPoint* pPoints, int iNumberofPoints = 1) const;
void LPtoDP(CSize* pSize) const;
void LPtoDP(CRect* pRect) const;
void LPtoDP(CPoint* pPoints, int iNumberOfPoints = 1) const;

IntersectClipRect limits the invalidated area (the part of the client area that is to be repainted).

int IntersectClipRect(CRect* pRect);

All of the methods above return true if the operation was successful.

The registry

The registry is a series of files, stored on the local hard drive, that stores application specific information. The MFC application class CWinApp has a number of methods to communicate with the registry. It is possible to read or write an integer, a block of memory, or a string. The global MFC function AfxGetApp returns a pointer to the application class object. There can only be one such object. The stSection in the next methods is usually the name of the application and the stEntry is the name of the value.

GetProfileBinary returns true if the reading was successful. GetProfileInt and GetProfileString take a default value that is returned if the entry was not found. WriteProfileBinary, WriteProfileInt, and WriteProfileString all return true on successful writing.

UINT GetProfileInt(CString stSection, CString stEntry, int iDefault);
CString GetProfileString(CString stSection, CString stEntry, CString stDefault);
BOOL GetProfileBinary(CString stSection, CString stEntry, LPBYTE* ppData, UINT* pBytes);
BOOL WriteProfileInt(CString stSection, CString stEntry, int nValue);
BOOL WriteProfileString(CString stSection, CString stEntry, CString stValue);
BOOL WriteProfileBinary(CString stSection, CString stEntry, LPBYTE pData, UINT nBytes);

The cursor

The message WM_CURSOR is sent to the view class when the application is in idle mode (not busy with anything else). The answer to the message is handled to the preferred cursor. There are a number of standard cursors that are returned by the CWinApp method LoadStandardCursor. The global function AfxGetApp returns a pointer to the application object (there is only one). The cursor is set in the view class by the SetCursor Win32 API function.

CWinApp* AfxGetApp();
HCURSOR LoadStandardCursor(CString stCursorName) const;
HCURSOR SetCursor(HCURSOR hCursor);

The parameter stCursorName could be any of the following predefined constants.



Standard arrow cursor.


Standard text-insertion cursor.


Cross-hair cursor for selection.


A four-pointed arrow, the cursor used to resize a window.


Horizontal two-headed arrow.


Vertical two-headed arrow.


Two-headed arrow aimed at the upper left and lower right corner.


Two-headed arrow aimed at the upper right and lower left corner.



Serialization is the process of writing to and reading from a file. When the user chooses to open or save a file, the framework calls the method Serialize of the document class. Every serialized class must have a default constructor and implement the serial macro. If we read an object of an unknown class, which we do in the Tetris and Draw applications, the class must be a subclass of the MFC root class CObject.

We can store or load values in three ways. If we want to read or write a block of memory (such as an array), we can use the CArchive methods Write and Read to transfer a block between the file and the memory. Read takes the maximum number of bytes (the buffer size) to be read to the buffer and returns the number of bytes actually read.

void Write(const void* pBuffer, UINT uSize);
UINT Read(void* pBuffer, UINT uMaxSize);

For the basic types of C++, the stream operators are overloaded. Many classes define their own Serialize; in that case, we just have to call it with the archive object as parameter. The following code comes from the Tetris application.

void CRingDoc::Serialize(CArchive& archive)
if (archive.IsStoring())
archive << m_iRow << m_iCol;
archive.Write(&m_northArray, sizeof m_northArray);
if (archive.IsLoading())
archive >> m_iRow >> m_iCol;
archive.Read(&m_northArray, sizeof m_northArray);

On some occasions, we do not know which class the object to be read or written is an instance of. In those cases, we can use the methods WriteClass, ReadClass, WriteObject, and ReadObject. When writing an object, we first write information about its class. We can extract that information with the CObject method GetRuntimeClass that returns a pointer to an object of CRuntimeClass. As GetRuntimeClass is a method of CObject, the class of the object to be read or written must be a (direct or indirect) subclass of CObject.

CRuntimeClass* GetRuntimeClass() const;
void WriteClass(const CRuntimeClass* pClass);
void WriteObject(const CObject* pObject);
CRuntimeClass* ReadClass(const CRuntimeClass* pClass);
CObject* ReadObject(const CRuntimeClass* pClass);

When writing an object, we first call WriteClass to store information about the object, and then we call WriteObject to store the object itself. Likewise, when reading an object, we first call ReadClass to read information about the next object in the file, and then we call ReadObject to read the actual object. This technique is used in the Tetris and Draw applications, the following code is from the Draw application. Figure is an abstract baseclass and we store and load objects of subclasses to Figure. In each case, we do not know which subclass, we only know that it is a subclass of Figure.

Figure* pFigure = m_figurePtrList.GetAt(position);

CRuntimeClass* pClass = archive.ReadClass();
Figure* pFigure = (Figure*) archive.ReadObject(pClass);


  • The Application Wizard creates an application framework that we extend with code specific for the application. The Class Wizard helps us catch and handle messages.
  • The document/view model consists of one document object, handling the application logic, and one or several view objects, handling user input and data presentation.
  • Every time an event occurs, a message is sent to the application in focus. We can generate handling code with Class Wizard.
  • The device context can be viewed both as a canvas to paint on, a toolbox holding pens and brushes, and a connection to the screen and printer.
  • Between executions, we can store the state of the application in the registry.
  • The cursor can be set to standard representations.
  • We can save and load document data by the means of Serialization.

You've been reading an excerpt of:

Microsoft Visual C++ Windows Applications by Example

Explore Title