65.9K
CodeProject 正在变化。 阅读更多。
Home

世界时间

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.90/5 (23投票s)

2004年3月15日

CPOL

6分钟阅读

viewsIcon

91243

downloadIcon

5040

显示不同时区的当前时间。还演示了一些 Win32 API 编程技巧。

Sample Image - WorldTime.jpg

引言

此应用程序在系统托盘中作为图标运行,并显示世界各地不同的当前时间。工具提示还会显示设置为默认值的位置的当前时间。

当我开始在日本工作时,这个应用程序的想法就产生了。我来自印度,每次需要联系家乡的人,无论是打电话还是聊天,我都必须先弄清楚那里的时间。我发现总是要从日本当前时间减去 3.5 小时(而且我总是算错)很烦人。当我第一次创建这个应用程序时,它只显示印度的当前时间。那时我称之为“印度时间”。然后我才想到将其更改为“世界时间”。

由于此应用程序不需要显示任何窗口,所以我选择了一个 Windows 应用程序(不使用 MFC)。我不想包含像 CMainFrame 或任何 CView 派生类,因为我不会使用它们。代码中包含了一个注册表类,该类使用 stl vector 类进行枚举。

对于我们大多数人来说,Windows 应用程序的代码是一团糟,函数跨越数千行,带有大的 switch 语句。借助本文中的代码,我还想分享一些大多数使用 Win32 API 编程的人不会做的一些技巧。遵循这些规则应该可以让你摆脱仅使用 Win32 API 编程的噩梦。

关于演示程序

该程序创建一个永远不会显示的重叠窗口(我称之为隐藏窗口)和一个托盘图标。右键单击托盘图标时显示的弹出菜单是动态创建的。最初,当创建弹出菜单时,会向菜单中插入“退出”、“添加时区”和几个分隔符等项目。许多时区信息存储在注册表中。这些信息在启动时被读取、加载并排序到向量中。当用户右键单击托盘图标以显示弹出菜单时,时区信息将从向量中获取,并根据当前系统时间计算相应的时间。然后,在显示弹出菜单之前,会将这些时间添加到弹出菜单中。

弹出菜单中有一个名为“添加时区”的选项。使用此选项,用户可以添加一个新的时区。目前我没有提供删除或修改时区的选项。但通过扩展“添加时区”对话框可以轻松完成。该对话框接受地点名称以及与格林威治标准时间 (GMT) 的时间差。点击“设置”按钮后,这些信息将被添加到注册表和菜单中。

Add Timezone Dialog

菜单中的其中一个时区可以通过单击将其设置为默认时区。这意味着它将显示为托盘图标的工具提示。

使用代码

这里是对项目文件及其内容的简要描述

Stdafx.h 包含 Windows 和 C 运行时头文件。
Registry.h CRegistry 类的类声明。
Registry.cpp CRegistry 类的实现。
WorldTime.h 包含一些 #define 值和函数原型。
WorldTime.cpp 应用程序的主代码,包含 WinMain 函数。
WndFunc.h 处理主隐藏窗口和托盘图标的函数原型。
WndFunc.cpp 上述函数的函数定义。
DlgFunc.h 处理“添加时区”对话框的函数原型。
DlgFunc.cpp 上述函数的函数定义。

大多数程序员会在不使用 WndFuncDlgFunc 文件的情况下编写此程序。整个程序通常会写在 WorldTime.cpp 文件中,这最终将难以维护。我在这里所做的是将与托盘图标和对话框相关的功能逻辑上分组到单独的文件中。使用这种技术,主文件中的 switch 语句变得非常简单。

这里是处理对话框消息的过程的 switch 语句。

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;
}

这里,每个收到的消息都被委托给一个函数,从而赋予它 MFC 的感觉。您还会注意到命名空间(DlgFunc::)的使用,这使得在不同文件中拥有同名函数成为可能。DlgFunc.hWndfunc.h 文件中的函数原型都声明在一个命名空间内。在此代码中,WndFuncDlgFunc 命名空间都具有相同的 OnCommand 函数。另一个需要注意的地方是,switch 语句中的每个委托函数都具有相同的签名。这样,就可以通过使用函数指针或指向成员的指针运算符(.* 和 ->*)来完全消除 switch 语句。

关于代码的更多内容

现在让我们看一些代码中值得一提的内容。

Registry.h 文件有一个 VALUES 结构,用于在枚举注册表值时使用。该结构包含一个字符数组和一个 dword 的联合,因为注册表值只能是字符串、dword 或二进制值。该类目前不支持二进制值。

VALUES 结构有一个重载的 <(小于)运算符。这被 STL sort 算法用于对向量的内容进行排序。

要更改存储时区的注册表路径,请在 WorldTime.h 文件中更改 REG_KEY #define 常量。

WndFunc.cpp 文件中的 ShowPopupMenu 函数在用户右键单击托盘图标时被调用。该函数首先从菜单中删除所有地点条目,然后在计算当前时间后将它们重新添加。

WndFunc.cpp 文件中的 GetZoneTime 函数根据注册表中存储的传入的偏差信息返回格式化时间。实际的时间计算使用 SystemTimeToTzSpecificLocalTime API 完成,该 API 以时区信息和格林威治标准时间作为输入参数,并返回特定的本地时间。GetSystemTime API 根据系统时间给出 GMT。

WndFunc.cpp 文件中的 OnTimer 函数由计时器调用,用于更改托盘图标的工具提示。当应用程序启动或用户更改默认时区时,会设置一个间隔为 10 毫秒的计时器。此计时器事件在 OnTimer 函数中被删除,并设置一个新的间隔为 1 分钟的计时器。

我认为 DlgFunc.cpp 文件中没有太多值得一提的内容,但是接受时区信息的控件是列表框,其值根据向上/向下控件(微调按钮)发送的通知而改变。而且,列表框中的项目在获得焦点时被选中,在失去焦点时被取消选中。

© . All rights reserved.