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 |
|Registry.cpp||Implementaion of the |
|WorldTime.h||Contains a few |
#define values and function prototypes.
|WorldTime.cpp||Main code of the application which contains the |
|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
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)
DlgFunc::OnInitDialog(hDlg, message, wParam, lParam);
DlgFunc::OnCommand(hDlg, message, wParam, lParam);
DlgFunc::OnNotify(hDlg, message, wParam, lParam);
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
Wndfunc.h files have the function prototypes declared within a namespace. In this code, both the
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.
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.
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
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.
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.
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.