用VC++编写网络计时工具 盛超 2000年 第49期   我们每次在Windows中拨号上网之后,拨号程序都会最小化到系统托盘区里面。当我们把鼠标移到它的图标上时,就会显示出接收和发送的数据流量,但我们最关心的往往是我们已经上网有多长时间了以及连网费用。下面笔者将向你介绍如何用VC++编写一个有这些功能的软件,希望能对你有所帮助。   我们的程序界面如图(如^49020401a^1),当按下确定时,它会最小化到系统托盘区。将鼠标移到它的图标上,如果你已经连到了网上,就会显示连网的时间和费用; 如果你没有连网,那么就显示“未连接”。图中的三个编辑框分别显示连接时间,上网方式(有拨号上网、局域上网等)和即时费用。按下设置键,会弹出一个对话框,要求你输入每小时的收费(如^49020401b^2)。   下面我们来一步一步实现这个程序的各项功能。   用AppWizard建立一个基于对话框的MFC应用程序NetTimer。我们先将程序外观和变量定义的工作做完。   在资源编辑区内编辑对话框,最终结果如图1。设置三个静态文本的ID分别为IDC_STATIC_SHOWTIME,IDC_STATIC_METHOD和IDC_STATIC_FEE,三个编辑框的ID分别为IDC_EDIT_SHOWTIME,IDC_EDIT_METHOD和IDC_EDIT_FEE,“确定”按钮的ID是IDOK,“设置”按钮是IDC_BUTTON_SETUP。添加一个新的对话框IDD_DIALOG1,最终结果如图2,编辑框的ID号是IDC_EDIT_YUAN.   在NetTimerDlg.h的protected区添加如下变量:   CString m_strFee;   CString m_strTime;   int m_iTime;   BOOL m_bConnected;   LPDWORD flags;   在它的构造函数里分别初始化如下:   m_bConnected = FALSE;   m_iTime = 0;   m_strTime = ″″;   flags = new DWORD;   m_strFee = ″″;   首先实现程序最小化时在系统区显示图标的功能。为“确定”按钮对应的OnOk事件响应函数写入如下代码:   NOTIFYICONDATA nc;   nc.cbSize = sizeof(NOTIFYICONDATA);   nc.hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);   nc.hWnd = m_hWnd;   lstrcpy(nc.szTip,″hello″);   nc.uCallbackMessage = WM_NC;//自定义的消息   nc.uID = IDC_NC;   nc.uFlags = NIF_TIP | NIF_MESSAGE | NIF_ICON;   Shell_NotifyIcon(NIM_ADD,&nc); //在托盘区显示IDR_MAINFRAME对应的图标   ShowWindow(false);//隐藏窗口   然后在NetTimerDlg.cpp的文件头部写入:   #define WM_NC 1001   #define IDC_NC 1002   以上代码中关键的函数是Shell_NotifyIcon,第一个参数表示你要增加、修改还是删除图标;第二个参数是一个NOTIFYICONDATA结构变量对应的地址,它的主要作用是提供必要的信息,使托盘区图标能够正常工作。需要注意的一点是一定要把原先默认的CButton::OnOk();语句从函数里删除,否则程序会在每次按下回车之后就自动退出。   下面代码是双击图标时的响应函数:   void CNetTimerDlg::OnNc(WPARAM wParam,LPARAM lParam)   { switch(lParam)   { case WM_LBUTTONDBLCLK://如果是双击图标   ShowWindow(true);//显示窗口   break;   case WM_MOUSEMOVE://如果鼠标移动到了图标对应的矩形区域   NOTIFYICONDATA nc; //以下代码用来显示当前连接时间和上网费用,如果没有连接则显示“未连接”   nc.cbSize = sizeof(NOTIFYICONDATA);   nc.hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);   nc.hWnd = m_hWnd;   nc.uCallbackMessage = WM_NC;   nc.uFlags = NIF_MESSAGE | NIF_TIP | NIF_ICON;   nc.uID = IDC_NC;   if(m_bConnected)   lstrcpy(nc.szTip,m_strTime + ″\n″ + m_strFee);   else   lstrcpy(nc.szTip,″未连接″);   Shell_NotifyIcon(NIM_MODIFY,&nc);   break;}}   别忘了在NetTimerDlg.h里定义函数:   afx_msg void OnNc(WPARAM wParam,LPARAM lParam);   同时在NetTimerDlg.cpp的BEGIN_MESSAGE_MAP(CNetTimerDlg,CDialog)和BEGIN_MESSAGE_MAP(CNetTimerDlg,CDialog)之间插入ON_MESSAGE(WM_NC,OnNc),用来申明对该消息的响应函数为OnNc。   下面在CNetTimerDlg的OnInitDialog函数后部(return TRUE;语句前)加入:   SetTimer(1,1000,NULL); //设置一个时钟,编号是1,每过1秒钟响应一次。   用ClassWizard为WM_TIMER加一个响应函数,代码如下:   void CNetTimerDlg::OnTimer(UINT nIDEvent)   { m_bConnected = ::InternetGetConnectedState(flags,NULL); //判断是否连网的API   if(m_bConnected) //如果连网   OnConnected();//处理连网情况下的函数   else//如果没有连网   OnDisconnected();//处理未连网情况下的函数   CDialog::OnTimer(nIDEvent);}   以上代码中的InternetGetConnectedState是一个API函数,用来判断当前是否处于连网状态。它的第一个参数是一个指向DWORD类型的指针,用于接收上网方式的信息。第二个参数由系统保留,指定为NULL。   由于我们用到了有关网络的函数,所以在文件头部得添加一句:   #include ″afxinet.h″   接下去分别定义OnConnected和OnDisconnected函数:   void CNetTimerDlg::OnConnected()   { int hour = 0;//定义小时,分,秒   int minute = 0;   int second = 0;   if (m_iTime < 60) //如果总的时间少于60秒...   { hour = 0;   minute = 0;   second = m_iTime;}   if (m_iTime >= 60 && m_iTime < 3600)//如果大于60小于3600   { hour = 0;   minute = m_iTime / 60;   second = m_iTime - minute * 60;}   if (m_iTime >= 3600) //如果大于3600   { hour = m_iTime / 3600;   minute = (m_iTime - hour * 3600) / 60;   second = m_iTime - 60 * minute - 3600 * hour;}   m_iTime ++;//累加   m_strTime.Format(″%d小时 %d分 %d秒″,hour,minute,second); //格式化数据SetDlgItemText(IDC_EDIT_SHOWTIME,m_strTime);//显示连网时间   switch(*flags) //判断以什么方式上网   { case INTERNET_CONNECTION_MODEM://拨号上网   SetDlgItemText(IDC_EDIT_METHOD,″拨号上网″);   break;   case INTERNET_CONNECTION_LAN://通过局域网上网   SetDlgItemText(IDC_EDIT_METHOD,″通过局域网上网″);   break;   case INTERNET_CONNECTION_PROXY: //通过代理服务器   SetDlgItemText(IDC_EDIT_METHOD,″通过代理服务器上网″);   break;   case INTERNET_CONNECTION_MODEM_BUSY://Modem忙于其他   SetDlgItemText(IDC_EDIT_METHOD,″Modem正忙,但不是上网″);   break;}   double value;//以下用于显示上网费用   m_strFee = AfxGetApp() ->GetProfileString(″Fee section″,″fee″,NULL);   //得到保存在文件中的相应数据   value = atof(m_strFee);   //调用atof函数将文字信息转化为double类型   value = value * m_iTime / 3600;   m_strFee.Format(″%lf元″,value);   SetDlgItemText(IDC_EDIT_FEE,m_strFee);   //显示费用}   void CNetTimerDlg::OnDisconnected()   { SetDlgItemText(IDC_EDIT_SHOWTIME,″未连接″);//在编辑框中显示“非连接”   m_iTime = 0;}    然后分别在NetTimerDlg.h中定义如下:   public:   void OnDisconnected();   void OnConnected();   下一步为ID号为IDD_DIALOG1的对话框建立一个新类CFeeDlg,并且在NetTimerDlg.cpp头部添加:   #include ″FeeDlg.h″   然后为它的“确定”按钮添加响应函数如下:   void CFeeDlg::OnOK()   { CString text;   GetDlgItemText(IDC_EDIT_YUAN,text);//获取编辑框IDC_EDIT_YUAN中的输入:   AfxGetApp() ->WriteProfileString(″Fee section″,″fee″,text);//写入文件   CDialog::OnOK();}   然后回过头来为CNetTimerDlg的“设置”按钮添加响应函数如下:   void CNetTimerDlg::OnButtonSetup()   { CFeeDlg FeeDlg;   FeeDlg.DoModal();}   最后,别忘了做一些必要的编译设置:因为我们用到了wininet.lib库,所以别忘了在“工程/设置”菜单下的link选项卡里将“对象/库模块”一项设为Wininet.lib。   OK,最后到网上下载个漂亮的图标替代VC默认的那个图标,编译工程文件,一个属于你自己的网络计时工具就产生了!   注:本程序在Windows 98,Visual C++ 6.0下通过。