用VC++编写网络计时工具

Author: 盛超 Date: 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下通过。