Enumerating Network Resources

Microsoft Windows provides several functions (called WNet functions) that help us in implementing networking capabilities in our application. This article explains how to use the WNet functions to connect and disconnect the remote drive programmatically. It also shows how to enumerate the resources connected to the network.

  1. Create a dialog-based application using AppWizard and give the project name as enumnet.
  2. Add controls to the dialog template as shown in the following figure.

  3. The controls and their IDs are given in the following table.

    Control

    ID

    Connect Button

    IDC_CONNECT

    Disconnect Button

    IDC_DISCONNECT

    List

    IDC_LISTRES

    Browse Button

    IDC_BROWSE

    Exit Button

    IDCANCEL

  4. #include "winnetwk.h" file and link 'mpr.lib' library file. To link the file, select ‘Project | Settings | Link’ and in the edit box ‘Object/library modules’ type ‘mpr.lib’.
  5. Add a control variable m_listres of type CListCtrl for the list control. After m_listres is added to the class declare it as static since we wish to access it in a different thread. To use this variable add the following statement to the ‘enumDlg.cpp’ file.
  6. CListCtrl CEnumnetDlg::m_listres ;

  7. Create handlers for the ‘Connect’ and ‘Disconnect’ buttons and add the following code to it.
  8. void CEnumnetDlg::OnConnect( )

    {

    if ( WNetConnectionDialog ( m_hWnd, RESOURCETYPE_DISK ) == NO_ERROR )

    MessageBox ( "Drive Mapped" ) ;

    }

    void CEnumnetDlg::OnDisconnect( )

    {

    if ( WNetDisconnectDialog ( m_hWnd, RESOURCETYPE_DISK ) == NO_ERROR )

    MessageBox ( "Drive Disconnected" ) ;

    }

    WNetConnectionDialog( ) function displays a standard ‘Map Network Drive’ dialog box which allows the user to connect (Map) a remote drive to network resource. In WNetConnectionDialog( ) we have passed handle to the window as the first parameter and type of the resource to be connected. RESOURCETYPE_DISK specifies that we are going to connect a drive of the remote computer. If the function fails it automatically shows an error message dialog box.

    In the OnDisconnect( ) handler we have called the function WNetDisconnectDialog( ) which displays a standard dialog box to unmap the selected drive. WNetDisconnectDialog( ) takes the same parameters as the WNetConnectionDialog( ) function.

  9. Now, let us see how to display network resource in the list control. Add the following lines to CDialog::OnInitDialog( ) function.

m_listres.InsertColumn ( 0, "Remote Name", LVCFMT_LEFT, 160, 0 ) ;

m_listres.InsertColumn ( 1, "Comment", LVCFMT_LEFT, 150, 0 ) ;

m_listres.InsertColumn ( 2, "Display Type", LVCFMT_LEFT, 100, 0 ) ;

We have to display in the list control all the resources connected to the network, their comments and type of resource. Since browsing and displaying all this information can take some time we should do this job in a separate thread so that user can interact with the application even if browsing is going on. We should create this thread in OnBrowse( ) handler by calling the AfxBeginThread( ) function as shown below.

AfxBeginThread ( enumnet, NULL ) ;

AfxBeginThread( ) takes address of a thread function (enumnet in our case) as the first parameter. The second parameter is the parameter that would be passed to the thread function when it is called.

Declare enumnet in ‘enumnetDlg.h’ as follows:

UINT enumnet ( LPVOID param ) ;

The function enumnet is given below:

UINT enumnet ( LPVOID param )

{

LPNETRESOURCE lpnr = ( LPNETRESOURCE ) param ;

HANDLE h ;

DWORD cn = 0xFFFFFFFF, s = 16384 ;

LPNETRESOURCE nr ;

static int item = 0 ;

int res = WNetOpenEnum ( RESOURCE_GLOBALNET, RESOURCETYPE_ANY, 0, lpnr, &h ) ;

nr = ( LPNETRESOURCE ) new char[s] ;

do

{

res = WNetEnumResource ( h, &cn, nr, &s ) ;

if ( res == NO_ERROR )

{

for ( int i = 0 ; i <= cn ; i++ )

{

if (nr[i].dwType != RESOURCEDISPLAYTYPE_GENERIC )

{

CEnumnetDlg::m_listres.InsertItem ( item, nr[i].lpRemoteName ) ;

CString comment = nr[i].lpComment ;

if ( comment == "" )

comment = "None" ;

CEnumnetDlg::m_listres.SetItemText ( item, 1, comment ) ;

CString distype = CEnumnetDlg::getdisplaytype ( nr[i].dwDisplayType ) ;

CEnumnetDlg::m_listres.SetItemText ( item, 2, distype ) ;

item++ ;

}

if ( ( nr[i].dwUsage & RESOURCEUSAGE_CONTAINER ) == RESOURCEUSAGE_CONTAINER )

enumnet ( &nr[i] )

}

}

} while ( res == ERROR_NO_MORE_ITEMS ) ;

WNetCloseEnum ( h ) ;

return 0 ;

}

To enumerate a network resource, we have to use WNetOpenEnum( ) and WNetEnumResource( ) functions. In the first call to WNetOpenEnum( ) function we have passed NULL to it since passing NULL retrieves handle to the root of the network. The first parameter of WNetOpenEnum( ) is the scope of the enumeration. RESOURCE_GLOBALNET is given to enumerate all the resources of the net. The second parameter specifies the type of the resource (either disk or print or both) to be enumerated. Passing 0 as the third parameter indicates that we wish to enumerate the container as well as the connectable resources. (If a computer with name Alpha is connected to the network and has C and D drives, then Alpha becomes container and C and D drives become connectables). The fourth parameter is a pointer to the NETRESOURCE structure. Last parameter is the address of a HANDLE variable. The WNetOpenEnum( ) function sets up a handle to the network resource in the HANDLE variable. This handle is passed to WNetEnumResource( ) subsequently. A call to this function returns information about the resource in the form of an array of NETRESOURCE structures. The second parameter of WNetEnumResource( ) is the pointer to the variable specifying number of entries to list. 0xFFFFFFFF means that the function will return as many entries as possible. The third parameter is the buffer that receives enumeration results. Last parameter is the size of the buffer specified in the third parameter. Generally, the size is given as 16 KB. The information filled in the NETRESOURCE structure is then displayed in the list control.

To get the type of the resource we have called a function getdisplaytype( ). To add this function right click the CEnumnetDlg class in the ClassView tab. From the popup menu select ‘Add Member Function’ menu item. Specify the return type as CString and declaration as getdisplaytype (DWORD type). Check the Static check box. The getdisplaytype( ) function is declared as static since it is called from the thread function. The function is given below:

CString CEnumnetDlg::getdisplaytype ( DWORD type )

{

CString str ;

switch ( type )

{

case RESOURCEDISPLAYTYPE_DOMAIN :

str = "Domain" ;

break ;

case RESOURCEDISPLAYTYPE_SERVER :

str = "Container" ;

break ;

case RESOURCEDISPLAYTYPE_SHARE :

str = "Share" ;

}

return str ;

}

The function is self-explanatory. We have passed dwDisplayType element of NETRESOURCE structure to the function, checked the type and have returned the resource type accordingly.

If the dwUsage member of the NETRESOURCE structure is RESOURCEUSAGE_CONTAINER then we should enumerate every resource present in the container. For this we have called the function enumnet( ) recursively.

Balloon Tooltip

A tool tip is small help-text window that appears when the cursor pauses over a tool such as a button or a list box. The tool tip control monitors mouse-movements and automatically displays a tool tip if the mouse cursor remains motionless over a tool for a for a predetermined amount of time. The functionality of the tool tips have been implemented in the CToolTipCtrl MFC class.You simply have to create tool tip control and register the tools which you would like to have tool tips displayed and the text to go with them. The tooltip control does the rest. Instead of getting the normal rectangular shaped tooltips in this article we would see a program which displays balloon shaped tooltips. On positioning the mouse on any of the controls for a moment a balloon tooltip appears. To implement the balloon tooltips we have defined a tipwnd class apart from the normal application, frame window and dialog class. The tipwnd class has been derived from the CToolTipCtrl class. The action begins in the mydialog class. In the OnInitDialog( ) function of this class we have created the tooltip window by calling CToolTipCtrl::Create( ). This leads to a call to the tipwnd::OnCreate( ). Here we have created an elliptic region to represent the tooltip window. When the user performs a mouse or a keyboard operation in the dialog box the message is caught by the mydialog::PreTranslateMessage( ). Since we want to exercise control over these messages we have called the tipwnd::RelayEvent( ) function. In the RelayEvent( ) function the most important message that has to be tackled in WM_MOUSEMOVE. Here if it is ascertained that the tooltip window should appear then using the SetWindowPos( ) and the ShowWindow( ) function the elliptical window is displayed. The actual drawing of the elliptic window and displaying of text within it is done by tipwnd::OnPaint( ) function.

Creating and closing threads programmatically

Threads are ideal for performing tasks in the background while processing user input in the background. For example a Word processor does its spell checking uses a thread and can continue to process messages and allow the user to continue to work while the spell checker runs. MFC encapsulates threads of execution in the CWinThread class. Asfar as Windows is concerned, all threads are alike. MFC, however, distinguishes between two types of threads: User Interface Threads and Worker Threads. The UI threads have message loops and worker threads dont. UI thread can create windows and process messages sent to those windows. Worker threads perform background tasks that receive no direct input from the user. For example when you open a folder in operating system shell, the shll launches a UI thread that creates a window showing the folder's contents. If you drag-copy a group of files to the newly open folder, that folder's thread performs the files transfer. A classic example of a worker thread is the thread that an animation control uses to play AVI clips.
This article is an exmple of a worker thread which shows how to create and stop threads programatically. A menu displays two top level menus Start and Stop. When clicked on start two threads paint1 and paint2 are started. The Stop menu has two menu items paint1 and paint2. If user clicks on paint1 the thread stops. This is done by calling ::TerminateThread( ) function. paint1 and paint2 are thread functions. A thread function is a callback function so it must be either a static function or global function. The prototype of a thread function is as follows:
UINT threadfunc ( LPVOID param ) ;
param is a 32-bit value whose value equals the param passed to AfxBeginThread( ).

Displaying icon in the system tray

The system tray is the area on the far-right corner (or on the bottom if you are using a vertical taskbar) of the taskbar. This area is also known as the status area or the notification area. In most cases, you will see the time or an icon displayed in this area. This can include icons representing things such as a print job in progress, anti-virus software, sound settings, display monitor icon, to name a few. By double-clicking one of these icons, you bring up part of the application, such as a dialog to change current settings. You can also right-click the icon in the system tray to access a context menu, which can give you even more options specific to that application. Even we can plant our own application-specific icon in this area as shown in the following figure.

We have made the following provisions in the program:
  • On right clicking the icon a menu appears.
  • By choosing one of the menu items the icon of our application in the system tray gets animated.
  • On double clicking the icon of our application in the system tray the default menu item is considered to be selected and the handler related to this menu item gets called.
To implement the system tray we have defined a class called csystray. In the myframe class we have an object m_trayicon as one of its private data members. When the frame window gets created the control reaches myframe::OnCreate( ). From this function we have called the csystray::create( ) function. In the csystray::create( ) function we have created a window of size 10 x 10 using the CreateEx( ) function. Then we have created a menu that would later on get displayed on right clicking our application icon from the system tray. Note that we have not displayed this window or even the frame window. Next we have set up values in the NOTIFYICONDATA structure through the statements:

m_nid.cbSize = sizeof ( NOTIFYICONDATA ) ;

m_nid.hWnd = m_hWnd ;

m_nid.uID = 1 ;

m_nid.hIcon = i ;

m_nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP ;

m_nid.uCallbackMessage = message ;

trcpy ( m_nid.szTip, tip ) ;

Lastly, we have called the function Shell_NotifyIcon( ). On calling this function our application starts receiving Windows messages from the system tray icon. The Shell_NotifyIcon( ) function is used to send several different messages that control how your icon in the system tray operates. The first parameter passed to it specifies the operation that will be performed. To add a new icon for your application, it should be set to NIM_ADD, to change the current icon it should be set to NIM_MODIFY and to remove an existing icon it should be set to NIM_DELETE. The second parameter to Shell_NotifyIcon( ) is a pointer to a NOTIFYICONDATA structure, which is used to pass a variety of information. This structure is shown below:

typedef struct _NOTIFYICONDATA

{

DWORD cbSize ;

HWND hWnd ;

UINT uID ;

U INT uFlags ;

UINT uCallbackMessage ;

HICON hIcon ;

CHAR szTip[64] ;

} NOTIFYICONDATA, *PNOTIFYICONDATA ;

The purpose of each element is as follows: cbSize field should always be set to sizeof (NOTIFYICONDATA). When mouse events occur on the icon in the system tray, such as moving over the icon, clicking, or double-clicking, the icon will send a Windows message back to your application. The hWnd field gives the handle of the window that will receive the message, while uID specifies a value that will be passed back to you application in the wParam of this message. The uFlags field is used to indicate which of the remaining fields contains valid data. This may be a combination of NIF_ICON, NIF_MESSAGE, or NIF_TIP. To pass a new icon, set uFlags to NIF_ICON and pass a handle to the icon in hIcon. The value passed in uCallbackMessage gives the message ID of the Windows message that will be sent to your application when the user does some thing with the icon. Lastly, you may pass a short string for tooltip help text for the icon in szTip by setting NIF_TIP in uFlags. Handling Notifications When mouse events occur in the icon that we have added to the system tray, a message will be sent to the window whose handle we have set up in the hWnd field of the NOTIFYICONDATA structure. Thus the window that we created in csystray::create( ) will receive a message with a message ID of WM_USER + 15, with the WPARAM field set to 1, the id of our icon. In addition, the low word of LPARAM in the message will be set to one of the mouse event message ids, such as WM_LBUTTONDOWN. Now, let's take a look at how to handle these messages. First, you will need to add a message handler for the message ID that you passed in the call to Shell_NotifyIcon( ). We have done this through the message map entry: ON_MESSAGE ( WM_USER + 15, onusermessage ) The value 15 has been added here just to ensure that this message doesn't conflict with any other user-defined message that some other application might be using. We have defined the onusermessage( ) function within the class to tackle the right mouse and the double click mouse messages.

A simple splitter window example

A splitter can be divided into two or more panes horizontally as well as vertically. Each pane size can be changed dynamically using movable splitter bars. Each pane can contain one view of the document’s data. The views are children of the splitter window, whereas, the splitter window is a child of a frame window. In this article we have created a static splitter window with two panes, one displays text and another graphics.

Creating a simple screen saver

Anybody who has dabbled with Windows even for a few hours has knowledge about the existence of a program called Screen Saver. A Screen Saver can be installed by right clicking on the screen saver’s executable file and selecting the ‘Install’ option from the menu that appears. As soon as we do this, our screen saver file is entered in the list of screen savers that the Windows OS maintains. This list is displayed in a combo box in a dialog along with its preview. This dialog box, the combo box and the preview window are shown in the Figure 1. Note that our screen saver ‘mysaver’ also appears in the list.


Figure 1

The screen savers are different than other executables in their extension. All screen savers have an extension of ‘.scr’ rather than the traditional ‘.exe’. To ensure that it gets this extension, follow these steps:

  1. Select ‘Project | Settings | Debug’ property page from the ‘Developer Studio’. Change the extension from ‘.exe’ file to ‘.scr’ in the edit box with a title ‘Executable for debug session’.
  2. Select ‘Project | Settings | Link’ property page from the ‘Developer Studio’. Change the extension from ‘.exe’ file to ‘.scr’ in the edit box that has a title ‘Output file name’. Press OK.



Figure 2

If we click on the ‘Preview’ button in the dialog box in Figure 1 the screen saver would be put into action. If we click on another button called ‘Settings’ one more dialog box would appear which would help the user to control the various elements of the screen saver. Naturally, the appearance and contents of this dialog box would vary from one screen saver to another. The dialog box for our screen saver is shown in the following figure.


Figure 3

As soon as we change any of the settings in the dialog box the change is immediately noticed in the preview window in the dialog box.

Working

The screen saver developed here randomly displays colorful squares/circles on the screen. It uses an array of structures s[ ] to display the squares/circles. We have defined this array to hold 10 elements. The information stored in this array controls the size, position and color of each square/circle. At any point of time, a total of 10 squares/circles are displayed. When the 11th square/circle is drawn the 1st one is erased. This continues till either some key is depressed or the mouse is moved or any button on the mouse is clicked.

The screen saver program runs in three different modes:

  1. Full Screen mode: The Screen Saver runs in this mode in 4 cases:
  1. When the PC is idle for a time greater than one that has been set up through the spin button control in the ‘Start | Settings | Control Panel | Display | Screen Saver’ property page.
  2. When the ‘Preview’ button in ‘Start | Settings | Control Panel | Display | Screen Saver’ property page is clicked.
  3. When we right click on the screen saver file and select ‘Test’ from the menu that appears.
  4. When we run the screen saver by double clicking the screen saver from the ‘Explorer’ or from ‘Start | Run’.
  1. Small Preview Window mode: The Screen Saver runs in this mode in 2 cases:
  1. When ‘Start | Settings | Control Panel | Display | Screen Saver’ property page is displayed.
  2. When we right click on ‘.scr’ file and select ‘Install’.
  1. Settings Dialog Preview mode: The Screen Saver runs in this mode in 3 cases:
  1. When we run the screen saver from the ‘Developer Studio’.
  2. When we right click on the screen saver file and select ‘Configure’ from the menu that appears as shown in the Figure 4.
  1. When we click on the ‘Settings’ button from ‘Start | Settings | Control Panel | Display | Screen Saver’ property page.


Figure 4

Figure 1 shows the screen saver active in the ‘Small Window Preview’ mode, whereas Figure 3 shows it active in the ‘Settings Dialog Preview’ mode. The following figure shows it active in the ‘Full Screen’ mode.


Figure 5

Depending upon which of the above three modes is used to invoke the screen saver it is passed an argument ‘s’, ‘p’ and ‘c’ respectively. However there are two exceptions here. In cases 3(a) and 3(b) above no argument is passed to the program. However, in these two cases too we want the screen saver to run in ‘Settings Dialog preview mode’. It is necessary to identify which of these three cases has occurred, because the size of the window where the random squares/circles are to appear is different in the three cases.

When the ‘Settings’ dialog box is popped up, we have made a provision to let the user select the shape of the objects (rectangle or circle) that are going to get displayed on the screen when the screen saver goes into action. Additionally, the user can also select the fill style for the rectangles/circles. We have provided three styles; solid, hatch and pattern to choose from. For permitting the user to select the shape and the style we have provided two combo boxes in the ‘Settings’ dialog box.

Irrespective of the mode in which our Screen Saver is running, the control would ultimately land in the myapp::InitInstance( ) function. Here it is first determined which command line argument has been passed (if at all) to our program by calling the myapp::checkoption( ) function. It then appropriately calls the function myapp::doconfig( ), myapp::dofullscreen( ), or myapp:: doperview( ).

The myapp::doconfig( ) Function

If it is determined that the settings dialog box should be popped up, an object of the settings dialog class is created through the statement,

settingdialog d ;

This calls the constructor of the settings dialog. Here the settings dialog is created in memory. This dialog is then displayed by calling CDialog::DoModal( ).

The myapp::dopreview( ) Function

The code of this function given below:

void myapp::dopreview( )
{

CWnd* parent = CWnd::FromHandle ( ( HWND ) atol ( __argv[2] ) ) ;
CRect r ;
parent -> GetClientRect ( &r ) ;
drawwnd* p = new drawwnd ( TRUE ) ;
p -> create ( NULL, WS_VISIBLE | WS_CHILD, r, parent, NULL ) ;
m_pMainWnd = p ;

}

When the function is called the drawing activity should take place in the preview window. The drawing activity would be managed by the drawwnd class object. When this object is created the window associated with it should be the child of the preview window present in the property page. Hence the address of this parent window needs to be determined. This has been achieved by calling the function CWnd::FromHandle( ). The argument passed to this function is the handle to the preview window of the property page. This handle is passed to our program as a command line argument (argv[2]). Once the pointer to the parent window has been obtained the drawwnd::create( ) function is called to create the window.

The myapp::dofullscreen( ) Function

Lastly, if it is determined that the actual screen saver should be put into action then a full screen window is created. The code of dofullscreen( ) is shown below:

void myapp::dofullscreen( )
{

saverwindow* p = new saverwindow ( TRUE ) ;
p -> create( ) ;
m_pMainWnd = p ;

}

The Settings Dialog

When the DoModal( ) function is called to display the dialog box control first reaches settingdialog::OnInitDialog( ). The code of this function is given below.

BOOL mydialog::OnInitDialog( )
{

m_shape = AfxGetApp( ) -> GetProfileInt ( "Config", "Shape", 0 ) ;
m_fillstyle = AfxGetApp( ) -> GetProfileInt ( "Config", "FillStyle", 0 ) ;
CDialog::OnInitDialog( ) ;
CRect r ;
CStatic *s = ( CStatic * ) GetDlgItem ( IDC_PREVIEW ) ;
s -> GetWindowRect ( &r ) ;
ScreenToClient ( &r ) ;
m_preview.create ( NULL, WS_VISIBLE | WS_CHILD, r, this, NULL ) ;
CenterWindow( ) ;
return TRUE ;

}

In this function, to begin with, the values of shape and fill style are read from the registry by calling the CWinApp::GetProfileInt( ) function. When you run the program for the first time there won’t be any entry in the registry. Hence default values of 0 and 0 (last parameter passed to GetProfileInt( )) would be assumed for the shape and fill style. The values read from the registry (or the assumed values if there are no entries in the registry) are used to show the default selections for shape and fill style when the dialog is popped up.

Next, the base class implementation of OnInitDialog( ) is called, which in turn calls settingdialog::DoDataExchange( ). In this function the values of m_shape and m_fillstyle are used to set up the default selections of the two combo boxes. Next, in the OnInitDialog( ) function the size of the preview window is obtained and a preview window is created. Note that the object m_preview (object of drawwnd class) which is a private data member is used to create the preview window. As the window gets created, the drawwnd::OnCreate( ) handler gets called. Here, to set the speed at which the squares/circles should be displayed in the window, the CWnd::SetTimer( ) function is called. This function simply sets a timer. Later on we would see how this timer gets serviced.

As we make selections in the dialog box its results are immediately shown in the preview window. To ensure this a function settingdialog::newshapestyle( ) is called as soon as the selections from the combo boxes are changed. This function updates the drawing tools by calling the drawwnd::setdrawingtool( ) function.

Finally, when the user dismisses the dialog box by clicking OK the selections made by him are written to the registry by calling CWinApp::WriteProfileInt( ) function. The logic of drawing in the preview window has been managed in the drawwnd class.

Drawing in The Window

To manage drawing in the small preview window or the full screen window we have developed a class called drawwnd. Whenever an object of this class is created its zero argument constructor gets called. This constructor function is shown below:

drawwnd::drawwnd ( BOOL deleteflag )
{

m_deleteflag = deleteflag ;
m_shape = AfxGetApp( ) -> GetProfileInt ( "Config", "Shape", 0 ) ;
m_fillstyle = AfxGetApp( ) -> GetProfileInt ( "Config", "FillStyle", 0 ) ;

for ( int i = 0 ; i < 10 ; i++ )
s[i].x = s[i].y = -1 ;

setdrawingtool ( m_shape, m_fillstyle ) ;
currentnum = 0 ;

}

Here, to begin with, the values of shape and fill style are read from the registry. Next, the array s[ ] is initialised such that the x, y coordinates of all 10 squares/circles are set to -1. These values are later on used to keep track of how many square/circles have been drawn so far. Lastly, the values read from the registry are used to create 10 brushes by calling the drawwnd::setdrawingtool( ) function.

Whenever the full screen window or the small preview window is created, control reaches drawwnd::OnCreate( ). Here we have set up a timer by calling CWnd::SetTimer( ).

When the time interval set in CWnd::SetTimer( ) is over the function drawwnd::OnTimer( ) gets called. The code of this function is shown below:

void drawwnd::OnTimer ( UINT id )
{

CClientDC *dc ;
dc = new CClientDC ( this ) ;
CRect rect ;
GetClientRect ( &rect ) ;
draw ( dc, rect ) ;
delete dc ;

}

In this function we have obtained the device context, determined the client area size and then called drawwnd::draw( ) to actually carry out the drawing.

Full Screen Mode

When the screen saver runs in the full screen mode the myapp::dofullscreen( ) function gets called. In this function we have created an object of saverwindow class which has been derived from the drawwnd class. On creating this object the constructor of saverwindow gets called. In the constructor we have set up the saverwindow::lastpoint variable with a value (-1,-1). This value is later on used in the saverwindow::OnMouseMove( ) function, as we would soon see. Once the object has been created, using it, saverwindow::create( ) function is called. This function in turn calls drawwnd::create( ) with WS_EX_TOPMOST as the first argument and WS_POPOUP as one of the values in the second argument. The first argument ensures that the full screen window created becomes the topmost window. The value WS_POP ensures that the window created doesn’t have a caption bar and the border.

Once the window is created the drawnd::OnCreate( ) gets called which once again sets up a timer in response to which the OnTimer( ) function calls drawwnd::draw( ) to do the drawing in the full screen window.

Disabling The Screen Saver

When the screen saver runs in the full screen mode it should get disabled whenever the user presses any key or performs any mouse operation. It means in the saverwindow class we must handle all keyboard and mouse related messages. Hence we have written the handlers OnKeyDown( ), OnSysKeyDown( ), OnLButtonDown( ), etc. in the saverwindow class. In each of these handlers all that we have done is, sent a WM_CLOSE message to the message queue and then called the base class implementation of the handler. When the WM_CLOSE message would get picked up from the message queue, the application would close itself. The only exception is the OnMouseMove( ) handler. The code of this handler is as shown below:

void saverwindow::OnMouseMove ( UINT flags, CPoint pt )
{

if ( lastpoint == CPoint ( -1, -1 ) )
lastpoint = pt ;

else if ( lastpoint != pt )
PostMessage ( WM_CLOSE ) ;

drawwnd::OnMouseMove ( flags, pt ) ;

}

If you remember, in the constructor of the saverwindow class we have set up the variable saverwindow::lastpoint to a value (-1, -1). In the OnMouseMove( ) handler we have checked whether the coordinates of lastpoint are still (-1, -1). If they are then we do not post the WM_CLOSE message into the message queue. It would be necessary to do so in the following situation. Suppose a WM_MOUSEMOVE message is present in the message queue and the screen saver gets active before the WM_MOUSEMOVE message could get processed. In such an event, as soon as the screen saver becomes active it would get deactivated when the pending WM_MOUSEMOVE message gets processed. Our code given above prevents this by not posting the WM_CLOSE into the message queue in this situation. We also update the value of lastpoint such that next time a WM_MOUSEMOVE message arrives the screen saver is shut down by posting a WM_CLOSE into the message queue.

Destroying The Window

Once the screen saver becomes active we can deactivate it in a number of ways, as discussed above. Even though it has been deactivated from the screen it continues to run in memory. If once again a considerable time elapses without you hitting a key or clicking a mouse yet again it would become active. If it gets activated again then it would yet again reach the myapp::dofullscreen( ) function to create a window. Here the code,

p = new saverwindow;

would get executed again.

But what has happened to the object that p was pointing last time around. It is still surviving, and now if p starts pointing to another object then a memory leak would occur since we would have no way to access the earlier object. Note that the earlier object would not get destroyed through the destructor of myapp. This is because the destructor doesn’t get called since the application is still running and the myapp a object has not gone out of scope.

To avoid this situation we have used a drawwnd::deleteflag variable. While creating the preview window of ‘Start | Control Panel | Display | Screen Saver’, or the full screen window we have passed a value TRUE to the drawwnd constructor which in turn sets it up in drawwnd::m_deleteflag. As against this, in case of the preview window of the settings dialog we have passed a value FALSE to the drawwnd constructor. This is because on dismissing the dialog box the application would come to an end resulting in call to the myapp class’s destructor function. This would destroy the object associated with the preview window using its address which is stored in myapp::m_pMainWnd.

In case of the preview window of ‘Start | Control Panel | Display | Screen Saver’ and the full screen window the window object would be destroyed in the drawwnd::PostNcDestroy( ) function using the m_deleteflag variable.

A program that runs only for limited hours

Many software, especially the ones that we download freely from the net have an expiry period. When we run these software a dialog is popped up which informs that we can use the software only for a specific number of days. And this count keeps reducing every day. Once the d-day passes we can no longer use the software. Writing such programs is easy, as the following program would demonstrate. Instead of deciding the expiry on number of days, I have taken the number of executions and number of minutes of usage as the basis for deciding expiry. Suppose the number of executions permissible are 100 and number of hours of usage permissible are 10. Every time the software is executed the user is prompted with a dialog shown in Figure 1. When the software is closed the remaining executions and remaining hours are reported. This is managed by updating the counts in registry every time the software is executed.

A simple printing application

One of the most intimidating aspects of Windows programming is printing. Even printing a single document involves lot of work. Fortunately, because of Windows’ device independence the same GDI functions that we used to draw on the screen can be used to draw on a sheet of paper. While printing a document, there are lots of details that one has to account for. These include paginating the output, print previewing, provision for terminating an unfinished job, etc. Print previewing permits the user to see exactly how the printed output would look like before it is sent to the printer. The application framework provides a lot of supportive code, which makes the process of writing code for printing and print previewing that bit easier.

Let us now understand the printing process step by step. To begin with, CView::OnFilePrint( ) calls the virtual function OnPreparePrinting( ). This function is always overridden. A minimal OnPreparePrinting( ) overridden function looks like this:

BOOL myview::OnPreparePrinting ( CPrintInfo *info )
{

return DoPreparePrinting ( info ) ;

}

The DoPreparePrinting( ) function displays a ‘Print’ dialog and creates a printer DC for us. If a non-zero value is returned from OnPreparePrinting( ) the printing process can begin. However, if a zero is returned the print job is cancelled. It is the DoPreparePrinting( ) function which returns a 0 if the user cancels the print job by clicking the ‘Cancel’ button in the ‘Print’ dialog, if no printers are installed or if the framework is unable to create the printer DC.

If we wish, we can set values in the various elements of the ‘Print’ dialog when the DoPreparePrinting( ) function displays it. For this we must use member functions of the CPrintInfo class. For example, we can specify print parameters like the starting and the ending page numbers of the document to be printed as shown below:

BOOL myview::OnPreparePrinting ( CPrintInfo *info )
{

info -> SetMinPage ( 2 ) ;
info -> SetMaxPage ( 3 ) ;
return DoPreparePrinting ( info ) ;

}

The OnBeginPrinting( ) function is called just before the printing begins. When this function is called, two parameters are passed to it: pointer to an initialized CPrintInfo structure and a pointer to a CDC object representing the printer DC the framework created when we called DoPreparePrinting( ).

OnBeginPrinting( ) is a CView virtual function. If it is overridden it’s done so for two puposes:

  1. To set the maximum page number if we haven’t done it already in OnPreparePrinting( ). This is shown in the follow-ing code snippet:
  2. void myview::OnBeginPrinting ( CDC *p, CPrintInfo *info )
    {

    int pageht = p -> GetDeviceCaps ( VERTRES ) ;
    int doclength = GetDocument( ) -> getdoclength( ) ;
    int max_page = max ( 1, ( doclength + ( pageht - 1 ) ) / pageht ) ;
    info -> SetMaxPage ( max_page ) ;

    }

    Here GetDeviceCaps( ) returns the height of the printable page area in pixels and getdoclength( ) is a document function that returns the length of the document in pixels.

  3. To create fonts to be used in the printing process. These fonts are based on the printer metrics rather than the screen metrics.

When the OnPrepareDC( ) function is called two pointers are passed to it—a pointer to a device context and a pointer to a CPrintInfo object. OnPrepareDC( ) is also a virtual function and is called before each page is printed. This function is overridden for two purposes:

  1. To perform print-time pagination: It is important to inform the framework the maximum number of pages in the document using SetMaxPage( ). With this information the framework can decide how many times it should call OnPrint( ) to print a page. If we have not informed this to the framework either in OnPreparePrinting( ) or in OnBeginPrinting( ) then we must do print-time pagination in OnPrepareDC( ). While doing this pagination we should set CPrintInfo::m_bContinuePrinting to TRUE if there are pages remaining to be printed; and to FALSE if the last page is to be printed and the print job is terminated.
  2. To calculate a new viewport origin from the current page number so that OnDraw( ) can output the current page to the printer. This is shown in the following code snippet:

void myview::OnPreapreDC ( CDC *p, CPrintInfo *info )
{

CScrollView::OnPrepareDC ( p, info ) ;
if ( p -> IsPrinting( ) )
{
int y = ( info -> m_nCurPage - 1 ) * pageht ;
p -> SetViewportOrg ( 0, -y ) ;
}

}

Here the viewport origin is moved in the y direction such that the device point (0,0)—the pixel in the upper-left corner of the printed page—corresponds to the logical point in the upper-left corner of the document’s current page. pageht is a myview data member holding the printable page height. OnPrepareDC( ) is called before repainting the screen as well as before outputting a page to the printer. To determine whether OnPrepareDC( ) was called for the screen or the printer we have called CDC::IsPrinting( ).

Like OnPrepareDC( ), the CView::OnPrint( ) function also gets called by the framework for each page to be printed. This function too, receives a pointer to the printer DC and a pointer to a CPrintInfo object.

OnPrint( ) is a virtual function. Its default implementation contains a call to the OnDraw( ) function. If it is overridden then what code we write in it depends upon whether we are planning to do the printing through the OnDraw( ) function or not. If we plan to do the printing as well as painting on the screen through OnDraw( ) then within OnPrint( ) we should have provision to print page elements that do not appear on the screen. These include headers and footers to be printed on each page. This is shown in the following code snippet:

void myview::OnPrint ( CDC *p, CPrintInfo *info )
{

displayheader ( p ) ;
OnDraw ( p ) ;
displayfooter ( p ) ;

}

Thus, headers and footers can be printed through local member functions, whereas, the body of the page is printed through the OnDraw( ) function.

On the other hand if we decide to do all our printing through OnPrint( ) and do the painting through OnDraw( ) then we should include the code to print one page also in OnPrint( ).

Once the printing is over, the OnEndPrinting( ) function is called. Here any fonts that were created in OnBeginPrinting( ) are freed. This function too is a virtual function. It is usually overridden only if we have also overridden OnBeginPrinting( ).

Now we know when in the printing process each virtual CView printing function is called. Armed with this knowledge we can write a program that can put all this in action.

In this article we would draw a circle and a rectangle, display some text and then try to print it on the printer. We would also provide support for print previewing and print setup. The ‘File’ menu would contain items like ‘Print’, ‘Print Preview’ and ‘Print Setup’. On selecting the ‘Print’ menu item a ‘Print’ dialog box would popup. In this dialog the user can specify printing options like printer type, starting and ending page numbers and the number of copies.

Selecting ‘Print Preview’ option would put the application in the print preview mode such that the user can see exactly how the printed output would look like before it is sent to the printer. On selecting the ‘Print Setup’ menu item a dialog would popup. Using this dialog we can choose the printer, the paper size and the page orientation—portrait or landscape.

MFC provides predefined command ids and default command handlers for the ‘File’ menu items ‘Print’, ‘Print Preview’ and ‘Print Setup’. These items have ids ID_FILE_PRINT, ID_FILE_PRINT_PREVIEW and ID_FILE_PRINT_SETUP. These are handled by the handlers CView::OnFilePrint( ), CView::OnFilePrintPreview( ) & CWinApp::OnFilePrintSetup( ) respectively. To enable these handlers we have used two message map entries in the myview’s message map and one in myapp’s message map.

We have decided to use myview::OnDraw( ) to do the printing as well as the painting. Hence we have not overridden any of the CView printing functions. It is important that the circle and the rectangle should have same proportions irrespective of whether they are being printed on the printer, painted on the screen or display in the print preview window. To ensure this we have set the mapping mode to MM_LOENGLISH. The pixel per each value for screen and printer are different. Hence had we drawn the figures in the default MM_TEXT mapping mode the figures would have appeared smaller on a 600 dpi printer as compared to those on the screen.

By using a mapping mode like MM_LOENGLISH, the logical units are scaled to physical distance and not to pixel counts. This allows the GDI to do the scaling and ensure that OnDraw( ) produces consistent output on screen as well as on the printer.

Palette Demo

When we draw on a SVGA the GDI maps each COLORREF value to the nearest static color using a color matching algorithm. For many applications this form of color mapping is acceptable. But in an application where a more accurate color output is required 20 colors are too little to choose from. In such cases we can use a logical palette to add 236 more colors to the DAC registers. We, of course, cannot program the DAC registers directly because the same VDU is being shared amongst several applications running in memory. Hence, to get our jobs done we have to use a GDI object called logical palette. A logical palette is nothing but a table of RGB color values. The process of adding the colors in the logical palette to the DAC registers (often called Hardware Palette) is known as realizing the palette. This article shows how to create a logical palette and realize it. The program displays a window containing 24 rectangles drawn with a solid pen of thickness 8 pixels. The first 12 rectangles are drawn using different shades of red color. As mentioned earlier, these colors are mapped to the nearest out of the 20 static colors. As a result, all 12 shades mapped to one of the two shades of red colors present in the list of static colors (Refer Table 1). The next 12 rectangles have been drawn after realizing the palette. Hence you can notice 12 distinct shades of red. In MFC, to create logical palette we have to use the CPalette class's member functions. Once a logical palette is created it can be selected into a device context and realized with CDC member functions. In our program once the window is created the myframe::OnCreate( ) function gets called. Here we have first ascertained whether using a logical palette will improve color output, by calling CDC::GetDeviceCaps( ) as shown below.
if ( ( d.GetDeviceCaps ( RASTERCAPS ) & RC_PALETTE ) == 0 )
{
MessageBox ( "Palette is not supported by this device \n Change Settings", "Sorry" ) ;
return -1 ;
}
It is a good idea to perform this check. If you are running the application on XGA then you don't need a logical palette because in a XGA, perfect color output is available free. If you are running the application on a VGA, palettes are meaningless since the system palette is initialized with 16 static colors that leave no room for colors in logic palette. It is when the application is run on SVGA, we can get more color accuracy by using a logical palette. Hence the check. Next we have created 12 pens for drawing the first 12 rectangles. Then we have created a logical palette through the statement,
m_palette.CreatePalette ( pLP ) ;
Before we call CPalette::CreatePalette( ) we have to fill in a LOGPALETTE structure with information describing the palette colors. The LOGPALETTE structure has been defined as follows.
typedef struct tagLOGPALETTE
{
WORD palVersion ;
WORD palNumEntries ;
PALETTEENTRY palPlaEntry[1] ;
} LOGPALETTE ;
palVersion specifies the LOGPALETTE version number; in all current releases of Windows, it should be set to 0x300. palNumEntires specifies the number of colors in logical palette. palPalEntry is an array of PALETTEENTRY structures defining the colors themselves. The number of elements in the array should equal the value of palNumEntries. PALETTEENTRY is defined as follows:
typedef struct tagPALETTEENTRY
{
BYTE peRed ;
BYTE peGreen ;
BYTE peBlue ;
} PALETTEENTRY ;
peRed, peGreen and peBlue specify a color's 8-bit RGB components. peFlags contains zero or more bit flags describing the type of palette entry. The PLAETTENETRY array in the LOGPALETTE structure is declared with just one array element because Windows has no way of anticipating how many colors a logical palette will contain. As a result, you cannot just declare an instance of LOGPALETTE on the stack and fill it in; instead, we have to allocate memory for it based on numbers of PALETTEENTRY structures it contains. Once the palette has been created it needs to be selected into the device context and then realized. Since this must be done before any drawing takes place we have done these two tasks in the OnPaint( ) handler. Note that palette is selected with CDC::SelectPalette( ) instead of CDC::SelectObject( ). CDC::RealizePalette( ) realizes the palette that is currently selected into the device context by asking the palette manager to map colors from logical palette to the system palette. Once the palette has been realized we are ready to start drawing. If we use CDC::BitBlt( ) to display a bitmap, the realized colors are used automatically. However, if we are using a pen or a brush and are using the RGB macro, the GDI maps the COLORREF value only to the static colors. Since we want the GDI to use all the palette colors we must use the PALETTERGB( ) macro instead of RGB( ).

Displaying mouse coordinates in status bar

This article shows how to display mouse coordinates on the status bar. The coordinates are displayed only if the cursor is in the view window. But if the toolbar is kept floating in the view window and cursor is placed on the toolbar the coordinates are not displayed. In the OnMouseMove( ) handler the mouse is captured so that even if mouse cursor goes out of the client area our window continues to receive the mouse messages. Then it is made sure that the given mouse coordinates are in our window. This is done by calling WindowFromPoint( ) function and passing to it the address CPoint object. WindowFromPoint( ) takes the coordinates relative to the screen. Therefore the coordinates which we get in the last parameter of OnMouseMove( ) are converted into screen coordinates by calling ClientToScreen( ) function.

Ownerdrawn Menu with Colored Linest

Most of the times menu items are strings, but sometimes picture makes more sense than text strings. For example, a color menu should show colors rather than text strings containing names of various colors. It would make more sense for the user if the colors like magenta and cyan are shown as they are going to look like rather than as strings saying "Cyan" and "Magenta". If the colors are shown, their meaning would be crystal clear. Graphics are displayed in the menu using owner-drawn menu items. When a menu containing an owner-drawn menu item is displayed, Windows sends the menu's owner (the window to which the menu is attached) a WM_DRAWITEM message as well as the position where it should be drawn. Windows also supplies a device context in which to do the drawing. The WM_DRAWITEM handler might display a bitmap, or it could use GDI function to literally draw the menu item at the specified location. Before a menu containing an owner-drawn menu item is displayed for the first time, Windows sends the menu's owner a WM_MEASUREITEM message to inquire about the menu item's dimensions. If a submenu contains, say, five owner-drawn menu items, the window that the menu is attached to will receive five WM_MEASUREITEM messages the first time the submenu is displayed and five WM_DRAWITEM messages. Each time the submenu is displayed thereafter, the window will receive five WM_DRAWITEM messages but no further WM_MEASUREITEM messages. The very first step in implementing an owner-drawn menu is to stamp all the owner-drawn menu items with the label MF_OWNERDRAW. Unfortunately, this can't be done using the Resource Editor. Instead, you must either create MF_OWNERDRAW menu items programmatically and add them to your menu with AppendMenu( ) or InsertMenu( ), or use ModifyMenu( ) to add the MF_OWNERDRAW attribute to existing menu items. We have used the second method in our program. The second step is adding an OnMeasureItem( ) handler and associated message-map entry so that your application can respond to WM_MEASUREITEM messages. The OnMeasureItem( ) function receives two parameters. The first parameter is an ID identifying the control to which the message pertains and is meaningless for owner-drawn menus. The second parameter is pointer to a structure of type MEASUREITEMSTRUCT. OnMeasureItem()'s job is to fill in the item Width and item Height fields of this structure, informing Windows of the menu item's horizontal and vertical dimensions, in pixels. To compensate for differing video resolutions, better approach is to base the width and height of items in an owner- drawn menu on some standard such as the SM_CYMENU value returned by ::GetSystemMetrics( ):

p -> itemWidth = ::GetSystemMetrics (SM_CYMENU) * 4 ;
p -> itemHeight = ::GetSystemMetrics (SM_CYMENU) ;

here p is the pointer to MEASUREITEMSTRUCT. SM_CYMENU is the height of the menu bars the system draws for top-level menus. By basing the height of owner-drawn menu items on this value and scaling the width accordingly, you can ensure that owner-drawn menu items will have roughly the same proportions as menu items drawn by Windows. The third and final step in implementing owner-drawn menu items is to provide an OnDrawItem( ) handler and a message-map entry for WM_DRAWITEM messages. Inside OnDrawItem( ) is where the actual drawing is done. This function too receives two parameters. The first item once again is meaningless for owner drawn-menu items. The second parameter is a pointer to a DRAWITEMSTRUCT structure, which contains the following members:


typedef struct tagDRAWITEMSTRUCT
{
UINT CtlType ;
UINT CtlID ;
UINT itemID ;
UINT itemAction ;
UINT itemState ;
HWND hwndItem ;
HDC hDC ;
RECT rcItem ;
DWORD itemData ;
} DRAWITEMSTRUCT ;


The field CtlType of DRAWITEMSTRUCT is set to ODT_MENU if the message pertains to an owner-drawn menu item. The field itemID holds the menu item ID, whereas the fields CtlID and itemData are unused. hDC holds the handle of device context in which the menu item is drawn, and rcItem is a RECT structure containing the coordinates of the rectangle in which the item appears. The size of the rectangle described by rcItem is based on the dimensions you provided to Windows in response to the WM_MEASUREITEM message for this particular menu item. hwndItem holds the handle of the menu to which the menu item belongs. This value isn't often used because the other fields provide most or all of the information that's needed. DRAWITEMSTRUCT's itemState field describes the current state of the menu item - checked or unchecked, enabled or disabled, and so on. These states are indicated using zero or more bit flags as shown in the table below.


ODS_CHECKED: The menu item is currently checked.
ODS_DISABLED: The menu item is currently disabled.
ODS_GRAYED: The menu item is currently grayed.
ODS_SELECTED: The menu item is currently selected.


This state information is important because it tells you how the menu item should be drawn. Which of the bit flags you examine depends on which states you allow the menu item to assume. You should always check the ODS_SELECTED flag and highlight the menu item if the flag is set. If your application includes code to check and uncheck owner-drawn menu items, you should check ODS_CHECKED and draw a check mark next to the menu item if the flag is set. Similarly, if you allow the item to be enabled and disabled and grayed and ungrayed, you should check for ODS_DISABLED and ODS_GRAYED flag and draw accordingly.

Recognizing which Shift/Toggle key is Pressed

It is an easy job to recognize which key the user has pressed by checking its virtual key code. But some efforts are needed to be taken to distinguish between left and right keys. The eighth bit of virtual key code contains 1 if Alt and Ctrl keys at the right side of the keyboard, the cursor movement keys including Insert and Delete that are not the part of the Numeric Keypad, Slash(/) and Enter keys on the Num Pad and the Num Lock key are hit. If this particular bit is checked along with the virtual key code we can easily print out which key has been hit.

Browser Application

We can use the MFC AppWizard to create an application that displays any Web content that was specified.

Steps in building Web browser application:

Create a new project of type MFC AppWizard (exe) using the DevStudio Appwizard. Name it Browser. and allow all default setting by clicking next. In Step 6 of the wizard, use the Base class drop-down list to select 'CHtmlView'. Click Finish. The CHtmlView class acts as a wrapper for the Web browser control, which gives your application a view onto a Web, or HTML page. The wizard creates an override to the OnInitialUpdate( ) function in the view class, providing a navigational link to the Microsoft Visual C++ Web site:

void CBrowserView::OnInitialUpdate( )
{
CHtmlView::OnInitialUpdate();

// TODO: This code navigates to a popular spot on the web.
// change the code to go where you'd like.
Navigate2(_T("http://www.microsoft.com/visualc/"),NULL,NULL);
}

Rebar's are added to the Mainframe window in OnCreate( ) handler. for knowing how to add rebar's to the window refer to 'ToolBars' section in this website. Handlers are added to all the buttons present on the rebar and CHtmlView members GoBack( ), GoForward( ), Stop( ), Refresh( ), GoHome( ), GoSearch( ) for the respective handlers.

Creating Cliparts

Many a times we need the same user defined graphic in different applications like MS-Word, MS-PowerPoint etc. Designing the same graphic again and again results in waste of time. The better idea is to save this graphic and use it whenever it is needed. We can save the graphic in a file. But this article shows a different way to save the graphic. This article shows how to and save a user created graphic in a clipart. Once saved in the clipart the user can insert it wherever and whenever he needs it. An object of CMetaFileDC is created. Using this object CMetaFileDC::CreateEnhanced( ) function is called. In the CreateEnhanced( ) function give the filename of '.emf' file which will get created as the second parameter. Draw the graphic in the metafile using the object of CMetaFileDC. Once the '.emf' file is created it can be added to the clipart by following the steps given below.

  1. Open MS-Word application

  2. Select Insert/Picture/ClipArt.

  3. In the ClipArt diaolg box, click on the 'Import Clips' button.0

  4. From the 'Add Clip Art To Clip Gallary' dialog box select '.emf' file created by your appplication. Click on Open.

  5. From the Clip Properties dialog box select the category from 'Category' list box in which you have to add your clip.