Introduction
This application runs as an icon in the system tray and displays the current time in different parts of the world. A tooltip also shows the current time in the place which is set as the default.
The idea of making this application came to me when I started work here in Japan. I come from India and everytime I needed to contact somebody back home, maybe by telephone or by chat, I had to first find out what time it was there. I found it to be annoying to always subtract 3.5 hours from the current Japan time (And I always got it wrong). When I first created this application, it showed only the current time in India. I called the program 'India Time' then. That's when I thought of changing it to 'World Time'.
Since this application didn't require any window to be displayed, I decided on a windows application (Not using MFC). I didn't want it to include classes like CMainFrame
or any CView
derivatives which I'm not going to use. A registry class in included, which uses the stl vector class for enumeration.
For most of us, the code of a windows application is a real chaos, where functions span thousands of lines with big switch statements. With the help of the code in this article I would also like to share some tips that most people programming using Win32 APIs do not do. Following these rules should get you out of the nightmare of programming using only the Win32 API.
Something about the demo program
The program creates an overlapped window which is never displayed (I call this a hidden window) and a tray icon. The popup menu to be displayed when the tray icon is right clicked is created dynamically. Initially, when the popup menu is created, some items like 'Exit', 'Add Timezone' and a couple of separators are inserted into the menu. Many timezone information are stored in the registry. This information is read, loaded and sorted into a vector at startup. When the user right clicks the tray icon to display the popup menu, the timezone information is taken from the vector and the corresponding time is calculated with respect to the current system time. These times are then added to the popup menu before it is displayed.
There is an option in the popup menu called 'Add Timezone'. Using this the user can add a new timezone. I have currently not given any options for deleting or modifying a timezone. But that can be easily done by extending the 'Add Timezone' dialog. The dialog box accepts the name of the place and the difference in time from the Greenwich Mean Time (GMT). On Clicking the 'Set' button, this information is added to the registry and the menu.
One of the timezones in the menu can be set as the default timezone by clicking on it. What this means is that it will be shown as a tooltip of the tray icon.
Using the code
Here a brief description of the projects files and their contents
Stdafx.h |
Includes the windows and C runtime header files. |
Registry.h |
Class declaration of the CRegistry class. |
Registry.cpp |
Implementaion of the CRegistry class. |
WorldTime.h |
Contains a few #define values and function prototypes. |
WorldTime.cpp |
Main code of the application which contains the WinMain function. |
WndFunc.h |
Function prototypes dealing with the main hidden window and the tray icon. |
WndFunc.cpp |
Function definitions of the above mentioned functions. |
DlgFunc.h |
Function prototypes dealing with the 'Add Timezone' dialog box. |
DlgFunc.cpp |
Function definitions of the above mentioned functions. |
Most programmers would write this propram without using the WndFunc
and DlgFunc
files. The whole program will usually be written in the WorldTime.cpp
file, which will eventually not be an easy ride to maintain. What I have done here is to logically group functionality dealing with the tray icon and the dialog box into separate files. Using this technique, the switch statements in the main file becomes very simple.
Here is the switch statement of the procedure dealing with the dialog box messages.
LRESULT CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_INITDIALOG:
DlgFunc::OnInitDialog(hDlg, message, wParam, lParam);
break;
case WM_COMMAND:
DlgFunc::OnCommand(hDlg, message, wParam, lParam);
break;
case WM_NOTIFY:
DlgFunc::OnNotify(hDlg, message, wParam, lParam);
break;
}
return 0;
}
Here each message received is delegated to a function, thereby giving it an MFC feel. You will also notice the use of namespaces (DlgFunc::
), which makes it possible to have functions with the same name in different files. The DlgFunc.h
and Wndfunc.h
files have the function prototypes declared within a namespace. In this code, both the WndFunc
and DlgFunc
namespaces have the same OnCommand
function. Another thing to notice here is that each delegate function in the switch statement has the same signature. This way it would be possible to completely eliminate the switch statement by using function pointers or pointer-to-member operators (.* and ->*).
Something more about the code
Now lets look at some things worth mentioning in the code.
The Registry.h
file has a VALUES
structure used while enumerating registry values. The structure contains a union of a character array and a dword, because a registry value can only either be a string or a dword or a binary value. The class currently does not support binary values.
The VALUES
structure has an overloaded < (less than) operator defined. This is used by the STL sort algorithm
to sort the contents of the vector.
To change the registry path for storing timezones, change the REG_KEY
#define constant in the WorldTime.h
file.
The ShowPopupMenu
function in the WndFunc.cpp
file is called whenever the user right clicks on the tray icon. This function first deletes all place entries from the menu and then adds them back after calculating the current time.
The GetZoneTime
function in the WndFunc.cpp
file returns the formatted time based on the passed in bias information stored in the registry. The actual time calculation is done using the SystemTimeToTzSpecificLocalTime
API which takes the timezone information and the Greenwich Mean Time as input parameters and returns the specific local time. The GetSystemTime
API gives the GMT based on the system time.
The OnTimer
function in the WndFunc.cpp
file is called by a timer to change the tooltip of the tray icon. When the application is started or when the user changes the default timezone, a timer of 10 milliseconds interval is set. This timer event is deleted within the OnTimer
function and a new timer of 1 minute interval is set.
I guess there is nothing worth mentioning in the DlgFunc.cpp
file, but the controls accepting timezone information are list boxes whose values are changed depending on the notification sent by the updown control (spin button). And also the items in the list boxes are selected when they get focus and deselected when they loose focus.