同步世界标准时间
1 // TimeDlg.cpp : implementation file 2 3 #include "stdafx.h" 4 #include "Time.h" 5 #include "TimeDlg.h" 6 7 #ifdef _DEBUG 8 #define new DEBUG_NEW 9 #undef THIS_FILE 10 static char THIS_FILE[] = __FILE__; 11 #endif 12 13 ///////////////////////////////////////////////////////////////////////////// 14 #include <winsock2.h> 15 #pragma comment(lib, "wsock32") 16 #define WM_SOCKET_NOTIFY (WM_USER + 11) 17 18 char m_szIPAddr[32]={0}; 19 SOCKET m_sock; 20 ULONG m_ulTime ; 21 22 23 class CAboutDlg : public CDialog 24 { 25 public: 26 CAboutDlg(); 27 28 // Dialog Data 29 //{{AFX_DATA(CAboutDlg) 30 enum { IDD = IDD_ABOUTBOX }; 31 //}}AFX_DATA 32 33 // ClassWizard generated virtual function overrides 34 //{{AFX_VIRTUAL(CAboutDlg) 35 protected: 36 virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support 37 //}}AFX_VIRTUAL 38 39 // Implementation 40 protected: 41 //{{AFX_MSG(CAboutDlg) 42 //}}AFX_MSG 43 DECLARE_MESSAGE_MAP() 44 }; 45 46 CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) 47 { 48 //{{AFX_DATA_INIT(CAboutDlg) 49 //}}AFX_DATA_INIT 50 } 51 52 void CAboutDlg::DoDataExchange(CDataExchange* pDX) 53 { 54 CDialog::DoDataExchange(pDX); 55 //{{AFX_DATA_MAP(CAboutDlg) 56 //}}AFX_DATA_MAP 57 } 58 59 BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) 60 //{{AFX_MSG_MAP(CAboutDlg) 61 // No message handlers 62 //}}AFX_MSG_MAP 63 END_MESSAGE_MAP() 64 65 ///////////////////////////////////////////////////////////////////////////// 66 // CTimeDlg dialog 67 68 CTimeDlg::CTimeDlg(CWnd* pParent /*=NULL*/) 69 : CDialog(CTimeDlg::IDD, pParent) 70 { 71 //{{AFX_DATA_INIT(CTimeDlg) 72 //}}AFX_DATA_INIT 73 // Note that LoadIcon does not require a subsequent DestroyIcon in Win32 74 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); 75 } 76 77 void CTimeDlg::DoDataExchange(CDataExchange* pDX) 78 { 79 CDialog::DoDataExchange(pDX); 80 //{{AFX_DATA_MAP(CTimeDlg) 81 DDX_Control(pDX, IDC_TEXTOUT, m_edit); 82 DDX_Control(pDX, IDC_LIST1, m_list); 83 //}}AFX_DATA_MAP 84 } 85 86 BEGIN_MESSAGE_MAP(CTimeDlg, CDialog) 87 //{{AFX_MSG_MAP(CTimeDlg) 88 ON_WM_SYSCOMMAND() 89 ON_WM_PAINT() 90 ON_WM_QUERYDRAGICON() 91 ON_MESSAGE(WM_SOCKET_NOTIFY,OnSocketNotify) 92 ON_WM_TIMER() 93 ON_LBN_DBLCLK(IDC_LIST1, OnDblclkList1) 94 ON_WM_CTLCOLOR() 95 ON_BN_CLICKED(IDC_BUTTON1, OnButton1) 96 ON_NOTIFY(NM_DBLCLK, IDC_LIST1, OnDblclkList1) 97 ON_WM_LBUTTONDOWN() 98 //}}AFX_MSG_MAP 99 END_MESSAGE_MAP() 100 101 ///////////////////////////////////////////////////////////////////////////// 102 // CTimeDlg message handlers 103 104 BOOL CTimeDlg::OnInitDialog() 105 { 106 CDialog::OnInitDialog(); 107 108 // Add "About..." menu item to system menu. 109 110 // IDM_ABOUTBOX must be in the system command range. 111 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); 112 ASSERT(IDM_ABOUTBOX < 0xF000); 113 114 CMenu* pSysMenu = GetSystemMenu(FALSE); 115 if (pSysMenu != NULL) 116 { 117 CString strAboutMenu; 118 strAboutMenu.LoadString(IDS_ABOUTBOX); 119 if (!strAboutMenu.IsEmpty()) 120 { 121 pSysMenu->AppendMenu(MF_SEPARATOR); 122 pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); 123 } 124 } 125 126 // Set the icon for this dialog. The framework does this automatically 127 // when the application's main window is not a dialog 128 SetIcon(m_hIcon, TRUE); // Set big icon 129 SetIcon(m_hIcon, FALSE); // Set small icon 130 131 132 SetDlgItemText(IDC_TEXTOUT,"\r\n\r\n\r\n\r\n\t请选择右侧时间服务器\r\n\r\n\t然后鼠标单击校准时间\r\n\r\n\r\n"); 133 134 135 m_list.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES); 136 m_list.InsertColumn(0,"时间服务器域名",LVCFMT_LEFT,150); 137 m_list.InsertColumn(1,"IP地址",LVCFMT_CENTER,120); 138 /* 139 m_list.InsertItem(0,"time-a.nist.gov"); 140 m_list.InsertItem(1,"time-b.nist.gov"); 141 m_list.InsertItem(2,"time-a.timefreq.bldrdoc.gov"); 142 m_list.InsertItem(3,"time-b.timefreq.bldrdoc"); 143 m_list.InsertItem(4,"time-c.timefreq.bldrdoc.gov"); 144 m_list.InsertItem(5,"utcnist.colorado.edu"); 145 m_list.InsertItem(6,"time.nist.gov"); 146 m_list.InsertItem(7,"time-nw.nist.gov"); 147 m_list.InsertItem(8,"nist1.datum.com"); 148 m_list.InsertItem(9,"nist1-dc.glassey.com"); 149 m_list.InsertItem(10,"nist1-ny.glassey.com"); 150 m_list.InsertItem(11,"nist1-sj.glassey.com"); 151 m_list.InsertItem(12,"nist1.aol-ca.truetime.com"); 152 m_list.InsertItem(13,"nist1.aol-va.truetime.com"); 153 m_list.SetItemText(0,1,"129.6.15.28"); 154 m_list.SetItemText(1,1,"129.6.15.29"); 155 m_list.SetItemText(2,1,"132.163.4.101"); 156 m_list.SetItemText(3,1,"132.163.4.102"); 157 m_list.SetItemText(4,1,"132.163.4.103"); 158 m_list.SetItemText(5,1,"128.138.140.44"); 159 m_list.SetItemText(6,1,"192.43.244.18"); 160 m_list.SetItemText(7,1,"131.107.1.10"); 161 m_list.SetItemText(8,1,"66.243.43.21"); 162 m_list.SetItemText(9,1,"216.200.93.8"); 163 m_list.SetItemText(10,1,"208.184.49.9"); 164 m_list.SetItemText(11,1,"207.126.98.204"); 165 m_list.SetItemText(12,1,"207.200.81.113"); 166 m_list.SetItemText(13,1,"205.188.185.33"); 167 */ 168 m_list.InsertItem(0,"time-a.timefreq.bldrdoc.gov"); 169 m_list.InsertItem(1,"time-c.timefreq.bldrdoc.gov"); 170 m_list.SetItemText(0,1,"132.163.4.101"); 171 m_list.SetItemText(1,1,"132.163.4.103"); 172 173 174 return TRUE; // return TRUE unless you set the focus to a control 175 } 176 177 void CTimeDlg::OnSysCommand(UINT nID, LPARAM lParam) 178 { 179 if ((nID & 0xFFF0) == IDM_ABOUTBOX) 180 { 181 CAboutDlg dlgAbout; 182 dlgAbout.DoModal(); 183 } 184 else 185 { 186 CDialog::OnSysCommand(nID, lParam); 187 } 188 } 189 190 // If you add a minimize button to your dialog, you will need the code below 191 // to draw the icon. For MFC applications using the document/view model, 192 // this is automatically done for you by the framework. 193 194 void CTimeDlg::OnPaint() 195 { 196 if (IsIconic()) 197 { 198 CPaintDC dc(this); // device context for painting 199 200 SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0); 201 202 // Center icon in client rectangle 203 int cxIcon = GetSystemMetrics(SM_CXICON); 204 int cyIcon = GetSystemMetrics(SM_CYICON); 205 CRect rect; 206 GetClientRect(&rect); 207 int x = (rect.Width() - cxIcon + 1) / 2; 208 int y = (rect.Height() - cyIcon + 1) / 2; 209 210 // Draw the icon 211 dc.DrawIcon(x, y, m_hIcon); 212 } 213 else 214 { 215 CDialog::OnPaint(); 216 } 217 } 218 219 // The system calls this to obtain the cursor to display while the user drags 220 // the minimized window. 221 HCURSOR CTimeDlg::OnQueryDragIcon() 222 { 223 return (HCURSOR) m_hIcon; 224 } 225 226 void CTimeDlg::OnOK() 227 { 228 POSITION pos = m_list.GetFirstSelectedItemPosition(); 229 if(pos) 230 { 231 int nItem = m_list.GetNextSelectedItem(pos); 232 strcpy(m_szIPAddr,m_list.GetItemText(nItem,1) ); 233 } //从列表框中获取所选择的时间服务器的IP地址 234 235 CString str; str=m_szIPAddr; //判断是否选择了IP地址 236 if(str.IsEmpty()) 237 { 238 MessageBox("请选择时间服务器!","时间校正",MB_ICONEXCLAMATION); 239 return; 240 } 241 242 SetDlgItemText(IDC_TEXTOUT,""); 243 244 WSADATA WSAData; 245 ::WSAStartup (MAKEWORD(2,0), &WSAData); // Call "WSAStartup"显示szDescription字串,并简要提供了一些版本资讯。MAKEWORD(2,0)将第一个参数设定为0x0200(表示2.0版本) 246 EditPrintf ("\r\nStarted up %hs\r\n\r\n", WSAData.szDescription); 247 248 /* 第一个参数AF_INET是一个位址种类,表示此处是某种Internet位址。 249 第二个参数表示资料以资料流的形式传回,而不是以资料封包的形式传回 250 (我们需要的资料只有4个位元组长,而资料封包适用于较大的资料块)。 251 最后一个参数是一个协定,我们指定使用的Internet协定是TCP。它是RFC-868所定义的两个协定之一。 252 socket函数的传回值储存在SOCKET型态的变数中,以便后面的Socket函数的调用。 */ 253 m_sock = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP) ; 254 if (m_sock == INVALID_SOCKET) 255 { 256 EditPrintf ( "Socket 创建失败! #%i\r\n\r\n", ::WSAGetLastError ()) ; 257 ::WSACleanup () ; 258 return ; 259 } 260 EditPrintf ("Socket %i 已经成功创建!\r\n\r\n", m_sock) ; 261 262 /* Call "WSAAsyncSelect" 263 WSAAsynchSelect是另一个Windows特有的Socket函数。此函数用于避免因Internet回应过慢而造成应用程序当住。 264 在WinSock文件中,有些函数与「阻碍性(blocking)」有关。也就是说,它们不能保证立即把控制项权传回给程序。 265 WSAAsyncSelect函数强制阻碍性的函数转为非阻碍性的,即在函数执行完之前把控制项传回给程序。 266 函数的结果以讯息的形式报告给应用程序。WSAAsyncSelect函数让应用程序指定讯息和接收讯息的视窗的数值。 267 使用程序定义的一个讯息,该讯息称为WM_SOCKET_NOTIFY,最后一个参数来指定讯息发送的条件, 268 特别在连结和接收资料时(FD_CONNECT | FD_READ) */ 269 if (SOCKET_ERROR== ::WSAAsyncSelect(m_sock,GetSafeHwnd(),WM_SOCKET_NOTIFY,FD_CONNECT|FD_READ)) 270 { 271 EditPrintf ("WSAAsyncSelect 错误: #%i.\r\n\r\n",::WSAGetLastError ()); 272 ::closesocket (m_sock); 273 ::WSACleanup (); 274 return; 275 } 276 277 /* Call "connect" with IP address and time-server port 278 将sin_port设定为埠号,这里是时间协定的埠号,RFC-868显示为37。 279 但不要像我最初时那样,将此栏位设为37。当大多数数字通过Internet时, 280 结构的这个埠号栏位必须是「big endian」的,即最高的位元组排第一个。 281 Intel微处理器是little endian。幸运的是,htons(「host-to-network short」)函数使位元组翻转 282 用inet_addr函数将储存在m_szIPAddr字串中的伺服器位址转化为无正负号长整数 */ 283 static struct sockaddr_in sa ; 284 sa.sin_family = AF_INET ; //#define AF_INET 2 (internetwork: UDP, TCP, etc.) 285 sa.sin_port = ::htons (IPPORT_TIMESERVER) ; //#define IPPORT_TIMESERVER 37 286 sa.sin_addr.S_un.S_addr = ::inet_addr (m_szIPAddr) ; 287 ::connect(m_sock, (SOCKADDR*) &sa, sizeof(sa)) ; 288 289 290 /* connect函数通常已经会阻碍著后面程序的执行,这是因为连结成功以前需要花些时间。 291 然而,由于调用了WSAAsyncSelect,所以connect不会等待连结,事实上,它会立即传回SOCKET_ERROR的值。 292 并不是出现了错误,这只是表示现在还没有连线成功而已。NETTIME也不会检查这个传回值,只是调用::WSAGetLastError而已。 293 如果::WSAGetLastError传回WSAEWOULDBLOCK(即函数的执行通常要受阻,但这里并没有受阻),那就一切都还很正常。 */ 294 if (WSAEWOULDBLOCK != ::WSAGetLastError ()) 295 { 296 EditPrintf ("连接错误: #%i.\r\n\r\n", ::WSAGetLastError ()) ; 297 ::closesocket (m_sock) ; 298 ::WSACleanup () ; 299 return; 300 } 301 EditPrintf ( "正在连接: %hs...", m_szIPAddr) ; 302 303 /* 连结最终完成时,主对话框由WM_SOCKET_NOTIFY消息-NETTIME在WSAAsyncSelect函数中指定的程序自订讯息所通知。 304 lParam的低字组等于FD_CONNECT,高字组表示错误。这时的错误可能是程序不能连结到指定的伺服器。 305 NETTIME还列出了其他伺服器,供您选择,让您可以试试其他的伺服器。 306 如果一切顺利,那么NETTIME将调用recv(「receive:接收」)函数来读取资料. 307 设定了一个计时器,只是在程序视窗中显示句点,以指示程序正在执行。*/ 308 SetTimer (1, 1000, NULL); 309 310 311 } 312 313 314 void CTimeDlg::OnSocketNotify(WPARAM wp,LPARAM lp) 315 { 316 WORD wEvent = WSAGETSELECTEVENT (lp) ; // ie, LOWORD 317 WORD wError = WSAGETSELECTERROR (lp) ; // ie, HIWORD 318 319 //处理WSAAsyncSelect中指定得两种事件 FD_CONNECT | FD_READ 320 321 switch (wEvent) 322 { 323 case FD_CONNECT: 324 EditPrintf ("\r\n"); 325 326 if (wError) 327 { 328 EditPrintf ( "连接错误: #%i.", wError); 329 return ; 330 } 331 EditPrintf ( "已成功连接到时间服务器: %hs.\r\n\r\n", m_szIPAddr) ; 332 333 /* 调用recv(「receive:接收」)函数来读取资料. 334 该调用将产生WM_SOCKET_NOTIFY讯息,这时带有FD_READ的事件代码。产生一个WSAEWOULDBLOCK错误以表示函数通常受阻, 335 但这时没有受阻。理论上来说(当然这不大可能),函数至少能传回资料的一部分,然后透过再次调用以获得其余的32个位元组值。 336 (char *) &m_ulTime, 4意味著,用4个位元组来储存m_ulTime变数 337 最后一个参数MSG_PEEK表示只是读此资料,并不将其从输入伫列中删除WM_SOCKET_NOTIFY讯息 */ 338 ::recv (m_sock, (char *) &m_ulTime, 4, MSG_PEEK); 339 EditPrintf ( "正在接受数据,请稍候..."); 340 return ; 341 342 case FD_READ: 343 KillTimer (1); 344 EditPrintf ( "\r\n"); 345 346 if (wError) 347 { 348 EditPrintf ( "FD_READ 错误! #%i.", wError); 349 return ; 350 } 351 352 /* 获得其余的32个位元值,这时最后的参数是0,用于从伫列中删除WM_SOCKET_NOTIFY讯息。 353 接收的32位元的m_ulTime值是从1990年1月1日开始的0:00 UTC秒数 354 但最高顺序的位元组是第一个位元组,因此该值必须通过ntohl(「network-to-host long」)函数处理来调整位元组顺序, 355 以便Intel微处理器能够处理。然后调用ChangeSystemTime函数。 */ 356 ::recv(m_sock, (char *) &m_ulTime, 4, 0) ; 357 m_ulTime = ::ntohl (m_ulTime) ; 358 EditPrintf ( "接受的时间为从1900年1月1日起 %u 秒 \r\n", m_ulTime) ; 359 360 //改变系统时间 361 this->ChangeSystemTime() ; 362 } 363 } 364 void CTimeDlg::ChangeSystemTime() 365 { 366 FILETIME ftNew ; 367 SYSTEMTIME stOld, stNew ; 368 ::GetLocalTime (&stOld) ; //首先取得目前的本地时间 369 370 stNew.wYear = 1900 ; 371 stNew.wMonth = 1 ; 372 stNew.wDay = 1 ; 373 stNew.wHour = 0 ; 374 stNew.wMinute = 0 ; 375 stNew.wSecond = 0 ; 376 stNew.wMilliseconds = 0 ; 377 ::SystemTimeToFileTime (&stNew, &ftNew); 378 379 /* 将SYSTEMTIME结构设定为1900年1月1日午夜(0时)。 380 并将这个SYSTEMTIME结构传递给SystemTimeToFileTime,将此结构转化为FILETIME结构。 381 FILETIME实际上只是由两个32位元的DWORD一起组成64位元的整数, 382 用来表示从1601年1月1日至今间隔为100奈秒(nanosecond)的间隔数。 */ 383 384 LARGE_INTEGER li ; //64位大整数 385 li = * (LARGE_INTEGER *) &ftNew; 386 li.QuadPart += (LONGLONG) 10000000 * m_ulTime; 387 ftNew = * (FILETIME *) &li; 388 ::FileTimeToSystemTime (&ftNew, &stNew); 389 390 if (::SetSystemTime (&stNew)) //调用SetSystemTime来设定时间 391 { 392 ::GetLocalTime (&stNew); 393 this->FormatUpdatedTime (&stOld, &stNew); //最初的本地时间和新的本地时间一起传递给FormatUpdatedTime 这个函数用::GetTimeFormat函数和::GetDateFormat函数将时间转化为ASCII字串。 394 } 395 else 396 EditPrintf ("不能设置新的日期和时间!"); 397 } 398 399 void CTimeDlg::FormatUpdatedTime(SYSTEMTIME *pstOld, SYSTEMTIME *pstNew) 400 { 401 TCHAR szDateOld [64], szTimeOld [64], szDateNew [64], szTimeNew [64] ; 402 403 ::GetDateFormat (LOCALE_USER_DEFAULT, LOCALE_NOUSEROVERRIDE | DATE_SHORTDATE, 404 pstOld, NULL, szDateOld, sizeof (szDateOld)) ; 405 406 ::GetTimeFormat (LOCALE_USER_DEFAULT, LOCALE_NOUSEROVERRIDE |TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT, 407 pstOld, NULL, szTimeOld, sizeof (szTimeOld)) ; 408 409 ::GetDateFormat (LOCALE_USER_DEFAULT, LOCALE_NOUSEROVERRIDE | DATE_SHORTDATE, 410 pstNew, NULL, szDateNew, sizeof (szDateNew)) ; 411 412 ::GetTimeFormat (LOCALE_USER_DEFAULT, LOCALE_NOUSEROVERRIDE |TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT, 413 pstNew, NULL, szTimeNew, sizeof (szTimeNew)) ; 414 415 EditPrintf ( "\r\n系统的日期和时间已成功更改:" 416 "\r\n以前\t%s, %s.%03i \r\n现在\t%s, %s.%03i.", 417 418 szDateOld, szTimeOld, pstOld->wMilliseconds, 419 szDateNew, szTimeNew, pstNew->wMilliseconds) ; 420 } 421 422 void CTimeDlg::EditPrintf(TCHAR *szFormat,...) 423 { 424 TCHAR szBuffer [1024]; 425 va_list pArgList; //typedef char * va_list; 426 427 va_start (pArgList, szFormat); 428 ::wvsprintf (szBuffer, szFormat, pArgList); 429 va_end (pArgList); 430 431 m_edit.SetSel(-1,-1); //将插入光标放于最后 432 m_edit.ReplaceSel(szBuffer); 433 m_edit.ScrollWindow(0,0); //滚动到插入点 434 } 435 436 void CTimeDlg::OnTimer(UINT nIDEvent) 437 { 438 EditPrintf("."); 439 440 CDialog::OnTimer(nIDEvent); 441 } 442 443 void CTimeDlg::OnCancel() 444 { 445 ::closesocket (m_sock); 446 m_sock = 0 ; 447 ::WSACleanup () ; 448 KillTimer (1) ; 449 450 CDialog::OnCancel(); 451 } 452 453 void CTimeDlg::OnDblclkList1() 454 { 455 POSITION pos = m_list.GetFirstSelectedItemPosition(); 456 if(pos) 457 { 458 int nItem = m_list.GetNextSelectedItem(pos); 459 CString str; 460 str.Format("时间服务器%s",m_list.GetItemText(nItem,1)); 461 MessageBox(str,"校正时间",MB_ICONINFORMATION); 462 } 463 } 464 465 HBRUSH CTimeDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) 466 { 467 HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); 468 469 if (pWnd->GetDlgCtrlID() == IDC_TEXTOUT) 470 { 471 // pDC->SetBkColor(RGB(156,174,189)); 472 pDC->SetTextColor(RGB(0,0,255)); 473 // return (HBRUSH)CreateSolidBrush(RGB(156,174,189)); 474 } 475 476 477 return hbr; 478 } 479 480 void CTimeDlg::OnButton1() 481 { 482 483 CAboutDlg dlg; 484 dlg.DoModal(); 485 } 486 487 488 489 490 void CTimeDlg::OnLButtonDown(UINT nFlags, CPoint point) 491 { 492 SendMessage(WM_NCLBUTTONDOWN,HTCAPTION,0); 493 494 CDialog::OnLButtonDown(nFlags, point); 495 } 496 497 BOOL CTimeDlg::PreTranslateMessage(MSG* pMsg) 498 { 499 if(pMsg -> message == WM_KEYDOWN) 500 { 501 if(pMsg -> wParam == VK_ESCAPE) 502 return TRUE; 503 if(pMsg -> wParam == VK_RETURN) 504 return TRUE; 505 } 506 507 return CDialog::PreTranslateMessage(pMsg); 508 }
同步世界标准时间 转载